You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在.NET 8 Blazor WASM中通过Keycloak OIDC获取令牌

.NET 8 Blazor WASM 集成Keycloak OIDC获取令牌的解决方案

一、修正OIDC配置,添加令牌接收事件处理

你可以通过扩展AddOidcAuthentication的配置,注册TokenReceived事件来捕获并存储令牌:

builder.Services.AddOidcAuthentication(options =>
{
    options.ProviderOptions.MetadataUrl = "https://my-domain.com/.well-known/openid-configuration";
    options.ProviderOptions.Authority = "https://my-domain.com/realms/my-realm";
    options.ProviderOptions.ClientId = "website-auth";
    options.ProviderOptions.ResponseType = "id_token token";
    // 必须包含openid scope,这是OIDC协议的必填项
    options.ProviderOptions.Scope.Add("openid");
    options.UserOptions.NameClaim = "preferred_username";
    options.UserOptions.RoleClaim = ClaimTypes.Role;
    options.UserOptions.ScopeClaim = "scope";
})
.AddAccountClaimsPrincipalFactory<CustomAccountClaimsPrincipalFactory>()
.ConfigureProviderOptions<OpenIdConnectProviderOptions, OidcProviderOptions>(options =>
{
    // 注册TokenReceived事件,捕获id_token并存储
    options.Events.OnTokenReceived = context =>
    {
        var idToken = context.TokenEndpointResponse.IdToken;
        if (!string.IsNullOrEmpty(idToken))
        {
            // 示例:存储到浏览器localStorage
            context.HttpContext.RequestServices.GetRequiredService<IJSRuntime>()
                .InvokeVoidAsync("localStorage.setItem", "keycloak_id_token", idToken);
        }
        return Task.CompletedTask;
    };
});

二、正确使用IAccessTokenProvider获取令牌

你遇到的空令牌问题,大概率是注入方式或调用时机错误:

  1. 直接注入IAccessTokenProvider:无需通过IAccessTokenProviderAccessor,直接在组件/服务中注入即可:
private readonly IAccessTokenProvider _tokenProvider;

public YourComponent(IAccessTokenProvider tokenProvider)
{
    _tokenProvider = tokenProvider;
}
  1. 确保在已认证状态下调用:只有用户登录后才能获取有效令牌,调用前先验证认证状态:
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
if (authState.User.Identity?.IsAuthenticated == true)
{
    var tokenResult = await _tokenProvider.RequestAccessToken();
    if (tokenResult.TryGetToken(out var token))
    {
        var accessToken = token.Value;
        // 也可从认证状态中提取id_token
        var idToken = authState.User.FindFirst("id_token")?.Value;
    }
}
  1. 指定请求的Scope:如果需要特定权限的令牌,可在请求时明确Scope:
var tokenResult = await _tokenProvider.RequestAccessToken(new AccessTokenRequestOptions
{
    Scopes = new[] { "openid", "profile" }
});

三、从认证状态直接提取令牌

Keycloak返回的令牌通常会以Claim形式存在用户认证信息中,可直接提取:

var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
var idToken = authState.User.Claims.FirstOrDefault(c => c.Type == "id_token")?.Value;
var accessToken = authState.User.Claims.FirstOrDefault(c => c.Type == "access_token")?.Value;

如果Claim中没有对应令牌,可在自定义CustomAccountClaimsPrincipalFactory中添加:

public class CustomAccountClaimsPrincipalFactory : AccountClaimsPrincipalFactory<RemoteUserAccount>
{
    public CustomAccountClaimsPrincipalFactory(IAccessTokenProviderAccessor accessor) 
        : base(accessor) { }

    public override async ValueTask<ClaimsPrincipal> CreateUserAsync(
        RemoteUserAccount account,
        RemoteAuthenticationUserOptions options)
    {
        var principal = await base.CreateUserAsync(account, options);
        
        // 将令牌添加到Claims集合
        if (account.AdditionalProperties.TryGetValue("id_token", out var idToken))
        {
            ((ClaimsIdentity)principal.Identity).AddClaim(new Claim("id_token", idToken.ToString()));
        }
        if (account.AdditionalProperties.TryGetValue("access_token", out var accessToken))
        {
            ((ClaimsIdentity)principal.Identity).AddClaim(new Claim("access_token", accessToken.ToString()));
        }
        
        return principal;
    }
}

四、常见问题排查

  • 确认ResponseType设置为id_token token,确保同时返回两种令牌
  • 检查Keycloak客户端配置,将"Access Type"设为"public"(Blazor WASM属于单页公开应用)
  • 确保Scope包含openid,缺少该Scope会导致OIDC流程不完整
  • 调用RequestAccessToken前,确认用户已完成登录跳转流程

内容的提问来源于stack exchange,提问作者Dmitriy Kudinov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 00:22:45