OAuth con verifica in .NET


103

Sto cercando di creare un'app client basata su .NET (in WPF, anche se per il momento lo sto facendo solo come app per console) da integrare con un'applicazione abilitata per OAuth, in particolare Mendeley ( http: // dev .mendeley.com ), che a quanto pare utilizza OAuth a tre vie.

È la prima volta che utilizzo OAuth e ho molte difficoltà a iniziare. Ho trovato diverse librerie o helper .NET OAuth, ma sembrano essere più complicati di quanto penso di aver bisogno. Tutto quello che voglio fare è essere in grado di inviare richieste REST all'API di Mendeley e ricevere risposte!

Finora ho provato:

Il primo (DotNetOpenAuth) sembra che potrebbe fare quello di cui avevo bisogno se passassi ore e ore a cercare di capire come. Il secondo e il terzo, come meglio posso dire, non supportano i codici di verifica che Mendeley sta restituendo, anche se potrei sbagliarmi su questo :)

Ho una chiave utente e un segreto da Mendeley e con DotNetOpenAuth sono riuscito a lanciare un browser con la pagina Mendeley che fornisce un codice di verifica che l'utente può accedere all'applicazione. Tuttavia, a questo punto mi sono perso e non sono riuscito a capire come restituirlo in modo ragionevole all'applicazione.

Sono molto disposto ad ammettere che non ho idea da dove iniziare con questo (anche se sembra che ci sia una curva di apprendimento piuttosto ripida) - se qualcuno può indicarmi la giusta direzione lo apprezzerei!

Risposte:


182

Sono d'accordo con te. Le classi di supporto OAuth open source disponibili per le app .NET sono difficili da capire, eccessivamente complicate (quanti metodi sono esposti da DotNetOpenAuth?), Mal progettate (guarda i metodi con 10 parametri di stringa nel modulo OAuthBase.cs di quel google link che hai fornito - non esiste alcuna gestione statale) o altrimenti insoddisfacente.

Non è necessario che sia così complicato.

Non sono un esperto di OAuth, ma ho prodotto una classe di gestione lato client OAuth, che utilizzo con successo con Twitter e TwitPic. È relativamente semplice da usare. È open source e disponibile qui: Oauth.cs

Per la revisione, in OAuth 1.0a ... abbastanza divertente, c'è un nome speciale e sembra uno "standard" ma per quanto ne so l'unico servizio che implementa "OAuth 1.0a" è Twitter. Immagino sia abbastanza standard . ok, comunque in OAuth 1.0a, il modo in cui funziona per le app desktop è questo:

  1. Tu, lo sviluppatore dell'app, registra l'app e ottieni una "chiave utente" e un "segreto utente". Su Arstechnica, c'è un'analisi ben scritta del perché questo modello non è il migliore , ma come si suol dire, è quello che è .

  2. La tua app viene eseguita. La prima volta che viene eseguito, deve convincere l'utente a concedere esplicitamente l'approvazione affinché l'app effettui richieste REST autenticate da oauth a Twitter e ai suoi servizi gemelli (come TwitPic). Per fare ciò è necessario eseguire un processo di approvazione, che implica l'approvazione esplicita da parte dell'utente. Ciò accade solo la prima volta che l'app viene eseguita. Come questo:

    • richiedere un "token di richiesta". Token temporaneo Aka.
    • pop una pagina web, passando quel token di richiesta come parametro di query. Questa pagina web presenta l'interfaccia utente all'utente, chiedendo "vuoi concedere l'accesso a questa app?"
    • l'utente accede alla pagina web di Twitter e concede o nega l'accesso.
    • viene visualizzata la pagina html di risposta. Se l'utente ha concesso l'accesso, viene visualizzato un PIN con un carattere di 48 punti
    • l'utente ora deve tagliare / incollare quel pin in una casella di Windows form e fare clic su "Avanti" o qualcosa di simile.
    • l'app desktop quindi esegue una richiesta autenticata da oauth per un "token di accesso". Un'altra richiesta REST.
    • l'app desktop riceve il "token di accesso" e il "segreto di accesso".

Dopo il ballo di approvazione, l'app desktop può semplicemente utilizzare il "token di accesso" e il "segreto di accesso" specifici per l'app (insieme alla "chiave utente" e al "segreto consumatore" specifici dell'app) per eseguire richieste autenticate per conto dell'utente a Twitter. Questi non scadono, anche se se l'utente revoca l'autorizzazione dell'app, o se Twitter per qualche motivo revoca l'autorizzazione della tua app, o se perdi il token di accesso e / o il segreto, dovrai ripetere la danza di approvazione .


Se non sei intelligente, il flusso dell'interfaccia utente può in qualche modo rispecchiare il flusso di messaggi OAuth in più passaggi. C'è un modo migliore.

Utilizzare un controllo WebBrowser e aprire la pagina Web di autorizzazione all'interno dell'app desktop. Quando l'utente fa clic su "Consenti", prendi il testo della risposta da quel controllo WebBrowser, estrai il PIN automaticamente, quindi ottieni i token di accesso. Si inviano 5 o 6 richieste HTTP ma l'utente deve vedere solo una singola finestra di dialogo Consenti / Nega. Semplice.

Come questo:
testo alternativo


Se hai ordinato l'interfaccia utente, l'unica sfida che rimane è produrre richieste firmate oauth. Questo fa inciampare molte persone perché i requisiti per la firma di oauth sono un po 'particolari. Questo è ciò che fa la classe OAuth Manager semplificata.

Codice di esempio per richiedere un token:

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

QUESTO È . Semplice. Come puoi vedere dal codice, il modo per accedere ai parametri oauth è tramite un indicizzatore basato su stringhe, qualcosa come un dizionario. Il metodo AcquireRequestToken invia una richiesta firmata oauth all'URL del servizio che concede i token di richiesta, ovvero i token temporanei. Per Twitter, questo URL è " https://api.twitter.com/oauth/request_token ". La specifica oauth dice che è necessario impacchettare il set di parametri oauth (token, token_secret, nonce, timestamp, consumer_key, version e callback), in un certo modo (URL-codificato e unito da e commerciale) e in un lessicograficamente- ordinato, genera una firma su quel risultato, quindi impacchetta gli stessi parametri insieme alla firma, memorizzati nel nuovo parametro oauth_signature, in un modo diverso (uniti da virgole). La classe del gestore OAuth lo fa automaticamente. Genera automaticamente nonc e timestamp, versioni e firme : la tua app non ha bisogno di preoccuparsi o di essere a conoscenza di queste cose. Basta impostare i valori del parametro oauth ed effettuare una semplice chiamata al metodo. la classe manager invia la richiesta e analizza la risposta per te.

Ok, allora cosa? Una volta ottenuto il token di richiesta, si apre l'interfaccia utente del browser Web in cui l'utente concederà esplicitamente l'approvazione. Se lo fai bene, lo inserirai in un browser incorporato. Per Twitter, l'URL per questo è " https://api.twitter.com/oauth/authorize?oauth_token= " con oauth_token aggiunto. Fallo in codice in questo modo:

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

(Se lo facessi in un browser esterno, useresti System.Diagnostics.Process.Start(url).)

L'impostazione della proprietà Url fa sì che il controllo WebBrowser passi automaticamente a quella pagina.

Quando l'utente fa clic sul pulsante "Consenti", verrà caricata una nuova pagina. È un modulo HTML e funziona come in un browser completo. Nel tuo codice, registra un gestore per l'evento DocumentedCompleted del controllo WebBrowser e in quel gestore, prendi il 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();

È un po 'di scraping dello schermo HTML.

Dopo aver afferrato il segnaposto, non è più necessario il browser web, quindi:

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

... e potresti anche chiamare Dispose () su di esso.

Il passaggio successivo è ottenere il token di accesso, inviando un altro messaggio HTTP insieme a quel pin. Questa è un'altra chiamata oauth firmata, costruita con l'ordine e la formattazione oauth che ho descritto sopra. Ma ancora una volta questo è davvero semplice con la classe OAuth.Manager:

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

Per Twitter, tale URL è " https://api.twitter.com/oauth/access_token ".

Ora hai i token di accesso e puoi usarli nelle richieste HTTP firmate. Come questo:

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

... dov'è urll'endpoint della risorsa. Per aggiornare lo stato dell'utente, sarebbe " http://api.twitter.com/1/statuses/update.xml?status=Hello ".

Quindi imposta quella stringa nell'intestazione HTTP denominata Autorizzazione .

Per interagire con servizi di terze parti, come TwitPic, è necessario creare un'intestazione OAuth leggermente diversa , come questa:

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

Per Twitter, i valori per l'URL di verifica delle credenziali e l'ambito sono rispettivamente " https://api.twitter.com/1/account/verify_credentials.json " e " http://api.twitter.com/ ".

... e inserisci quella stringa di autorizzazione in un'intestazione HTTP chiamata X-Verify-Credentials-Authorization . Quindi invialo al tuo servizio, come TwitPic, insieme a qualsiasi richiesta stai inviando.

Questo è tutto.

Tutto sommato, il codice per aggiornare lo stato di Twitter potrebbe essere qualcosa del genere:

// 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 è un po 'complicato sotto le coperte, ma non è necessario che lo sia. OAuth.Manager gestisce la generazione di richieste oauth in uscita e la ricezione e l'elaborazione del contenuto oauth nelle risposte. Quando la richiesta Request_token ti fornisce un oauth_token, la tua app non ha bisogno di memorizzarlo. Oauth.Manager è abbastanza intelligente da farlo automaticamente. Allo stesso modo, quando la richiesta access_token ottiene un token di accesso e un segreto, non è necessario archiviarli esplicitamente. OAuth.Manager gestisce quello stato per te.

Nelle esecuzioni successive, quando si dispone già del token di accesso e del segreto, è possibile creare un'istanza di OAuth.Manager in questo modo:

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;

... e quindi genera le intestazioni di autorizzazione come sopra.

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

Puoi scaricare una DLL contenente la classe OAuth.Manager qui . C'è anche un file della guida in quel download. Oppure puoi visualizzare il file della guida in linea .

Vedi un esempio di un Windows Form che utilizza questo gestore qui .


ESEMPIO DI FUNZIONAMENTO

Scarica un esempio funzionante di uno strumento da riga di comando che utilizza la classe e la tecnica descritte qui:


Ciao, grazie mille per la tua risposta! In realtà sono passato da OAuth (ho rinunciato a Mendeley e ho optato per un'alternativa), ma ho letto la tua risposta e ha avuto molto senso ed è molto esauriente. Ho anche aggiunto ai segnalibri la classe che hai scritto per le volte in cui potrei averne bisogno! Molte grazie ancora.
John

2
Ciao Cheeso, grazie per aver condiviso il tuo codice e la tua spiegazione dettagliata. Hai fornito un'ottima ma semplice soluzione. Tuttavia, ti consigliamo di apportare una piccola modifica al tuo metodo GetSignatureBase per supportare soluzioni non "oob". Per non "oob", devi codificare l'URL del callback, quindi ti consigliamo di aggiungere qualcosa di simile quando stai iterando attraverso this._params: if (p1.Key == "callback") {p.Add ( "oauth_" + p1.Key, UrlEncode (p1.Value)); continue;}
Johnny Oshika

1
Questo non funziona per OAuth 2.0. Questa classe è per OAuth 1.0a. OAuth2.0 è notevolmente più semplice da usare, in quanto non c'è firma e ordinamento lessicografico dei vari parametri. Quindi probabilmente non hai bisogno di una classe esterna per eseguire OAuth 2.0, o ... se hai bisogno di una classe esterna, sarà molto più semplice di questa.
Cheeso

1
helpfile online non trovato: cheeso.members.winisp.net/OAuthManager1.1
Kiquenet

3
Tutti i collegamenti sembrano essere interrotti. Ne ho trovata una copia qui: gist.github.com/DeskSupport/2951522#file-oauth-cs
John
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.