OAuth z weryfikacją w .NET

103

Próbuję utworzyć aplikację kliencką opartą na .NET (w WPF - chociaż na razie robię to tylko jako aplikacja konsolowa), aby zintegrować się z aplikacją obsługującą OAuth, w szczególności z Mendeley ( http: // dev .mendeley.com ), który najwyraźniej korzysta z trzyetapowej autoryzacji OAuth.

Używam OAuth po raz pierwszy i mam wiele trudności z rozpoczęciem korzystania z niego. Znalazłem kilka bibliotek lub pomocników .NET OAuth, ale wydają się one być bardziej skomplikowane, niż myślę, że potrzebuję. Chcę tylko móc wysyłać żądania REST do Mendeley API i otrzymywać odpowiedzi!

Do tej pory próbowałem:

Pierwsza (DotNetOpenAuth) wydaje się, że mogłaby zrobić to, czego potrzebowałem, jeśli spędziłem wiele godzin próbując dowiedzieć się, jak to zrobić. Druga i trzecia, o ile wiem, nie obsługują kodów weryfikacyjnych, które odsyła Mendeley - chociaż mogę się co do tego mylić :)

Otrzymałem klucz konsumenta i sekret od Mendeleya, a dzięki DotNetOpenAuth udało mi się uruchomić przeglądarkę ze stroną Mendeley zawierającą kod weryfikacyjny do wprowadzenia przez użytkownika do aplikacji. Jednak w tym momencie zgubiłem się i nie mogłem wymyślić, jak rozsądnie przekazać to z powrotem do aplikacji.

Bardzo chętnie przyznam, że nie mam pojęcia, od czego zacząć (chociaż wydaje się, że jest to dość stroma krzywa uczenia się) - jeśli ktoś może wskazać mi właściwy kierunek, będę wdzięczny!

Jan
źródło

Odpowiedzi:

182

Zgadzam się z Tobą. Klasy obsługi OAuth typu open source dostępne dla aplikacji .NET są trudne do zrozumienia, zbyt skomplikowane (ile metod jest udostępnianych przez DotNetOpenAuth?), Źle zaprojektowane (spójrz na metody z 10 parametrami ciągu w module OAuthBase.cs z tego google link, który podałeś - nie ma w ogóle zarządzania stanem) lub w inny sposób niezadowalający.

To nie musi być takie skomplikowane.

Nie jestem ekspertem w dziedzinie OAuth, ale stworzyłem klasę menedżera po stronie klienta OAuth, której z powodzeniem używam w Twitterze i TwitPic. Jest stosunkowo prosty w użyciu. Jest open source i jest dostępny tutaj: Oauth.cs

Do przejrzenia, w OAuth 1.0a ... trochę zabawne, jest specjalna nazwa i wygląda jak „standardowa”, ale o ile wiem, jedyną usługą, która implementuje „OAuth 1.0a” jest Twitter. Myślę, że to wystarczająco standardowe . ok, w każdym razie w OAuth 1.0a, sposób, w jaki działa dla aplikacji komputerowych, jest taki:

  1. Ty, twórca aplikacji, rejestrujesz aplikację i uzyskujesz „klucz klienta” i „sekret klienta”. Na Arstechnicy jest dobrze napisana analiza, dlaczego ten model nie jest najlepszy , ale jak mówią, jest tym, czym jest .

  2. Twoja aplikacja działa. Przy pierwszym uruchomieniu musi skłonić użytkownika do wyraźnego zatwierdzenia aplikacji do wysyłania uwierzytelnionych przez Oauth żądań REST do Twittera i jego usług siostrzanych (takich jak TwitPic). Aby to zrobić, musisz przejść przez proces zatwierdzania, obejmujący wyraźne zatwierdzenie przez użytkownika. Dzieje się tak tylko przy pierwszym uruchomieniu aplikacji. Lubię to:

    • zażądać „tokena żądania”. Aka tymczasowy token.
    • pop stronę internetową, przekazując ten token żądania jako parametr zapytania. Ta strona internetowa przedstawia interfejs użytkownika użytkownikowi, pytając „czy chcesz przyznać dostęp do tej aplikacji?”
    • użytkownik loguje się na stronie internetowej Twittera i udziela lub odmawia dostępu.
    • pojawi się strona odpowiedzi HTML. Jeśli użytkownik przyznał dostęp, jest wyświetlany kod PIN w 48-punktowej czcionce
    • użytkownik musi teraz wyciąć / wkleić ten pin do okna formularza i kliknąć „Dalej” lub coś podobnego.
    • aplikacja komputerowa wysyła następnie uwierzytelnione przez OAuth żądanie „tokena dostępu”. Kolejne żądanie REST.
    • aplikacja komputerowa otrzymuje „token dostępu” i „klucz dostępu”.

Po zatwierdzeniu aplikacja komputerowa może po prostu użyć „tokena dostępu” i „klucza dostępu” specyficznego dla użytkownika (wraz z „kluczem klienta” i „kluczem klienta”) do wykonywania uwierzytelnionych żądań w imieniu użytkownika. do Twittera. Nie wygasają, chociaż jeśli użytkownik cofnie autoryzację aplikacji lub jeśli Twitter z jakiegoś powodu cofnie autoryzację Twojej aplikacji lub jeśli utracisz token dostępu i / lub sekret, będziesz musiał ponownie wykonać taniec zatwierdzenia .


Jeśli nie jesteś sprytny, przepływ interfejsu użytkownika może odzwierciedlać wieloetapowy przepływ komunikatów OAuth. Jest lepszy sposób.

Użyj formantu WebBrowser i otwórz autoryzowaną stronę internetową w aplikacji komputerowej. Gdy użytkownik kliknie „Zezwalaj”, pobierz tekst odpowiedzi z tej kontrolki WebBrowser, wyodrębnij kod PIN automatycznie, a następnie uzyskaj tokeny dostępu. Wysyłasz 5 lub 6 żądań HTTP, ale użytkownik musi zobaczyć tylko jedno okno dialogowe Zezwalaj / Odmów. Prosty.

Lubię to:
tekst alternatywny


Jeśli masz już uporządkowany interfejs użytkownika, jedynym wyzwaniem, które pozostaje, jest tworzenie żądań podpisanych przez OAuth. To denerwuje wiele osób, ponieważ wymagania dotyczące podpisywania OAuth są specyficzne. To właśnie robi uproszczona klasa OAuth Manager.

Przykładowy kod do żądania tokena:

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");

TO JEST TO . Prosty. Jak widać z kodu, sposobem na uzyskanie parametrów oauth jest indeksator oparty na ciągach znaków, coś w rodzaju słownika. Metoda AcquireRequestToken wysyła żądanie podpisane przez OAuth na adres URL usługi, która przyznaje tokeny żądań, czyli tokeny tymczasowe. W przypadku Twittera ten adres URL to „ https://api.twitter.com/oauth/request_token ”. Specyfikacja oauth mówi, że musisz spakować zestaw parametrów oauth (token, token_secret, nonce, timestamp, Consumer_key, version i callback), w określony sposób (zakodowany w adresie URL i połączony znakami ampersand) oraz w postaci leksykograficznej posortowaną kolejność, wygeneruj podpis na tym wyniku, a następnie spakuj te same parametry wraz z podpisem, zapisane w nowym parametrze oauth_signature, w inny sposób (połączone przecinkami). Klasa menedżera OAuth robi to automatycznie. Automatycznie generuje wartości jednorazowe i znaczniki czasu oraz wersje i podpisy - Twoja aplikacja nie musi się tym przejmować ani być tego świadomym. Po prostu ustaw wartości parametru oauth i wykonaj proste wywołanie metody. klasa menedżera wysyła żądanie i analizuje odpowiedź za Ciebie.

Ok, więc co? Po otrzymaniu tokena żądania otwierasz interfejs użytkownika przeglądarki internetowej, w którym użytkownik wyraźnie udzieli zgody. Jeśli zrobisz to dobrze, umieścisz to we wbudowanej przeglądarce. W przypadku Twittera adres URL to „ https://api.twitter.com/oauth/authorize?oauth_token= ” z dołączonym oauth_token. Zrób to w kodzie w następujący sposób:

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

(Jeśli robisz to w zewnętrznej przeglądarce, z której korzystasz System.Diagnostics.Process.Start(url)).

Ustawienie właściwości Url powoduje, że formant WebBrowser automatycznie przechodzi do tej strony.

Gdy użytkownik kliknie przycisk „Zezwól”, zostanie załadowana nowa strona. Jest to formularz HTML i działa tak samo jak w pełnej przeglądarce. W swoim kodzie zarejestruj procedurę obsługi dla zdarzenia DocumentedCompleted formantu WebBrowser, a w tym module obsługi weź pin:

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();

To trochę skrobania ekranu HTML.

Po złapaniu pinezki nie potrzebujesz już przeglądarki internetowej, więc:

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

... i możesz również chcieć wywołać na nim Dispose ().

Następnym krokiem jest uzyskanie tokena dostępu poprzez wysłanie kolejnej wiadomości HTTP wraz z tym pinem. Jest to kolejne wywołanie Oauth ze podpisem, skonstruowane przy użyciu kolejności i formatowania Oauth, które opisałem powyżej. Ale po raz kolejny jest to naprawdę proste w przypadku klasy OAuth.Manager:

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

W przypadku Twittera ten adres URL to „ https://api.twitter.com/oauth/access_token ”.

Teraz masz tokeny dostępu i możesz ich używać w podpisanych żądaniach HTTP. Lubię to:

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

... gdzie urljest punkt końcowy zasobu. Aby zaktualizować stan użytkownika, będzie to „ http://api.twitter.com/1/statuses/update.xml?status=Hello ”.

Następnie ustaw ten ciąg w nagłówku HTTP o nazwie Authorization .

Aby korzystać z usług innych firm, takich jak TwitPic, musisz utworzyć nieco inny nagłówek OAuth, na przykład:

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

W przypadku Twittera wartości adresu URL i obszaru weryfikacji danych uwierzytelniających to odpowiednio „ https://api.twitter.com/1/account/verify_credentials.json ” i „ http://api.twitter.com/ ”.

... i umieść ten ciąg autoryzacji w nagłówku HTTP o nazwie X-Verify-Credentials-Authorization . Następnie wyślij to do swojej usługi, takiej jak TwitPic, wraz z każdym wysyłanym żądaniem.

Otóż ​​to.

Podsumowując, kod aktualizujący status na Twitterze może wyglądać mniej więcej tak:

// 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 jest trochę skomplikowany pod osłonami, ale używanie go nie musi. Menedżer OAuth.Manager obsługuje generowanie wychodzących żądań OAuth oraz odbieranie i przetwarzanie treści OAuth w odpowiedziach. Gdy żądanie Request_token daje oauth_token, aplikacja nie musi go przechowywać. Oauth.Manager jest wystarczająco inteligentny, aby robić to automatycznie. Podobnie, gdy żądanie access_token zwraca token dostępu i klucz tajny, nie trzeba ich jawnie przechowywać. OAuth.Manager obsługuje ten stan za Ciebie.

W kolejnych uruchomieniach, gdy masz już token dostępu i hasło, możesz utworzyć wystąpienie OAuth.Manager w następujący sposób:

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;

... a następnie wygeneruj nagłówki autoryzacji jak powyżej.

// 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);
}

Możesz pobrać bibliotekę DLL zawierającą klasę OAuth.Manager tutaj . W tym pobieraniu znajduje się również plik pomocy. Lub możesz przejrzeć plik pomocy online .

Zobacz przykład formularza systemu Windows, który używa tego menedżera tutaj .


PRZYKŁAD DZIAŁANIA

Pobierz działający przykład narzędzia wiersza poleceń, które wykorzystuje klasę i technikę opisaną tutaj:

Cheeso
źródło
Cześć, bardzo dziękuję za odpowiedź! Właściwie odszedłem od OAuth (zrezygnowałem z Mendeleya i wybrałem alternatywę) - ale przeczytałem twoją odpowiedź i miała ona dużo sensu i jest bardzo wszechstronna. Dodałem też zakładkę do zajęć, które napisałeś, aby móc ich potrzebować w przyszłości! Jeszcze raz wielkie dzięki.
John
2
Cześć Cheeso, dziękujemy za udostępnienie kodu i szczegółowe wyjaśnienie. Zapewniłeś świetne, ale proste rozwiązanie. Jednak będziesz chciał wprowadzić niewielką zmianę w metodzie GetSignatureBase, aby obsługiwać rozwiązania inne niż „oob”. W przypadku braku „oob” musisz zakodować URL wywołanie zwrotne, więc będziesz chciał dodać coś takiego podczas iteracji przez this._params: if (p1.Key == „callback”) {p.Add ( "oauth_" + p1.Key, UrlEncode (p1.Value)); continue;}
Johnny Oshika
1
To nie działa w przypadku OAuth 2.0. Ta klasa jest przeznaczona dla protokołu OAuth 1.0a. OAuth2.0 jest znacznie prostszy w użyciu, ponieważ nie ma podpisywania i sortowania leksykograficznego różnych parametrów. Więc prawdopodobnie nie potrzebujesz zewnętrznej klasy do OAuth 2.0 lub ... jeśli potrzebujesz zewnętrznej klasy, będzie to znacznie prostsze niż ta.
Cheeso,
1
Nie znaleziono pliku pomocy online : cheeso.members.winisp.net/OAuthManager1.1
Kiquenet
3
Wszystkie linki wydają się być uszkodzone. Znalazłem kopię tutaj: gist.github.com/DeskSupport/2951522#file-oauth-cs
John