.NET Core Identity Google External Provider
今天來學習如何在 .Net 中使用第三方登入, 我們之前在 Github 上面了解
微軟提供了 Google
、Twitter
、Facebook
等外部登入驗證
在使用之前需要先到Google API & Services 申請一組 Client ID
和 Client Secret
,詳細可以參考 微軟文檔
需要注意填入 redirect URI 為 https://localhost:{PORT}/signin-google
其中 {PORT}
為自己專案的端口,signin-google
為預設路徑可以不用修改
接下來在我們專案開啟 UserSecrets 功能,並且添加我們剛剛保存的 Id 跟 Secret
dotnet user-secrets init
dotnet user-secrets set "Authentication:Google:ClientId" "<client-id>"
dotnet user-secrets set "Authentication:Google:ClientSecret" "<client-secret>"
完成後回到我們的專案添加新的 Google Package
dotnet add package Microsoft.AspNetCore.Authentication.Google
並且到 Program.cs
加入新的 Google 驗證
builder.Services
.AddAuthentication(IdentityConstants.ApplicationScheme)
.AddCookie(IdentityConstants.ApplicationScheme,options =>
{
options.ExpireTimeSpan = TimeSpan.FromSeconds(10);
}).AddGoogle(IdentityConstants.ExternalScheme,o =>
{
o.ClientId = configuration["Authentication:Google:ClientId"];
o.ClientSecret = configuration["Authentication:Google:ClientSecret"];
});;
最後到 AccountController.cs
新增一個 Controller
[HttpGet(template: "~/externalSignin", Name = "ExternalSignin")]
public async Task ExternalSignin()
{
await HttpContext.ChallengeAsync(IdentityConstants.ExternalScheme,
new AuthenticationProperties()
{
RedirectUri = "/swagger/index.html"
});
}
完成之後運行專案發送請求之後發現 Response Headers 裡的 location 會帶入 Google 登入的連結
https://accounts.google.com/o/oauth2/v2/auth
?response_type=code
&client_id=4346799237-7d033l9p89o0rjavo0j2blvk3k7eqltg.apps.googleusercontent.com
&redirect_uri=https://localhost:7011/signin-google
&scope=openid profile email
&state=CfDJ8N83xunRSMFEnHYjbQSeE4ThIgXpHDiNr336vaI0A2XGr9OV_MvVQVuyB8JMzrcWdeTRLZXsWKvgu6C6ReIwWXTKvpzZjUcd_sFNVsyoDy9RosuoHu_gy4R4_SvXvN6gi3KC190ySBQ7Nv4CL1idpD30BqKFSgOgSPQnqk1MoAfdYFeCshQfwuOs8ae-HgSkyyA7Kt-OXTqHNfn5XVtmLLGdCL122B5295qYqY2S_eDL
我們可以手動在瀏覽器開啟此連結,瀏覽器會跳出我們經常使用的 Google 登入,並且要求使用者允許提供資料 按下同意之後 Google 會根據一開始設定的 redirect URI 進行轉跳回到我們的專案
https://localhost:7011/signin-google
?state=CfDJ8N83xunRSMFEnHYjbQSeE4RycERh7J02o8Qr90pBqpvCZIirwM5OpIWQbs0ao3Uz1NMD3nSpL_QAhwBI3DAD414_XYmzAPCNEpaNoY_cMCC8C0JVNOzccry8N-l6r307VjXLfO75_XGYY-iMn3WffbYb6KjeTh6KdGAX42PSN0taT_NC7oROcMI5WRs1wsEbMtd8osvpsIAWMfMldS3wo7xMKL4OIporbl6UrFayVy3M
&code=8/2B69qvKQkKtbceUNphCzkYa6BS7y3NVM-PgDBy9ckf3dKsvST2KfWNqRVGwCZntRtFbDWBM
&scope=email+profile+openid+https://www.googleapis.com/auth/userinfo.email+https://www.googleapis.com/auth/userinfo.profile
&authuser=0
&prompt=none
同時專案 GoogleHandler 會運行 CreateTicketAsync
方法並處理剩下的流程 GoogleHandler.cs
這段流程中最重要的是 Google 會傳給你 AccessToken 也叫做 Authentication Code,程式接收到之後會再拿這組 AccessToken 去請求 Google 的資源伺服器
最後 Google 資源伺服器驗證成功之後才會給我們使用者的私人資料
到此我們可以得知上面第一個請求 https://accounts.google.com/o/oauth2/v2/auth
是去跟驗證伺服器要一個臨時的通行證,因為有設定 scope
所以這個通行證的使用範圍會受到限制,並且會請問使用者允不允許提供這些資料給我們的網站,得到允許之後 Google 會帶著 AccessToken 發起我們網站設定的路徑請求
也就是上面第二個請求 https://localhost:7011/signin-google
因為 AccessToken 只有要求查看 openid profile email
這三個內容並且
使用者只同意我們網站查看這三份資料的內容,我們最後到資源伺服器也只能拿到三份資料的內容最後 OAuthCreatingTicketContext 會根據資源伺服器給我們的資料
建立出一個 ClaimsPrincipal 也可以理解成 User 並且設定到 HttpContext 內部,我們就可以認定使用者是登入成功了
可以使用之前建立的方法來取得 HttpContext 目前登入 User 的 Claim 來驗證看看 https://localhost:7011/listHttpContextClaim
[
{
"claim": {
"Type": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier",
"Value": "112873593891211851878"
}
},
{
"claim": {
"Type": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name",
"Value": "Allen Gao"
}
},
{
"claim": {
"Type": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname",
"Value": "Allen"
}
},
{
"claim": {
"Type": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname",
"Value": "Gao"
}
},
{
"claim": {
"Type": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress",
"Value": "allengaodev@gmail.com"
}
}
]
Summary
今天學習了如何使用 OAuth2 來取得外部使用者的資料,不過有一個問題是這個外部使用者跟我們當初呼叫 CreateUser 建立的使用者不太一樣 因為他並沒有添加到資料庫內部,所以之後有些權限相關的問題可能會造成困擾例如之前建立的年齡驗證,我們的外部使用者因為沒有在資料庫建立 IdentityUser 所以並沒有辦法添加生日的 Claims,這個問題會在之後解決
今天的進度 Github