コンテンツにスキップ

トークン検証

トークン検証は@authrim/serverの中核機能です。validateToken()メソッドは、JWTの解析、署名鍵の取得、署名の検証、すべての標準クレームのチェックという完全な検証パイプラインを実行します。

const result = await authrim.validateToken(token);
パラメータ説明
tokenstring生のJWTアクセストークン(Bearerプレフィックスなし)
追加の検証ルールは、サーバーインスタンス作成時の tokenValidation に設定します。
interface TokenValidationOptions {
clockToleranceSeconds?: number;
requiredScopes?: string[];
validateDPoP?: boolean;
tenantClaim?: string;
requireTenantClaim?: boolean;
requiredTenantId?: string;
allowedTenantIds?: string[];
tenantPredicate?: (
tenantId: string,
claims: AccessTokenClaims,
) => boolean | Promise<boolean>;
}
オプション説明
clockToleranceSecondsnumber標準クレーム検証のクロックスキュー許容値
requiredScopesstring[]トークンのscopeクレームに存在する必要があるスコープ
validateDPoPbooleanミドルウェア統合で使うDPoP bound token検証ポリシー用の予約フィールド
tenantClaimstringtenant idを格納するクレーム名。デフォルトは tenant_id
requireTenantClaimbooleantenantクレームを含まないトークンを拒否
requiredTenantIdstringトークンtenantがこのtenant idと完全一致することを要求
allowedTenantIdsstring[]トークンtenantがこのリストのいずれかであることを要求
tenantPredicate(tenantId, claims) => boolean | Promise<boolean>高度なABAC/ReBAC向けのカスタムtenant判定

validateToken() は判別可能なresult objectを返します。通常の検証失敗ではthrowしません。

type TokenValidationResult =
| { data: ValidatedToken; error: null }
| { data: null; error: { code: string; message: string } };

検証に成功した場合、data に検証済みトークンが入ります。

interface ValidatedToken {
claims: AccessTokenClaims;
token: string;
issuer: string;
tenantId?: string;
tokenType: 'Bearer' | 'DPoP';
expiresIn?: number;
}
フィールド説明
claimsAccessTokenClaims解析・検証済みのJWTクレーム
tokenstring元のトークン文字列
issuerstring検証済みissuer
tenantIdstring | undefinedtenantクレームがある場合の検証済みtenant id
tokenType'Bearer' | 'DPoP'クレームに基づいて検出されたトークンタイプ
expiresInnumber | undefinedトークンの有効期限までの秒数(expから計算)
interface AccessTokenClaims {
iss: string;
sub: string;
aud: string | string[];
exp: number;
iat: number;
nbf?: number;
jti?: string;
scope?: string;
client_id?: string;
cnf?: { jkt?: string };
[key: string]: unknown; // Custom claims
}

SDKは以下のパイプラインでトークンを検証します。トークンが受け入れられるには、各ステップをパスする必要があります。

flowchart TD
    A["JWT文字列を受信"] --> B["サイズチェック<br>(最大 8 KB)"]
    B --> C["JWTを解析<br>(Header + Payload + Signature)"]
    C --> D["アルゴリズムチェック<br>(alg: none を拒否)"]
    D --> E["署名鍵を取得<br>(JWKS by kid)"]
    E --> F["署名を検証<br>(RS/PS/ES/EdDSA)"]
    F --> G["issを検証<br>(タイミングセーフ)"]
    G --> H["audを検証<br>(タイミングセーフ)"]
    H --> I["expを検証<br>(+ クロック許容値)"]
    I --> J["nbfを検証<br>(+ クロック許容値)"]
    J --> K["iatを検証<br>(未来でないこと)"]
    K --> L["必要なスコープをチェック"]
    L --> M["tenant policyをチェック"]
    M --> N["トークンタイプを検出<br>(Bearer vs DPoP)"]
    N --> O["TokenValidationResultを返却"]

いずれかのステップが失敗した場合、SDKは { data: null, error } を返します。以下のエラータイプセクションを参照してください。

scopeとtenantの検証は、サーバー作成時に設定します。

const authrim = createAuthrimServer({
issuer: 'https://auth.example.com',
audience: 'https://api.example.com',
tokenValidation: {
requiredScopes: ['orders:read'],
requireTenantClaim: true,
allowedTenantIds: ['tenant_a', 'tenant_b'],
},
});

SDKは署名検証に以下のJWSアルゴリズムをサポートしています:

アルゴリズム鍵タイプ曲線 / サイズ説明
RS256RSA2048+ビットRSASSA-PKCS1-v1_5 with SHA-256
RS384RSA2048+ビットRSASSA-PKCS1-v1_5 with SHA-384
RS512RSA2048+ビットRSASSA-PKCS1-v1_5 with SHA-512
PS256RSA2048+ビットRSASSA-PSS with SHA-256
PS384RSA2048+ビットRSASSA-PSS with SHA-384
PS512RSA2048+ビットRSASSA-PSS with SHA-512
ES256ECP-256ECDSA with SHA-256
ES384ECP-384ECDSA with SHA-384
ES512ECP-521ECDSA with SHA-512
EdDSAOKPEd25519Edwards-curve Digital Signature

issクレームは、設定されたissuer値のいずれかと正確に一致する必要があります。比較にはサイドチャネル攻撃を防止するためにタイミングセーフアルゴリズムが使用されます。

// Single issuer
const authrim = createAuthrimServer({
issuer: 'https://auth.example.com',
audience: 'https://api.example.com',
});
// Multiple issuers
const authrim = createAuthrimServer({
issuer: [
'https://auth.example.com',
'https://auth.partner.com',
],
audience: 'https://api.example.com',
});

audクレームには、設定されたaudience値の少なくとも1つが含まれている必要があります。audクレームは単一の文字列または文字列の配列です。すべての比較はタイミングセーフです。

// Token with aud: "https://api.example.com" — matches
// Token with aud: ["https://api.example.com", "other"] — matches
// Token with aud: "https://other.example.com" — rejected

expクレームは、現在時刻にclockToleranceSecondsを加えた値と照合されます:

token is valid if: exp + clockToleranceSeconds > now

デフォルトの許容値60秒では、最大60秒前に期限切れになったトークンでも受け入れられます。

存在する場合、nbfクレームは現在時刻からclockToleranceSecondsを引いた値と照合されます:

token is valid if: nbf - clockToleranceSeconds <= now

iatクレームは、トークンが未来に発行されていないことをサニティチェックします(クロック許容値を考慮):

token is valid if: iat - clockToleranceSeconds <= now

requiredScopesオプションを使用して必要なスコープを適用します:

// Require specific scopes
const authrim = createAuthrimServer({
issuer: 'https://auth.example.com',
audience: 'https://api.example.com',
tokenValidation: {
requiredScopes: ['read:users', 'write:users'],
},
});

SDKは、指定されたすべてのスコープがトークンのscopeクレーム(スペース区切り文字列)に存在するかをチェックします。必要なスコープが1つでも欠けている場合、validateToken()insufficient_scope のエラーコードを返します。

SDKはcnf(confirmation)クレームに基づいてトークンタイプを自動検出します:

  • トークンにcnf.jkt(JWK Thumbprint)が含まれている場合、トークンはDPoPとして分類されます
  • それ以外の場合、トークンはBearerとして分類されます
const result = await authrim.validateToken(token);
if (result.error) {
throw new Error(result.error.message);
}
if (result.data.tokenType === 'DPoP') {
// This token is sender-constrained — validate the DPoP proof too
await authrim.validateDPoP(dpopProof, {
method: 'GET',
uri: 'https://api.example.com/resource',
accessToken: token,
expectedThumbprint: result.data.claims.cnf!.jkt!,
});
}

完全なDPoP検証については、DPoP検証を参照してください。

import { createAuthrimServer } from '@authrim/server';
const authrim = createAuthrimServer({
issuer: 'https://auth.example.com',
audience: 'https://api.example.com',
});
await authrim.init();
try {
const result = await authrim.validateToken(accessToken);
if (result.error) {
throw new Error(result.error.message);
}
console.log('User:', result.data.claims.sub);
console.log('Scopes:', result.data.claims.scope);
console.log('Expires in:', result.data.expiresIn, 'seconds');
} catch (error) {
console.error('Token validation failed:', error.message);
}
const authrim = createAuthrimServer({
issuer: 'https://auth.example.com',
audience: 'https://api.example.com',
tokenValidation: {
requiredScopes: ['read:orders', 'write:orders'],
},
});
const result = await authrim.validateToken(accessToken);
if (result.error?.code === 'insufficient_scope') {
return res.status(403).json({ error: 'insufficient_scope' });
}
if (result.error) {
return res.status(401).json({ error: 'invalid_token' });
}
// Token is valid and has both scopes.

カスタムクレームへのアクセス

Section titled “カスタムクレームへのアクセス”

トークンには、認可サーバーによって追加されたカスタムクレーム(ロール、テナントIDなど)が含まれる場合があります。claimsオブジェクトを通じてアクセスします:

const result = await authrim.validateToken(accessToken);
if (result.error) {
throw new Error(result.error.message);
}
// Access custom claims with type assertion
const roles = result.data.claims['roles'] as string[];
const tenantId = result.data.claims['tenant_id'] as string;
// Or use a type parameter for full type safety
interface MyTokenClaims extends AccessTokenClaims {
roles: string[];
tenant_id: string;
}
const claims = result.data.claims as MyTokenClaims;
console.log(claims.roles); // ['admin', 'user']
console.log(claims.tenant_id); // 'tenant-123'

validateToken() は代表的な検証失敗を result.error.code に以下のコードで返します。

コード説明代表的なHTTPステータス
token_malformedトークンがcompact JWTとして不正、またはparser limitを超過401
algorithm_mismatchトークンがalg: noneまたはサポートされていないアルゴリズムを使用401
signature_invalid署名検証が失敗401
token_expiredトークンのexpクレームがクロック許容値を超えて期限切れ401
token_not_yet_validトークンのnbfクレームがクロック許容値を超えて未来401
invalid_issuerトークンのissが設定されたissuerと一致しない401
invalid_audienceトークンのaudが設定されたaudienceと一致しない401
invalid_tenanttenantクレームが欠落、不許可、またはtenant predicateで拒否403
insufficient_scopeトークンに必要なスコープが1つ以上不足403
jwks_key_not_foundトークンヘッダーに一致するJWKS keyが見つからない500