OAuth với xác minh trong .NET


103

Tôi đang cố gắng tạo một ứng dụng khách dựa trên .NET (trong WPF - mặc dù hiện tại tôi chỉ đang làm nó như một ứng dụng bảng điều khiển) để tích hợp với một ứng dụng hỗ trợ OAuth, cụ thể là Mendeley ( http: // dev .mendeley.com ), dường như sử dụng OAuth 3 chân.

Đây là lần đầu tiên tôi sử dụng OAuth và tôi đang gặp rất nhiều khó khăn khi bắt đầu với nó. Tôi đã tìm thấy một số thư viện hoặc trình trợ giúp .NET OAuth, nhưng chúng có vẻ phức tạp hơn tôi nghĩ. Tất cả những gì tôi muốn làm là có thể đưa ra các yêu cầu REST tới API Mendeley và nhận lại phản hồi!

Cho đến nay, tôi đã thử:

Đầu tiên (DotNetOpenAuth) có vẻ như nó có thể làm được những gì tôi cần nếu tôi đã dành hàng giờ đồng hồ để tìm ra cách. Điều thứ hai và thứ ba, tốt nhất tôi có thể nói, không hỗ trợ mã xác minh mà Mendeley đang gửi lại - mặc dù tôi có thể sai về điều này :)

Tôi đã có chìa khóa người tiêu dùng và bí mật từ Mendeley và với DotNetOpenAuth, tôi đã quản lý để khởi chạy trình duyệt với trang Mendeley cung cấp mã xác minh để người dùng nhập vào ứng dụng. Tuy nhiên, tại thời điểm này, tôi bị lạc và không thể tìm ra cách cung cấp hợp lý cho ứng dụng.

Tôi rất sẵn lòng thừa nhận rằng tôi không biết bắt đầu từ đâu (mặc dù có vẻ như có một đường cong học tập khá dốc) - nếu ai đó có thể chỉ cho tôi đi đúng hướng, tôi sẽ đánh giá cao điều đó!

Câu trả lời:


182

Tôi đồng ý với bạn. Các lớp hỗ trợ OAuth nguồn mở có sẵn cho các ứng dụng .NET rất khó hiểu, quá phức tạp (có bao nhiêu phương thức được hiển thị bởi DotNetOpenAuth?), Được thiết kế kém (hãy xem các phương thức có 10 tham số chuỗi trong mô-đun OAuthBase.cs từ google đó liên kết bạn đã cung cấp - không có quản lý nhà nước nào cả), hoặc không đạt yêu cầu.

Nó không cần phải phức tạp thế này.

Tôi không phải là chuyên gia về OAuth, nhưng tôi đã tạo ra một lớp người quản lý phía máy khách OAuth mà tôi sử dụng thành công với Twitter và TwitPic. Nó tương đối đơn giản để sử dụng. Nó là mã nguồn mở và có sẵn tại đây: Oauth.cs

Để xem lại, trong OAuth 1.0a ... thật buồn cười, có một cái tên đặc biệt và nó trông giống như một "tiêu chuẩn" nhưng theo tôi biết dịch vụ duy nhất triển khai "OAuth 1.0a" là Twitter. Tôi đoán vậy là đủ tiêu chuẩn . ok, dù sao trong OAuth 1.0a, cách nó hoạt động cho các ứng dụng máy tính để bàn là như sau:

  1. Bạn, nhà phát triển ứng dụng, đăng ký ứng dụng và nhận "khóa khách hàng" và "bí mật của người tiêu dùng". Trên Arstechnica, có một bài phân tích bằng văn bản về lý do tại sao mô hình này không phải là tốt nhất , nhưng như họ nói, nó là như thế nào .

  2. Ứng dụng của bạn chạy. Lần đầu tiên nó chạy, nó cần phải được người dùng phê duyệt rõ ràng để ứng dụng thực hiện các yêu cầu REST được xác thực bằng oauth tới Twitter và các dịch vụ chị em của nó (như TwitPic). Để làm điều này, bạn phải trải qua một quy trình phê duyệt, liên quan đến sự chấp thuận rõ ràng của người dùng. Điều này chỉ xảy ra lần đầu tiên ứng dụng chạy. Như thế này:

    • yêu cầu một "mã thông báo yêu cầu". Mã thông báo tạm thời Aka.
    • bật một trang web, chuyển mã thông báo yêu cầu đó dưới dạng tham số truy vấn. Trang web này hiển thị giao diện người dùng cho người dùng, hỏi "bạn có muốn cấp quyền truy cập vào ứng dụng này không?"
    • người dùng đăng nhập vào trang web twitter và cấp hoặc từ chối quyền truy cập.
    • trang html phản hồi xuất hiện. Nếu người dùng đã cấp quyền truy cập, sẽ có một mã PIN được hiển thị bằng phông chữ 48 pt
    • người dùng bây giờ cần cắt / dán ghim đó vào hộp biểu mẫu cửa sổ và nhấp vào "Tiếp theo" hoặc một cái gì đó tương tự.
    • sau đó ứng dụng dành cho máy tính để bàn thực hiện yêu cầu được xác thực bằng oauth đối với "Mã thông báo truy cập". Một yêu cầu REST khác.
    • ứng dụng dành cho máy tính để bàn nhận "mã thông báo truy cập" và "bí mật truy cập".

Sau bước nhảy phê duyệt, ứng dụng dành cho máy tính để bàn chỉ có thể sử dụng "mã thông báo truy cập" và "bí mật truy cập" dành riêng cho người dùng (cùng với "khóa người tiêu dùng" và "bí mật người tiêu dùng" dành riêng cho ứng dụng) để thực hiện các yêu cầu được xác thực thay mặt người dùng lên Twitter. Những thứ này không hết hạn, mặc dù nếu người dùng hủy cấp phép ứng dụng hoặc nếu Twitter vì lý do nào đó hủy cấp phép ứng dụng của bạn hoặc nếu bạn mất mã thông báo truy cập và / hoặc bí mật, bạn cần thực hiện lại bước phê duyệt .


Nếu bạn không khéo léo, luồng giao diện người dùng có thể phản ánh luồng thông báo OAuth gồm nhiều bước. Có một cách tốt hơn.

Sử dụng điều khiển WebBrowser và mở trang web ủy quyền trong ứng dụng dành cho máy tính để bàn. Khi người dùng nhấp vào "Cho phép", lấy văn bản phản hồi từ điều khiển WebBrowser đó, trích xuất mã PIN tự động, sau đó nhận mã thông báo truy cập. Bạn gửi 5 hoặc 6 yêu cầu HTTP nhưng người dùng chỉ cần thấy một hộp thoại Cho phép / Từ chối duy nhất. Đơn giản.

Như thế này:
văn bản thay thế


Nếu bạn đã sắp xếp giao diện người dùng, thách thức duy nhất còn lại là tạo ra các yêu cầu có chữ ký oauth. Điều này thu hút rất nhiều người bởi vì các yêu cầu ký oauth là một loại đặc biệt. Đó là những gì lớp Trình quản lý OAuth đơn giản hóa làm.

Mã mẫu để yêu cầu mã thông báo:

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

ĐÓ . Đơn giản. Như bạn có thể thấy từ mã, cách để truy cập các tham số oauth là thông qua trình chỉ mục dựa trên chuỗi, giống như một từ điển. Phương thức AcquireRequestToken gửi một yêu cầu có chữ ký oauth đến URL của dịch vụ cấp mã thông báo yêu cầu, hay còn gọi là mã thông báo tạm thời. Đối với Twitter, URL này là " https://api.twitter.com/oauth/request_token ". Thông số oauth cho biết bạn cần phải đóng gói tập hợp các tham số oauth (mã thông báo, mã thông báo_secret, nonce, dấu thời gian, khóa người tiêu dùng, phiên bản và lệnh gọi lại), theo một cách nhất định (được mã hóa url và kết hợp bằng ký hiệu và) và theo từ điển- sắp xếp thứ tự, tạo chữ ký trên kết quả đó, sau đó đóng gói các tham số tương tự đó cùng với chữ ký, được lưu trữ trong tham số oauth_signature mới, theo một cách khác (được nối bằng dấu phẩy). Lớp người quản lý OAuth tự động thực hiện việc này cho bạn. Nó tự động tạo ra các dấu không và dấu thời gian cũng như các phiên bản và chữ ký - ứng dụng của bạn không cần quan tâm hoặc nhận thức được những thứ đó. Chỉ cần đặt các giá trị tham số oauth và thực hiện một cuộc gọi phương thức đơn giản. lớp người quản lý gửi yêu cầu và phân tích phản hồi cho bạn.

Được rồi sau đó cái gì? Khi bạn nhận được mã thông báo yêu cầu, bạn bật giao diện người dùng của trình duyệt web mà người dùng sẽ cấp phê duyệt một cách rõ ràng. Nếu bạn làm đúng, bạn sẽ hiển thị nó trong một trình duyệt được nhúng. Đối với Twitter, URL cho điều này là " https://api.twitter.com/oauth/authorize?oauth_token= " với oauth_token được thêm vào. Làm điều này trong mã như sau:

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

(Nếu bạn đang làm điều này trong một trình duyệt bên ngoài mà bạn sử dụng System.Diagnostics.Process.Start(url).)

Đặt thuộc tính Url khiến điều khiển WebBrowser tự động điều hướng đến trang đó.

Khi người dùng nhấp vào nút "Cho phép", một trang mới sẽ được tải. Đó là một dạng HTML và nó hoạt động giống như trong một trình duyệt đầy đủ. Trong mã của bạn, hãy đăng ký một trình xử lý cho sự kiện DocumentedCompleted của điều khiển WebBrowser và trong trình xử lý đó, lấy ghim:

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

Đó là một chút về màn hình HTML.

Sau khi lấy mã pin, bạn không cần trình duyệt web nữa, vì vậy:

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

... và bạn cũng có thể muốn gọi Dispose () trên đó.

Bước tiếp theo là lấy mã thông báo truy cập, bằng cách gửi một thông báo HTTP khác cùng với mã pin đó. Đây là một lệnh gọi oauth có chữ ký khác, được xây dựng với thứ tự và định dạng oauth mà tôi đã mô tả ở trên. Nhưng một lần nữa điều này thực sự đơn giản với lớp OAuth.Manager:

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

Đối với Twitter, URL đó là " https://api.twitter.com/oauth/access_token ".

Bây giờ bạn có mã thông báo truy cập và bạn có thể sử dụng chúng trong các yêu cầu HTTP đã ký. Như thế này:

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

... urlđiểm cuối tài nguyên ở đâu. Để cập nhật trạng thái của người dùng, nó sẽ là " http://api.twitter.com/1/statuses/update.xml?status=Hello ".

Sau đó đặt chuỗi đó vào Tiêu đề HTTP có tên Ủy quyền .

Để tương tác với các dịch vụ của bên thứ ba, như TwitPic, bạn cần phải tạo một tiêu đề OAuth hơi khác một chút , như sau:

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

Đối với Twitter, các giá trị cho url và vùng xác minh tín dụng lần lượt là " https://api.twitter.com/1/account/verify_credentials.json " và " http://api.twitter.com/ ".

... và đặt chuỗi ủy quyền đó trong tiêu đề HTTP được gọi là X-Verify-Credentials-Authorization . Sau đó, gửi nó đến dịch vụ của bạn, như TwitPic, cùng với bất kỳ yêu cầu nào bạn đang gửi.

Đó là nó.

Tất cả cùng nhau, mã để cập nhật trạng thái twitter có thể giống như sau:

// 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 khá phức tạp nhưng không cần thiết phải sử dụng nó. OAuth.Manager xử lý việc tạo ra các yêu cầu oauth gửi đi và nhận và xử lý nội dung oauth trong các phản hồi. Khi yêu cầu Request_token cung cấp cho bạn một oauth_token, ứng dụng của bạn không cần phải lưu trữ nó. Oauth.Manager đủ thông minh để làm điều đó tự động. Tương tự như vậy khi yêu cầu access_token nhận lại mã thông báo truy cập và bí mật, bạn không cần phải lưu trữ chúng một cách rõ ràng. OAuth.Manager xử lý trạng thái đó cho bạn.

Trong các lần chạy tiếp theo, khi bạn đã có mã thông báo truy cập và bí mật, bạn có thể khởi tạo OAuth.Manager như sau:

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;

... và sau đó tạo tiêu đề ủy quyền như trên.

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

Bạn có thể tải xuống một tệp DLL chứa lớp OAuth.Manager tại đây . Cũng có một tệp trợ giúp trong bản tải xuống đó. Hoặc bạn có thể xem hồ sơ trợ giúp trực tuyến .

Xem ví dụ về Biểu mẫu Windows sử dụng trình quản lý này tại đây .


VÍ DỤ LÀM VIỆC

Tải xuống ví dụ làm việc của một công cụ dòng lệnh sử dụng lớp và kỹ thuật được mô tả tại đây:


Xin chào, cảm ơn rất nhiều vì phản hồi của bạn! Tôi thực sự đã chuyển từ OAuth (Tôi đã từ bỏ Mendeley và đã chọn một giải pháp thay thế) - nhưng tôi đã đọc qua câu trả lời của bạn và nó có rất nhiều ý nghĩa và rất toàn diện. Tôi cũng đã đánh dấu lớp bạn đã viết cho bất kỳ lúc nào tôi có thể cần nó trong tương lai! Rất cám ơn một lần nữa.
John

2
Xin chào Cheeso, cảm ơn bạn đã chia sẻ mã và giải thích chi tiết của bạn. Bạn đã cung cấp một giải pháp tuyệt vời nhưng đơn giản. Tuy nhiên, bạn sẽ muốn thực hiện một thay đổi nhỏ trong phương thức GetSignatureBase của mình để hỗ trợ các giải pháp không "oob". Đối với không phải "oob", bạn cần phải mã hóa URL cho lệnh gọi lại, vì vậy bạn sẽ muốn thêm một cái gì đó như thế này khi bạn lặp qua this._params: if (p1.Key == "callback") {p.Add ( "oauth_" + p1.Key, UrlEncode (p1.Value)); tiếp tục;}
Johnny Oshika

1
Điều này không hoạt động cho OAuth 2.0. Lớp này dành cho OAuth 1.0a. OAuth2.0 đơn giản hơn đáng kể để sử dụng, vì không có ký hiệu và phân loại từ vựng của các tham số khác nhau. Vì vậy, bạn có thể không cần một lớp bên ngoài để thực hiện OAuth 2.0, hoặc ... nếu bạn cần một lớp bên ngoài, nó sẽ đơn giản hơn nhiều so với lớp này.
Cheeso

1
Không tìm thấy hồ sơ trợ giúp trực tuyến : cheeso.members.winisp.net/OAuthManager1.1
Kiquenet Ngày

3
Tất cả các liên kết dường như bị phá vỡ. Tôi đã tìm thấy một bản sao ở đây: gist.github.com/DeskSupport/2951522#file-oauth-cs
John
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.