OAuth з верифікацією в .NET


103

Я намагаюся створити клієнтський додаток на основі .NET (у WPF - хоча зараз я це роблю лише як консольний додаток) для інтеграції з програмою, що підтримує OAuth, зокрема Mendeley ( http: // dev .mendeley.com ), який, очевидно, використовує 3-ногу OAuth.

Це вперше я використовую OAuth, і у мене виникають великі труднощі з початком роботи з ним. Я знайшов кілька .NET OAuth бібліотек або помічників, але вони здаються складнішими, ніж я думаю, що мені потрібно. Все, що я хочу зробити, - це можливість видати REST-запити в API Mendeley і отримати відповіді!

Поки я намагався:

Перший (DotNetOpenAuth) здається, що він міг би зробити те, що мені потрібно, якби я витратив години та години, намагаючись розібратися, як це зробити. Другий і третій, як я можу сказати, не підтримують верифікаційні коди, які Менделі надсилає назад, хоча я можу помилитися з цього приводу :)

У мене з'явився споживчий ключ та секрет від Менделі, і за допомогою DotNetOpenAuth мені вдалося запустити браузер зі сторінкою Менделі, який надає код підтвердження для входу користувача в програму. Однак у цей момент я загубився і не зміг розібратися, як розумно повернути це до програми.

Я дуже хочу визнати, що я не маю уявлення, з чого почати з цього (хоча, здається, існує досить крута крива навчання) - якщо хтось може вказати мені в правильному напрямку, я би вдячний!

Відповіді:


182

Я погоджуюсь з тобою. Класи підтримки відкритого коду OAuth, доступні для додатків .NET, важко зрозуміти, надмірно складні (скільки методів піддається DotNetOpenAuth?), Погано розроблені (дивіться методи з 10 параметрами рядків у модулі OAuthBase.cs з цього google надане вами посилання - управління державою взагалі не існує) або інакше незадовільне.

Це не повинно бути таким складним.

Я не фахівець з OAuth, але я створив клас менеджера OAuth на стороні клієнта, який успішно використовую у Twitter та TwitPic. Це порівняно просто у використанні. Це відкритий код і доступний тут: Oauth.cs

Для перегляду, в OAuth 1.0a ... якось смішно, є спеціальна назва, і це схоже на "стандарт", але, наскільки я знаю, єдиний сервіс, який реалізує "OAuth 1.0a" - це Twitter. Я думаю, що це досить стандартно . Ок, так чи інакше в OAuth 1.0a, так це працює для настільних додатків :

  1. Ви, розробник програми, зареєструєте додаток та отримаєте "споживчий ключ" та "споживчу таємницю". У компанії Arstechnica є добре написаний аналіз того, чому ця модель не найкраща , але, як кажуть, вона є такою, яка вона є .

  2. Ваш додаток працює. Перший раз, коли він запускається, йому потрібно дозволити користувачеві явно надати дозвіл на додаток для подання аутентифікованих на аутентифікованих REST запитів до Twitter та його сестринських служб (наприклад, TwitPic). Для цього необхідно пройти процедуру затвердження, що передбачає явне схвалення користувачем. Це відбувається лише в перший раз, коли програма запускається. Подобається це:

    • запит "маркер запиту". Ака тимчасовий жетон.
    • відкрийте веб-сторінку, передаючи маркер цього запиту як параметр запиту. Ця веб-сторінка представляє користувачеві користувальницький інтерфейс, запитуючи "ви хочете надати доступ до цієї програми?"
    • користувач заходить на веб-сторінку twitter і надає або забороняє доступ.
    • З'являється сторінка html-відповіді. Якщо користувач надав доступ, PIN-код відображатиметься шрифтом 48 pt
    • тепер користувачеві потрібно вирізати / вставити цей штифт у вікно форми Windows і натиснути «Далі» або щось подібне.
    • тоді настільний додаток робить запит, підтверджений аутентичністю, на "маркер доступу". Ще один REST-запит.
    • настільний додаток отримує "маркер доступу" та "секрет доступу".

Після танцю схвалення настільний додаток може просто використовувати специфічний для користувача "маркер доступу" та "секрет доступу" (разом із специфічним для програми "споживчим ключем" та "секретом споживача"), щоб робити запити, що підтверджуються автентичністю від імені користувача у Twitter. Вони не закінчуються, хоча якщо користувач скасовує додаток, або Twitter з якоїсь причини деавторує ваш додаток, або якщо ви втрачаєте маркер доступу та / або секрет, вам потрібно буде повторити танець схвалення .


Якщо ви не розумні, потік інтерфейсу може віддзеркалити багатоступеневий потік повідомлень OAuth. Є кращий спосіб.

Використовуйте керування WebBrowser та відкрийте веб-сторінку авторизації в додатку для настільних ПК. Коли користувач натискає "Дозволити", захопіть текст відповіді з цього елемента управління WebBrowser, витягніть PIN-код автоматично, а потім отримайте маркери доступу. Ви надсилаєте 5 або 6 HTTP-запитів, але користувачеві потрібно бачити лише одне діалогове вікно Дозволити / Заборонити. Простий.

Подобається це:
alt текст


Якщо у вас впорядковано інтерфейс користувача, єдине завдання, яке залишається, - це створення запитів, підписаних oauth. Це подорожує багато людей, оскільки вимоги до підписання oauth є певними. Ось що робить спрощений клас OAuth Manager.

Приклад коду для запиту маркера:

var oauth = new OAuth.Manager();
// the URL to obtain a temporary "request token"
var rtUrl = "https://api.twitter.com/oauth/request_token";
oauth["consumer_key"] = MY_APP_SPECIFIC_KEY;
oauth["consumer_secret"] = MY_APP_SPECIFIC_SECRET;    
oauth.AcquireRequestToken(rtUrl, "POST");

ЦЕ ТАК . Простий. Як видно з коду, спосіб дістатися до параметрів oauth - це за допомогою рядкового індексатора - щось на зразок словника. Метод AcquireRequestToken надсилає запит, підписаний oauth, на URL сервісу, який надає маркер запиту, також тимчасові маркери. Для Twitter ця URL-адреса - " https://api.twitter.com/oauth/request_token ". Специфікація oauth говорить, що вам потрібно спакувати набір параметрів oauth (токен, token_secret, ніколи, часова марка, споживча_відочка, версія та зворотний виклик), певним чином (кодований URL-адресою та приєднаний амперсандами), і лексикографічно- сортуйте порядок, генеруйте підпис за цим результатом, потім запакуйте ті самі параметри разом із підписом, що зберігається в новому параметрі oauth_signature, по-іншому (об'єднані комами). Клас менеджера OAuth робить це для вас автоматично. Він генерує терміни та часові позначки, версії та підписи автоматично - вашому додатку не потрібно дбати про це і не знати про це. Просто встановіть значення параметра oauth та зробіть простий виклик методу. клас менеджера надсилає запит і аналізує відповідь за вас.

Гаразд, що тоді? Як тільки ви отримаєте маркер запиту, ви відкриєте інтерфейс веб-браузера, в якому користувач явно надасть схвалення. Якщо ви зробите це правильно, ви викладете це у вбудований веб-переглядач. Для Twitter URL для цього " https://api.twitter.com/oauth/authorize?oauth_token= " із доданим oauth_token. Зробіть це в коді так:

var url = SERVICE_SPECIFIC_AUTHORIZE_URL_STUB + oauth["token"];
webBrowser1.Url = new Uri(url);

(Якщо ви робили це у зовнішньому браузері, який ви використовували б System.Diagnostics.Process.Start(url).)

Встановлення властивості Url змушує керування веб-браузером автоматично переходити до цієї сторінки.

Коли користувач натисне кнопку "Дозволити", буде завантажена нова сторінка. Це форма HTML, і вона працює так само, як у повному браузері. У своєму коді зареєструйте обробник для події DocumentedCompleted управління WebBrowser і в цьому оброблювачі візьміть шпильку:

var divMarker = "<div id=\"oauth_pin\">"; // the div for twitter's oauth pin
var index = webBrowser1.DocumentText.LastIndexOf(divMarker) + divMarker.Length;
var snip = web1.DocumentText.Substring(index);
var pin = RE.Regex.Replace(snip,"(?s)[^0-9]*([0-9]+).*", "$1").Trim();

Це трохи вискоблювання екрана HTML.

Після захоплення шпильки вам більше не потрібен веб-браузер, тому:

webBrowser1.Visible = false; // all done with the web UI

... і ви можете також зателефонувати на Dispose ().

Наступним кроком є ​​отримання маркера доступу шляхом надсилання іншого HTTP-повідомлення разом із цим PIN-кодом. Це ще один підписаний виклик oauth, побудований з упорядкуванням та форматуванням oauth, описаним мною вище. Але ще раз це дуже просто з класом OAuth.Manager:

oauth.AcquireAccessToken(URL_ACCESS_TOKEN,
                         "POST",
                         pin);

Для Twitter ця URL-адреса - " https://api.twitter.com/oauth/access_token ".

Тепер у вас є маркери доступу, і ви можете використовувати їх у підписаних HTTP-запитах. Подобається це:

var authzHeader = oauth.GenerateAuthzHeader(url, "POST");

... де urlкінцева точка ресурсу. Щоб оновити статус користувача, це було б " http://api.twitter.com/1/statuses/update.xml?status=Hello ".

Потім встановіть цей рядок у заголовку HTTP під назвою Авторизація .

Для взаємодії з сторонніми службами, такими як TwitPic, вам потрібно побудувати трохи інший заголовок OAuth, як це:

var authzHeader = oauth.GenerateCredsHeader(URL_VERIFY_CREDS,
                                            "GET",
                                            AUTHENTICATION_REALM);

Для Twitter значеннями URL-адреси та області перевірки кредитів є " https://api.twitter.com/1/account/verify_credentials.json " та " http://api.twitter.com/ " відповідно.

... і покладіть цей рядок авторизації у заголовку HTTP під назвою X-Verify-Credentials-Authorization . Потім надішліть це до своєї служби, як-от TwitPic, разом із будь-яким запитом, який ви надсилаєте.

Це воно.

Все разом код для оновлення статусу твітера може бути приблизно таким:

// the URL to obtain a temporary "request token"
var rtUrl = "https://api.twitter.com/oauth/request_token";
var oauth = new OAuth.Manager();
// The consumer_{key,secret} are obtained via registration
oauth["consumer_key"] = "~~~CONSUMER_KEY~~~~";
oauth["consumer_secret"] = "~~~CONSUMER_SECRET~~~";
oauth.AcquireRequestToken(rtUrl, "POST");
var authzUrl = "https://api.twitter.com/oauth/authorize?oauth_token=" + oauth["token"];
// here, should use a WebBrowser control. 
System.Diagnostics.Process.Start(authzUrl);  // example only!
// instruct the user to type in the PIN from that browser window
var pin = "...";
var atUrl = "https://api.twitter.com/oauth/access_token";
oauth.AcquireAccessToken(atUrl, "POST", pin);

// now, update twitter status using that access token
var appUrl = "http://api.twitter.com/1/statuses/update.xml?status=Hello";
var authzHeader = oauth.GenerateAuthzHeader(appUrl, "POST");
var request = (HttpWebRequest)WebRequest.Create(appUrl);
request.Method = "POST";
request.PreAuthenticate = true;
request.AllowWriteStreamBuffering = true;
request.Headers.Add("Authorization", authzHeader);

using (var response = (HttpWebResponse)request.GetResponse())
{
    if (response.StatusCode != HttpStatusCode.OK)
        MessageBox.Show("There's been a problem trying to tweet:" +
                        Environment.NewLine +
                        response.StatusDescription);
}

OAuth 1.0a начебто складний під кришками, але використовувати його не потрібно. OAuth.Manager обробляє генерацію вихідних запитів oauth та отримання та обробку вмісту oauth у відповідях. Коли запит Request_token надає вам oauth_token, вашій програмі його не потрібно зберігати. Oauth.Manager досить розумний, щоб зробити це автоматично. Так само, коли запит access_token повертає маркер доступу та секрет, їх не потрібно явно зберігати. OAuth.Manager обробляє цей стан для вас.

У наступних запусках, коли у вас уже є маркер доступу та секрет, ви можете створити OAuth.Manager таким чином:

var oauth = new OAuth.Manager();
oauth["consumer_key"] = CONSUMER_KEY;
oauth["consumer_secret"] = CONSUMER_SECRET;
oauth["token"] = your_stored_access_token;
oauth["token_secret"] = your_stored_access_secret;

... а потім генеруйте заголовки авторизації, як зазначено вище.

// now, update twitter status using that access token
var appUrl = "http://api.twitter.com/1/statuses/update.xml?status=Hello";
var authzHeader = oauth.GenerateAuthzHeader(appUrl, "POST");
var request = (HttpWebRequest)WebRequest.Create(appUrl);
request.Method = "POST";
request.PreAuthenticate = true;
request.AllowWriteStreamBuffering = true;
request.Headers.Add("Authorization", authzHeader);

using (var response = (HttpWebResponse)request.GetResponse())
{
    if (response.StatusCode != HttpStatusCode.OK)
        MessageBox.Show("There's been a problem trying to tweet:" +
                        Environment.NewLine +
                        response.StatusDescription);
}

Ви можете завантажити DLL, що містить клас OAuth.Manager, тут . У цьому завантаженні також є файл довідки. Або ви можете переглянути файл довідки в Інтернеті .

Дивіться приклад форми Windows, яка використовує цей менеджер тут .


РОБОЧИЙ ПРИКЛАД

Завантажте робочий приклад інструменту командного рядка, який використовує описаний тут клас та техніку:


Привіт, дуже дякую за вашу відповідь! Я фактично перейшов від OAuth (я відмовився від Менделі і пішов з альтернативою) - але я прочитав вашу відповідь, і це мало сенс і є дуже вичерпним. Я також зробив закладки класу, який ви написали, для будь-якого майбутнього часу, мені це може знадобитися! Ще раз дякую.
Іван

2
Привіт Чесо, дякую за те, що поділився кодом та детальним поясненням. Ви запропонували чудове, але просте рішення. Однак ви хочете внести невелику зміну у ваш метод GetSignatureBase, щоб підтримати рішення, що не стосуються «oob». Якщо не "oob", вам потрібно кодувати URL-адресу зворотного дзвінка, тому ви хочете додати щось подібне під час ітерації через this._params: if (p1.Key == "callback") {p.Add ( "oauth_" + p1.Key, UrlEncode (p1.Value)); продовжити;}
Джонні Ошика

1
Це не працює для OAuth 2.0. Цей клас призначений для OAuth 1.0a. OAuth2.0 значно простіший у використанні, оскільки немає підписання та лексикографічного сортування різних параметрів. Тож вам, мабуть, не потрібен зовнішній клас, щоб робити OAuth 2.0, або ... якщо вам потрібен зовнішній клас, він буде набагато простішим, ніж цей.
Cheeso


3
Здається, всі посилання порушені. Я знайшов тут копію: gist.github.com/DeskSupport/2951522#file-oauth-cs
Джон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.