AuthorizationServer
The new HTTP client shipped with nevisAuth 4.38.0.12 will likely require changes in this auth state configuration, specifically in the area of certificate configuration and handling.
Visit Appendix H for additional information.
Introduction
The OAuth 2.0 authorization server implements the authorization endpoint and the token endpoint of the OAuth 2.0 authorization framework. In addition to plain OAuth2, it also supports OpenID Connect (Core).
In accordance with the specification, the AuthorizationServer issues tokens based on the client requests, the requested scopes and the end user's consent to these requests. The client and the request scopes need to be registered at the AuthorizationServer before clients can request tokens. Failure to do so will result in an error.
The AuthState validates incoming authorization and token requests and selects the transition that best matches the validation result. It is typically the first state in the processing chain and should be configured to be called on authenticate as well as stepup to act according to the specification.
Results
The invalid-client and invalid-redirect-uri transitions signal that the client is not correctly registered at the authorization server, whereas an invalid-authorization-request signals that the authorization request was not valid. In these cases, a transition to the AuthError AuthState itself is recommended to make sure the authorization server acts as required by the OAuth 2.0 specification and sends the correct error responses.
A valid OAuth 2.0 authorization request or OpenID Connect authentication request is signaled with valid-authorization-request and valid-authorization-request-authentication-required, respectively. In both cases, the AuthState is registered as a finisher, such that the AuthEngine will hand back control to the AuthorizationServer AuthState once AUTH_DONE or AUTH_ERROR is reached to generate a meaningful authorization response. For example., from valid-authorization-request-authentication-required and valid-authorization-request, the state engine can be configured as usual. Note that the OAuth 2.0 specification requires user authentication to take place in that case. After successfully authenticating the user, a transition to the AuthDoneAuthState is recommended to hand back control to the authorization server, which then issues a successful authorization response. When the user cannot be successfully authenticated, a transition to the AuthError AuthState is recommended to hand back control to the authorization server, which then issues an authorization error response.
When receiving a token request, the AuthState validates it and signals the result using the valid-token-request or invalid-token-request transition. In both cases, the AuthState is registered as a finisher, such that the AuthEngine will hand back control to the AuthorizationServer AuthState once AUTH_DONE or AUTH_ERROR is reached to generate a meaningful token response. Note that the client authentication as required by the specification already took place at this point.
The server-error result signals an internal server error. A transition to the AuthState itself is recommended to make sure the authorization server acts as required by the OAuth 2.0 specification and sends the correct error responses.
Client and scope registration and policies
Before clients can request access tokens, the clients need to be registered and configured at the AuthorizationServer. Besides the client ID, this also includes a client name, redirect URIs and policies applying for that client (e.g., whether a client may request certain scopes). Based on the client configuration, the AuthorizationServer authenticates clients and checks whether the policy allows the request sent by them. The following policies can be configured for in
- Scopes: Defines what scopes a client is allowed to request.
- Redirect URIs: Redirection URI values used by the client.
- Response types: OAuth2 response type values the client is restricted to use.
- Grant types: OAuth2 grant types the client is restricted to use.
- Authentication required policy: Defines whether a request by this client requires forced (re)-authentication of the end user.
These policies are enforced by the AuthorizationServer. If a request violates a policy, this is signaled by an authorization error response, a token error response or an error displayed to the end user, and the result is set to invalid-authorization-request, invalid-token-request or invalid-redirect-uri respectively.
As with the clients, scopes must be registered at the AuthorizationServer before they can be requested. Besides the scope name, this also includes policies applying to a scope. The following policies can be configured for in
- Implicit flow policy: Defines whether the scope can be requested via the implicit flow and how the end user's consent can be established.
- Authorization code flow policy: Defines whether the scope can be requested via the authorization code flow and how the end user's consent can be established.
- Client credentials flow policy: Defines whether the scope can be requested via the client credentials flow.
- Refresh token request policy: Defines whether the scope can be requested in combination with requesting a refresh token to be issued and how the end user's consent can be established.
- Authentication required policy: Defines whether requesting the scope requires forced (re)-authentication.
These policies are enforced by the AuthorizationServer. If multiple policies apply to a request, the most restrictive policy is applied. If a request violates a policy, this is signaled by an authorization error response or a token error response and the result is set to invalid-authorization-request or invalid-token-request respectively.
Scope processing
This chapter contains a brief clarification of scope processing in the AuthorizationServer. In general, the behavior follows RFC 6749. Some underspecified points in the RFC are made explicit here:
Authorization Request processing
The authorization request scope parameter is validated against the set of scopes the corresponding client may request (according to the client metadata) and against the policies for the given response type. If no scope request parameter is given, this is accepted and evaluated as the empty scope.
Token Request processing
Independent of the grant type, the token request scope parameter is validated against the set of scopes the corresponding client may request. This happens even in cases where there should not be a scope request parameter (see RFC 6749, section 4.1.3 for the details). Whether the scope request parameter is taken into account when issuing the tokens differs between the grant types:
- Token request with authorization code grant (authorization code flow): The token request scope request parameter is not taken into account. The scope requested in the original authorization request is granted in the issued access token (and refresh token if requested).
- Token request with a refresh token grant (see RFC 6749, section 6): The token request scope request parameter is not taken into account. The scope granted in the original authorization request is granted in the issued access token and refresh token.
- Token request with client credentials grant (Client Credentials flow, see RFC 6749, section 4.4.2): With this grant type, the token request scope request parameter is taken into account. The scope requested by the client is granted in the issued access token.
Special scopes
The authorization server implements a few scopes that are treated specially at the AuthorizationServer when requested. Unlike regular scopes, these scopes are understood and interpreted by the AuthorizationServer itself, and measures are taken to implement the behavior specified for these scopes.
The following scope values defined by the OpenID Connect specification are understood by the AuthorizationServer:
- openid
- profile
- address
- phone
Furthermore, the scope value offline_access requests that a Refresh Token will be issued if allowed according to the specification. See the OpenID Connect specification for details.
As with the regular scopes, these special scopes must be registered before they can be used.
Supported flows
The OAuth 2.0 authorization server plug-in supports the implicit flow, hybrid flow, authorization code flow and client credentials flow.
Implicit flow
In the implicit flow, access tokens and ID tokens are issued by the authorization endpoint.
Here is an example of an HTTP authentication request for the implicit flow:
GET /authorize?
response_type=id_token%20token
&client_id=s6BhdRkqt3
&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb
&scope=openid%20profile
&state=af0ifjsldkj
&nonce=n-0S6_WzA2Mj HTTP/1.1
Host: server.example.com
Authorization code flow
In the authorization code flow, an authorization code must be requested from the authorization endpoint.
Authorization endpoint is supporting response_mode
You can find definitions for query, fragment and form_post here and here.
Here are examples of HTTP authentication requests for the authorization code flow:
GET /authorize?
response_type=code
&scope=openid%20profile%20email
&client_id=s6BhdRkqt3
&state=af0ifjsldkj
&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb HTTP/1.1
Host: server.example.com
GET /authorize?
response_type=code
&scope=openid%20profile%20email
&client_id=s6BhdRkqt3
&response_mode=form_post
&state=af0ifjsldkj
&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb HTTP/1.1
Host: server.example.com
Using the acquired authorization code, tokens can be requested from the token endpoint using a token request. The token request must contain the authorization code issued by the authorization endpoint. This AuthState implements both endpoints.
When the client requests an access token via a token request, the client must authenticate itself. There are two possible ways:
- The client includes its credentials in the request body, via the client_id and client_secret parameters.
- The client submits its credentials via the Authorization header.
If the client is configured as public, it must not necessarily have a secret. For clients without secret, no client authentication is required in the request. This follows the following excerpt of the OAuth specification:
The authorization server MAY establish a client authentication method with public clients. However, the authorization server MUST NOT rely on public client authentication for the purpose of identifying the client."
However, if the client does have a secret, authentication is required, as specified in the following section of the OAuth specification:
Confidential clients or other clients issued client credentials MUST authenticate with the authorization server as described in Section 2.3 when making requests to the token endpoint.
Here is an example of an HTTP token request containing the credentials in the request body:
POST /token HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=SplxlOBeZQQYbYS6WxSbIA
&client_id=s6BhdRkqt3
&client_secret=7Fjfp0ZBr1KtDRbnfVdmIw
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
Alternatively, the HTTP basic authentication scheme can be used.
Here is an example of an HTTP token request using the basic authentication scheme:
POST /token HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb
In the previous example, czZCaGRSa3F0MzpnWDFmQmF0M2JW represents the base64-encoded client ID and client secret, separated by a colon (<client id>:<client secret>
).
Note that in the authorization code flow, the Proof Key for Code Exchange or "PKCE" extension specified in RFC 7636 is fully supported. If PKCE is used, requests from the client contain the additional parameters (code_challenge, code_challenge_method, code_verifier) described in RFC 7636. For more information, see RFC 7636.
The level of enforcement of PKCE can be defined on a per client basis. By default and for backwards compatibility reasons, PKCE will be allowed for the client. As soon as a client uses a code challenge, the challenge will automatically be verified at the token endpoint. However, the client is not forced to send PKCE information. For security reasons, it is recommended enforcing the use of PKCE using the S256 code challenge method if the client supports it. Use the attribute pkceMode
for a client using a local metadata source. See the nevisMeta reference guide for information regarding how to configure PKCE when nevisMeta is used as metadata source.
Client credentials flow
In the client credentials flow, a client can request an access token using only its client credentials. In that case, the client makes an HTTP request directly to the token endpoint. Example:
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
If the authorization server could authenticate the client, an access token response containing the access token is issued.
Token format
The AuthorizationServer returns tokens in JSON format. The format of the response differs depending on whether OpenID is supported. If yes, the response contains an additional token (in the attribute id_token).
- The web page OpenID core specification describes the format of the response containing the tokens if OpenID is supported.
- The RFC-6749 describes the format in case OpenID is not supported.
The next example shows a response (with a shortened token value in the attribute id_token
):
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{
"access_token": "SlAV32hkKG",
"token_type": "Bearer",
"refresh_token": "8xLOxBtZp8",
"expires_in": 3600,
"id_token": "eyJhbGciOiJ"
}
OAuth2 access token format
The response generated by the AuthorizationServer includes the attribute access_token
. The value of the access_token
attribute is a JSON web encryption JWE or JWS object containing a JSON web signature JWS object.
The access token payload contains the following claims:
sub
: Subject, string. Set to the user ID of the authenticated user (or the client ID, in case of the client credentials flow).scope
: Scopes requested. The scope values are separated by whitespace.client_id
: Client who sent the request.iss
: The issuer of the ID Token and Access Token. This field is only written when OpenID Connect is enabled.openid.claims.requested
: If applicable, defines the OpenID Connect claims requested from the Userinfo endpoint as a JSON string.nbf
: Date before which the token should not be accepted. Format: seconds since epoch.exp
: Date after which the token should not be accepted anymore. Format: seconds since epoch.iat
: Date when token was issued. Format: Seconds since epoch.ch.adnovum.nevisidm.profileId
: This claim is automatically set to the value${sess:ch.adnovum.nevisidm.profileId}
, contained when issuing the original authorization response. Given the same key material, it is compatible with the AccessTokenConsumer AuthState.
Signature
To sign the claims, the AuthorizationServer uses the private key that is specified in the attributes keyobjectref and keystoreref.
The signature algorithm is:
- RS256: RSASSA-PKCS-v1_5, using the SHA-256 hash algorithm, in case RSA key is configured.
- ES256: ECDSA using P-256 (secp256r1) curve and SHA-256 hash algorithm, in case Elliptic Curve key is configured.
Encryption
To encrypt the signed claims (a JWS object), the AuthorizationServer uses the public key specified in the attributes keyobjectref and keystoreref.
The encryption algorithm is:
- RSA_OAEP_256: RSAES, using Optimal Asymmetric Encryption Padding (OAEP) (RFC 3447), with the SHA-256 hash function and the MGF1 with SHA-256 mask generation function, in case RSA key is configured.
- ECDH-ES: Elliptic Curve Diffie-Hellman Ephemeral Static (RFC 6090) key agreement using the Concat KDF, as defined in section 5.8.1 of NIST.800-56A, with the agreed-upon key being used directly as the Content Encryption Key (CEK) (rather than being used to wrap the CEK), in case Elliptic Curve key is configured.
The encryption method is A256GCM: AES in Galois/Counter Mode (GCM) (NIST.800-38D) using a 256 bit key in both cases.
Serialization
The token is returned as a URL-safe string using JWE or JWS compact serialization as described in RFC 7516 and RFC 7515.
Example
In the next example, the AuthorizationServer generates an access token with a payload containing the following claims:
{
"aud":"audience",
"sub":"the subject",
"nbf":1562741304,
"scope":"scope1",
"iss":"an issuer",
"exp":1864594800,
"iat":1562741304,
"client_id":"the client id"
}
This payload is signed according to the RFC 7515, using the private key defined in the AuthorizationServer configuration. The signed payload (a JWS object) is then encrypted as described in RFC 7516 or signed with RFC 7515, with the public key that is also specified in the AuthorizationServer configuration. Subsequently, the encrypted JWS object (a JWE object) or signed JWS object is transformed into a URL-safe string using compact serialization. The result is a token shown as a string in the next code block - note the different elements, separated by period characters:
eyJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiUlNBLU9BRVAtMjU2In0.MNhMZxhNJZ08IIwXitroji5-GFbH3qG0PokojvbZ1yM6-7Jc7mJjqxMzwEeYefvjpNmuQP5SKw3HKG52qaGGMaKQrYZA0U7pt9-r3Ees5DFfJq5ODXLbb1zL-eK9axpUUMlqKPI2bWaGfbnhhw5M4sOqmYCb809cnIOhBWYObBg.pgHZBBdr2pqq-VHQ.58bC5A7VB0Y1iwInmVfTKIOioZEXV2TKX2fC9DYQNMunhw1OK-oDkQhgIDNSEAdE9UFO_1K1G7eT6SfFjRmJpry0_bNWnvwwI3oBKMBGaJjnyR8-XVa7czNIrzlXu7JcuVmzBVxsELrYApzlnYr6dWxSrVBlqUAWOG6DH1LLLXkYT5oGTi_GueetSze0RTwNqZU3s6Oo2riMh2y-DZkF43pWZJcb0Dj49EvSpx_wWYLG4HZPh7Tnu2u8Gr549vBA_yi94DOBWJQl77BbmgFmuQKvBP9ftW9lWbtDLgy4A5h6uFQPVnkb4uIOoNQ_QX0rjYek620nEldpr7wr4rsGo4qDFxty8LXiALEmizT9nggLOo7yzwdEvR9rZoClGanHzJQIhdeFuxbVZ_MBRTBBOl0jema5yW2-wfOEzqKv4B4vAfTiTYkS7BcWOTjCJD5On5fuztYpNopARK_QhfxCgiY9BuReH6o_IaUbUVdIQjOujOgQ2gdpHLEsRWJtI65pYf8aGPmuTuHCaz7j.4TuBTuQOiklfmHOyKI9hUA
Claims extracting code - example
The following Java code example uses the Nimbus OAuth 2.0 SDK and the Nimbus JOSE + JWT Library to extract the claims from a given access token. For a description of additional operations, such as signature verification, see the examples in the Nimbus documentation.
import java.io.FileInputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.KeyStore;
import java.security.interfaces.RSAPrivateKey;
import java.text.ParseException;
import java.util.Enumeration;
import java.util.Objects;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWEObject;
import com.nimbusds.jose.Payload;
import com.nimbusds.jose.crypto.RSADecrypter;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
public class AccessTokenClaimsExtractor {
private final RSADecrypter decrypter;
public static void main(String[] args) throws Exception {
// Test method, it is assumed that the token was signed using the public key of a certificate
// whose asociated private is stored in a PKCS12 keystore located in /tmp/keystore.p12.
String token = "eyJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiUlNBLU9BRVAtMjU2In0.mbzOp9wMydLJ3m6SNX0YaqTzRRom7LRBEBT0cDuNVtuHS8RGy3ScvBWvN1ct2OLYZQd1cDUII4jTz7b0AR3lEnGKqlNyh50g0PRskZD5MEVh98Vs657kBv3kJxOQdnxBPmZAh6VCpBDkfxu6qiOogUcag4rHKdqqdog1Ugf6nXiffiIGBR2SkToB4KRkSmJBAX3Wubm38k2hyo2iXeIU7tg3bE3J2GrunhmoXqkEWcZ4iZktmOyhhPlyx990_w7_n05QoQ8t5Dmemmg16XouR0gb1oJgkOYxYIlBg5BYtACiJWOq2puL2VhQs1hf2tZeMO8cvMqMLyfi4rnb7PfGxQ.220heA9uswUGUWr2.e16gdo679Njexk-b2TEslkG8nsq_zv_Oq2Vl7luZfw5tDf_8NG1WrNtpNnjj0AKKG74NPUQVpJxFm6rZ0FS8ITAYjK9UgY3pOL8YiGYCHyj9b7l5B2xldmuLAT_ngw86FkrqT7ZH8iZ5_lamOA5vs_ycjkgj9bd5T2uosGt2JtS9jV9e1GF2M7inj9Mi90e2pYwp3mYUlc0GDi1qYvBU1bfVUNJe4fZThY0qvn84wjg4vCSahq966qzJGd5_Ez1rLThTOBqrnRNCJjBi_N-rwosilcxhNHhnyN650FIxCh3xUJbuqdZ0OlddxqeFrc1P9aFbZbvH8slzkCLndrDhJZHWuZ_B_K6Ra14iQQcySTdLSw5zIV7yNGqDg0vAZ3k9Pn2B_50ENXgR2Yrt4cgfyg-woqHQrzo-iUAlfLIub954pwt0POO5rYFqq7nBMFn5nCsGPid63wrW1yaR4yWLL0BxjXYaENSD-kC6zG2QDrzgQje713k_zfdwiGBTfDc_mSb5PyH-afp64Ve3xHUsln5uYDYwwhivPkiCzLqDyLhGtHJAEz2xS2MtLc0ta2vWvHS2T2PYJAiiAVGzAM4u-_fXoYG_ZiCMJ06jKcgpR5T1-FZgQVT8kj7MW9bNEmoYOl2QBNfkRYCgIeK1UlS4l-9gzpBYOZ8LPM7ONNuxpbnRoj8lkWRKcsDo0BJ1ZSKFDc3ssMkeRdp36lFsA1nlQLGxrUSlDRqRJrHAyhMiM8C-K4jKImMO.HW2mVej8bYmZ5gTJmGD9iQ";
RSAPrivateKey rsaPrivateKey = extractPrivateKey("/tmp/keystore.p12",
"password".toCharArray(), "PKCS12");
RSADecrypter decrypter = new RSADecrypter(rsaPrivateKey);
AccessTokenClaimsExtractor extractor = new AccessTokenClaimsExtractor(decrypter);
JWTClaimsSet claimsSet = extractor.extractClaims(token);
// Handle the claims, here the code just converts the claims to a JSON string and prints them.
String json = claimsSet.toJSONObject().toString();
System.out.println("Claims as JSON:");
System.out.println(json);
}
public AccessTokenClaimsExtractor(RSADecrypter decrypter) {
this.decrypter = Objects.requireNonNull(decrypter);
}
public JWTClaimsSet extractClaims(String token) throws ParseException, JOSEException {
JWEObject jweObject = JWEObject.parse(token);
jweObject.decrypt(decrypter);
Payload payload = jweObject.getPayload();
SignedJWT signedJwt = payload.toSignedJWT();
return signedJwt.getJWTClaimsSet();
}
private static RSAPrivateKey extractPrivateKey(String keyStorePath, char[] password,
String keyStoreType) throws IOException, GeneralSecurityException {
FileInputStream is = new FileInputStream(keyStorePath);
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(is, password);
Enumeration<String> aliases = keyStore.aliases();
if (!aliases.hasMoreElements()) {
throw new IllegalStateException("No aliases defined in keystore");
}
// The example assumes that the keystore contains only one alias
String alias = aliases.nextElement();
// The example assumes that both the keystore and the keys are protected using the same
// password
Key key = keyStore.getKey(alias, password);
// Currently, RSA and Elliptic Curve (EC) keys are supported, but this example only shows how to use RSA
if (!(key instanceof RSAPrivateKey)) {
throw new IllegalStateException("The key in the keystore is not an RSAPrivateKey");
}
return ((RSAPrivateKey)key);
}
}
OpenID ID token format
If OpenID Connect is supported, the AuthorizationServer will generate an ID token in addition to the access token. This ID token is provided as the value of the id_token
attribute of the response. The ID token is a signed JSON web token JWT object containing the following default claims:
iss
: Issuer identifier, string. See the propertyopenid.issuerId
. This table describes the AuthorizationServer AuthState.sub
: Subject, string. Set to the user ID of the authenticated end user.aud
: Audience, array of strings. Set to the client ID.exp
: Expiration time, number (Unix timestamp in seconds).iat
: Issued at, number (Unix timestamp in seconds).auth_time
: Time when the End-User authentication occurred at, number (Unix timestamp in seconds).
Furthermore, the ID token contains the claims that are requested to be included (via scope or claims request), according to the OpenID Connect specification.
The AuthorizationServer currently ignores the acr claim request, only supports acr_values.
Signature
To sign the claims, the AuthorizationServer uses the private key that is specified in the attributes keyobjectref
and keystoreref
.
The signature algorithm is RS256: RSASSA-PKCS-v1_5, using the SHA-256 hash algorithm.
Encryption
Encryption of signed claims (a JWS object) is done only if idTokenEncryptedResponseAlg
is set on the client. The public key for the encryption will be taken from the client's jwksUri
or jwks
properties. The key will be picked by algorithm family and type enc
, in case more than one key is found the one having the longest validity will be taken. (note that keys having no validity defined are valid indefinitely).
The supported encryption algorithms are:
- RSA1_5, RSA-OAEP, RSA-OAEP-256, RSA-OAEP-384, RSA-OAEP-512: in case RSA key is configured.
- ECDH-ES, ECDH-ES+A128KW, ECDH-ES+A192KW, ECDH-ES+A256KW: in case Elliptic Curve key is configured.
Serialization
The token is returned as a URL-safe string using JWS compact serialization as described in RFC 7515.
Example
In the next example, the AuthorizationServer generates an ID token with a payload containing the following claims:
{
"sub":"authenticatedUser",
"aud":"responseTypeCodeTokenIdTokenClientId",
"iss":"https:\/\/siven.ch\/oauth2",
"exp":1563439848,
"iat":1563439248,
"nonce":"xyz"
}
This payload is signed according to the RFC 7515, using the private key defined in the AuthorizationServer configuration. The JWS object is then transformed into a URL-safe string using compact serialization. The result is a token shown as a string in the next code block. Note the different elements, separated by period characters:
eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJhdXRoZW50aWNhdGVkVXNlciIsImF1ZCI6InJlc3BvbnNlVHlwZUNvZGVUb2tlbklkVG9rZW5DbGllbnRJZCIsImlzcyI6Imh0dHBzOlwvXC9zaXZlbi5jaFwvb2F1dGgyIiwiZXhwIjoxNTYzNDQwNTg2LCJpYXQiOjE1NjM0Mzk5ODYsIm5vbmNlIjoieHl6In0.Uzvl7rzjFww7rwXp1KAKMf8IqxKuoPQFo9SkfcFE6us9gIAn47jU94fO6p-TVISE_4KCkBtqFw52Ph26ztS73y8pHd6VebjK7I4uwu7EcwCVlugCupoRysba-DFKWGyl1A3ZwUSreOxJ4ZYj90XR1SWE1jrvK3J9hNHPcITlAAs
Claims extracting code - example
The following Java code example uses the Nimbus OAuth 2.0 SDK and the Nimbus JOSE + JWT Library to extract the claims from a given ID token. For a description of additional operations, such as signature verification, see the examples in the Nimbus documentation.
import java.text.ParseException;
import com.nimbusds.jwt.JWT;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
public class IDTokenClaimsExtractor {
public static void main(String[] args) throws ParseException {
String token = "eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJhdXRoZW50aWNhdGVkVXNlciIsImF1ZCI6InJlc3BvbnNlVHlwZUNvZGVUb2tlbklkVG9rZW5DbGllbnRJZCIsImlzcyI6Imh0dHBzOlwvXC9zaXZlbi5jaFwvb2F1dGgyIiwiZXhwIjoxNTYzNDQwNTg2LCJpYXQiOjE1NjM0Mzk5ODYsIm5vbmNlIjoieHl6In0.Uzvl7rzjFww7rwXp1KAKMf8IqxKuoPQFo9SkfcFE6us9gIAn47jU94fO6p-TVISE_4KCkBtqFw52Ph26ztS73y8pHd6VebjK7I4uwu7EcwCVlugCupoRysba-DFKWGyl1A3ZwUSreOxJ4ZYj90XR1SWE1jrvK3J9hNHPcITlAAs";
JWT idTokenDeserialized = SignedJWT.parse(token);
// Handle the claims, here the code just converts the claims to a JSON string and prints them.
IDTokenClaimsExtractor extractor = new IDTokenClaimsExtractor();
JWTClaimsSet claimsSet = extractor.extract(token);
String json = claimsSet.toJSONObject().toString();
System.out.println("Claims as JSON:");
System.out.println(json);
}
private JWTClaimsSet extract(String token) throws ParseException {
SignedJWT signedJwt = SignedJWT.parse(token);
return signedJwt.getJWTClaimsSet();
}
}
OAuth2 and OpenID Connect setups with nevisMeta
The OAuth 2.0 authorization server of nevisAuth offers a connector to nevisMeta to support the management of OAuth2 and OpenID Connect setups. With nevisMeta, it is possible to manage entities such as clients and resources in a central location via a comfortable GUI. Using nevisMeta, clients and scopes are not required to be configured within the AuthState but can be managed within nevisMeta. The connector can be activated by setting the dataSource
property of the OAuth 2.0 authorization server configuration to nevismeta. It is necessary to provide the endpoint address of a nevisMeta instance as well as a specific SecToken. The parameters involved are described in below.
It is important to know that the nevisMeta connector in nevisAuth uses a time-out-based caching mechanism for querying data from nevisMeta. During the startup, the nevisMeta connector fetches all data (clients, resources, etc.) stored in the connected nevisMeta instance. Once this time-out is reached, the connector fetches the whole dataset again. The connector uses the ETag
and the If-None-Match
headers, so the cache is not updated if there was no change in nevisMeta. If nevisAuth cannot find a client in its cache then it fetches the metadata from nevisMeta again. This can be controlled using the nevismeta.updateMetadataWhenClientNotFound
property. The properties related to the caching as well as further connection-related properties are described in the table below.
To protect nevisMeta from unnecessary requests generated by unknown clients, you can set nevismeta.blockClientInterval
to skip the metadata update for a while in case the unknown client is still not found after the metadata update. These client IDs are stored in the configured OOCD - one entry per client ID - see Shared out-of-context data.
Configuration of the AuthorizationServer
For all flows, the prior registration of the supported scopes, the client and its redirect URIs at the AuthorizationServer is mandatory. An architecture has been implemented to support multiple sources for client and scope configuration data.
Scope policies
Scope configuration allows to set policies for the approval process of in
- Implicit flow policy (
CONSENT_POLICY
): Configures how consent for the scope is established when requested via the implicit flow. - Authorization code flow policy (
CONSENT_POLICY
): Configures how consent for the scope is established when requested via the authorization code flow. - Client credentials flow policy (true/false): Defines whether the scope can be requested via the client credentials flow.
- Refresh token request policy (
CONSENT_POLICY
): Defines how consent is established when requested in combination with a refresh token. - Authentication required policy (true/false): Configures whether a request for that scope should lead to forced (re-)authentication.
CONSENT_POLICY
can take one of the following values (ordered according to their strictness):
- `DISALLOWED`` This scope cannot be granted.
CONSENT_REQUIRED
End-user consent is always required.CONSENT_PERISTED
(default) End-user consent is required once and then persisted in the session.NO_CONSENT_REQUIRED
End-user consent is not required.
For the consent policies consent required, consent persisted and no consent required, compliance is enforced by the ConsentState (see chapter 5.9.1.2 "ConsentState"). If more than one policy applies to a request, the strictest policy is enforced.
Description
The following table and chapters describe the characteristics of the AuthState.
Topic | Description |
---|---|
Class | ch.nevis.esauth.auth.states.oauth2.AuthorizationServer |
Logging | OAuth2 |
Auditing | none |
Marker | "OAUTH2:token" and "OpenIDConnect:federation" |
Methods
process
Will initiate the OAuth 2.0 protocol. Implements the authorization endpoint as well as the token endpoint. It thus will accept OAuth 2.0 authorization requests as well as OAuth 2.0 token requests.
finish
Will generate the authorization/token response.
handleError
Will generate the authorization/token error response.
Properties
accessTokenLifetime
(seconds, 3600)Defines how long an access token issued by the authorization server should be valid per default (can be overwritten by client policies).
infoNote that the calculation of the expiration time is based on the issuing time of the authorization code, not on the time the access token is returned in the AuthorizationServer's response.
refreshTokenLifetime
(seconds, 1 year)How long a refresh token issued by the authorization server should be valid per default (can be overwritten by client policies).
authCodeLifetime
(seconds, 600)How long an authorization code issued by the authorization server should be valid.
keystoreref
(string, "DefaultKeyStore"),keyobjectref
(string, "DefaultSigner")This property configures the key and certificate to use when issuing tokens. The access token is encrypted using the configured public key. The ID token is signed using the configured private key. Currently, RSA and Elliptic Curve (EC) keys are supported.
If the nevisAuth instances are configured in a load balancing or load balancing configuration, the
keystoreref
of all the instances should reference the same keys.keyID
(string, - )This property configures the kid JOSE header value of the issued access and ID tokens. This value allows the authorization server to explicitly signal a change of key material to recipients. The meaning of the
kid
header is slightly different for signed and encrypted tokens. (Note that ID Token encryption is using JWKS therefore the keyID for the encrypted ID Token will contain the keyID belonging to the key used from the JWKS. Therefore setting this keyID property has no effect on ID Token encryption.)dataSource
(string, "local")Determines what data source should be used to configure OAuth 2.0 clients and scopes. With dataSource=local, client and scope configuration data is supplied via configuration properties. With
dataSource=nevismeta
, client and scope configuration data is supplied via nevisMeta.propagationScope
(string, "session")Define propagation scope to store information for following AuthStates.
encryptAccessToken
(boolean, true)This property defines the format of the Access Token. If set to:
- true, the Access Token will be encrypted (JWE).
- false, the Access Token will not be encrypted (JWS).
nestedJWSAccessToken
(boolean, false)This property defines how JWS Access Token is generated. If set to:
- true, the JWS Access Token is nested (JWS inside JWS Access Token).
- false, the JWS Access Token is not nested.
This property can only use when encryptAccessToken set to false to generate JWS Access Token.
rotateRefreshToken
(boolean, false)This property defines if a new Refresh Token is issued together with the Access Token on the Token Endpoint while exchanging a refresh token for a new access token (
grant_type=refresh_token
).- true, a new Refresh Token is issued, the existing Refresh token is deleted.
- false, the existing Refresh token is returned and remains valid.
openid.jwks.httpclient.*
(String)Configure the outgoing HTTP communication to the jwksUri specified for the client, to download keys for ID Token encryption. For a list of valid HTTP properties, see HTTP Client.
Effective if dataSource=local
Basic client configuration
All clients must be registered and configured before sending authorization and token requests.
client.[clientId]
(string, - )Registers a client.
client.[clientId].secret
(string, - )Client secret for client with ID [clientId]. Replace [clientId] by the client identifier.
This attribute is required if the client type is "confidential". If the client is "public", it is optional. The client type is set in the property
client.[clientId].type
.
Client metadata configuration
For every client, client metadata needs to be configured. The following values are internally mapped to a client metadata representation. The client metadata is then propagated as described in the topic Output.
client.[clientId].scope
(string, - )Space-separated list of scopes the client is allowed to request. These scopes must be registered at the AuthorizationServer. Client metadata mapping: scope field of the client metadata.
client.[clientId].clientName
(string, clientId )Name of the client. Note that this is not the same as the clientId. The clientId is a unique identifier, often random, whereas the client name is a human-readable name. Client metadata mapping: client_name field of the client metadata.
client.[clientId].clientName.[langTag]
(string, - )Language-specific name of the client. Client metadata mapping: client_name#langTag field of the client metadata.
client.[clientId].redirectUrl
(string: URIs, -)Space-separated list of registered redirection URIs for the client [clientId]. The redirection URIs of the client must be registered to be accepted as valid redirection URIs by the AuthorizationServer. Client metadata mapping: redirect_uris field of the client metadata.
client.[clientId].responseTypes
(string, "code")Space-separated list of the OAuth2 response types the client will restrict itself to using. Valid values are code, token, and id_token. Client metadata mapping: response_types field of the client metadata.
client.[clientId].grantTypes
(string, "authorization_code")Space-separated list of the OAuth2 grant types the client will restrict itself to using. Valid values are authorization_code, implicit, refresh_token, and client_credentials. Client metadata mapping: grant_types field of the client metadata.
client.[clientId].authenticationRequiredPolicy
(boolean, "false")Defines whether a request by this client requires forced (re)-authentication of the end user (result valid-authorization-request-authentication-required). Client metadata mapping: client_force_authentication field of the client metadata.
client.[clientId].accessTokenTtl
(accessTokenLifetime property)Defines the lifetime of the access token when requested by this client. Client metadata mapping: access_token_ttl field of the client metadata.
client.[clientId].idTokenTtl
(seconds, openid.idTokenLifetime property)Defines the lifetime of the ID token when requested by this client. Client metadata mapping: id_token_ttl field of the client metadata.
client.[clientId].refreshTokenTtl
(seconds, refreshTokenLifetime property)Defines the lifetime of the refresh token when requested by this client. Client metadata mapping: refresh_token_ttl field of the client metadata.
client.[clientId].logoUri
(string: URIs, -)Default logo of the client. Client metadata mapping: logo_uri field of the client metadata.
client.[clientId].logoUri.[langTag]
(string: URIs, -)Language specific logo of the client. Client metadata mapping: logo_uri#langTag field of the client metadata.
client.[clientId].tosUri
(string: URIs, -)Default Terms of Service of the client. Client metadata mapping: tos_uri field of the client metadata.
client.[clientId].tosUri.[langTag]
(string: URIs, -)Language specific Terms of Service of the client. Client metadata mapping: tos_uri#langTag field of the client metadata.
client.[clientId].policyUri
(string: URIs, -)Default policy of the client. Client metadata mapping: policy_uri field of the client metadata.
client.[clientId].policyUri.[langTag]
(string: URIs, -)Language specific policy of the client. Client metadata mapping: policy_uri#langTag field of the client metadata.
client.[clientId].type
(string, "confidential")The type of client.
The AuthorizationServer supports two types of clients: "confidential" (default) and "public".
client.[clientId].pkceMode
(string, "allowed")Whether the client requires the use of PKCE in the authorization flow. The following types of PKCE modes are supported:
- "allowed" (default): If the client sends PKCE information in the form of a code challenge in the authorization request, the code challenge will be validated. If the code challenge is not valid, the authorization will fail. But if no code challenge is included in the authorization request, the authorization will not fail.
- "required": The client must send valid PKCE information. If no code challenge is included in the authorization request, the authorization will fail.
- "s256-required": The client must send valid PKCE information using the S256 code challenge method. The authorization will fail if no code challenge is included in the authorization request, or if the code challenge does not use the S256 code challenge method.
If the client supports the s256 code challenge method, then "s256-required" is the recommended value.
For more information, see PKCE.
client.[clientId].jwksUri
(string: URIs, -)Specific URI of JWKS endpoint for each client where the keys will be downloaded from.
client.[clientId].jwks
(string: JSON, -)JWKS JSON containing public keys for each client. The
jwksUri
property takes precedence overjwks
.client.[clientId].tokenEndpointAuthMethod
({none, client_secret_basic, client_secret_post}, default according to type (see below) )Method used to authenticate at the token endpoint. The following methods are supported:
- "none": for public clients without secret
- "client_secret_basic": for confidential clients and public clients with secret
- "client_secret_post": for confidential clients and public clients with secret
The default value depends on the type of clients:
- For public clients without secret, the default authentication method is
none
- For confidential clients or public clients with secret, the default authentication method is
client_secret_basic
client.[clientId].requirePushedAuthorizationRequests
(string: boolean, "false")Set option for the client with Pushed Authorization Requests.
- "false" (default): The client can use Pushed Authorization Requests as an optional.
- "true": The client is forced to use Pushed Authorization Requests.
client.[clientId].idTokenSignedResponseAlg
(string, RS256 )JWS algorithm for signing the ID Token issued to this Client. Default value is RS256. Supported values are: RS256, RS384, RS512, ES256, ES256K, ES384, ES512
client.[clientId].idTokenEncryptedResponseAlg
(string, - )JWE algorithm for encrypting the ID Token. This property signals that the ID Token should be encrypted. If the property is not set, no encryption will be done. The public key for the encryption will be taken from the
jwksUri
orjwks
properties. The key will be picked by algorithm family and typeenc
, in case more than one key is found the one having the longest validity will be taken. (note that keys having no validity defined are valid indefinitely). Supported values are: RSA1_5, RSA-OAEP, RSA-OAEP-256, RSA-OAEP-384, RSA-OAEP-512, ECDH-ES, ECDH-ES+A128KW, ECDH-ES+A192KW, ECDH-ES+A256KWclient.[clientId].idTokenEncryptedResponseEnc
(string, A128CBC-HS256 )JWE encryption method for the ID Token. In case nothing is provided A128CBC-HS256 will be used as default. This property only makes sense if
idTokenEncryptedResponseAlg
is also set, setting this property without theidTokenEncryptedResponseAlg
will result in an error. Supported values are: A128CBC-HS256, A192CBC-HS384, A256CBC-HS512, A128GCM, A192GCM, A256GCM
Scope configuration
All scopes must be registered before they can be requested by clients.
scope.[scopeName]
(string, -)Registers a scope. Set value with defined claim(s) to mapping between custom scope and custom claim(s) for adding more data to ID Token and Access Token.
scope.[scopeName].implicitFlowPolicy
(string: [Consent Policy], "CONSENT_PERSISTED")Configures how consent for the scope is established when requested via the implicit flow.
scope.[scopeName].clientCredentialsFlowPolicy
(boolean, "true")Configures whether the scope can be requested via the client credentials flow.
scope.[scopeName].authorizationCodeFlowPolicy
(string: [Consent Policy], "CONSENT_PERSISTED")Configures how consent for the scope is established when requested via the authorization code flow.
scope.[scopeName].refreshTokenRequestPolicy
(string: [Consent Policy], "CONSENT_PERSISTED")Configures how consent for the scope is established when requested in combination with a refresh token.
scope.[scopeName].authenticationRequiredPolicy
(boolean, "false")Configures whether a request of this scope should lead to forced (re)-authentication of the end user (signaled by the valid-authorization-request-authentication-required result).
Effective if dataSource=nevisMeta
nevismeta.location
(string, -)The entity endpoint of a nevisMeta instance (for example:
https:// [server]:[port]/nevismeta/rest/modules/oauthv2/setups/[setup_id]/ entities
)nevismeta.maxAge
(int, 600)Caching of responses from a nevisMeta instance. After this time (in seconds), a response is considered outdated and attempts are made to update it.
nevismeta.blockClientInterval
(int,nevismeta.maxAge
)To protect nevisMeta from unnecessary requests generated by unknown clients. The unknown client does not trigger metadata updates for seconds, that is configured by this parameter. By default, this parameter has the same value as
nevismeta.maxAge
. To turn this feature off, set zero as the value.nevismeta.discardInterval
(int, 60)Caching of responses from a nevisMeta instance. If a query/update failed, do not try to update again until this interval (in seconds) has passed. This is to avoid continuous requests in case of connection failures. A default retry interval of one minute is defined.
nevismeta.updateMetadataWhenClientNotFound
(boolean, "true")Configures whether nevisAuth should update its metadata from nevisMeta in case a client is not found in its current cache.
nevismeta.httpclient.*
(String)Configure the outgoing HTTP communication to nevisMeta. For a list of valid HTTP properties, see HTTP Client.
OpenID Connect
openid.support
(boolean, "false")If set to true, OpenID Connect is supported.
openid.issuerId
(string, -)The iss claim value of the ID token and the access token. Identifies the issuer of the ID token and the access token. Must be set to a case-sensitive URL using the https scheme.
openid.idTokenLifetime
(seconds, 600)How long the ID token should be valid per default (can be overwritten by client policies). At most a few minutes are recommended.
openid.claimsParameterSupported
(boolean, "true")If this property is set to "false", the system will ignore the claims requested via the claims parameter in the OpenID Authorization Request. Only the claims requested through the scope values will be considered. If set to "true", the system will manage both the claims requested via the scope values and the claims parameter.
For more details, see ClaimsParameter. The claims_parameter_supported result mentioned in this specification is equivalent to the property
openid.claimsParameterSupported
described here.openid.acr_values_supported
(string, -)Space-separated string that specifies the acr values that the Authorization Server supports.
openid.claim.[claimName]
(string, -)Defines the value of the claim to be integrated in the ID token when requested. You can use JSON syntax to specify arrays and JSON values.
Generates a single value ('value1,value2') for the claim 'myattr'<property name="openid.claim.myattr" value="value1,value2"/>
Generates a single value ('null') for the claim 'myattr'<property name="openid.claim.myattr" value="null"/>
Generates an array with three values (1, 'value2' and null) for the claim 'myattr'<property name="openid.claim.myattr" value="[1,"value2",null]"/>
Generates a JSON object consisting of one attribute named 'attr1' with a value ('value1') for the claim<property name="openid.claim.myattr" value="{"attr1,":"value1"}"/>
Assuming that notevalue contains a value '[null]', generates an array with one value (null) for the claim 'myattr'<property name="openid.claim.myattr" value="${notes:notevalue}"/>
infoNote that requesting claims via scope does not necessarily lead to their inclusion in the ID token. This is according to the specification and documented in ScopeClaims.
infoNote if
openid.claim.amr
is defined then its value is put into the ID Token.openid.userInfoEndpointUri
(string, -)If this property is set, the userinfo data that comes from the configured endpoint is used to specify the claim values in the ID token. Individual claim values are replaced by the values explicitly configured via
openid.claim.[claimName]
.The claim values used to generate the ID token are fetched from the configured userinfo endpoint. nevisAuth sends the request on behalf of the subject using a self-issued SecToken. It expects the configured endpoint to return a userinfo response as defined in UserInfoResponse.
infoThis property is required if relying parties use refresh tokens to retrieve fresh ID tokens (i.e., authorization code flow in combination with OpenID Connect support).
The following example describes a scenario in which set this property: After a successful OpenID Connect authentication request, the relying party receives an ID token and a refresh token from the Identity Provider. Later - when the ID token expires - the relying party may intend to receive a fresh ID token using the refresh token. This happens outside the context of the subject’s session (the request is directly made by the relying party without the subject’s presence). Therefore, the Identity Provider does not have access to the subject’s up-to-date profile information within the session (as opposed to when the original authentication happened). The Identity Provider thus needs other means to get access to the subject’s profile data.
infoNote that if OpenID Connect is supported and the attribute
openid.userInfoEndpointUri
is set, hostname verification is enabled by default. The relevant property isopenid.userinfo.httpclient.checkHostname
(boolean, true). When establishing an HTTPS connection, the client will check the hostname in the HTTPS server certificate against the actual server hostname by default. To disable hostname verification,set the propertyopenid.userinfo.httpclient.checkHostname
to "false". See below for more information about the prefixopenid.userinfo.httpclient
.openid.userinfo.httpclient.*
(String)If the property openid.userInfoEndpointUri is set, configure the HTTP properties for the outgoing HTTP communication towards
openid.userInfoEndpointUri
. For a list of valid HTTP properties, see HTTP Client.
OAuth2
oauth2.claim.[claimName]
(string, -)Defines the value of the claim to be integrated in the access token when requested. You can use JSON syntax to specify arrays and JSON values.
Generates a single value ('value1,value2') for the claim 'myattr'<property name="oauth2.claim.myattr" value="value1,value2"/>
Generates a single value ('null') for the claim 'myattr'<property name="oauth2.claim.myattr" value="null"/>
Generates an array with three values (1, 'value2' and null) for the claim 'myattr'<property name="oauth2.claim.myattr" value="[1,"value2",null]"/>
Generates a JSON object consisting of one attribute named 'attr1' with a value ('value1') for the claim<property name="oauth2.claim.myattr" value="{"attr1,":"value1"}"/>
Assuming that notevalue contains a value '[null]', generates an array with one value (null) for the claim 'myattr'<property name="oauth2.claim.myattr" value="${notes:notevalue}"/>
Input
none
Transitions
invalid-client
The client sending the request is not registered.
invalid-redirect-uri
The
redirect_uri
request parameter value is not registered for the client sending the request.invalid-authorization-request
The authorization request is invalid.
invalid-request-uri
The request uri is invalid.
valid-authorization-request
Authorization request is valid, end-user authentication expected as the next step.
valid-authorization-request-authentication-required
Authorization request is valid, forced end-user authentication expected as the next step.
valid-token-request
Token request is valid. Tokens are about to be issued. The user ID has been set to the
client_id
.invalid-token-request
Token request is invalid. Token error response is about to be issued.
server-error
Something went wrong internally.
Output
Authorization request
[propagationScope]:oauth2.authorization_request.[requestParameter]
All authorization request parameters are propagated using the configured propagationScope (see property propagationScope in this table). [requestParameter] is a placeholder for the name of the request parameter.
Client configuration
[propagationScope]:oauth2.client.id
Client ID of the client that issued the authorization request.
[propagationScope]:oauth2.client.metadata.[field]
Metadata of the client that issued the authorization request.
Scope configuration
[propagationScope]:oauth2.scope.policy.clientCredentialsFlow
Scope policy parameter as configured by the property dataSource.
[propagationScope]:oauth2.scope.policy.authorizationCodeFlow
Scope policy parameter as configured by the property dataSource.
[propagationScope]:oauth2.scope.policy.implicitFlow
Scope policy parameter as configured by the property dataSource.
[propagationScope]:oauth2.scope.policy.refreshTokenRequest
Scope policy parameter as configured by the property dataSource.
[propagationScope]:oauth2.scope.policy.authenticationRequired
Scope policy parameter as configured by the property dataSource.
[propagationScope]:oauth2.scope.metadata.[field]
Scope metadata as configured by the property dataSource.
Errors
none
Notes
none
Example
<AuhState
class="ch.nevis.esauth.auth.states.oauth2.AuthorizationServer"
final="false"
name="AuthorizationServer"
resumeState="true">
<ResultCond name="invalid-client" next="AuthError" />
<ResultCond name="invalid-redirect-uri" next="AuthError" />
<ResultCond name="invalid-authorization-request" next="AuthError" />
<ResultCond name="server-error" next="AuthError" />
<ResultCond name="invalid-token-request" next="AuthError" />
<ResultCond name="valid-token-request" next="AuthDone" />
<ResultCond name="valid-authorization-request" next="Login" />
<property name="client.clientId_1.clientName" value="abc" />
<property name="client.clientId_1.redirectUrl"
value="http://example.ch:8080/oauth/client/" />
<property name="client.clientId_1.scope"
value="openid email profile address phone" />
<property name="client.clientId_1.secret" value="secret" />
<property name="dataSource" value="local" />
<property name="openid.issuerId" value="https://www.nevis.ch" />
<property name="openid.support" value="true" />
<property name="scope.address" value="" />
<property name="scope.email" value="" />
<property name="scope.openid" value="" />
<property name="scope.phone" value="" />
<property name="scope.profile" value="" />
</AuthState>