跳至主要內容

為你的 .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 資源 (API resource) 至設定檔

若要取得可用於存取 Logto 中定義之 API 資源 (API resource) 的 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" // 請確保 key "resource" 為小寫
}
}
}

Resource 屬性用於將 API 資源 (API resource) 加入驗證請求 (auth request)。ExtraTokenParams 屬性則用於將 API 資源 (API resource) 加入權杖請求 (token request)。由於 Logto 遵循 RFC 8707 標準,這兩個屬性皆為必填。

警告:

由於 WebAssembly 屬於用戶端應用程式,權杖請求 (token request) 僅會發送至伺服器端一次。基於此特性,LoadUserInfo 與取得 API 資源 (API resource) 存取權杖 (access token) 會產生衝突。

使用存取權杖 (Access token)

使用者驗證後,你可以透過 User?.AccessToken 屬性存取 API 資源 (API resource)。例如,你可以使用 HttpClient 存取 API 資源 (API resource):

using Blorc.OpenIdConnect;

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

延伸閱讀

終端使用者流程:驗證流程、帳號流程與組織流程 (End-user flows: authentication flows, account flows, and organization flows) 設定連接器 (Configure connectors) 授權 (Authorization)