mendeley - OAuth con Verification in.NET




(2)

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

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

Finora, ho provato:

Il primo (DotNetOpenAuth) sembra che potrebbe fare ciò di cui avevo bisogno se avessi passato 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 inviando, anche se potrei sbagliarmi :)

Ho una chiave del consumatore e un segreto da Mendeley, e con DotNetOpenAuth sono riuscito a ottenere un browser lanciato con la pagina Mendeley che fornisce un codice di verifica per l'utente da inserire nell'applicazione. Tuttavia, a questo punto mi sono perso e non ho potuto capire come restituire sensibilmente all'applicazione.

Sono molto disposta ad ammettere che non ho idea di dove cominciare (anche se sembra che ci sia una curva di apprendimento abbastanza ripida) - se qualcuno mi può indirizzare nella giusta direzione lo apprezzerei!


Answers

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

Non ha bisogno di essere così complicato.

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

Per la revisione, in OAuth 1.0a ... un po 'divertente, c'è un nome speciale e sembra uno "standard", ma per quanto ne so l'unico servizio che implementa "OAuth 1.0a" è Twitter. Immagino che 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 del consumatore" e un "segreto del consumatore". 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 funziona. La prima volta che viene eseguito, è necessario che l'utente conceda in modo esplicito l'approvazione per l'app per effettuare richieste REST autenticate da oauth su Twitter e sui suoi servizi affiliati (come TwitPic). Per fare ciò devi seguire un processo di approvazione, che implica l'approvazione esplicita da parte dell'utente. Questo 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.
    • appare la pagina HTML di risposta. Se l'utente ha concesso l'accesso, viene visualizzato un PIN in un font da 48 pt
    • ora l'utente deve tagliare / incollare quel segnaposto in una finestra del modulo di Windows e fare clic su "Avanti" o qualcosa di simile.
    • l'app desktop esegue quindi una richiesta autenticata da oauth per un "token di accesso". Un'altra richiesta REST.
    • l'app desktop riceve "token di accesso" e "accesso segreto".

Dopo l'approvazione, l'app desktop può semplicemente utilizzare "token di accesso" e "accesso segreto" specifici dell'utente (insieme alla specifica "chiave utente" e "segreto consumatore") per eseguire richieste autenticate per conto dell'utente su Twitter. Questi non scadono, anche se se l'utente de-autorizza l'app, o se per qualche motivo Twitter de-autorizza la tua app, o se perdi il tuo token di accesso e / o segreto, devi eseguire nuovamente l'approvazione per ballare .

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

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

Come questo:

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

Esempio di codice 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");

È COSÌ . Semplice. Come puoi vedere dal codice, il modo per ottenere i 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 token temporanei. Per Twitter, questo URL è " https://api.twitter.com/oauth/request_token ". Le specifiche di oauth dicono che è necessario impacchettare il set di parametri oauth (token, token_secret, nonce, timestamp, consumer_key, version e callback), in un certo modo (url-encoded e uniti da e commerciali), e in un lessicograficamente- ordine ordinato, generare una firma su quel risultato, quindi impacchettare gli stessi parametri insieme alla firma, memorizzati nel nuovo parametro oauth_signature, in un modo diverso (uniti da virgole). La classe manager OAuth lo fa automaticamente per te. Genera automaticamente spie e timestamp e versioni e firme - la tua app non ha bisogno di preoccuparsi o essere consapevole di quella roba. Basta impostare i valori dei parametri oauth e effettuare una semplice chiamata di 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 concede esplicitamente l'approvazione. Se lo fai bene, lo visualizzerai in un browser incorporato. Per Twitter, l'URL per questo è " https://api.twitter.com/oauth/authorize?oauth_token= " con l'oauth_token aggiunto. Fai questo 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 dovresti usare 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 codice, registrare un gestore per l'evento DocumentedCompleted del controllo WebBrowser e, in tale gestore, afferrare 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();

Questo è un po 'di raschiatura dello schermo HTML.

Dopo aver afferrato il pin, non hai più bisogno del browser web, quindi:

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

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

Il passo successivo è ottenere il token di accesso, inviando un altro messaggio HTTP insieme a quel pin. Questa è un'altra chiamata oauth firmata, costruita con l'ordinamento o 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 accesso ai token e puoi usarli nelle richieste HTTP firmate. Come questo:

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

... dove url è l'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 costruire un'intestazione OAuth leggermente diversa , come questa:

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

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

... e inserisci la stringa di autorizzazione in un'intestazione HTTP denominata X-Verify-Credentials-Authorization . Quindi invia questo al tuo servizio, come TwitPic, insieme a qualsiasi richiesta tu invii.

Questo è tutto.

Tutti insieme, 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 copertine, ma non è necessario utilizzarlo. OAuth.Manager gestisce la generazione di richieste oauth in uscita e la ricezione e l'elaborazione di contenuto oauth nelle risposte. Quando la richiesta Request_token ti dà un oauth_token, la tua app non ha bisogno di memorizzarla. Oauth.Manager è abbastanza intelligente da farlo automaticamente. Allo stesso modo, quando la richiesta access_token restituisce un token di accesso e un segreto, non è necessario memorizzarli in modo esplicito. OAuth.Manager gestisce quello stato per te.

Nelle esecuzioni successive, quando hai già il token di accesso e il segreto, puoi 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 generare 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 di aiuto in quel download. Oppure puoi visualizzare il file di help online .

Guarda un esempio di un Windows Form che usa questo manager here .

ESEMPIO DI LAVORO

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


Puoi usare UseUrls per questo:

public class Program
{
    public static void Main(string[] args)
    {
        var host = new WebHostBuilder()
            .UseKestrel()
            .UseContentRoot(Directory.GetCurrentDirectory())
            .UseIISIntegration()
            .UseStartup<Startup>()
            .UseUrls("http://localhost:5001/")
            .Build();

        host.Run();
    }
}

In alternativa, è possibile configurare l'indirizzo del server utilizzando lo stack di configurazione. Ecco come puoi usare gli argomenti della riga di comando per far scorrere l'indirizzo che vuoi:

public class Program
{
    public static void Main(string[] args)
    {
        var configuration = new ConfigurationBuilder()
            .AddCommandLine(args)
            .Build();

        var host = new WebHostBuilder()
            .UseKestrel()
            .UseContentRoot(Directory.GetCurrentDirectory())
            .UseConfiguration(configuration)
            .UseIISIntegration()
            .UseStartup<Startup>()
            .Build();

        host.Run();
    }
}

dotnet run --server.urls=http://localhost:5001/




.net oauth mendeley