從
Bee.OAuth2改版而來的Polhem.OAuth2,這次連 .NET MAUI 也一起涵蓋

在 .NET 應用程式加上「用 Google 登入」,看起來是早就有現成答案的事,但手上不只一種應用程式時就不一樣了。網站用 ASP.NET Core 的驗證處理常式,桌面工具需要能開瀏覽器、接住回呼的東西,行動 App 又是另一套做法,而那個還在線上服務的舊 Web Forms 網站,以上都用不上。
我想要一個能用同一套方式處理這些情境的小型函式庫,所以做了 Polhem.OAuth2,以 MIT 授權開源,可從 NuGet 取得。
如果你讀過我之前那篇 Bee.OAuth2 for .NET:快速整合 OAuth2 登入,Polhem.OAuth2 就是它的後繼版本。它保留了「每個 provider(登入服務供應商,如 Google、LINE)一組設定」的概念,其餘大多重新打造,第 6 節列出改了什麼。
127.0.0.1 或 localhost)接收回呼,並使用 PKCE。支援 Windows、macOS、Linux。WebAuthenticator 登入,使用 PKCE,App 裡不放 client secret。有自己 ASP.NET Core 後端的 App,也可以改經由後端登入,provider 的 token 就不會到裝置上。System.Web 上的 Web Forms 與 MVC 應用程式用。OAuth2Client 分兩步執行流程,不相依任何 HTTP 框架。PKCE(Proof Key for Code Exchange)是 OAuth2 的延伸規格:發起登入時先產生一組隨機值,換 token 時再出示它,即使授權碼被攔截,沒有這組值也換不到 token。對保不住 client secret 的桌面與手機 App 來說,這是必備的保護。
支援的 provider:Google、Facebook、LINE、Microsoft Entra ID、Auth0、Okta。每一家回傳的結果格式都一樣:使用者 ID、名稱與 email,加上 access、refresh 與 ID token。
| 套件 | 目標框架 | 用途 |
|---|---|---|
| Polhem.OAuth2 | netstandard2.0、net10.0 | 各 provider;桌面、主控台與 .NET MAUI 應用程式;其他伺服器框架 |
| Polhem.OAuth2.AspNetCore | net10.0 | ASP.NET Core |
| Polhem.OAuth2.AspNet | net472 | System.Web 上的 ASP.NET Web Forms 與 MVC |
LoopbackOAuth2Client 在回呼網址上監聽,用預設瀏覽器開啟登入頁,等 provider 導回來,再交換授權碼:
using Polhem.OAuth2;
var options = new GoogleOAuth2Options
{
ClientId = "your-client-id",
ClientSecret = "your-client-secret",
RedirectUri = "http://127.0.0.1:0/callback"
};
var client = new LoopbackOAuth2Client(options);
AuthorizationResult result = await client.SignInAsync();
if (result.IsSuccess)
Console.WriteLine($"{result.UserInfo.UserId} {result.UserInfo.UserName} {result.UserInfo.Email}");
Port 0 會在每次登入時挑一個空的 port。這只適用於接受任意 loopback port 的 provider(例如 Google),其他 provider 要用你註冊的 port。
在啟動時註冊 client:
builder.Services.AddOAuth2Client("Google", new GoogleOAuth2Options
{
ClientId = "your-client-id",
ClientSecret = "your-client-secret",
RedirectUri = "https://localhost:7032/auth/callback"
});
接著用兩個 action 開始與完成登入:
public class AuthController(OAuth2Manager oauth2Manager) : ControllerBase
{
[HttpGet("/auth/login")]
public IActionResult Login()
{
return Redirect(oauth2Manager.CreateAuthorizationUrl(HttpContext, "Google"));
}
[HttpGet("/auth/callback")]
public async Task<IActionResult> Callback()
{
AuthorizationResult result = await oauth2Manager.CompleteAuthorizationAsync(HttpContext, HttpContext.RequestAborted);
return result.IsSuccess
? Content($"Hello, {result.UserInfo.UserName}")
: Content($"The sign-in failed: {result.Exception.Message}");
}
}
每次登入的 state、PKCE code verifier 與回呼網址各自存在一個 cookie 裡,以 ASP.NET Core Data Protection 加密。因此不需要 Session,在多個分頁同時開始的登入不會互相覆蓋,多台伺服器只要共用 Data Protection 金鑰就能一起運作。
行動 App 裡的任何東西都可能被解開,保不住 client secret,所以 AppOAuth2Client 一律使用 PKCE、不帶 client secret。你要傳給它一個開啟瀏覽器的函式,通常就是 WebAuthenticator:
var options = new Auth0OAuth2Options
{
Domain = "your-tenant.auth0.com",
ClientId = "your-native-client-id",
RedirectUri = "com.example.app:/oauth2redirect"
};
var client = new AppOAuth2Client(options, async (url, redirectUri, cancellationToken) =>
(await WebAuthenticator.Default.AuthenticateAsync(url, redirectUri)).CallbackUri);
AuthorizationResult result = await client.SignInAsync();
如果 App 要登入自己的後端,可以由後端以 web client 的身分完成登入,再交給 App 一個只能用一次的 code。App 用這個 code 換取使用者資訊,provider 的 token 則留在伺服器上。
幾個決策決定了這個函式庫的行為:
result.Exception;未註冊的 client 名稱屬於程式錯誤,直接擲出。每個決策的理由都以 ADR(Architecture Decision Record,架構決策紀錄)寫在 repo 的 docs/adr/ 裡。
Bee.OAuth2.WinForms 與 Bee.OAuth2.Desktop 改由核心套件裡的 LoopbackOAuth2Client 取代。OAUTH2_STATE_KEY 環境變數:每次登入有自己的 cookie,以 ASP.NET Core Data Protection 或 MachineKey 保護。AddOAuth2Client 取代原本手動組出來的 OAuth2Manager singleton。Bee.Base 或 Newtonsoft.Json,JSON 改用 System.Text.Json 解析。README 有一份遷移指南,逐一對應舊的型別與方法該換成什麼。
安裝核心套件:
dotnet add package Polhem.OAuth2
README 涵蓋各類應用程式的用法,包括如何向各 provider 註冊回呼網址。repo 裡也附了範例應用程式:主控台、.NET 與 .NET Framework 上的 Windows Forms、ASP.NET Core、.NET MAUI。
這個函式庫只處理「登入並取得使用者資訊與 token」這一段,不驗證 ID token 的簽章,也不管登入後的帳號與權限。如果你的專案已經用 ASP.NET Core 內建的驗證機制或 OpenID Connect 套件處理得很好,不一定需要換;它比較適合手上同時有桌面、手機、網站幾種應用程式,想用同一套寫法的情境。歡迎回饋、開 issue 或送 pull request。
📘 HackMD 原文筆記:
👉 https://hackmd.io/@jeff377/polhem-oauth2
📢 歡迎轉載,請註明出處
📬 歡迎追蹤我的技術筆記與實戰經驗分享
Facebook | HackMD | GitHub | NuGet