
![[8.5.5.4 以降]](../ng_v8554.gif)
JSON Web Token (JWT) for OAuth Client Authorization Grants
JWT for OAuth Client Authorization Grants により、クライアントは、OAuth 2.0 アクセス・トークンと交換に署名済み JWT トークンを OpenID Connect プロバイダーに送信できます。
JWT for OAuth Client Authorization Grants は、openidConnectServer-1.0 フィーチャーに含まれています。 これにより、クライアントは、OAuth 2.0 アクセス・トークンと交換に署名済み JWT トークンを OpenID Connect プロバイダーに送信できます。
この機能の使用シナリオ例としては、電力会社の顧客が、オンライン・バンクからの自動的な月次支払いを許可する場合が挙げられます。電力会社とオンライン・バンクが、そのような要求を満たすための信頼関係を確立しているものとします。電力会社は、毎月 OAuth 2.0 アクセス・トークンを要求するために、オンライン・バンク用に構成された、OpenID Connect プロバイダーのトークン・エンドポイント URI に、適切なクレームが含まれた署名済み JWT トークンを送信できます。その後、電力会社は、そのアクセス・トークンを使用して、オンライン・バンクから月次支払いを現金化できます。
JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants 仕様の一部は、OpenID Connect プロバイダーとして構成されている Liberty プロファイル・サーバーでサポートされます。JWT クライアント機能をサポートする必要があるユーザーは、独自のアプリケーションを使用してサポートする必要があります。
許可スコープ
OpenID Connect クライアントは、JWT が含まれた HTTPS 要求を OpenID Connect プロバイダーのトークン・エンドポイントに送信して、アクセス・トークンを要求します。この処理時に、スコープの使用を許可するための同意フォームはユーザーに表示されません。JWT ハンドラーは、以下の基準に基づいて、許可スコープを処理します。
- 要求でスコープ・パラメーターが指定されていない場合、OpenID Connect プロバイダーはアクセス・トークンでスコープを指定しません。
- OpenID Connect プロバイダー構成で OpenID Connect クライアントに autoAuthorized クライアントの資格が指定されている場合、要求でクライアントによって指定されているスコープが、アクセス・トークンのスコープ・リストに指定されます。
- OpenID Connect クライアントに autoAuthorized クライアントの資格が指定されていない場合、要求に含まれているスコープを、クライアント構成内のスコープのリストによってフィルターに掛け、また preAuthorizedScope リストで指定する必要があります。HTTPS 要求内のスコープがクライアント構成の scope および preAuthorizeScope リストに含まれている場合、アクセス・トークンのスコープ・リストでそのスコープを指定できます。
クライアントに autoAuthorized クライアントの資格が指定されていない場合、アクセス・トークンのスコープ・リストに含めることができるスコープは、クライアント構成で適切に構成されている必要があります。スコープは、OpenID Connect プロバイダーのクライアント構成内の scope および preAuthorizedScope 属性の値に含まれている必要があります。示している例では、スコープ profile および email は、両方とも scope および preAuthorizedScope 値リストに含まれているため、アクセス・トークンのスコープ・リストで指定されます。スコープは、クライアント構成の scope 属性にリストされていない場合、アクセス・トークンのスコープ・リストから省略されます。スコープがクライアント構成内の scope 属性にリストされているが、preAuthorizedScope リストに含まれていない場合、許可要求は、OpenID Connect プロバイダーからの応答で invalid_grant エラーをトリガーします。
<openidConnectProvider id="OidcConfigSample" oauthProviderRef="OAuthConfigSample" />
<oauthProvider id="OAuthConfigSample" ... >
...
<localStore>
<client name="client01" secret="{xor}..."
displayname="client01"
scope="profile email phone"
preAuthorizedScope="profile email"
enabled="true" />
...
</localStore>
</oauthProvider>
JSON Web トークン内のクレーム
有効な JSON Web トークンは署名済みでなければなりません。OpenID Connect プロバイダーとして構成されている Liberty プロファイル・サーバーでは、トークン署名アルゴリズムとして HMAC-SHA256 のみがサポートされます。各 OpenID Connect クライアントの署名鍵は、OpenID Connect プロバイダーのクライアント構成内の secret 属性です。示している例では、使用される署名鍵は、"{xor}LDo8LTor" になります。
<client name="client01" displayname="client01" secret="{xor}LDo8LTor" ... />
OpenID Connect プロバイダーは、JWT 内の以下のクレームの検証も行います。
- 'iss' (発行者)
- このクレームは、JWT で必須です。iss クレームは、OpenID Connect プロバイダーでのクライアント構成の name 属性または redirect 属性に一致している必要があります。以下の例では、iss クレームは、client01 または http://op201406.ibm.com:8010/oauthclient/redirect.jsp のいずれかに一致している必要があります。
<client name="client01" redirect="http://op201406.ibm.com:8010/oauthclient/redirect.jsp" scope="openid profile email" ... />
- 'sub' (サブジェクト)
- このクレームは、JWT で必須です。サブジェクトの値は、OpenID Connect プロバイダー・サーバーのユーザー・レジストリーに含まれている有効なユーザー名でなければなりません。
- 'aud' (対象者)
- このクレームは、JWT で必須です。対象者クレームの値は、issuerIdentifier 属性が openidConnectProvider 構成で指定されている場合、issuerIdentifier の名前です。
issuerIdentifier 属性が openidConnectProvider 構成で指定されていない場合、対象者は、OpenID Connect プロバイダーのトークン・エンドポイント URI でなければなりません。以下の例では、対象者クレームの値は、"OpenIDConnectProviderID1" になります。
<openidConnectProvider id="OidcConfigSample" oauthProviderRef="OAuthConfigSample" issuerIdentifier="OpenIDConnectProviderID1" />
- 'exp' (有効期限)
- このクレームは、JWT で必須であり、JWT を使用できる時間枠を制限します。OpenID Connect プロバイダーは、システム・クロックと許容クロック・スキューに照らして exp を検証します。
- 'nbf' (以降)
- これはオプションのクレームです。これが存在する場合、トークンは、このクレームで指定されている時刻以降にのみ有効になります。OpenID Connect プロバイダーは、システム・クロックと許容クロック・スキューに照らしてこの時刻を検証します。
- 'iat' (発行時刻)
- デフォルトでは、これはオプションのクレームです。ただし、jwtGrantType エレメントの iatRequired 属性が true に設定されている場合、すべての JWT に iat クレームを含める必要があります。 これが存在する場合、iat クレームは、JWT が発行された時刻を示します。JWT は、maxTokenLifetime より長い間、発行できません。
- 'jti' (JWT ID)
- これはオプションのクレームであり、JWT トークンの固有 ID です。これが存在する場合、発行者は同じ JWT ID を再使用できません。 例えば、jti が id6098364921 である JWT を client01 が発行した場合、client01 によって発行された他の JWT の jti の値を id6098364921 にすることはできません。別の JWT と同一の jti クレームが含まれている JWT は、リプレイ・アタックと見なされます。OpenID Connect プロバイダーとして構成されている Liberty プロファイル・サーバーは、サーバーで jti キャッシュをセットアップします。キャッシュのサイズは、jwtGrantType 構成の maxJtiCacheSize で指定されます。 キャッシュに保持されている jti ID が、新しい着信 jti ID に対して検査されます。キャッシュに保管されている jti ID は、キャッシュが満杯になるまで破棄されません。
JSON Web トークン要求の送信
JWT 要求を送信する際に、HTTP ではなく HTTPS プロトコルを使用するのがベスト・プラクティスです。OpenID Connect プロバイダーのトークン・エンドポイントが、HTTPS JWT 要求の処理に使用されます。OpenID Connect プロバイダーのトークン・エンドポイントを決定するには、『OpenID Connect のトークン・エンドポイントの起動』または『OAuth エンドポイント URL』を参照してください。
要求には、以下のパラメーターが含まれている必要があります。
- grant_type - このパラメーターの値は、"urn:ietf:params:oauth:grant-type:jwt-bearer" でなければなりません。
- assertion - このパラメーターの値には、単一の署名済み JWT トークンが含まれている必要があります。
- scope - このパラメーターはオプションです。scope が省略されている場合、返されるアクセス・トークンにスコープは含まれません。 scope パラメーターにリストされているスコープ値は、OpenID Connect プロバイダー構成に対して検査されます。詳しくは、前述のセクション『許可スコープ』を参照してください。
- client_id - このパラメーターの値は、OpenID Connect プロバイダーのクライアント構成内の name 属性に一致している必要があります。
- client_secret - このパラメーターの値は、OpenID Connect プロバイダーのクライアント構成内の secret 属性に一致している必要があります。
HTTPS 要求の例:
POST /token.oauth2 HTTP/1.1
Host: oidc.ibm.com
Content-Type: application/x-www-form-utlencoded
client_id=client01
&client_secret=secret
&grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer
&assertion=eyJhbGc[---omitted---]kIn0.eyJpc[---ommitted---]A4fQ.MB6ZFlCsHg5MJ-weIHZYz6xgF1jdSZn7ErchHs8-8Rk
&scope=profile email
署名済み JWT トークンを作成する Java の例:
package com.ibm.sample;
import java.security.SignatureException;
import com.google.gson.JsonObject;
import net.oauth.jsontoken.crypto.HmacSHA256Signer;
import net.oauth.jsontoken.SystemClock;
import net.oauth.jsontoken.JsonToken;
import org.joda.time.Duration;
import org.joda.time.Instant;
public class SampleJWTToken {
private static final Duration SKEW = Duration.standardMinutes(5);
JsonToken jwtToken = null;
String[] allPayloadKeys = { "iss", "sub", "aud", "exp", // required
"nbf", "iat", "jti" }; // optional
public SampleJWTToken(String clientId,
String keyId,
String signKey,
String audience,
String subject, // user
String jtiId) throws Exception { // InvalidKeyException
byte[] hs256Key = signKey.getBytes();
HmacSHA256Signer hmacSha256Signer = new HmacSHA256Signer(
clientId, keyId, hs256Key);
// _rsaSha256Signer = new RsaSHA256Signer(clientId, _keyId,
// _privateKey);
SystemClock clock = new SystemClock(SKEW);
jwtToken = new JsonToken(hmacSha256Signer, clock);
JsonObject headerObj = jwtToken.getHeader();
JsonObject payloadObj = jwtToken.getPayloadAsJsonObject();
headerObj.addProperty("alg", "HS256");
Instant instantExp = clock.now().plus(600000); // 10 minutes
jwtToken.setExpiration(instantExp);
jwtToken.setAudience(audience);
payloadObj.addProperty("iss", clientId);
payloadObj.addProperty("sub", subject);
// optional
payloadObj.addProperty("jti", jtiId);
jwtToken.setIssuedAt(clock.now()); // issued at time
}
public String getJWTTokenString() throws Exception {
String signedAndSerializedString = null;
try {
signedAndSerializedString = jwtToken.serializeAndSign();
} catch (SignatureException e) {
throw e;
}
return signedAndSerializedString;
}
}