OAuth avec vérification dans .NET

103

J'essaie de créer une application client basée sur .NET (dans WPF - bien que pour le moment je le fasse simplement comme une application console) à intégrer à une application compatible OAuth, en particulier Mendeley ( http: // dev .mendeley.com ), qui utilise apparemment OAuth en 3 étapes.

C'est la première fois que j'utilise OAuth, et j'ai beaucoup de mal à démarrer. J'ai trouvé plusieurs bibliothèques ou helpers .NET OAuth, mais ils semblent être plus compliqués que ce dont je pense avoir besoin. Tout ce que je veux faire, c'est pouvoir émettre des requêtes REST vers l'API Mendeley et obtenir des réponses!

Jusqu'à présent, j'ai essayé:

Le premier (DotNetOpenAuth) semble pouvoir faire ce dont j'avais besoin si je passais des heures et des heures à essayer de comprendre comment. Les deuxième et troisième, pour autant que je sache, ne prennent pas en charge les codes de vérification que Mendeley renvoie - bien que je puisse me tromper à ce sujet :)

J'ai une clé de consommateur et un secret de Mendeley, et avec DotNetOpenAuth j'ai réussi à lancer un navigateur avec la page Mendeley fournissant un code de vérification à l'utilisateur pour entrer dans l'application. Cependant, à ce stade, je me suis perdu et je n'ai pas pu trouver comment le fournir de manière raisonnable à l'application.

Je suis tout à fait prêt à admettre que je n'ai aucune idée par où commencer (bien qu'il semble y avoir une courbe d'apprentissage assez raide) - si quelqu'un peut m'indiquer la bonne direction, je l'apprécierais!

John
la source

Réponses:

182

Je suis d'accord avec toi. Les classes de support OAuth open source disponibles pour les applications .NET sont difficiles à comprendre, trop compliquées (combien de méthodes sont exposées par DotNetOpenAuth?), Mal conçues (regardez les méthodes avec 10 paramètres de chaîne dans le module OAuthBase.cs de ce google lien que vous avez fourni - il n'y a pas du tout de gestion de l'état), ou insatisfaisant.

Cela n'a pas besoin d'être aussi compliqué.

Je ne suis pas un expert sur OAuth, mais j'ai produit une classe de gestionnaire côté client OAuth, que j'utilise avec succès avec Twitter et TwitPic. C'est relativement simple à utiliser. Il est open source et disponible ici: Oauth.cs

Pour examen, dans OAuth 1.0a ... plutôt drôle, il y a un nom spécial et cela ressemble à un "standard" mais pour autant que je sache, le seul service qui implémente "OAuth 1.0a" est Twitter. Je suppose que c'est assez standard . ok, de toute façon dans OAuth 1.0a, la façon dont cela fonctionne pour les applications de bureau est la suivante:

  1. Vous, le développeur de l'application, enregistrez l'application et obtenez une "clé consommateur" et un "secret consommateur". Sur Arstechnica, il existe une analyse bien écrite des raisons pour lesquelles ce modèle n'est pas le meilleur , mais comme on dit, c'est ce qu'il est .

  2. Votre application s'exécute. La première fois qu'elle s'exécute, elle doit amener l'utilisateur à autoriser explicitement l'application à envoyer des requêtes REST authentifiées par oauth à Twitter et à ses services frères (comme TwitPic). Pour ce faire, vous devez passer par un processus d'approbation, impliquant une approbation explicite par l'utilisateur. Cela se produit uniquement la première fois que l'application s'exécute. Comme ça:

    • demander un "jeton de demande". Jeton temporaire Aka.
    • pop une page Web, en passant ce jeton de demande en tant que paramètre de requête. Cette page Web présente l'interface utilisateur à l'utilisateur et lui demande "Voulez-vous autoriser l'accès à cette application?"
    • l'utilisateur se connecte à la page Web Twitter et accorde ou refuse l'accès.
    • la page html de réponse apparaît. Si l'utilisateur a accordé l'accès, un code PIN est affiché dans une police de 48 points
    • l'utilisateur doit maintenant couper / coller cette épingle dans une boîte de formulaire Windows, et cliquer sur "Suivant" ou quelque chose de similaire.
    • l'application de bureau fait alors une demande authentifiée par oauth pour un «jeton d'accès». Une autre requête REST.
    • l'application de bureau reçoit le «jeton d'accès» et le «secret d'accès».

Après la danse d'approbation, l'application de bureau peut simplement utiliser le "jeton d'accès" et le "secret d'accès" spécifiques à l'utilisateur (ainsi que la "clé consommateur" et le "secret consommateur" spécifiques à l'application) pour effectuer des demandes authentifiées au nom de l'utilisateur. sur Twitter. Ceux-ci n'expirent pas, bien que si l'utilisateur désautorise l'application, ou si Twitter pour une raison quelconque désautorise votre application, ou si vous perdez votre jeton d'accès et / ou secret, vous devrez refaire la danse d'approbation. .


Si vous n'êtes pas intelligent, le flux de l'interface utilisateur peut en quelque sorte refléter le flux de messages OAuth en plusieurs étapes. Il y a un meilleur moyen.

Utilisez un contrôle WebBrowser et ouvrez la page Web d'autorisation dans l'application de bureau. Lorsque l'utilisateur clique sur "Autoriser", récupérez le texte de réponse de ce contrôle WebBrowser, extrayez le code PIN automatiquement, puis obtenez les jetons d'accès. Vous envoyez 5 ou 6 requêtes HTTP mais l'utilisateur n'a besoin de voir qu'une seule boîte de dialogue Autoriser / Refuser. Facile.

Comme ça:
texte alternatif


Si vous avez trié l'interface utilisateur, le seul défi qui reste est de produire des requêtes signées oauth. Cela fait trébucher beaucoup de gens parce que les exigences de signature OAuth sont en quelque sorte particulières. C'est ce que fait la classe OAuth Manager simplifiée.

Exemple de code pour demander un jeton:

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

C'EST C'EST . Facile. Comme vous pouvez le voir dans le code, le moyen d'accéder aux paramètres oauth est via un indexeur basé sur une chaîne, quelque chose comme un dictionnaire. La méthode AcquireRequestToken envoie une demande signée oauth à l'URL du service qui accorde des jetons de demande, c'est-à-dire des jetons temporaires. Pour Twitter, cette URL est " https://api.twitter.com/oauth/request_token ". La spécification oauth indique que vous devez emballer l'ensemble des paramètres oauth (token, token_secret, nonce, timestamp, consumer_key, version et callback), d'une certaine manière (encodés en url et joints par des esperluettes), et dans un lexicographiquement- trié, générez une signature sur ce résultat, puis regroupez ces mêmes paramètres avec la signature, stockée dans le nouveau paramètre oauth_signature, d'une manière différente (jointe par des virgules). La classe de gestionnaire OAuth le fait automatiquement pour vous. Il génère automatiquement des nonces et des horodatages, des versions et des signatures - votre application n'a pas besoin de s'en soucier ou d'en être consciente. Définissez simplement les valeurs du paramètre oauth et effectuez un simple appel de méthode. la classe de gestionnaire envoie la demande et analyse la réponse pour vous.

Ok, alors quoi? Une fois que vous obtenez le jeton de demande, vous affichez l'interface utilisateur du navigateur Web dans laquelle l'utilisateur accordera explicitement son approbation. Si vous le faites correctement, vous l'afficherez dans un navigateur intégré. Pour Twitter, l'URL pour cela est " https://api.twitter.com/oauth/authorize?oauth_token= " avec le oauth_token ajouté. Faites ceci dans le code comme ceci:

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

(Si vous faisiez cela dans un navigateur externe que vous utiliseriez System.Diagnostics.Process.Start(url).)

La définition de la propriété Url oblige le contrôle WebBrowser à accéder automatiquement à cette page.

Lorsque l'utilisateur clique sur le bouton «Autoriser», une nouvelle page est chargée. C'est un formulaire HTML et il fonctionne de la même manière que dans un navigateur complet. Dans votre code, inscrivez un gestionnaire pour l'événement DocumentedCompleted du contrôle WebBrowser, et dans ce gestionnaire, saisissez la broche:

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

C'est un peu de grattage d'écran HTML.

Après avoir saisi l'épingle, vous n'avez plus besoin du navigateur Web, donc:

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

... et vous pouvez également appeler Dispose ().

L'étape suivante consiste à obtenir le jeton d'accès, en envoyant un autre message HTTP avec cette broche. Ceci est un autre appel oauth signé, construit avec l'ordre et le formatage oauth que j'ai décrits ci-dessus. Mais encore une fois, c'est vraiment simple avec la classe OAuth.Manager:

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

Pour Twitter, cette URL est " https://api.twitter.com/oauth/access_token ".

Vous avez maintenant des jetons d'accès et vous pouvez les utiliser dans des requêtes HTTP signées. Comme ça:

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

... où urlest le point de terminaison de la ressource. Pour mettre à jour le statut de l'utilisateur, ce serait " http://api.twitter.com/1/statuses/update.xml?status=Hello ".

Définissez ensuite cette chaîne dans l'en-tête HTTP nommé Authorization .

Pour interagir avec des services tiers, comme TwitPic, vous devez créer un en-tête OAuth légèrement différent , comme ceci:

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

Pour Twitter, les valeurs de l'URL et du domaine de vérification des creds sont respectivement " https://api.twitter.com/1/account/verify_credentials.json " et " http://api.twitter.com/ ".

... et placez cette chaîne d'autorisation dans un en-tête HTTP appelé X-Verify-Credentials-Authorization . Ensuite, envoyez-le à votre service, comme TwitPic, avec la demande que vous envoyez.

C'est tout.

Dans l'ensemble, le code pour mettre à jour le statut de Twitter pourrait être quelque chose comme ceci:

// 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 est un peu compliqué sous les couvertures, mais son utilisation n'a pas besoin de l'être. OAuth.Manager gère la génération des requêtes oauth sortantes, ainsi que la réception et le traitement du contenu oauth dans les réponses. Lorsque la requête Request_token vous donne un oauth_token, votre application n'a pas besoin de le stocker. Oauth.Manager est suffisamment intelligent pour le faire automatiquement. De même, lorsque la demande access_token récupère un jeton d'accès et un secret, vous n'avez pas besoin de les stocker explicitement. OAuth.Manager gère cet état pour vous.

Dans les exécutions suivantes, lorsque vous avez déjà le jeton d'accès et le secret, vous pouvez instancier le OAuth.Manager comme ceci:

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;

... puis générez les en-têtes d'autorisation comme ci-dessus.

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

Vous pouvez télécharger une DLL contenant la classe OAuth.Manager ici . Il existe également un fichier d'aide dans ce téléchargement. Ou vous pouvez consulter le fichier d'aide en ligne .

Consultez un exemple de formulaire Windows utilisant ce gestionnaire ici .


EXEMPLE DE TRAVAIL

Téléchargez un exemple de travail d'un outil de ligne de commande qui utilise la classe et la technique décrites ici:

Cheeso
la source
Salut, merci beaucoup pour votre réponse! En fait, j'ai quitté OAuth (j'ai abandonné Mendeley et j'ai opté pour une alternative) - mais j'ai lu votre réponse et elle avait beaucoup de sens et est très complète. J'ai également mis en signet le cours que vous avez écrit pour les moments futurs où j'en aurai besoin! Merci encore une fois.
John
2
Bonjour Cheeso, merci d'avoir partagé votre code et votre explication détaillée. Vous avez fourni une solution formidable mais simple. Cependant, vous souhaiterez apporter une petite modification à votre méthode GetSignatureBase pour prendre en charge les solutions non "oob". Pour les non "oob", vous devez encoder l'URL du rappel, vous voudrez donc ajouter quelque chose comme ça lorsque vous itérez à travers this._params: if (p1.Key == "callback") {p.Add ( "oauth_" + p1.Key, UrlEncode (p1.Value)); continue;}
Johnny Oshika
1
Cela ne fonctionne pas pour OAuth 2.0. Cette classe est pour OAuth 1.0a. OAuth2.0 est nettement plus simple à utiliser, car il n'y a pas de signature et de tri lexicographique des différents paramètres. Vous n'avez donc probablement pas besoin d'une classe externe pour faire OAuth 2.0, ou ... si vous avez besoin d'une classe externe, ce sera beaucoup plus simple que celle-ci.
Cheeso
1
fichier d'aide en ligne introuvable: cheeso.members.winisp.net/OAuthManager1.1
Kiquenet
3
Tous les liens semblent rompus. J'ai trouvé une copie ici: gist.github.com/DeskSupport/2951522#file-oauth-cs
John