OAuth mit Überprüfung in .NET

103

Ich versuche, eine .NET-basierte Client-App (in WPF - obwohl ich sie derzeit nur als Konsolen-App verwende) zu erstellen, um sie in eine OAuth-fähige Anwendung zu integrieren, insbesondere in Mendeley ( http: // dev .mendeley.com ), die anscheinend dreibeinige OAuth verwendet.

Ich benutze OAuth zum ersten Mal und habe große Schwierigkeiten, damit zu beginnen. Ich habe mehrere .NET OAuth-Bibliotheken oder -Helfer gefunden, aber sie scheinen komplizierter zu sein, als ich denke, dass ich sie brauche. Ich möchte nur REST-Anfragen an die Mendeley-API senden und Antworten zurückerhalten können!

Bisher habe ich versucht:

Das erste (DotNetOpenAuth) scheint möglicherweise das zu tun, was ich brauchte, wenn ich stundenlang herausfinden wollte, wie. Der zweite und dritte unterstützen, wie ich am besten beurteilen kann, nicht die Bestätigungscodes, die Mendeley zurücksendet - obwohl ich mich darin irren könnte :)

Ich habe einen Verbraucherschlüssel und ein Geheimnis von Mendeley und mit DotNetOpenAuth konnte ich einen Browser mit der Mendeley-Seite starten, der einen Bestätigungscode für den Benutzer zur Eingabe in die Anwendung bereitstellt. Zu diesem Zeitpunkt habe ich mich jedoch verlaufen und konnte nicht herausfinden, wie ich das sinnvoll für die Anwendung bereitstellen kann.

Ich bin sehr bereit zuzugeben, dass ich keine Ahnung habe, wo ich damit anfangen soll (obwohl es anscheinend eine ziemlich steile Lernkurve gibt) - wenn mich jemand in die richtige Richtung weisen kann, würde ich es schätzen!

John
quelle

Antworten:

182

Ich stimme mit Ihnen ein. Die Open-Source-OAuth-Unterstützungsklassen, die für .NET-Apps verfügbar sind, sind schwer zu verstehen, übermäßig kompliziert (wie viele Methoden werden von DotNetOpenAuth verfügbar gemacht?) Und schlecht gestaltet (sehen Sie sich die Methoden mit 10 Zeichenfolgenparametern im OAuthBase.cs-Modul von Google an Link, den Sie bereitgestellt haben - es gibt überhaupt keine staatliche Verwaltung) oder auf andere Weise unbefriedigend.

Es muss nicht so kompliziert sein.

Ich bin kein OAuth-Experte, aber ich habe eine clientseitige OAuth-Manager-Klasse erstellt, die ich erfolgreich mit Twitter und TwitPic verwende. Es ist relativ einfach zu bedienen. Es ist Open Source und hier verfügbar: Oauth.cs

Zur Überprüfung, in OAuth 1.0a ... irgendwie lustig, gibt es einen speziellen Namen und es sieht aus wie ein "Standard", aber soweit ich weiß, ist der einzige Dienst, der "OAuth 1.0a" implementiert, Twitter. Ich denke, das ist Standard genug . OK, in OAuth 1.0a funktioniert es für Desktop-Apps wie folgt:

  1. Sie, der Entwickler der App, registrieren die App und erhalten einen "Verbraucherschlüssel" und ein "Verbrauchergeheimnis". Auf Arstechnica gibt es eine gut geschriebene Analyse, warum dieses Modell nicht das beste ist , aber wie sie sagen, ist es das, was es ist .

  2. Ihre App wird ausgeführt. Bei der ersten Ausführung muss der Benutzer explizit die Genehmigung für die App erteilen, um oauth-authentifizierte REST-Anforderungen an Twitter und seine Schwesterdienste (wie TwitPic) zu senden. Dazu müssen Sie einen Genehmigungsprozess durchlaufen, der eine explizite Genehmigung durch den Benutzer umfasst. Dies geschieht nur beim ersten Start der App. So was:

    • Fordern Sie ein "Request Token" an. Aka temporäres Zeichen.
    • Öffnen Sie eine Webseite und übergeben Sie dieses Anforderungstoken als Abfrageparameter. Diese Webseite zeigt dem Benutzer die Benutzeroberfläche mit der Frage "Möchten Sie Zugriff auf diese App gewähren?".
    • Der Benutzer meldet sich auf der Twitter-Webseite an und gewährt oder verweigert den Zugriff.
    • Die Antwort-HTML-Seite wird angezeigt. Wenn der Benutzer Zugriff gewährt hat, wird eine PIN in einer 48-Punkt-Schriftart angezeigt
    • Der Benutzer muss nun diesen Stift ausschneiden / in ein Windows-Formularfeld einfügen und auf "Weiter" oder ähnliches klicken.
    • Die Desktop-App führt dann eine oauth-authentifizierte Anforderung für ein "Zugriffstoken" durch. Eine weitere REST-Anfrage.
    • Die Desktop-App erhält das "Zugriffstoken" und das "Zugriffsgeheimnis".

Nach dem Genehmigungstanz kann die Desktop-App nur das benutzerspezifische "Zugriffstoken" und "Zugriffsgeheimnis" (zusammen mit dem app-spezifischen "Verbraucherschlüssel" und "Verbrauchergeheimnis") verwenden, um authentifizierte Anforderungen im Namen des Benutzers auszuführen zu Twitter. Diese verfallen nicht. Wenn der Benutzer die App abautorisiert oder wenn Twitter Ihre App aus irgendeinem Grund abautorisiert oder wenn Sie Ihr Zugriffstoken und / oder Ihr Geheimnis verlieren, müssen Sie den Genehmigungstanz erneut ausführen .


Wenn Sie nicht schlau sind, kann der UI-Fluss den mehrstufigen OAuth-Nachrichtenfluss spiegeln. Es gibt einen besseren Weg.

Verwenden Sie ein WebBrowser-Steuerelement und öffnen Sie die Autorisierungswebseite in der Desktop-App. Wenn der Benutzer auf "Zulassen" klickt, greifen Sie auf den Antworttext dieses WebBrowser-Steuerelements zu, extrahieren Sie die PIN automatisch und rufen Sie die Zugriffstoken ab. Sie senden 5 oder 6 HTTP-Anforderungen, aber der Benutzer muss nur ein einziges Dialogfeld "Zulassen / Verweigern" sehen. Einfach.

So was:
Alt-Text


Wenn Sie die Benutzeroberfläche sortiert haben, besteht die einzige Herausforderung darin, von Oauth signierte Anforderungen zu erstellen. Dies stolpert über viele Leute, weil die Anforderungen an die Unterzeichnung besonders sind. Das macht die vereinfachte OAuth Manager-Klasse.

Beispielcode zum Anfordern eines Tokens:

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

DAS IST ES . Einfach. Wie Sie dem Code entnehmen können, erfolgt der Weg zu den oauth-Parametern über einen stringbasierten Indexer, ähnlich einem Wörterbuch. Die AcquireRequestToken-Methode sendet eine von oauth signierte Anforderung an die URL des Dienstes, der Anforderungstoken, auch als temporäre Token bezeichnet, gewährt. Für Twitter lautet diese URL " https://api.twitter.com/oauth/request_token ". Die oauth-Spezifikation besagt, dass Sie den Satz von oauth-Parametern (Token, Token_Sekret, Nonce, Zeitstempel, Consumer_key, Version und Rückruf) auf eine bestimmte Weise (URL-codiert und durch kaufmännisches Und verbunden) in einem lexikografisch gepackten Paket packen müssen sortierte Reihenfolge, generieren Sie eine Signatur für dieses Ergebnis und packen Sie dann dieselben Parameter zusammen mit der Signatur, die im neuen Parameter oauth_signature gespeichert ist, auf andere Weise (durch Kommas verbunden). Die OAuth-Manager-Klasse erledigt dies automatisch für Sie. Sie generiert automatisch Nonces und Zeitstempel sowie Versionen und Signaturen - Ihre App muss sich nicht darum kümmern oder sich dessen bewusst sein. Legen Sie einfach die oauth-Parameterwerte und fest Führen Sie einen einfachen Methodenaufruf durch. Die Manager-Klasse sendet die Anforderung und analysiert die Antwort für Sie.

OK was dann? Sobald Sie das Anforderungstoken erhalten haben, öffnen Sie die Benutzeroberfläche des Webbrowsers, in der der Benutzer die Genehmigung explizit erteilt. Wenn Sie es richtig machen, wird dies in einem eingebetteten Browser angezeigt. Für Twitter lautet die URL hierfür " https://api.twitter.com/oauth/authorize?oauth_token= ", wobei das oauth_token angehängt ist. Tun Sie dies in Code wie folgt:

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

(Wenn Sie dies in einem externen Browser tun würden, würden Sie verwenden System.Diagnostics.Process.Start(url).)

Durch Festlegen der Url-Eigenschaft navigiert das WebBrowser-Steuerelement automatisch zu dieser Seite.

Wenn der Benutzer auf die Schaltfläche "Zulassen" klickt, wird eine neue Seite geladen. Es ist ein HTML-Formular und funktioniert genauso wie in einem vollständigen Browser. Registrieren Sie in Ihrem Code einen Handler für das DocumentedCompleted-Ereignis des WebBrowser-Steuerelements, und greifen Sie in diesem Handler nach dem 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();

Das ist ein bisschen HTML-Screen-Scraping.

Nachdem Sie den Stift gepackt haben, benötigen Sie den Webbrowser nicht mehr.

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

... und vielleicht möchten Sie auch Dispose () aufrufen.

Der nächste Schritt ist das Abrufen des Zugriffstokens, indem eine weitere HTTP-Nachricht zusammen mit dieser PIN gesendet wird. Dies ist ein weiterer signierter oauth-Aufruf, der mit der oben beschriebenen oauth-Reihenfolge und -Formatierung erstellt wurde. Aber auch dies ist mit der OAuth.Manager-Klasse ganz einfach:

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

Für Twitter lautet diese URL " https://api.twitter.com/oauth/access_token ".

Jetzt haben Sie Zugriffstoken und können diese in signierten HTTP-Anforderungen verwenden. So was:

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

... wo urlist der Ressourcenendpunkt? Um den Status des Benutzers zu aktualisieren, lautet er " http://api.twitter.com/1/statuses/update.xml?status=Hello ".

Setzen Sie diese Zeichenfolge dann in den HTTP-Header Authorization .

Um mit Diensten von Drittanbietern wie TwitPic zu interagieren, müssen Sie einen etwas anderen OAuth-Header erstellen :

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

Für Twitter lauten die Werte für die URL und den Bereich für die Überprüfung der Creds " https://api.twitter.com/1/account/verify_credentials.json " bzw. " http://api.twitter.com/ ".

... und fügen Sie diese Autorisierungszeichenfolge in einen HTTP-Header mit dem Namen X-Verify-Credentials-Authorization ein . Senden Sie das dann zusammen mit der von Ihnen gesendeten Anfrage an Ihren Dienst wie TwitPic.

Das ist es.

Alles in allem könnte der Code zum Aktualisieren des Twitter-Status ungefähr so ​​aussehen:

// 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 ist unter der Decke etwas kompliziert, muss aber nicht verwendet werden. Der OAuth.Manager übernimmt die Generierung ausgehender oauth-Anforderungen sowie den Empfang und die Verarbeitung von oauth-Inhalten in den Antworten. Wenn die Request_token-Anforderung Ihnen ein oauth_token gibt, muss Ihre App es nicht speichern. Der Oauth.Manager ist intelligent genug, um dies automatisch zu tun. Wenn die access_token-Anforderung ein Zugriffstoken und ein Geheimnis zurückerhält, müssen Sie diese ebenfalls nicht explizit speichern. Der OAuth.Manager behandelt diesen Status für Sie.

Wenn Sie in nachfolgenden Läufen bereits über das Zugriffstoken und das Geheimnis verfügen, können Sie den OAuth.Manager wie folgt instanziieren:

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;

... und generieren Sie dann Autorisierungsheader wie oben.

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

Sie können hier eine DLL herunterladen, die die OAuth.Manager-Klasse enthält . In diesem Download befindet sich auch eine Hilfedatei. Oder Sie können die Hilfedatei online anzeigen .

Ein Beispiel für ein Windows Form, das diesen Manager verwendet, finden Sie hier .


ARBEITSBEISPIEL

Laden Sie ein Arbeitsbeispiel eines Befehlszeilentools herunter , das die hier beschriebene Klasse und Technik verwendet:

Cheeso
quelle
Hallo, vielen Dank für Ihre Antwort! Ich bin tatsächlich von OAuth weitergezogen (ich habe Mendeley aufgegeben und mich für eine Alternative entschieden) - aber ich habe Ihre Antwort gelesen und es hat viel Sinn gemacht und ist sehr umfassend. Ich habe auch die Klasse mit einem Lesezeichen versehen, die Sie für zukünftige Zeiten geschrieben haben, in denen ich sie möglicherweise benötige! Vielen Dank nochmal.
John
2
Hallo Cheeso, danke, dass du deinen Code und deine ausführliche Erklärung geteilt hast. Sie haben eine großartige und dennoch einfache Lösung bereitgestellt. Sie sollten jedoch eine kleine Änderung an Ihrer GetSignatureBase-Methode vornehmen, um nicht "oob" -Lösungen zu unterstützen. Für nicht "oob" müssen Sie den Rückruf per URL codieren, daher möchten Sie beim Durchlaufen von this._params Folgendes hinzufügen: if (p1.Key == "callback") {p.Add ( "oauth_" + p1.Key, UrlEncode (p1.Value)); weiter;}
Johnny Oshika
1
Dies funktioniert nicht für OAuth 2.0. Diese Klasse ist für OAuth 1.0a. OAuth2.0 ist wesentlich einfacher zu verwenden, da die verschiedenen Parameter nicht signiert und lexikografisch sortiert werden. Sie benötigen also wahrscheinlich keine externe Klasse, um OAuth 2.0 auszuführen, oder ... wenn Sie eine externe Klasse benötigen, wird dies viel einfacher als diese.
Cheeso
1
Hilfedatei online nicht gefunden: cheeso.members.winisp.net/OAuthManager1.1
Kiquenet
3
Alle Links scheinen defekt zu sein. Ich habe hier eine Kopie gefunden: gist.github.com/DeskSupport/2951522#file-oauth-cs
John