跳至主要內容

為你的 .NET Core (Blazor WASM) 應用程式新增驗證 (Authentication)

提示:

先決條件

安裝

將 NuGet 套件新增到你的專案中:

dotnet add package Blorc.OpenIdConnect

整合

新增腳本引用

index.html 文件中包含 Blorc.Core/injector.js

index.html
<head>
<!-- ... -->
<script src="_content/Blorc.Core/injector.js"></script>
<!-- ... -->
</head>

註冊服務

將以下程式碼新增至 Program.cs 文件:

Program.cs
using Blorc.OpenIdConnect;
using Blorc.Services;

builder.Services.AddBlorcCore();
builder.Services.AddAuthorizationCore();
builder.Services.AddBlorcOpenIdConnect(
options =>
{
builder.Configuration.Bind("IdentityServer", options);
});

var webAssemblyHost = builder.Build();

await webAssemblyHost
.ConfigureDocumentAsync(async documentService =>
{
await documentService.InjectBlorcCoreJsAsync();
await documentService.InjectOpenIdConnectAsync();
});

await webAssemblyHost.RunAsync();
資訊:

不需要使用 Microsoft.AspNetCore.Components.WebAssembly.Authentication 套件。Blorc.OpenIdConnect 套件將負責驗證 (Authentication) 流程。

配置重定向 URI

在深入細節之前,以下是終端使用者體驗的快速概覽。登入流程可簡化如下:

  1. 你的應用程式呼叫登入方法。
  2. 使用者被重定向至 Logto 登入頁面。對於原生應用程式,系統瀏覽器會被開啟。
  3. 使用者登入後被重定向回你的應用程式(配置為重定向 URI)。

關於基於重導的登入

  1. 此驗證流程遵循 OpenID Connect (OIDC) 協議,Logto 強制執行嚴格的安全措施以保護使用者登入。
  2. 如果你有多個應用程式,可以使用相同的身分提供者 (IdP, Identity provider)(Logto)。一旦使用者登入其中一個應用程式,Logto 將在使用者訪問另一個應用程式時自動完成登入流程。

欲了解更多關於基於重導登入的原理和優勢,請參閱 Logto 登入體驗解析


備註:

在以下的程式碼片段中,我們假設你的應用程式運行在 http://localhost:3000/

配置重定向 URI

切換到 Logto Console 的應用程式詳細資訊頁面。新增一個重定向 URI http://localhost:3000/callback

Logto Console 中的重定向 URI

就像登入一樣,使用者應被重定向到 Logto 以登出共享會話。完成後,將使用者重定向回你的網站會很不錯。例如,將 http://localhost:3000/ 新增為登出後重定向 URI 區段。

然後點擊「儲存」以保存更改。

配置應用程式

將以下程式碼新增至 appsettings.json 文件:

appsettings.json
{
// ...
IdentityServer: {
Authority: 'https://<your-logto-endpoint>/oidc',
ClientId: '<your-logto-app-id>',
PostLogoutRedirectUri: 'http://localhost:3000/',
RedirectUri: 'http://localhost:3000/callback',
ResponseType: 'code',
Scope: 'openid profile', // 如有需要可新增更多權限範圍 (Scopes)
},
}

記得將 RedirectUriPostLogoutRedirectUri 新增至 Logto 應用程式設定中的允許重定向 URI 列表中。它們都是你的 WASM 應用程式的 URL。

新增 AuthorizeView 元件

在需要驗證的 Razor 頁面中,新增 AuthorizeView 元件。假設是 Home.razor 頁面:

Pages/Home.razor
@using Microsoft.AspNetCore.Components.Authorization
@page "/"

<AuthorizeView>
<Authorized>
@* 已登入視圖 *@
<button @onclick="OnLogoutButtonClickAsync">
登出
</button>
</Authorized>
<NotAuthorized>
@* 未驗證視圖 *@
<button @onclick="OnLoginButtonClickAsync">
登入
</button>
</NotAuthorized>
</AuthorizeView>

設定驗證 (Authentication)

Home.razor.cs 文件中(如果不存在則創建),新增以下程式碼:

Pages/Home.razor.cs
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Blorc.OpenIdConnect;
using Microsoft.AspNetCore.Components.Authorization;

[Authorize]
public partial class Home : ComponentBase
{
[Inject]
public required IUserManager UserManager { get; set; }

public User<Profile>? User { get; set; }

[CascadingParameter]
protected Task<AuthenticationState>? AuthenticationStateTask { get; set; }

protected override async Task OnInitializedAsync()
{
User = await UserManager.GetUserAsync<User<Profile>>(AuthenticationStateTask!);
}

private async Task OnLoginButtonClickAsync(MouseEventArgs obj)
{
await UserManager.SignInRedirectAsync();
}

private async Task OnLogoutButtonClickAsync(MouseEventArgs obj)
{
await UserManager.SignOutRedirectAsync();
}
}

一旦使用者驗證成功,User 屬性將填入使用者資訊。

檢查點:測試你的應用程式

現在,你可以測試你的應用程式:

  1. 執行你的應用程式,你會看到登入按鈕。
  2. 點擊登入按鈕,SDK 會初始化登入流程並將你重定向到 Logto 登入頁面。
  3. 登入後,你將被重定向回應用程式並看到登出按鈕。
  4. 點擊登出按鈕以清除權杖存儲並登出。

獲取使用者資訊

顯示使用者資訊

以下是在 Home.razor 頁面中顯示使用者資訊的一些範例:

<AuthorizeView>
<Authorized>
@* 已登入視圖 *@
@* ... *@
<p>你已登入為 @(@User?.Profile?.Name ?? "(unknown name)").</p>
</Authorized>
@* ... *@
</AuthorizeView>

如需更多屬性和宣告,請檢查 Blorc.OpenIdConnect 套件中的 UserProfile 類別。

請求額外的宣告

你可能會發現從 User 返回的物件中缺少一些使用者資訊。這是因為 OAuth 2.0 和 OpenID Connect (OIDC) 的設計遵循最小權限原則 (PoLP, Principle of Least Privilege),而 Logto 是基於這些標準構建的。

預設情況下,僅返回有限的宣告 (Claims)。如果你需要更多資訊,可以請求額外的權限範圍 (Scopes) 以存取更多宣告。

資訊:

「宣告 (Claim)」是對主體所做的斷言;「權限範圍 (Scope)」是一組宣告。在目前的情況下,宣告是關於使用者的一部分資訊。

以下是權限範圍與宣告關係的非規範性範例:

提示:

「sub」宣告表示「主體 (Subject)」,即使用者的唯一識別符(例如使用者 ID)。

Logto SDK 將始終請求三個權限範圍:openidprofileoffline_access

要請求額外的權限範圍 (Scopes),可以在 appsettings.json 檔案中將有效的權限範圍新增到 IdentityServer.Scope 屬性。

{
// ...
"IdentityServer": {
// ...
"Scope": "openid profile email phone"
}
}

需要網路請求的宣告

為了避免使用者物件過於龐大,某些宣告需要透過網路請求來獲取。例如,即使在權限範圍中請求了 custom_data 宣告,它也不會包含在使用者物件中。要獲取這些宣告,可以在 appsettings.json 檔案中將 IdentityServer.LoadUserInfo 屬性設為 true

例如,要獲取使用者的電子郵件地址和自訂資料,可以使用以下配置:

{
// ...
"IdentityServer": {
// ...
"Scope": "openid profile email custom_data",
"LoadUserInfo": true
}
}

權限範圍 (Scopes) 和宣告 (Claims)

Logto 使用 OIDC 權限範圍 (Scopes) 和宣告 (Claims) 慣例 來定義從 ID 權杖 (ID token) 和 OIDC 使用者資訊端點 (userinfo endpoint) 獲取使用者資訊的權限範圍和宣告。無論是「權限範圍 (Scope)」還是「宣告 (Claim)」,都是 OAuth 2.0 和 OpenID Connect (OIDC) 規範中的術語。

以下是支援的權限範圍 (Scopes) 及其對應的宣告 (Claims):

openid

宣告名稱類型描述需要使用者資訊嗎?
substring使用者的唯一識別符

profile

宣告名稱類型描述需要使用者資訊嗎?
namestring使用者的全名
usernamestring使用者的用戶名
picturestring使用者個人資料圖片的 URL。此 URL 必須指向圖像文件(例如 PNG、JPEG 或 GIF 圖像文件),而不是包含圖像的網頁。請注意,此 URL 應特別參考適合在描述使用者時顯示的個人資料照片,而不是使用者拍攝的任意照片。
created_atnumber使用者創建的時間。時間以自 Unix epoch(1970-01-01T00:00:00Z)以來的毫秒數表示。
updated_atnumber使用者資訊最後更新的時間。時間以自 Unix epoch(1970-01-01T00:00:00Z)以來的毫秒數表示。

其他 標準宣告 包括 family_namegiven_namemiddle_namenicknamepreferred_usernameprofilewebsitegenderbirthdatezoneinfolocale 也將包含在 profile 權限範圍中,無需請求使用者資訊端點。與上述宣告的不同之處在於,這些宣告僅在其值不為空時返回,而上述宣告在值為空時將返回 null

備註:

與標準宣告不同,created_atupdated_at 宣告使用毫秒而非秒。

email

宣告名稱類型描述需要使用者資訊嗎?
emailstring使用者的電子郵件地址
email_verifiedboolean電子郵件地址是否已驗證

phone

宣告名稱類型描述需要使用者資訊嗎?
phone_numberstring使用者的電話號碼
phone_number_verifiedboolean電話號碼是否已驗證

address

請參閱 OpenID Connect Core 1.0 以獲取地址宣告的詳細資訊。

custom_data

宣告名稱類型描述需要使用者資訊嗎?
custom_dataobject使用者的自訂資料

identities

宣告名稱類型描述需要使用者資訊嗎?
identitiesobject使用者的連結身分
sso_identitiesarray使用者的連結 SSO 身分

roles

宣告名稱類型描述需要使用者資訊嗎?
rolesstring[]使用者的角色

urn:logto:scope:organizations

宣告名稱類型描述需要使用者資訊嗎?
organizationsstring[]使用者所屬的組織 ID
organization_dataobject[]使用者所屬的組織資料

urn:logto:scope:organization_roles

宣告名稱類型描述需要使用者資訊嗎?
organization_rolesstring[]使用者所屬的組織角色,格式為 <organization_id>:<role_name>

考慮到效能和資料大小,如果「需要使用者資訊嗎?」為「是」,則表示該宣告不會顯示在 ID 權杖中,而會在 使用者資訊端點 回應中返回。

API 資源

我們建議先閱讀 🔐 角色型存取控制 (RBAC, Role-Based Access Control),以瞭解 Logto RBAC 的基本概念以及如何正確設定 API 資源。

預設情況下,當你存取 User?.AccessToken 時,你將獲得一個不透明權杖 (Opaque token),其長度較短且不是 JWT (JSON Web Token)。此權杖用於存取 userinfo 端點。

將 API 資源新增到配置

若要獲得可用於存取 Logto 中定義的 API 資源的 JWT 權杖,請在 appsettings.json 文件中新增以下參數(以 https://my-api-resource 為例):

appsettings.json
{
// ...
"IdentityServer": {
// ...
"Scope": "openid profile email <your-api-scopes>", // 在此新增你的 API 權限範圍 (Scopes)
"Resource": "https://my-api-resource",
"ExtraTokenParams": {
"resource": "https://my-api-resource" // 確保鍵 "resource" 為小寫
}
}
}

Resource 屬性用於將 API 資源新增到驗證請求中。ExtraTokenParams 屬性用於將 API 資源新增到權杖請求中。由於 Logto 遵循 RFC 8707 的 API 資源規範,因此這兩個屬性都是必需的。

警告:

由於 WebAssembly 是客戶端應用程式,權杖請求只會發送到伺服器端一次。由於這一特性,LoadUserInfo 與獲取 API 資源的存取權杖 (Access token) 會產生衝突。

使用存取權杖 (Access token)

一旦使用者通過驗證,你可以使用 User?.AccessToken 屬性來存取 API 資源。例如,你可以使用 HttpClient 來存取 API 資源:

using Blorc.OpenIdConnect;

builder.Services
.AddHttpClient("MyApiResource", client =>
{
client.BaseAddress = new Uri("https://my-api-resource");
})
.AddAccessToken(); // 將存取權杖新增到請求標頭

延伸閱讀

終端使用者流程:驗證流程、帳戶流程與組織流程 配置連接器 保護你的 API