Skip to content

Microsoft.IdentityModel.JsonWebTokens による JWT 生成移行ガイド

問題:新しい JsonWebTokens パッケージで JWT を生成する方法

古い System.IdentityModel.Tokens.Jwt パッケージのコードを、新しい高性能パッケージ Microsoft.IdentityModel.JsonWebTokens に移行する必要がある。具体的には以下の従来コードを変換したい:

cs
// 旧パッケージを使用したJWT生成
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("キー文字列"));
var claims = new List<Claim>
{
    new Claim(ClaimTypes.Name, "Testuser"),
    // 他のクレーム...
};

var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature);

var token = new JwtSecurityToken(
    "発行者",
    "対象者",
    claims,
    expires: DateTime.UtcNow.AddMinutes(120),
    signingCredentials: credentials
);

var tokenString = new JwtSecurityTokenHandler().WriteToken(token);

主な変更点の課題:

  • JwtSecurityToken から JsonWebTokenHandler へのインターフェース変更
  • クレーム情報の記述方法の差異
  • トークンハンドラーの設定オプション追加

解決策: JsonWebTokenHandler を使った最新実装

移行後の完全なコード例と主要変更点:

cs
using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;

// 秘密鍵の生成(変更なし)
var securityKey = new SymmetricSecurityKey(
    Encoding.UTF8.GetBytes("SomeStringFromConfig1234")
);

// クレームの定義方法変更:List<Claim> → Dictionary<string, object>
var claims = new Dictionary<string, object>
{
    [ClaimTypes.Name] = "Testuser",
    [ClaimTypes.GroupSid] = "Tenant1",
    [ClaimTypes.Sid] = "3c545f1c-cc1b-4cd5-985b-8666886f985b"
};

// トークン記述子の設定
var descriptor = new SecurityTokenDescriptor
{
    Issuer = "MyIssuer",          // 発行者
    Audience = "MyAudience",      // 対象者
    Claims = claims,              // クレーム辞書
    NotBefore = DateTime.UtcNow,  // 有効開始時刻
    Expires = DateTime.UtcNow.AddMinutes(120), // 有効期限
    SigningCredentials = new SigningCredentials(
        securityKey, 
        SecurityAlgorithms.HmacSha256Signature // 署名アルゴリズム
    )
};

// 新しいハンドラーの初期化
var handler = new JsonWebTokenHandler();
handler.SetDefaultTimesOnTokenCreation = false; // 自動時間設定を無効化

// JWTの生成
string tokenString = handler.CreateToken(descriptor);

変更点の詳細解説

1. クレーム定義方法の変更

従来の List<Claim> から Dictionary<string, object> へ移行:

cs
// 従来方式
var claims = new List<Claim> { 
    new Claim(ClaimTypes.Name, "値") 
};

// 新しい方式
var claims = new Dictionary<string, object> {
    [ClaimTypes.Name] = "値"
};

理由:
新しいパッケージではクレームをより柔軟に扱えるため、ディクショナリ形式が要求されます。既存の ClaimTypes はそのまま利用可能。

2. トークン記述子 (SecurityTokenDescriptor) の活用

JwtSecurityToken コンストラクタの代わりに、SecurityTokenDescriptor でプロパティベースの設定をします:

diff
- var token = new JwtSecurityToken(issuer, audience, ...);
+ var descriptor = new SecurityTokenDescriptor {
+     Issuer = issuer,
+     Audience = audience,
+     ...
+ };

利点:
設定項目が明確になり、オプションの追加・変更が容易になります。

3. ハンドラー設定 SetDefaultTimesOnTokenCreation

重要な設定オプション:

cs
handler.SetDefaultTimesOnTokenCreation = false;

この設定が必須な理由

デフォルト値 true の場合:

  • IssuedAt (iat) クレームが自動追加
  • NotBefore (nbf) が現在時刻-5分に設定

false に設定することで、旧実装との互換性を保ち、明示的に指定した時刻だけを使用します。

4. トークン生成プロセスの統合

最終的な生成プロセスが簡素化:

diff
- var handler = new JwtSecurityTokenHandler();
- var tokenString = handler.WriteToken(token);
+ var tokenString = handler.CreateToken(descriptor);

新旧実装のJWTペイロード比較

新旧で生成されたペイロードの構造は完全に一致:

json
{
  "aud": "MyAudience",
  "iss": "MyIssuer",
  "exp": 1709078400,
  "nbf": 1708992000,
  "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name": "Testuser",
  "...": "..."
}
json
{
  "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name": "Testuser",
  "...": "...",
  "nbf": 1708992000,
  "exp": 1709078400,
  "iss": "MyIssuer",
  "aud": "MyAudience"
}

クレーム順序の差異:
JSONのプロパティ順序は仕様上意味を持たないため、実質的に同等です。

実装時の注意点

  1. NuGetパッケージの依存関係
    Microsoft.IdentityModel.JsonWebTokens は独立したパッケージです。互換性のために以下をアンインストール:

    System.IdentityModel.Tokens.Jwt
  2. 時刻設定の検証
    有効期限を設定しない場合、Expires プロパティを明示的に null 設定:

    cs
    descriptor.Expires = null; // 有効期限なし
  3. 非対称鍵の使用
    RSA鍵などを使う場合も同じパターンで実装可能:

    cs
    var rsaKey = new RsaSecurityKey(rsaParameters);
    var credentials = new SigningCredentials(rsaKey, "RS256");

移行後はパフォーマンス向上と拡張性のメリットを得られます。新しいAPIはトークン処理をより直感的に制御できる設計となっています。