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
preAuthorizedScope
list. If a scope in the HTTPS request is in thescope
andpreAuthorizeScope
list 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
iss
claim must match thename
attribute or theredirect
attribute of the client configuration in the OpenID Connect Provider. In the following example, theiss
claim must match eitherclient01
orhttp://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
issuerIdentifier
when theissuerIdentifier
attribute is specified in theopenidConnectProvider
configuration. If theissuerIdentifier
attribute is not specified in theopenidConnectProvider
configuration, 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
exp
against 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
iatRequired
attribute of thejwtGrantType
element is set to true, then all JWTs are required to contain theiat
claim. When present, theiat
claim 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
client01
issues a JWT whosejti
isid6098364921
, then no other JWT issued byclient01
can have ajti
value ofid6098364921
. A JWT with ajti
claim identical to another JWT is considered to be a replay attack. Liberty servers that are configured as OpenID Connect Providers set up ajti
cache on the server. The size of the cache is specified by themaxJtiCacheSize
in thejwtGrantType
configuration. Thejti
IDs that are kept in the cache are checked against any new incomingjti
ID. Thejti
IDs 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
scope
is omitted, the access token that is returned does not contain any scopes. The scope values listed in thescope
parameter 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
name
attribute in the client configuration of the OpenID Connect Provider. - client_secret - The value of this parameter must match the
secret
attribute 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 email
A 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;
}
}