Federation - OAuth2 / OIDC

Overview

Ceptor supports OAuth2 / OpenID Connect (OIDC) and exchanging JWT tokens with 3rd parties.

To use these federations, you should add the authentication plugin dk.itp.security.authentication.oauth.jwt.JWTAuthenticationPlugin


This support is an integral part of Ceptor API Management but can also be used outside the API Management scope as a means for federation of identities both from Ceptor to Service Providers and from OpenID or OAuth2 Providers to Ceptor.

Ceptor supports the different variations of OAuth2 flows, including:

  • Implicit Flow
  • Authorization Code Flow
  • Authorization Code with PKCE Extension
  • Hybrid Flow
  • Refresh Token 
  • Client Credentials Flow
  • Resource Owner Password Credentials
  • Token Introspection / Revocation
  • Token Exchange

For each scope, you can configure which attributes are added to the various types of tokens, ID Token, Access Token and Userinfo JSON.

Available flows can be restricted for specific clients, optionally limiting them to use only specific flows and scopes.

General Configuration

These settings are stored directly in the Federation JSON Root object in the configuration for the session controller.

OAuth2 / OpenID Connect configuration


OAuth2 Client Datastore class

Specify which OAuth2 Datastore implementation you would like to use

Out of the box, Ceptor has 3 different datastores available.

  • dk.itp.security.authentication.oauth.data.properties.OAuth2DataStoreProperties
    This implementation stored information about clients in the configuration along with the rest of the Federation settings - this approach is very useful if you have a limited set of clients / partners, but it does not scale well for thousands of clients. In that case, and if you are using Ceptor API Management you should use the SQL variant instead.
  • dk.itp.security.authentication.oauth.data.OAuthSQLStore
    This implementation uses a database to store the clients. This is the recommended implementation to use unless you only have a limited number of clients you need. In addition, this allows self-service using Ceptor API Developer Portal where partners are able to administer themselves and their applications, including managing access credentials such as client secrets or API keys.
  • dk.itp.security.authentication.oauth.data.OAuthIgniteStore
    This implementation uses Apache Ignite - it is not recommended for use, instead you should be using the SQL implementation.

You also have the option of providing your own implementation in case you use a different system to store your OAuth2 clients in. Implementations must implement the Java Interface dk.itp.security.authentication.oauth.data.IOAuthDataStore  which looks like this:

IOAuthDataStore & ClientData clases
public interface IOAuthDataStore {
	/**
	 * Gives the datastore its configuration
	 * @param config Configuration
	 */
	public void setConfiguration(Properties config) throws ConfigurationException;
	
	/**
	 * Gives the datastore its configuration
	 * @param config Configuration
	 * @param json JSON Configuration
	 */
	public void setConfiguration(Properties config, JSONObject json) throws ConfigurationException, JSONException;
	
	/**
	 * Return a list of known client IDs
	 * @return List of known client IDs
	 */
	public Collection<String> getKnownClientIDs();
	
	/**
	 * Returns known ClientData from a clientID
	 * 
	 * @param clientID Client ID
	 * @return Client Data - includes secret, valid scopes, valid redirect URIs etc.
	 */
	public ClientData getClientData(String clientID);
	
	default void initialize(PTSServer sessionController) {
	}
}

public class ClientData {
	public enum AccessTokenType {
		JWT, UUID, RFC9068, RFC9060UP
	}
	
	/** Client ID */
	public String clientID;
	/** Client secret */
	public String clientSecret;
	/** Valid OAuth2 grant type; implicit, authorization_code */
	public Collection<String> validGrantTypes;
	/** Valid scopes, e.g. email, profile, name, photo */
	public Collection<String> allowedScopes;
	/** Allowed redirect URIs */
	public Collection<String> allowedUris;
	/** Allowed logout URIs */
	public Collection<String> allowedLogoutUris;
	/** Number of seconds the access token is valid for */
	public int accessTokenValiditySeconds;
	/** Number of seconds the refresh token is valid for */
	public int refreshTokenValiditySeconds;
	
	/** The maximum expiration time in minutes allowed */
	public int maximumIdTokenExpirationMinutes;
	/** Name of token configuration to use with this client */
	public String tokenname;
	/** Type of access token to generate */
	public AccessTokenType accessTokenType;
	
	/** A list of roles for this client - could be roles for an API partner */
	public List<String> roles;
	
	public Map<String, Object> properties = new LinkedHashMap<>();
	
	public String toJSON() {
		JSONObject jo = new JSONObject();
		try {
			jo.put("client_id", clientID);
			jo.put("client_secret", clientSecret);
			jo.put("valid_grant_types", validGrantTypes);
			jo.put("allowed_scopes", allowedScopes);
			jo.put("allowed_uris", allowedUris);
			jo.put("allowed_logout_uris", allowedLogoutUris);
			jo.put("accesstoken_valid_seconds", accessTokenValiditySeconds);
			jo.put("refreshtoken_validity_seconds", refreshTokenValiditySeconds);
			jo.put("maximum_idtoken_expiration_minutes", maximumIdTokenExpirationMinutes);
			jo.put("tokenname", tokenname);
			jo.put("accesstoken_type", accessTokenType.toString());
			
			for(String key : properties.keySet()) {
				if (!jo.has(key))
					jo.put(key,  properties.get(key));
			}
			return jo.toString();
		} catch(JSONException e) {
			return "{\"error\": \"" + JSONObject.quote(e.toString())+"\"}";
		}
	}
	
	public static ClientData fromJSON(JSONObject jo) throws JSONException {
		ClientData cd = new ClientData();
		cd.clientID = jo.optString("client_id", null);
		cd.clientSecret = jo.optString("client_secret", null);
		cd.validGrantTypes = fromJA(jo.optJSONArray("valid_grant_types"));
		cd.allowedScopes = fromJA(jo.optJSONArray("allowed_scopes"));
		cd.allowedUris = fromJA(jo.optJSONArray("allowed_uris"));
		cd.allowedLogoutUris = fromJA(jo.optJSONArray("allowed_logout_uris"));
		cd.accessTokenValiditySeconds = jo.optInt("accesstoken_valid_seconds");
		cd.refreshTokenValiditySeconds = jo.optInt("refreshTokenValiditySeconds");
		cd.maximumIdTokenExpirationMinutes = jo.optInt("maximumIdTokenExpirationMinutes");
		cd.tokenname = jo.optString("tokenname", null);
		cd.accessTokenType = AccessTokenType.valueOf(jo.optString("accesstoken_type", "UUID").toUpperCase());
		
		for(String key : JSONObject.getNames(jo)) {
			cd.properties.put(key, jo.get(key));
		}
		return cd;
	}
	
	private static Collection<String> fromJA(JSONArray ja) throws JSONException {
		if (ja == null || ja.length() == 0)
			return Collections.emptyList();
		
		List<String> result = new ArrayList<>(ja.length());
		for(int i = 0; i < ja.length(); i++) {
			result.add(ja.getString(i));
		}
		return result;
	}
}


Default: dk.itp.security.authentication.oauth.data.properties.OAuth2DataStoreProperties
JSON key is oauth2.datastoreclass

OAuth2 Access Token Datastore class

Specify which OAuth2 Access Token Datastore implementation to use.
This is used to store issued Access Tokens where they can be looked up when they are used as bearer tokens in API calls - or for calling the UserInfo endpoint.

The following implementations are available:

  • dk.itp.security.authentication.oauth.data.AccessTokenMemoryStore
    Issued Access tokens are stored in memory until they expire - they do not survive a restart, so use this only in cases where you have a small setup where re-authentication is ok if you need to restart your servers.
  • dk.itp.security.authentication.oauth.data.AccessTokenSQLStore
    This is the recommended approach, it stores issued Access Tokens in a database for later lookup - expired tokens are automatically deleted from the database.
  • dk.itp.security.authentication.oauth.data.AccessTokenIgniteStore
    Uses Apache Ignite to store access token - this implementation is not recommended.

You also have the option of providing your own implementation. Implementations must implement the Java Interface dk.itp.security.authentication.oauth.data.IAccessTokenDataStore which looks like this:

IAccessTokenDataStore & AccessTokenInfo clases
public interface IAccessTokenDataStore {
	/**
	 * Lookup a previously stored set of information for a particular client ID and access token
	 * 
	 * @param clientId Client ID - might be null if session is created from a ticket/bearer token
	 * @param refreshToken Refresh Token
	 * @return RefreshTokenInfo loaded from datastore.
	 */
	public AccessTokenInfo lookupToken(String clientId, String accessToken);
	
	/**
	 * Lookup a previously stored set of information for a particular client ID and authorization code
	 * An access token can only be looked up once - after that, it is marked as being used and cannot be reused.
	 * 
	 * @param clientId Client ID
	 * @param refreshToken Refresh Token
	 * @return RefreshTokenInfo loaded from datastore.
	 */
	public AccessTokenInfo lookupTokenFromCode(String clientId, String authorizationCode);
	
	/**
	 * Store the info in the data store.
	 * 
	 * @param info Access Token to store
	 */
	public void storeAccesToken(AccessTokenInfo info);
	
	/**
	 * Revoke an existing access token
	 * 
	 * @param info Access Token to revoke
	 */
	public void revokeAccessToken(AccessTokenInfo info);
	
	/**
	 * Called to initialize this datastore, after configuration has been set
	 * @param sessioncontroller
	 */
	public void start(PTSServer sessioncontroller) throws ConfigurationException;
	
	/**
	 * Gives this datastore a chance to stop and release any resources it might have
	 */
	public void stop();
	
	/**
	 * Gives the datastore its configuration
	 * @param config Configuration
	 */
	public void setConfiguration(Properties config);
	
	/**
	 * Gives the datastore its configuration
	 * @param config Configuration
	 * @param json JSON Configuration
	 */
	default public void setConfiguration(Properties config, JSONObject json) throws ConfigurationException, JSONException {
		setConfiguration(config);
	}
}

public class AccessTokenInfo implements Serializable {
	private static final long serialVersionUID = 1L;
	
	/** The access token */
	public String access_token;
	/** An ID token that might have be generated along with the access token */
	public String id_token;
	/** Client ID */
	public String clientid;
	/** Redirect URL */
	public String redirectUri;
	/** Refresh token, if any */
	public String refreshToken;
	/** Expiration time in seconds of the access token*/
	public int accessTokenExpireSeconds;
	/** Authorization code which can be used to obtain this access token */
	public String authorizationCode;
	/** The persisted session */
	public byte[] persistedSession;

	/* RFC7636 / PKCE support */
	/** Optional code_challenge - might be hashed */
	public String code_challenge;
	/** code_challenge method - either plain or S256 */
	public String code_challenge_method;
	
	// --- Information needed for introspection ---
	/** The requested scope */
	public String scope;
	/** Expires at (seconds) */
	public long expiresAt;
	/** Issued at (seconds) */
	public long issuedAt;
	/** Subject */
	public String sub;
	/** Username */
	public String username;
	/** Audience */
	public String audience;
	/** Issuer */
	public String issuer;
	/** Token ID */
	public String jti;
}

Default: dk.itp.security.authentication.oauth.data.AccessTokenMemoryStore
JSON key is oauth2.accesstoken.datastoreclass

OAuth2 Refresh Token Datastore class

Specify which OAuth2 Refresh Token Datastore implementation to use.
This is used to store issued Refresh Tokens where they can be looked up when the token endpoint (or introspection endpoint) is called with the token.

The following implementations are available:

  • dk.itp.security.authentication.oauth.data.RefreshTokenMemoryStore
    Issued Refresh tokens are stored in memory until they expire - they do not survive a restart, so use this only in cases where you have a small setup where re-authentication is ok if you need to restart your servers.
  • dk.itp.security.authentication.oauth.data.RefreshTokenSQLStore
    This is the recommended approach, it stores issued Refresh Tokens in a database for later lookup - expired tokens are automatically deleted from the database.
  • dk.itp.security.authentication.oauth.data.RefreshTokenIgniteStore
    Uses Apache Ignite to store refresh token - this implementation is not recommended.

You also have the option of providing your own implementation. Implementations must implement the Java Interface dk.itp.security.authentication.oauth.data.IRefreshTokenDataStore which looks like this:

IRefreshTokenDataStore & RefreshTokenInfo clases
public interface IRefreshTokenDataStore {
/**
* Lookup a previously stored set of session data for a particular client ID and refreshToken
* 
* @param clientId Client ID
* @param refreshToken Refresh Token
* @return RefreshTokenInfo loaded from datastore.
*/
public RefreshTokenInfo lookupToken(String clientId, String refreshToken);

/**
* Store the info in the data store.
* 
* @param info Refresh Token to store
*/
public void storeRefreshToken(RefreshTokenInfo info);

/**
* Revoke an existing refresh token
* 
* @param info Refresh token to revoke
*/
public void revokeRefreshToken(RefreshTokenInfo info);

/**
* Gives the datastore its configuration
* @param config Configuration
*/
public void setConfiguration(Properties config);

/**
* Gives the datastore its configuration
* @param config Configuration
* @param json JSON Configuration
*/
default public void setConfiguration(Properties config, JSONObject json) throws ConfigurationException, JSONException {
setConfiguration(config);
}

/**
* Called to initialize this datastore, after configuration has been set
* @param sessioncontroller
*/
public void start(PTSServer sessioncontroller) throws ConfigurationException;

/**
* Gives this datastore a chance to stop and release any resources it might have
*/
public void stop();
}

public class RefreshTokenInfo implements Serializable {
	private static final long serialVersionUID = 1L;
	
	/** The refresh token */
	public String refreshToken;
	/** The client ID this token is valid for */
	public String clientID;
	/** Expire time in msecs */
	public long expiresAt;
	/** Created at in msecs */
	public long createdAt;
	/** The persisted session */
	public byte[] persistedSession;
}

Default: dk.itp.security.authentication.oauth.data.RefreshTokenMemoryStore
JSON key is oauth2.refreshtoken.datastoreclass

OAuth2 Datastore name

This is the name of the datastore to use - used by the SQL implementations - must match one of the datastores configured - see Getting Started with Ceptor API Management and Datastores

Default: datastore-primary
JSON key is oauth2.datastorename

Default Token Name

This is the name of the token to use by default if nothing else is provided - each token definition contains separate configuration for which attributes are issued, and you can specify for specific clients that you want to issue non-default tokens to them.

Default: None
JSON key is oauth2.defaulttoken

Authorization code timeout

Specify the timeout in seconds for how long time issued authorization codes will live before they expire. You should keep this value low.

Default: 120
JSON key is oauth2.authorizationcode.timeoutseconds

HTTP Proxy configuration

This is kept in a JSON object called proxy. Note that this configuration is shared with the OpenID Connect / JWT Federation configuration.

Enabled

Check to enable HTTP proxy.

Default: false
JSON key is enabled

Hostname

Maximum acceptable time difference in minutes - allows for clock skew between issuer and service provider.

Default: 2
JSON key is host

Port

TCP Port number of proxy server

Default: 8080
JSON key is port

Userid

Userid to authenticate to proxy server.

Default: None
JSON key is user

Password

Password needed to authenticate to proxy server - can be encrypted, see Encrypting or Obfuscating Passwords

Default: None
JSON key is password

No Proxy For

Hostname matching this pattern will not be proxied

Default: None
JSON key is noproxyfor

Tokens

OAuth2 / OpenID Connect Token issuers and validators

Tokens specify either how tokens are issued to Service Providers / Partners / Clients or how tokens issued by other OAuth2 or OpenID Connect Identity Providers are validated.

Tokens

Here, you can add any number of tokens to the list - remember to set the default token to use in the settings.

Default: None
JSON key is tokens

For each individual token, you can specify the following setttings:

Configuration for each token is stored as a JSON Object within the tokens JSON array within the federations JSON.

Token Configuration

Name

Specify the name of the token - this name can be used in client/partner definitions to point to a specific token to issue for a specific client.

Default: None
JSON key is name

Description

Here, you can specify a description for the token

Default: None
JSON key is description 

Issuer

The issuer name - corresponds to the "iss" attribute in the token.

Default: 
JSON key is issuer

Token issuance related configuration

ID of key

Specify the ID of the key - this ID is used in the JWKS metadata that can be sent to clients.

Default: 
JSON key is keyid

Include keys in JWKS metadata

Check to include this key in the JWKS metadata send to clients,

Default: false
JSON key is include.in.jwks.metadata

Signing algorithm

Specify the Signing algorithm to use when issuing tokens - beware of using "None" - since it allows anyone to spoof the tokens and create their own.
The names starting with RS require private/public keypairs (signed by the private key and validated using the public key) and the names starting with HS use a shared secret key.

Default: RS256
JSON key is algorithm

Expiration time (exp) in minutes

Specify expiration time in minutes of issued tokens.

Default: 10
JSON key is expiration.minutes

NotBefore (nbf) minutes in past

When a token is issued, its nbf (not-before) attribute is set to the current time - but to allow for difference in clocks, it can be a good idea to set this time slightly in the past, this allows you to specify how many minutes in the past.

Default: 2
JSON key is notbefore.minutes.in.past

Claims

List of attributes/claims that are always added to the token. In addition, depending on scopes requested, there might be other claims / attributes in the token, since each scope has a list of different claims/attributes in the various types of tokens.

Default: None
JSON key is claims as a JSON array containing the claims issued.

OpenID Connect compliant token

Check if this token is OpenID Connnect compliant - if it is, specific settings are verified, e.g. issuer name should start with https://

Default: false
JSON key is openidconnect

Access Token Script

This script is called with the access token JSON payload (if the access token type is JWT and not UUID) before it is signed - this gives the script a chance to modify any of its content before issuing it.
See below for more details in input.

Default: None
JSON key is script.accesstoken

ID Token Script

This script is called with the ID token JSON payload - this gives the script a chance to modify any of its content before issuing it.
See below for more details in input.

Default: None
JSON key is script.idtoken

Userinfo Script

This script is called with the userinfo JSON object before it is sent to the client, the script can modify any attribute (or rewrite it completely).

Default: None
JSON key is script.userinfo

Input to Scripts

For the 3 script types above, see the code completion in the editor for detailed information about which variables are available to the script.

On high level, this is:

  • context
    Context containing other variables available to the script
  • context.configuredToken
    Contains the token configuration.
    See each field in the code completion help.
  • context.jo
    Parsed JSON object
  • context.json
    JSON (as a string) object containing the input  token
  • context.session
    Contains Ceptor's internal session for the user the token is created for - this contains all the information available about the authenticated user.
    Details about each field available in the code completion.

The script can return a modified token / JSON userinfo as a string.

Example:

Script to modify Acces Token / ID Token or Userinfo
function modifyToken() {
    var token = JSON.parse(context.json);
    
    token.custom = 'Hello There: ' + token.sub;
    token.sid= context.session.sessionID;
    
    return JSON.stringify(token);
}

modifyToken();

Token Exchange Script

This script is called whenever a token exchange is requested.

Default: None
JSON key is script.tokenexchange

Javascript, Groovy or Python code is executed to process the token exchange input, add validation or provide extra information for use when the access token is generated.


If incoming subject_token is not recognizable as earlier issued by Ceptor, you can parse it here and populate the session with relevant values before processing continues.

Variables
Your script is called when a variable called context with the following attributes within it:

  • configuredToken
    JWTHelper.JWTToken object, the configured token
  • session
    Ceptor's internal session object
  • sessionCtrl
    Reference to Ceptor's Session Controller
  • helper
    Reference to Ceptor's JWTHelper instance
  • actorSession
    Ceptor's session object for the actor token
  • resource
    Resource name this token is meant for
  • audience
    Audience from input
  • input
    Properties object containing all parameters from request to token URL
  • validAudiences
    List of valid audiences, or empty

If the subject_token was found and recognized as an earlier issued token, the context.session is populated with the contents of the session.

If not, the token is available in context.session.ticket and you can parse it and populate the session with the results. If context.session.userid is empty when the script returns, it is assumed that the parsing failed and an error will be sent back to the client informing that the token was not valid.

If an actor token was provided and recognized, the session of the actor is populated with information - if not recognized you can find the token (along with any other input parameters from the request in the input java.util.Properties object.

In this script, you can modify the session contents before the final access token is generated. The access token will be generated by default as a copy of the incoming subject_token but with an attribute "act" added with the subject of the actor token in the "sub" field, and the client_id in the "client_id" field of the "act" object.

In the access token generation script, you get a chance to modify the properties after the token is generated before it gets signed - the token exchange script allows you to modify the session context before the resulting access token is generated.


Example:

function exchange() {
    if (context.actorSession) {
        context.actorSession.userid = 'Killroy was here';
    }
    if (!context.input.get('actor_token')) {
        context.session.userid = 'On behalf of ' + context.session.userid
    }
}

exchange();


Token validation related configuration

UserID attribute

Name of the attribute to copy userid from - if not specified, the standard subject (sub) attribute is used instead.

Default: None
JSON key is userid.attribute.name

Username attribute

Name of the attribute to copy username from.

Default: name
JSON key is username.attribute.name

Role/Group attribute

Name of the attribute to copy user groups/roles from.

Default: groups
JSON key is role.attribute.name

Role pattern

When copying user groups/roles from the token into the session, only groups matching the specified pattern will be used.

Default: *
JSON key is role.pattern

Relax key checks

If checked, insecure tokens, such as those using weak keys, or crypto algorithm None will be allowed.

Default: false
JSON key is relax.key.checks

Expires at exact time

If checked, the session will not expire using normal idle timeout, but it will expire at the exact time that the issued token expires. Check this to disable idle-timeout for sessions.

Default: false
JSON key is expires.at.exact.time

Require subject (sub)

Check this if subject (sub) attribute must be present in the token.

Default: false
JSON key is require.subject

Valid audiences

If set, specify one or more valid audience strings that this token must match - if not set, audience (aud attribute) is not checked.

Default: No limits
JSON key is validaudiences as a JSON Array of strings

Clockskew (seconds)

Specify the number of seconds to allow for clock skew / time difference when validating nfb/exp attributes - setting this to a high value will allow tokens x seconds after they have expired or x seconds before they become valid. This can be useful to allow for a certain difference in clocks between machines.

Default: 0
JSON key is clockskew.seconds

Attributes to store

Patter matching list of attributtes to store in session - if attribute in token matches this pattern, it will be stored in the session.

If Custom Attribute Mapping is specified, "Attributes to store" is not used, and t he custom attribute mapping is used instead.


Default: *
JSON key is attributes.to.store.in.session

Custom Attribute Mapping

Allows customized mapping of attribute values.
Each attribute is stored as a JSON object with key and value attributes inside it.

Key is one of:

  • groups
    User groups
  • userid
    User ID
  • username
    User Name
  • customerid
    Customer ID
  • isinternal
    True if user is internal, false if not
  • agreementid
    Agreement ID
  • authmethod
    Authentication method ID
  • authlvl
    Authentication Level
  • _state_XXXX where XXXX is the name of a state variable.
  • Any other value XXXX
    The state variable XXXX is set to the value

The value is a string, where macros are replaced with the appropriate contents - a macro can be %{claim:XXXX} where XXXX is the name of a claim key in the JSON JWT token, or token returned from an identity provider such as facebook.

Macros are in the form %{type:name} where type can be claim, base64, urlencode, htmlencode, base64decode, urldecode - the default is claim if not specified. If set to e.g. base64, the value will be treated as base64 and decoded.
Like with Ceptor Gateway - Scripts and Macros you can use scripts (javascript, python or groovy) and %{rewrite} macros to do more advanced transformations of values. When scripts are used, the variable context will point to an object which contains two variables; session pointing to the users Ceptor Session, and jo pointing to a JSONObject containing the claims to map.

Examples:

%{claim:firstName} %{claim.lastName}
%{script}salary(); function salary() {var json = JSON.from(context.jo.toJSONString()); return (json.monthlySalary * 12) + json.yearlyBonus;}

Default: None
JSON key is fieldmappers - a JSON Array of JSON Objects

Issuer keys

Issuer keys are stored in the JSON Object keystore, within the token JSON object - except for "secretkey" which is stored directly within the token itself.

Key or certificate configuration

Secret key

Secret key, if signing algorithm is one of the HS* algoritms

Default: None
JSON key is secretkey

JCE Provider

Name of JCE provider - if left blank, platform default is used. Can be set to e.g. Luna to support hardware crypto providers assuming Luna JCE provider is installed.

Default: None
JSON key is provider

Keystore type

Keystore type, usually PKCS12 or JKS

Default: PKCS12
JSON key is type

Filename

Name of file to load keystore from - note that some keystores, e.g. Luna does not have a file

Default: None
JSON key is file

Password

Password for the keystore - can be encrypted, see Encrypting or Obfuscating Passwords

Default: None
JSON key is password

Password per alias

This is only needed if you have specific aliases in your keystore which have passwords that differ from the main keystore password - it allows you to specify a password for each alias specifically.

Default: None
JSON key is password.per.alias - this is a JSON Array with the format alias=password

Alias of private key

If provided, only the private key with the specified alias will be loaded.

Default: None
JSON key is alias.privkey

Alias of certificate

If provided, only the certificate with the specified alias will be loaded

Default: None
JSON key is alias.cert

Private key

Allows you to provide the private key as an RSA key in PKCS#8 format by pasting it directly.

Default: None
JSON key is privatekey

Certificate

Allows you to provide the certificate as Base64 encoded DER by pasting it directly instead of loading it from a keystore.

Default: None
JSON key is certificate


See also Keystore configuration for additional details of configuring keystores when issuing tokens.

Signer Certificates

secret key, url and refresh interval are stored directly in the token JSON, the rest of the certificates in the JSON Object signer.certificates inside the token JSON.

Secret key

Secret key, if token is signed with a shared secret key instead of a private key.

Default: None
JSON key is 

URL to load signer certificates from

Specify an URL, e.g. JWKS URL for where to load signed certificates from. Both standard JWKS, Googles and Microsoft proprietary formats are supported.

Default: None
JSON key is signer.certificates.url

Refresh interval (minutes)

Number of minutes between reloading certificates from the URL above.

Default: 60
JSON key is signer.certificates.refresh.interval.minutes


The following configuration for the certificates is stored in the JSON Object signer.certificates inside the token JSON.

JCE Provider

Name of JCE provider - if left blank, platform default is used. Can be set to e.g. Luna to support hardware crypto providers assuming Luna JCE provider is installed.

Default: None
JSON key is provider

Certificates

Provide a list of filenames or certificates directly within the configuration.

This allows you to specify additional trusted signer certificates for use when validating signed SAML Response - note that if you already are loading federation metadata from online, or have it pasted, these certificates configured here are in addition to the ones from the metadata.

Default: None
JSON key is certificates - which is a JSON Array of strings, each containing a filename of a certificate in .cer (binary or base64 encoded), .der or .p7b format or the certificate itself, if starting with -----BEGIN CERTIFICATE-----


See also Keystore configuration for additional details of configuring keystores

SSL

The SSL settings are used when loading certificates from remote, the settings are stored in the JSON object ssl inside the token JSON object.

SSL/TLS related configuration when loading metadata

Verify server SSL certificate

Uncheck to disable SSL server certificate validation - if checked, SSL server certificates must either match installed root certificates in the JVM, or one of the additionally specified certificates.

Default: true
JSON key is verify.server.cert

Verify server SSL hostname

Uncheck to disable SSL hostname verification - if checked, the SSL hostname is matched up against the SSL server certificate.

Default: true
JSON key is verify.hostname

JCE Provider

Name of JCE provider - if left blank, platform default is used. Can be set to e.g. Luna to support hardware crypto providers assuming Luna JCE provider is installed.

Default: None
JSON key is provider - inside accepted.certificates JSON object within the ssl object.

Certificates

Provide a list of filenames or certificates directly within the configuration.

This allows you to specify additional SSL server issuer trust certificates certificates for use when validating signed SSL server certificate when retrieving metadata from remote

Default: None
JSON key is certificates- inside accepted.certificates JSON object within the ssl object.

Scopes

Scopes are used to define which fields end up in which token - here you can specify that if a client asks for e.g. a scope called "salary" when requesting a token, then you control which attributes are added to the ID token, Access Token and Userinfo respectively.
This gives you fine grained control of which data you expose under which conditions.

Scopes are stored in a JSON Array called openid.scopes - each scope definition is a single JSON Object within this array.

Example Scope Definition
"openid.scopes": [
  {
    "name": "email",
    "description": "Email address in userinfo",
    "idtoken": ["email=email1"],
    "accesstoken": [],
    "userinfo": ["email=email1"]
  },
  {
    "name": "profile",
    "description": "User name",
    "idtoken": ["name=username"],
    "accesstoken": ["name=username"],
    "userinfo": ["name=username"]
  }
]

OAuth2 Scope settings

Name

Scope name, must match the name used by the client/partner - e.g. "openid", "profile" or "email".

See https://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims for a list of standard scope names part of the OpenID Connect standard.

Default: None
JSON key is name

Description

Description of this scope - only for informational use.

Default: None
JSON key is description

ID Token

List of attributes/claim to append to ID Token for this scope.
See SAML / JWT Attributes / Claims for details about the keys/values.

Default: None
JSON key is idtoken as a JSON Array of strings

Access Token

List of attributes/claim to append to Access Token for this scope.
See SAML / JWT Attributes / Claims for details about the keys/values.

Default: None
JSON key is accesstoken as a JSON Array of strings

Userinfo

List of attributes/claim to append to userinfo endpoint for this scope.
See SAML / JWT Attributes / Claims for details about the keys/values.

Default: None
JSON key is userinfo as a JSON Array of strings

Fields / Nested Attributes

These configuration values are stored as a JSON Array called openid.fields present within the federation object.

Example configuration
"openid.fields": [{
  "name": "address",
  "description": "User address",
  "attributes": [
    "street_address=address1",
    "locality=city",
    "region=state",
    "postal_code=postal",
    "country=country"
  ]
}]

Some attributes are themselves objects, such as the OpenID Connect "address" object - by configuring them here, you control which attributes are included, and from where the information within originates.

So, in other words - this is where you specify objects that contain nested attributes.

A single Field definition looks like this:

OAuth2 Nested Attribute

Name

Specify the attribute name, e.g. Address

Default: None
JSON key is name

Description

Optional description for informatilnal purposes.

Default: None
JSON key is description

Attributes

Attributes to add to this field.
See SAML / JWT Attributes / Claims for details about formats and values.

Default: None
JSON key is attributes - as a JSON Array containing string values

Identity Providers

When Ceptor authenticates users using foreign OpenID Connect Providers, you can define the authentication providers here.
These Identity Providers are then used within the Ceptor Gateway - see Location - Authentication for information about how to configure the gateway to use these identity providers under specific conditions. 

Ceptor has custom support for some identity providers which almost are following the OpenID Connect standard, such as LinkedIn and Facebook.

Configuration for identity providers are stored in a JSON Array called openid.identityproviders - each identity provider is a separate JSON Object.

Example Configuration
"openid.identityproviders": [
  {
    "name": "facebook",
    "description": "Authenticate using facebook",
    "clientid": "624082557774373",
    "secret": "{encoded}F89141217749F5FA6306CAF6F9656965F3A5A8E3069BE032136125F3A4B27183",
    "tokenurl": "https://graph.facebook.com/v2.9/oauth/access_token",
    "facebook": true,
    "linkedin": false,
    "fieldmappers": []
  },
  {
    "name": "google",
    "description": "Google as an identity provider",
    "clientid": "371213948273-79eceu24cm64ft69pln0hk2lfapok1bq.apps.googleusercontent.com",
    "secret": "{encoded}806F9FE0C7CBC28D5777D6DE91772DA4961482568956695A",
    "tokenurl": "https://accounts.google.com/o/oauth2/token",
    "facebook": false,
    "linkedin": false,
    "fieldmappers": []
  },
  {
    "name": "microsoft",
    "description": "Authenticate using microsoft as identity provider",
    "clientid": "317190f9-efec-4307-beb9-7f8380a8ae16",
    "secret": "{encoded}9EED6C32369008FE6F3DC027CC0C2195137300594A2620",
    "tokenurl": "https://login.microsoftonline.com/common/oauth2/v2.0/token",
    "facebook": false,
    "linkedin": false,
    "fieldmappers": []
  }
]


OpenID Connect Identity Provider

Name

Identity provider name

Default: None
JSON key is name

Description

Optional description - used for informational purposes only.

Default: None
JSON key is description

Client ID

Client ID - used together with the OAuth2 Authorization code flow to obtain a token from the identity provider.

Default: None
JSON key is clientid

Client Secret

Client secret.

Default: None
JSON key is secret

Token URL

Identity Provider Token URL - called to exchange the authorization code for a token.

Default: None
JSON key is tokenurl

Facebook

Check if Identity Provider is Facebook - Facebook has its own special interface.

Default: false
JSON key is facebook

Facebook Fields

Comma separated list of fields to read from facebook

Default: id,cover,name,first_name,last_name,age_range,link,gender,locale,picture,timezone,updated_time,verified
JSON key is facebookfields

Facebook URL

URL to facebook provider Graph API - required since Facebook is not really OpenID Connect compliant but has its own twist

Default: https://graph.facebook.com/me
JSON key is name

Linkedin

Check to enable special handling for using Linkedin as an identity provider, since it is not fully OpenID Connect compliant

Default: false
JSON key is linkedin

Linkedin Fields

Comma separated list of fields to read from linkedin

Default: id,firstName,lastName,headline,num-connections,picture-url,formatted-name,summary,location,public-profile-url
JSON key is linkedinfields

Linkedin URL

URL to linkedin API

Default: https://api.linkedin.com/v1/people/~
JSON key is linkedinurl

Custom Attribute Mapping

Allows customized mapping of attribute values.
Each attribute is stored as a JSON object with key and value attributes inside it.

Key is one of:

  • groups
    User groups
  • userid
    User ID
  • username
    User Name
  • customerid
    Customer ID
  • isinternal
    True if user is internal, false if not
  • agreementid
    Agreement ID
  • authmethod
    Authentication method ID
  • authlvl
    Authentication Level
  • _state_XXXX where XXXX is the name of a state variable.
  • Any other value XXXX
    The state variable XXXX is set to the value

The value is a string, where macros are replaced with the appropriate contents - a macro can be %{claim:XXXX} where XXXX is the name of a claim key in the JSON JWT token, or token returned from an identity provider such as facebook.

Macros are in the form %{type:name} where type can be claim, base64, urlencode, htmlencode, base64decode, urldecode - the default is claim if not specified. If set to e.g. base64, the value will be treated as base64 and decoded.
Like with Ceptor Gateway - Scripts and Macros you can use scripts (javascript, python or groovy) and %{rewrite} macros to do more advanced transformations of values. When scripts are used, the variable context will point to an object which contains two variables; session pointing to the users Ceptor Session, and jo pointing to a JSONObject containing the claims to map.

Examples:

%{claim:firstName} %{claim.lastName}
%{script}salary(); function salary() {var json = JSON.from(context.jo.toJSONString()); return (json.monthlySalary * 12) + json.yearlyBonus;}

Default: None
JSON key is fieldmappers

Clients / Partners

Normally, you would configure partners as part of API Management here: Managing Partners, Applications and Developers - but in smaller installations where you use "dk.itp.security.authentication.oauth.data.properties.OAuth2DataStoreProperties" as OAuth2 datastore implementation, you can configure the clients or partners here.

OAuth2 Clients/Partners are stored in a JSON Array called oauth2.clients - each client definition is a JSON object.

Example configuration
"oauth2.clients": [{
  "name": "Sample",
  "client_id": "https://www.example.com/",
  "description": "Example client",
  "client_secret": "secret",
  "accesstoken_type": "UUID",
  "allowed_uris": ["https://www.example.com/oauth2"],
  "allowed_logout_uris": [],
  "valid_grant_types": [
    "authorization_code",
    "implicit",
    "hybrid",
    "refresh_token"
  ],
  "allowed_scopes": [
    "openid",
    "profile",
    "offline_access"
  ],
  "refreshtoken_validity_seconds": 86400,
  "maximum_idtoken_expiration_minutes": 120
}]


OAuth2 / OpenID Connect Client

Name

Name of OAuth 2 / OpenID Connect client or partner.

Default: None
JSON key is name

Description

Optional description

Default: None
JSON key is description

Client ID

Client ID - must be unique across all configured clients.

Default: None
JSON key is client_id

Client Secret

Client secret - may be encrypted/obfuscated.

Default: None
JSON key is client_secret

Accesstoken type

Type of access token to issuer - either UUID or JWT/RFC9068/RFC9068UP - if UUID, the token itself does not contain any attributes, and the OAuth2 introspection (or userinfo) URL must be called using the access token to get any of the attributes related to it. If the type is JWT, the access token is similar to the ID token a signed JWT token containing the attributes itself.

For JWT tokens, RFC9068 / RFC9068UP are variants of JWT tokens where they set the "typ" header value to "at-jwt" or "at-JWT" respectively.

Why RFC9068 / RFC9068UP

RFC 9068 (see https://www.rfc-editor.org/rfc/rfc9068.html) mentions in the textual description and registration section that the type must be set to "at-jwt" but the examples within it set it to "at-JWT" with JWT in uppercase. Since some client might care about the case, you can select the type RFC9068 for the lowercase version, but also RFC9068UP for the uppercase version used in the examples.


Default: UUID
JSON key is accesstoken_type

Token name

Name of token to use if not the default -allows you to select specific tokens (and thus specific keys, content etc.) for specific clients.

Default: None
JSON key is tokenname

Redirect URIs

List of valid redirect URIs for this client/partner

Default: None
JSON key is allowed_uris as a JSON array of strings

Logout URIs

List of valid logout URIs for this client/partner

Default: None
JSON key is allowed_logout_uris as a JSON array of strings

Grant types

List of valid grant types to allow for this client. If this list is empty, all types are allowed, otherwise only the specified ones are allowed for this client to use.

Default: None
JSON key is valid_grant_types

Scopes

If not empty, this list restricts which scope names the client is allowed to use.

Default: None
JSON key is allowed_scopes as a JSON array of strings

Access token validity (secs)

If specified, can override the default validity period of issued access tokens.

Default: None
JSON key is accesstoken_valid_seconds

Refresh token validity (secs)

If specified, can override the default validity period of issued refresh tokens.

Default: None
JSON key is refreshtoken_validity_seconds

ID token validity (mins)

If specified, can override the default validity period of issued ID tokens.

Default: None
JSON key is maximum_idtoken_expiration_minutes

© Ceptor ApS. All Rights Reserved.