OAuth dengan Verifikasi di .NET

103

Saya mencoba membuat aplikasi klien berbasis .NET (di WPF - meskipun untuk saat ini saya hanya melakukannya sebagai aplikasi konsol) untuk diintegrasikan dengan aplikasi yang mendukung OAuth, khususnya Mendeley ( http: // dev .mendeley.com ), yang ternyata menggunakan OAuth bercabang 3.

Ini adalah pertama kalinya saya menggunakan OAuth, dan saya mengalami banyak kesulitan untuk memulainya. Saya telah menemukan beberapa pustaka atau pembantu OAuth .NET, tetapi tampaknya lebih rumit daripada yang saya kira. Yang ingin saya lakukan adalah dapat mengeluarkan permintaan REST ke Mendeley API dan mendapatkan tanggapan kembali!

Sejauh ini, saya sudah mencoba:

Yang pertama (DotNetOpenAuth) sepertinya bisa melakukan apa yang saya butuhkan jika saya menghabiskan berjam-jam mencoba mencari tahu caranya. Yang kedua dan ketiga, sejauh yang saya tahu, tidak mendukung kode verifikasi yang dikirim kembali Mendeley - meskipun saya bisa saja salah tentang ini :)

Saya mendapat kunci dan rahasia konsumen dari Mendeley, dan dengan DotNetOpenAuth saya berhasil meluncurkan browser dengan halaman Mendeley yang menyediakan kode verifikasi bagi pengguna untuk masuk ke dalam aplikasi. Namun, pada titik ini saya tersesat dan tidak dapat menemukan cara untuk mengembalikannya ke aplikasi secara bijaksana.

Saya sangat bersedia untuk mengakui bahwa saya tidak tahu harus mulai dari mana (meskipun sepertinya ada kurva pembelajaran yang cukup curam) - jika ada yang bisa mengarahkan saya ke arah yang benar, saya akan menghargainya!

John
sumber

Jawaban:

182

Saya setuju denganmu. Kelas dukungan OAuth sumber terbuka yang tersedia untuk aplikasi .NET sulit dipahami, terlalu rumit (berapa banyak metode yang diekspos oleh DotNetOpenAuth?), Dirancang dengan buruk (lihat metode dengan 10 parameter string dalam modul OAuthBase.cs dari google itu tautan yang Anda berikan - tidak ada pengelolaan negara sama sekali), atau sebaliknya tidak memuaskan.

Tidak perlu serumit ini.

Saya bukan ahli tentang OAuth, tetapi saya telah menghasilkan kelas manajer sisi klien OAuth, yang berhasil saya gunakan dengan Twitter dan TwitPic. Ini relatif mudah digunakan. Ini open source dan tersedia di sini: Oauth.cs

Untuk review, di OAuth 1.0a ... agak lucu, ada nama khusus dan terlihat seperti "standar" tapi setahu saya satu-satunya layanan yang mengimplementasikan "OAuth 1.0a" adalah Twitter. Saya rasa itu cukup standar . ok, bagaimanapun juga di OAuth 1.0a, cara kerjanya untuk aplikasi desktop adalah ini:

  1. Anda, pengembang aplikasi, mendaftarkan aplikasi dan mendapatkan "kunci konsumen" dan "rahasia konsumen". Di Arstechnica, ada analisis yang ditulis dengan baik tentang mengapa model ini bukan yang terbaik , tapi seperti yang mereka katakan, memang seperti itu .

  2. Aplikasi Anda berjalan. Saat pertama kali dijalankan, pengguna harus secara eksplisit memberikan persetujuan kepada aplikasi untuk membuat permintaan REST yang diautentikasi oauth ke Twitter dan layanan sejenisnya (seperti TwitPic). Untuk melakukan ini, Anda harus melalui proses persetujuan, yang melibatkan persetujuan eksplisit oleh pengguna. Ini hanya terjadi saat pertama kali aplikasi berjalan. Seperti ini:

    • meminta "token permintaan". Token sementara.
    • pop halaman web, meneruskan token permintaan itu sebagai parameter kueri. Halaman web ini menampilkan UI kepada pengguna, menanyakan "apakah Anda ingin memberikan akses ke aplikasi ini?"
    • pengguna log in ke halaman web twitter, dan memberikan atau menolak akses.
    • halaman html respon muncul. Jika pengguna telah memberikan akses, ada PIN yang ditampilkan dalam font 48-pt
    • pengguna sekarang perlu memotong / menempelkan pin itu ke dalam kotak formulir windows, dan klik "Next" atau yang serupa.
    • aplikasi desktop kemudian melakukan permintaan yang diautentikasi oauth untuk "Access token". Permintaan REST lainnya.
    • aplikasi desktop menerima "token akses" dan "rahasia akses".

Setelah tarian persetujuan, aplikasi desktop cukup menggunakan "token akses" dan "rahasia akses" khusus pengguna (bersama dengan "kunci konsumen" dan "rahasia konsumen" khusus aplikasi) untuk melakukan permintaan yang diautentikasi atas nama pengguna ke Twitter. Ini tidak akan kedaluwarsa, meskipun jika pengguna membatalkan otorisasi aplikasi, atau jika Twitter karena alasan tertentu membatalkan otorisasi aplikasi Anda, atau jika Anda kehilangan token akses dan / atau rahasia, Anda perlu melakukan tarian persetujuan lagi .


Jika Anda tidak pandai, alur UI dapat meniru alur pesan OAuth multi-langkah. Ada cara yang lebih baik.

Gunakan kontrol WebBrowser, dan buka halaman web otorisasi dalam aplikasi desktop. Saat pengguna mengklik "Izinkan", ambil teks respons dari kontrol WebBrowser tersebut, ekstrak PIN secara otomatis, lalu dapatkan token akses. Anda mengirim 5 atau 6 permintaan HTTP tetapi pengguna hanya perlu melihat satu dialog Izinkan / Tolak. Sederhana.

Seperti ini:
teks alt


Jika UI Anda sudah diurutkan, satu-satunya tantangan yang tersisa adalah menghasilkan permintaan yang ditandatangani oleh oauth. Ini membuat banyak orang tersandung karena persyaratan penandatanganan oauth agak khusus. Itulah yang dilakukan kelas OAuth Manager yang disederhanakan.

Contoh kode untuk meminta 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");

ITU ITU . Sederhana. Seperti yang Anda lihat dari kodenya, cara untuk mendapatkan parameter oauth adalah melalui pengindeks berbasis string, seperti kamus. Metode AcquireRequestToken mengirimkan permintaan bertanda tangan oauth ke URL layanan yang memberikan token permintaan, alias token sementara. Untuk Twitter, URL ini adalah " https://api.twitter.com/oauth/request_token ". Spesifikasi oauth mengatakan Anda perlu mengemas set parameter oauth (token, token_secret, nonce, timestamp, consumer_key, versi, dan callback), dengan cara tertentu (dienkode-url dan digabungkan dengan ampersand), dan dalam leksikografis- urutan yang diurutkan, buat tanda tangan pada hasil itu, lalu kemas parameter yang sama bersama dengan tanda tangan, disimpan dalam parameter oauth_signature baru, dengan cara yang berbeda (digabungkan dengan koma). Kelas manajer OAuth melakukan ini untuk Anda secara otomatis. Ini menghasilkan nonce dan stempel waktu serta versi dan tanda tangan secara otomatis - aplikasi Anda tidak perlu peduli atau menyadari hal itu. Cukup setel nilai parameter oauth dan buat panggilan metode sederhana. kelas manajer mengirimkan permintaan dan mem-parsing respons untuk Anda.

Oke, lalu apa? Setelah Anda mendapatkan token permintaan, Anda memunculkan UI browser web di mana pengguna akan secara eksplisit memberikan persetujuan. Jika Anda melakukannya dengan benar, Anda akan memunculkan ini di browser yang disematkan. Untuk Twitter, URL untuk ini adalah " https://api.twitter.com/oauth/authorize?oauth_token= " dengan oauth_token yang ditambahkan. Lakukan ini dalam kode seperti ini:

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

(Jika Anda melakukan ini di browser eksternal yang akan Anda gunakan System.Diagnostics.Process.Start(url).)

Menyetel properti Url menyebabkan kontrol WebBrowser menavigasi ke halaman itu secara otomatis.

Saat pengguna mengklik tombol "Izinkan", halaman baru akan dimuat. Ini adalah bentuk HTML dan berfungsi sama seperti di browser lengkap. Dalam kode Anda, daftarkan penangan untuk acara DocumentedCompleted dari kontrol WebBrowser, dan di penangan itu, ambil 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();

Itu sedikit gesekan layar HTML.

Setelah mendapatkan pin, Anda tidak memerlukan browser web lagi, jadi:

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

... dan Anda mungkin ingin memanggil Buang () di atasnya juga.

Langkah selanjutnya adalah mendapatkan token akses, dengan mengirim pesan HTTP lain bersama pin itu. Ini adalah panggilan oauth bertanda lainnya, dibuat dengan pengurutan dan pemformatan oauth yang saya jelaskan di atas. Tapi sekali lagi ini sangat sederhana dengan kelas OAuth.Manager:

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

Untuk Twitter, URL tersebut adalah " https://api.twitter.com/oauth/access_token ".

Sekarang Anda memiliki token akses, dan Anda dapat menggunakannya dalam permintaan HTTP yang ditandatangani. Seperti ini:

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

... di mana urltitik akhir sumber daya. Untuk memperbarui status pengguna, itu akan menjadi " http://api.twitter.com/1/statuses/update.xml?status=Hello ".

Kemudian setel string itu ke Header HTTP bernama Otorisasi .

Untuk berinteraksi dengan layanan pihak ketiga, seperti TwitPic, Anda perlu membuat header OAuth yang sedikit berbeda , seperti ini:

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

Untuk Twitter, nilai untuk verifikasi kredibilitas url dan ranah adalah " https://api.twitter.com/1/account/verify_credentials.json ", dan " http://api.twitter.com/ ".

... dan taruh itu otorisasi string dalam HTTP header yang disebut X-Verifikasi-Kredensial-Otorisasi . Kemudian kirimkan ke layanan Anda, seperti TwitPic, bersama dengan permintaan apa pun yang Anda kirim.

Itu dia.

Secara keseluruhan, kode untuk memperbarui status twitter mungkin seperti ini:

// 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 agak rumit di bawah sampulnya, tetapi menggunakannya tidak perlu seperti itu. OAuth.Manager menangani pembuatan permintaan oauth keluar, serta penerimaan dan pemrosesan konten oauth dalam respons. Jika permintaan Request_token memberi Anda oauth_token, aplikasi Anda tidak perlu menyimpannya. Oauth.Manager cukup pintar untuk melakukan itu secara otomatis. Begitu juga ketika permintaan access_token mendapatkan kembali token akses dan rahasia, Anda tidak perlu menyimpannya secara eksplisit. OAuth.Manager menangani status itu untuk Anda.

Dalam proses selanjutnya, jika Anda sudah memiliki token akses dan rahasia, Anda dapat membuat instance OAuth.Manager seperti ini:

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;

... lalu buat header otorisasi seperti di atas.

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

Anda dapat mengunduh DLL yang berisi kelas OAuth.Manager di sini . Ada juga file bantuan dalam unduhan itu. Atau Anda dapat melihat file bantuan online .

Lihat contoh Formulir Windows yang menggunakan manajer ini di sini .


CONTOH KERJA

Unduh contoh yang berfungsi dari alat baris perintah yang menggunakan kelas dan teknik yang dijelaskan di sini:

Cheeso
sumber
Hai, terima kasih banyak atas tanggapan Anda! Saya sebenarnya telah pindah dari OAuth (saya sudah menyerah pada Mendeley dan telah memilih alternatif) - tetapi saya membaca jawaban Anda dan itu sangat masuk akal dan sangat komprehensif. Saya juga telah menandai kelas yang Anda tulis untuk waktu mendatang yang mungkin saya perlukan! Terima kasih banyak lagi.
Yohanes
2
Hai Cheeso, terima kasih telah membagikan kode dan penjelasan rinci Anda. Anda memberikan solusi yang hebat namun sederhana. Namun, Anda ingin membuat perubahan kecil dalam metode GetSignatureBase Anda untuk mendukung solusi non "oob". Untuk non "oob", Anda perlu menyandikan URL panggilan balik, jadi Anda akan ingin menambahkan sesuatu seperti ini saat Anda mengulang melalui this._params: if (p1.Key == "callback") {p.Add ( "oauth_" + p1.Key, UrlEncode (p1.Value)); lanjutkan;}
Johnny Oshika
1
Ini tidak berfungsi untuk OAuth 2.0. Kelas ini untuk OAuth 1.0a. OAuth2.0 jauh lebih mudah digunakan, karena tidak ada penandatanganan dan penyortiran leksikografik dari berbagai parameter. Jadi Anda mungkin tidak memerlukan kelas eksternal untuk melakukan OAuth 2.0, atau ... jika Anda memang membutuhkan kelas eksternal, ini akan jauh lebih sederhana daripada yang ini.
Cheeso
1
helpfile online tidak ditemukan: cheeso.members.winisp.net/OAuthManager1.1
Kiquenet
3
Semua tautan tampaknya rusak. Saya menemukan salinannya di sini: gist.github.com/DeskSupport/2951522#file-oauth-cs
John