JSON Web Token (JWT) for OAuth Client Authorization Grants
JWT for OAuth Client Authorization Grants enables a client to send a signed JWT token to the OpenID Connect Provider in exchange for an OAuth 2.0 access token.
JWT for OAuth Client Authorization Grants is included in the openidConnectServer-1.0 feature.
It enables a client to send a signed JWT token to the OpenID Connect
Provider in exchange for an OAuth 2.0 access token.
An example usage scenario of this functionality might be a customer of an electric company who authorizes automatic monthly payments from an online bank. Assuming the electric company and the online bank have established a trusted relationship for the purposes of fulfilling such requests. The electric company can send a signed JWT Token with proper claims to the token endpoint URI of the OpenID Connect Provider that are configured for the online bank in order to request an OAuth 2.0 access token each month. The electric company can then use the access token to cash monthly payments from the online bank.
Portions of the JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants specification are supported for Liberty servers that are configured as OpenID Connect Providers. Users that want to support the JWT client functionality must do so by using their own application.
Authorized scopes
The OpenID Connect Client sends an HTTPS request with a JWT to the token endpoint of the OpenID Connect Provider to request an access token. During this process, the user sees no consent form for authorizing the use of scopes. The JWT handler will handle the authorized scopes that are based on the following criteria:
- If no scope parameter is specified in the request, the OpenID Connect Provider will not specify any scopes in the access token.
- When an OpenID Connect Client is qualified as an autoAuthorized client in the OpenID Connect Provider configuration, any scope that is specified by the client in the request is specified in the scope list of the access token.
- When an OpenID Connect Client is not qualified as an autoAuthorized
client, the scope that is included in the request needs to be filtered
by the list of scopes in the client configuration and must also be
specified in the
preAuthorizedScopelist. If a scope in the HTTPS request is in thescopeandpreAuthorizeScopelist of the client configuration, the scope can be specified in the scope list of the access token.
When a client is not qualified as an autoAuthorized client,
scopes that can be included in the scope list of the access token
must be properly configured in the client configuration. The scope
must be included in the values for the scope and preAuthorizedScope attributes
in the client configuration for the OpenID Connect Provider. In the
example that is shown, the scopes profile and email are
specified in the scope list of the access token because both are included
in the scope and preAuthorizedScope value
list. If a scope is not listed in the scope attribute
of the client configuration, it is omitted from the scope list of
the access token. If a scope is listed in the scope attribute
but is not included in the preAuthorizedScope list
within the client configuration, the authorization request triggers
an invalid_grant error in the response
from the OpenID Connect Provider.
<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>Claims in a JSON Web Token
A valid JSON Web Token must be signed. A Liberty server that is configured as an OpenID
Connect Provider only supports HMAC-SHA256 as the token signing algorithm. The
signing key for each OpenID Connect Client is the secret attribute in the client
configuration of the OpenID Connect Provider. In the example that is shown, the signing key that is
used would be "{xor}LDo8LTor".
<client name="client01" displayname="client01" secret="{xor}LDo8LTor" ... />The OpenID Connect Provider also verifies the following claims in a JWT:
- 'iss' (issuer)
- This claim is required in a JWT. The
issclaim must match thenameattribute or theredirectattribute of the client configuration in the OpenID Connect Provider. In the following example, theissclaim must match eitherclient01orhttp://op201406.ibm.com:8010/oauthclient/redirect.jsp.<client name="client01" redirect="http://op201406.ibm.com:8010/oauthclient/redirect.jsp" scope="openid profile email" ... /> - 'sub' (subject)
- This claim is required in a JWT. The value of the subject must be a valid user name in the user registry of the OpenID Connect Provider server.
- 'aud' (audience)
- This claim is required in a JWT. The value of the audience claim
is the name of the
issuerIdentifierwhen theissuerIdentifierattribute is specified in theopenidConnectProviderconfiguration. If theissuerIdentifierattribute is not specified in theopenidConnectProviderconfiguration, the audience must be the token endpoint URI of the OpenID Connect Provider. In the following example, the value of the audience claim would be"OpenIDConnectProviderID1".<openidConnectProvider id="OidcConfigSample" oauthProviderRef="OAuthConfigSample" issuerIdentifier="OpenIDConnectProviderID1" /> - 'exp' (expiration)
- This claim is required in a JWT and limits the time window that
the JWT can be used. The OpenID Connect Provider verifies the
expagainst its system clock, plus some allowable clock skew.
- 'nbf' (not before)
- This is an optional claim. When present, the token is only valid after the time specified by this claim. The OpenID Connect Provider verifies this time against its system clock, plus some allowable clock skew.
- 'iat' (issued at)
- By default, this is an optional claim. However, if the
iatRequiredattribute of thejwtGrantTypeelement is set to true, then all JWTs are required to contain theiatclaim. When present, theiatclaim indicates the time at which the JWT was issued. A JWT cannot be issued longer than themaxTokenLifetime. - 'jti' (JWT ID)
- This is an optional claim and is the unique identifier of a JWT Token. When present, the same
JWT ID cannot be reused by an issuer. For example, if
client01issues a JWT whosejtiisid6098364921, then no other JWT issued byclient01can have ajtivalue ofid6098364921. A JWT with ajticlaim identical to another JWT is considered to be a replay attack. Liberty servers that are configured as OpenID Connect Providers set up ajticache on the server. The size of the cache is specified by themaxJtiCacheSizein thejwtGrantTypeconfiguration. ThejtiIDs that are kept in the cache are checked against any new incomingjtiID. ThejtiIDs stored in the cache are not discarded unless the cache is full.
Submitting JSON Web Token requests
It is a best practice to use the HTTPS protocol instead of HTTP to submit a JWT request. The token endpoint of the OpenID Connect Provider is used for handling HTTPS JWT requests. To determine the token endpoint for the OpenID Connect Provider, see Invoking the Token Endpoint for OpenID Connect or OAuth endpoint URLs.
The request must contain the following parameters:
- grant_type - The value of this parameter must be
"urn:ietf:params:oauth:grant-type:jwt-bearer" - assertion - The value of this parameter must contain a single signed JWT Token.
- scope - This parameter is optional. If
scopeis omitted, the access token that is returned does not contain any scopes. The scope values listed in thescopeparameter are checked against the OpenID Connect Provider configuration. For more information, see the previous Authorized Scopes section. - client_id - The value of this parameter must match the
nameattribute in the client configuration of the OpenID Connect Provider. - client_secret - The value of this parameter must match the
secretattribute in the client configuration of the OpenID Connect Provider.
An example HTTPS request:
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 emailA Java example to create a signed JWT Token:
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;
}
}