Try   HackMD

dotnet Jwt User Claim data binding

起因

公司內部的 IdentityServer 所輸出的人員名稱是放在 token 中的 name 這個欄位,但是從 dotnet 7 開始,Jwt 的解析就有一些更新,這邊紀錄一些資料。

自行解析

雖然 .net 在 httpContext 中可以取得使用者資訊,但是僅限能被 token handler 解析配對資料的部分才會被設定到 httpContext.User.Identity 之中,其他資料可以用以下方式取得

httpContext.User.ClaimsFirstOrDefault(x => x.Type == "name")?.Value;

但是這是自己去抓 token 內的欄位,我個人比較偏好利用 .net 原生的處理模式取得人員資料,也就是利用 httpContext.User.Identity 來去取得相關資訊,因此有了後續章節的設定。

幫 Jwt Token Handler 加入配對資料 ( .net 7 )

在 .net 7 中,預設解析 jwt 的處理器是 JwtSecurityTokenHandler ,而 Token 中欄位的預設配對設定放在 DefaultInboundClaimTypeMap 這個屬性裡面,因此,只需要在這個屬性 (它是 IDictionary<string, string> ) 內埔上要配對的資訊即可。

程式碼範例:

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Add(JwtRegisteredClaimNames.Name, ClaimTypes.Name);

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
        {
            options.Authority = this._authOptions.Authority;
            options.RequireHttpsMetadata = false;
            options.Audience = this._authOptions.Audience;
        });

幫 Jwt Token Handler 加入配對資料 ( .net 8 )

在 dotnet 8 中,JwtBearer 的預設處理工具改變,由預設 JwtSecurityTokenHandler 改為 JsonWebTokenHandler,並且在 Options 中新增了 UseSecurityTokenValidators 這個屬性,當這個屬性為 true 時,才會使用 JwtSecurityTokenHandler 處理。

JwtBearerOptions.UseSecurityTokenValidators Property 說明
此欄位下的說明表示,JsonWebTokenHandler 比 JwtSecurityTokenHandler 快,所以預設改用 JsonWebTokenHandler

因此,處理方式有以下幾種

  1. 改在 JsonWebTokenHandler 上新增欠缺的設定
    ​​​​JsonWebTokenHandler.DefaultInboundClaimTypeMap.Add(JwtRegisteredClaimNames.Name, ClaimTypes.Name);
    
    ​​​​services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    ​​​​        .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
    ​​​​        {
    ​​​​            options.Authority = this._authOptions.Authority;
    ​​​​            options.RequireHttpsMetadata = false;
    ​​​​            options.Audience = this._authOptions.Audience;
    ​​​​        });
    
  2. 利用 UseSecurityTokenValidators 屬性將 jwt 處理器改回 JwtSecurityTokenHandler
    ​​​​JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Add(JwtRegisteredClaimNames.Name, ClaimTypes.Name);
    
    ​​​​services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    ​​​​        .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
    ​​​​        {
    ​​​​            options.UseSecurityTokenValidators = true;
    ​​​​            options.Authority = this._authOptions.Authority;
    ​​​​            options.RequireHttpsMetadata = false;
    ​​​​            options.Audience = this._authOptions.Audience;
    ​​​​        });
    
  3. 小孩才做選擇,我全都要 (但可能有點沒意義)
    ​​​​JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Add(JwtRegisteredClaimNames.Name, ClaimTypes.Name);
    ​​​​JsonWebTokenHandler.DefaultInboundClaimTypeMap.Add(JwtRegisteredClaimNames.Name, ClaimTypes.Name);
    
    ​​​​services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    ​​​​        .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
    ​​​​        {
    ​​​​            options.Authority = this._authOptions.Authority;
    ​​​​            options.RequireHttpsMetadata = false;
    ​​​​            options.Audience = this._authOptions.Audience;
    ​​​​        });
    
  4. 請 IdentityServer 的管理單位重新審視 Token 內儲存的資料所使用的欄位並調整,盡可能配合微軟官方常用屬性