LINE登入-使用.NET Core OAuth
LINE登入-使用.NET Core OAuth
這篇算是同場加映,因為LINE Login 微軟官方並沒有提供現成的OAuth.LINE可用,所以我研究了一下替代方案OAuth,這套件似乎不用額外安裝,因為中間也採了很多雷,覺得值得紀錄一下,順便分享出來,看是否有其他人適用,當然也不僅適用於LINE,只要是OAuth流程都可以使用,這方法主要是使用Microsoft.AspNetCore.Authentication.OAuth自己客製化。
LINE 應用程式服務建立請參考 LINE登入-手動建立登入 OAuth 2
OAuth 2.0,其工作方式如下:
- 應用程式(OAuth套件)將使用者導向第三方驗證連結
AuthorizationEndpoint
,就是那個登入視窗,使用者輸入帳號和密碼,並授權應用程式可以訪問哪些請求的資源,會不會出現請求資源的視窗,是透過設定Scope而定,並不一定會有。 - OAuth會攔截授權結果,若成功會取得
code
,這相當於使用者同意書的概念 - 接著應用程式會將授權代碼
code
,連同ClientId
、ClientSecret
、CallbackPath
,發送到TokenEndpoint,這是一個交換的過程,把授權書(code),還有API服務的帳號給驗證方,交換訪問令牌AccessToken
。 - 驗證方的授權服務器返回AccessToken。
- 若成功取得AccessToken,應用程式會把AccessToken附在
UserInformationEndpoint
連結中,告知要取得個人資料。 - 授權服務器返回所請求的個人資料。
使用Authentication.OAuth客製化LINE登入
-
要先給OAuth服務一個名稱,因為可能同時存在很多個第三方驗證,這邊的名稱要與authenticationScheme對應
1
AddOAuth("Line");
-
設置基本OAuth參數,如果對OAuth已經有初步了解,大概就知道這些參數的意義,多數是不能省略的,要特別留意
CallbackPath
這項(必填),他回傳是AuthorizationEndpoint
驗證的結果code
,這路徑不需要實際存在,只要與Line Developer API的回傳路徑對應即可,如果這邊填寫了實際回傳路徑,則會告訴你State
驗證值被改變了,反而不能登入,程序中他會將Token轉換為TokenResponse
。另外LINE Profile 並不提供Email,所以
options.Scope.Add("email")
也可以省略1 2 3 4 5 6 7 8 9 10 11 12
options.ClientId = "{應用程式編號}"; options.ClientSecret = "{應用程式密鑰}"; options.AuthorizationEndpoint = "https://access.line.me/oauth2/v2.1/authorize"; options.TokenEndpoint = "https://api.line.me/oauth2/v2.1/token"; options.UserInformationEndpoint = "https://api.line.me/v2/profile"; options.CallbackPath = new PathString("/signin-line"); options.Scope.Add("profile"); options.Scope.Add("openid"); // 這邊即使設定了Email,https://api.line.me/v2/profile 也沒有提供Email欄位 // options.Scope.Add("email");
-
對應使用者資料,這邊要看API Response什麼資料
1 2
options.ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "userId"); options.ClaimActions.MapJsonKey(ClaimTypes.Name, "displayName", "string");
-
創造OAuth事件,並將資料對應到Claims
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
options.Events = new OAuthEvents { OnCreatingTicket = async context => { var request = new HttpRequestMessage(HttpMethod.Get, context.Options.UserInformationEndpoint); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", context.AccessToken); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); var response = await context.Backchannel.SendAsync(request, context.HttpContext.RequestAborted); response.EnsureSuccessStatusCode(); var json = await response.Content.ReadAsStringAsync(); var user = JsonDocument.Parse(json); context.RunClaimActions(user.RootElement); }, OnRemoteFailure = context => { context.HandleResponse(); context.Response.Redirect("/Home/Error?message=" + context.Failure.Message); return Task.FromResult(0); } };
1 2 3 4 5 6 7 8 9
// Event執行順序: // 1.創建Ticket之前觸發 options.Events.OnCreatingTicket = context => Task.CompletedTask; // 2.創建Ticket失敗時觸發 options.Events.OnRemoteFailure = context => Task.CompletedTask; // 3.Ticket接收完成後觸發 options.Events.OnTicketReceived = context => Task.CompletedTask; // 4.Challenge時觸發,默認跳轉到OAuth Server options.Events.OnRedirectToAuthorizationEndpoint = context => context.Response.Redirect(context.RedirectUri);
-
.NET Core版本,自訂OAuth驗證,需要啟動
Authentication
服務,如果少了這行,他不會執行Events程序1 2
app.UseAuthentication(); app.UseAuthorization();
完整程式碼
Startup.cs
|
|
Controller
|
|
View
|
|
參考
Nuget OAuth 套件
https://www.nuget.org/packages?q=owners%3Aaspnet-contrib+title%3AOAuth
ASP.NET Core 中保存外部提供者的其他宣告
ASP.NET Core 2.0 Authentication and Authorization System Demystified
https://digitalmccullough.com/posts/aspnetcore-auth-system-demystified.html
結論
我在CallbackPath吃了不少虧,反覆測了很久,才了解那個傳回的是code
和state
,寫完後我覺得如果回傳值都有自己想要的,那會很方便,但LINE Profile沒有提供Email,也在這邊花了不少時間,還意外從SourceCode,仿造出Line擴充…
題外話…一開始以為仿造Google的擴充是一個好的起始點,但研究一陣子後,發現最大困難點在於OAuth
使用的TokenResponse
,不好擴充,所以與其從OAuth開始著手,不如像Twitter,由Microsoft.AspNetCore.Authentication
開始還比較簡單,真的是入門到放棄…