コンテンツにスキップ

イントロスペクションと失効

JWTアクセストークンは公開鍵を使用してローカルで検証できますが、一部のシナリオでは認可サーバーとの通信が必要です:

  • トークンイントロスペクション(RFC 7662) — トークンが現在有効かどうかを認可サーバーに問い合わせる
  • トークン失効(RFC 7009) — 認可サーバーにトークンの無効化を指示する

両方の機能は clientCredentials(コンフィデンシャルクライアント)が必要です。これは認可サーバーが呼び出し元を認証するためです。

イントロスペクションまたは失効を使用するには、エンドポイントとクライアント資格情報を設定します:

import { createAuthrimServer } from '@authrim/server';
const authrim = createAuthrimServer({
issuer: 'https://auth.example.com',
audience: 'https://api.example.com',
// Introspection endpoint
introspectionEndpoint: 'https://auth.example.com/oauth/introspect',
// Revocation endpoint
revocationEndpoint: 'https://auth.example.com/oauth/revoke',
// Required for both introspection and revocation
clientCredentials: {
clientId: 'my-resource-server',
clientSecret: 'resource-server-secret',
},
});
await authrim.init();

SDKはこれらのエンドポイントを呼び出す際、クライアント資格情報を使用してHTTP Basic認証を行います。


トークンイントロスペクション(RFC 7662)

Section titled “トークンイントロスペクション(RFC 7662)”

トークンイントロスペクションでは、認可サーバーにトークンの現在の状態を問い合わせることができます。これは特に以下の場合に有用です:

  • 不透明トークン — 自己完結型のJWTではなく、ローカルで検証できないトークン
  • リアルタイムの有効性チェック — 失効済みまたは期限切れのトークンを即座に検出
  • トークンメタデータ — JWTに含まれていないクレームの取得
const response = await authrim.introspect(token, tokenTypeHint?, issuerOptions?);
パラメータ説明
tokenstringイントロスペクトするアクセストークン
tokenTypeHint'access_token' | 'refresh_token' | 'device_secret'オプションのtoken type hint
issuerOptions{ issuer?: string; token?: ValidatedToken }multi-issuer構成で使うオプションのissuer selector
interface IntrospectionResponse {
active: boolean;
sub?: string;
scope?: string;
client_id?: string;
username?: string;
token_type?: string;
exp?: number;
iat?: number;
nbf?: number;
aud?: string | string[];
iss?: string;
jti?: string;
cnf?: { jkt?: string };
installation_id?: string;
app_display_name?: string;
platform?: string;
display_name?: string;
fallback_display_name?: string;
[key: string]: unknown; // Additional claims
}

最も重要なフィールドは active です:

  • active: true — トークンは有効で現在アクティブ
  • active: false — トークンは無効、期限切れ、失効済み、または不明
const response = await authrim.introspect(accessToken);
if (!response.active) {
// Token is not valid — reject the request
return res.status(401).json({ error: 'invalid_token' });
}
// Token is active — use the claims
console.log('Subject:', response.sub);
console.log('Scopes:', response.scope);
console.log('Client:', response.client_id);

アクティブ vs 非アクティブの処理

Section titled “アクティブ vs 非アクティブの処理”

イントロスペクションレスポンスの active: false は複数のシナリオをカバーします:

シナリオactive の値備考
有効なトークンtrueトークンは現在有効で使用可能
期限切れトークンfalseトークンの exp が過ぎている
失効済みトークンfalseトークンは明示的に失効された
不明なトークンfalseこのサーバーが発行したトークンではない
不正な形式のトークンfalseトークンを解析できない

スコープチェック付きイントロスペクション

Section titled “スコープチェック付きイントロスペクション”
const response = await authrim.introspect(accessToken);
if (!response.active) {
return res.status(401).json({ error: 'invalid_token' });
}
// Check scopes manually for introspected tokens
const grantedScopes = (response.scope ?? '').split(' ');
const requiredScopes = ['read:orders', 'write:orders'];
const hasAllScopes = requiredScopes.every(
(scope) => grantedScopes.includes(scope)
);
if (!hasAllScopes) {
return res.status(403).json({ error: 'insufficient_scope' });
}

トークン失効は、認可サーバーに特定のトークンの無効化を指示します。失効後、そのトークンは認可サーバーやイントロスペクションを使用するリソースサーバーでは受け入れられなくなります。

await authrim.revoke(token, tokenTypeHint?, issuerOptions?);
パラメータ説明
tokenstring失効するトークン
tokenTypeHint'access_token' | 'refresh_token' | 'device_secret'オプションのtoken type hint
issuerOptions{ issuer?: string; token?: ValidatedToken }multi-issuer構成で使うオプションのissuer selector
try {
await authrim.revoke(accessToken, 'access_token');
console.log('Token revoked successfully');
} catch (error) {
console.error('Revocation failed:', error.message);
}

Native SSO device_secret は同じイントロスペクション・失効エンドポイントを device_secret token type hint付きで使います。hintの指定漏れを避けるため、専用helperを優先してください。

const response = await authrim.introspectDeviceSecret(deviceSecret, {
issuer: 'https://auth.example.com',
});
if (response.active) {
console.log(response.installation_id);
console.log(response.platform);
}
await authrim.revokeDeviceSecret(deviceSecret, {
issuer: 'https://auth.example.com',
});

raw device_secret をログに出さないでください。refresh token相当のcredentialとして扱います。

  • ログアウト — ユーザーがサインアウトする際にアクセストークンを失効
  • セキュリティインシデント — 侵害されたトークンを即座に無効化
  • 権限変更 — ユーザーの権限が更新された際にトークンを失効(再認証を強制)
// Example: Revoke on logout
app.post('/api/logout', auth(), async (req, res) => {
const token = req.authrim!.token.token;
// Revoke the access token at the authorization server
await authrim.revoke(token, 'access_token');
res.json({ message: 'Logged out' });
});

ローカル検証 vs イントロスペクション

Section titled “ローカル検証 vs イントロスペクション”

ローカルJWT検証とトークンイントロスペクションの選択は、要件によって異なります:

基準ローカル検証イントロスペクション
トークン形式JWTのみJWTまたは不透明
ネットワーク呼び出しなし(キャッシュされたJWKSを使用)あり(毎回)
レイテンシローカルの暗号検証とキャッシュ参照認可サーバーへのネットワーク往復
失効の検出不可可能
リアルタイムの有効性不可(exp に依存)可能
クライアント資格情報が必要不要必要
オフライン動作可能(JWKSキャッシュ後)不可
flowchart TD
    A["トークン受信"] --> B{"トークン形式?"}
    B -->|JWT| C{"リアルタイムの<br>失効チェックが必要?"}
    B -->|不透明| D["イントロスペクション"]
    C -->|はい| E["イントロスペクション"]
    C -->|いいえ| F{"機密操作?"}
    F -->|はい| G["両方を使用:<br>ローカル + イントロスペクション"]
    F -->|いいえ| H["ローカル検証"]

機密操作では、ローカル検証とイントロスペクションを組み合わせます:

// Fast path: Local JWT validation
const tokenResult = await authrim.validateToken(accessToken);
// For sensitive operations, also check with the authorization server
if (isSensitiveOperation(req.path)) {
const introspectionResult = await authrim.introspect(accessToken);
if (!introspectionResult.active) {
return res.status(401).json({
error: 'invalid_token',
error_description: 'Token has been revoked',
});
}
}

これにより両方の利点を得られます:

  • ほとんどのリクエストに対する高速な検証(ローカルJWT検証)
  • 重要な操作に対するリアルタイムの有効性(イントロスペクション)

イントロスペクション結果のキャッシュ

Section titled “イントロスペクション結果のキャッシュ”

高スループットAPIでは、イントロスペクション結果を短期間キャッシュできます:

const INTROSPECTION_CACHE_TTL = 30_000; // 30 seconds
async function introspectWithCache(token: string) {
const cacheKey = `introspect:${hashToken(token)}`;
// Check cache first
const cached = await cache.get(cacheKey);
if (cached) return cached;
// Call authorization server
const result = await authrim.introspect(token);
// Cache the result (short TTL)
if (result.active) {
await cache.set(cacheKey, result, INTROSPECTION_CACHE_TTL);
}
return result;
}
エラー説明
IntrospectionErrorイントロスペクションエンドポイントの呼び出しに失敗
RevocationError失効エンドポイントの呼び出しに失敗
ClientCredentialsErrorクライアント資格情報の欠落または無効
NetworkErrorネットワーク接続の問題