OAuth с проверкой в ​​.NET

103

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

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

Пока что пробовал:

Первый (DotNetOpenAuth), похоже, мог бы сделать то, что мне нужно, если бы я часами пытался понять, как это сделать. Второй и третий, насколько я могу судить, не поддерживают коды подтверждения, которые Mendeley отправляет обратно - хотя я могу ошибаться в этом :)

У меня есть потребительский ключ и секрет от Mendeley, и с DotNetOpenAuth мне удалось запустить браузер со страницей Mendeley, предоставляющей проверочный код для входа пользователя в приложение. Однако в этот момент я заблудился и не мог понять, как разумно вернуть это приложению.

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

Джон
источник

Ответы:

183

Я согласен с тобой. Классы поддержки 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-запросы с аутентификацией oauth в Twitter и его родственные службы (например, TwitPic). Для этого вы должны пройти процесс утверждения, предполагающий явное одобрение со стороны пользователя. Это происходит только при первом запуске приложения. Как это:

    • запросить «токен запроса». Также известен как временный токен.
    • открыть веб-страницу, передав этот токен запроса в качестве параметра запроса. Эта веб-страница представляет пользовательский интерфейс пользователю с вопросом: «Вы хотите предоставить доступ к этому приложению?»
    • пользователь входит на веб-страницу Twitter и разрешает или запрещает доступ.
    • откроется страница ответа html. Если пользователь предоставил доступ, отображается PIN-код шрифтом размером 48 пунктов.
    • теперь пользователю нужно вырезать / вставить эту булавку в окно формы Windows и нажать «Далее» или что-то подобное.
    • затем настольное приложение выполняет запрос с аутентификацией oauth для «токена доступа». Еще один запрос REST.
    • настольное приложение получает «токен доступа» и «секрет доступа».

После танца утверждения настольное приложение может просто использовать специфичный для пользователя «токен доступа» и «секрет доступа» (вместе с специфичными для приложения «ключом потребителя» и «секретом потребителя») для выполнения аутентифицированных запросов от имени пользователя. в Twitter. Срок их действия не истекает, хотя, если пользователь деавторизует приложение, или если Twitter по какой-то причине деавторизует ваше приложение, или если вы потеряете свой токен доступа и / или секрет, вам придется повторить танец утверждения снова. .


Если вы не умны, поток пользовательского интерфейса может как бы отражать многоступенчатый поток сообщений OAuth. Существует лучший способ.

Используйте элемент управления WebBrowser и откройте веб-страницу авторизации в настольном приложении. Когда пользователь нажимает «Разрешить», берет текст ответа из этого элемента управления WebBrowser, автоматически извлекает PIN-код, а затем получает токены доступа. Вы отправляете 5 или 6 HTTP-запросов, но пользователю нужно видеть только одно диалоговое окно «Разрешить / запретить». Просто.

Как это:
альтернативный текст


Если у вас есть отсортированный пользовательский интерфейс, единственная проблема, которая остается, - это создание запросов с подписью 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-адрес: « Класс диспетчера OAuth делает это за вас автоматически. Он автоматически генерирует одноразовые номера, временные метки, версии и подписи - вашему приложению не нужно заботиться об этом или знать об этом. Просто установите значения параметра oauth и сделать простой вызов метода. класс менеджера отправляет запрос и анализирует ответ за вас. https://api.twitter.com/oauth/request_token . В спецификации oauth говорится, что вам нужно упаковать набор параметров oauth (токен, token_secret, nonce, timestamp, consumer_key, version и callback) определенным образом (в кодировке url и соединенных амперсандами) и лексикографически: отсортированный, сгенерируйте подпись для этого результата, затем упакуйте те же параметры вместе с подписью, хранящейся в новом параметре oauth_signature, другим способом (соединенным запятыми).

Хорошо, тогда что? Как только вы получите токен запроса, вы откроете пользовательский интерфейс веб-браузера, в котором пользователь явно предоставит одобрение. Если вы сделаете это правильно, вы откроете это во встроенном браузере. Для 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 заставляет элемент управления WebBrowser автоматически переходить на эту страницу.

Когда пользователь нажимает кнопку «Разрешить», будет загружена новая страница. Это 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-сообщения вместе с этим контактом. Это еще один подписанный вызов 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 с именем Authorization .

Для взаимодействия со сторонними сервисами, такими как 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 Form, в которой используется этот менеджер здесь .


РАБОЧИЙ ПРИМЕР

Загрузите рабочий пример инструмента командной строки, который использует класс и технику, описанные здесь:

Cheeso
источник
Привет, большое спасибо за ответ! Я фактически перешел от OAuth (я отказался от Mendeley и выбрал альтернативу), но я прочитал ваш ответ, и он имел большой смысл и очень всеобъемлющ. Я также добавил в закладки записанный вами курс на случай, если он мне понадобится в будущем! Еще раз большое спасибо.
Джон
2
Привет, Чизо, спасибо, что поделились своим кодом и подробным объяснением. Вы предоставили отличное, но простое решение. Однако вы захотите внести небольшое изменение в свой метод GetSignatureBase, чтобы поддерживать решения, отличные от «oob». Если вы не "oob", вам нужно URL-кодировать обратный вызов, поэтому вы захотите добавить что-то подобное, когда будете повторять this._params: if (p1.Key == "callback") {p.Add ( «oauth_» + p1.Key, UrlEncode (p1.Value)); continue;}
Джонни Ошика
1
Это не работает для OAuth 2.0. Этот класс предназначен для OAuth 1.0a. OAuth2.0 значительно проще в использовании, так как нет подписи и лексикографической сортировки различных параметров. Так что вам, вероятно, не нужен внешний класс для OAuth 2.0, или ... если вам действительно нужен внешний класс, он будет намного проще, чем этот.
Cheeso
1
файл справки в Интернете не найден: cheeso.members.winisp.net/OAuthManager1.1
Kiquenet 05
3
Все ссылки не работают. Я нашел копию здесь: gist.github.com/DeskSupport/2951522#file-oauth-cs
Джон