...
Ceptor supports OAuth2 / OpenID Connect (OIDC) and exchanging JWT tokens with 3rd parties.
Tip |
---|
To use these federations, you should add the authentication plugin |
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.
...
- 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.
...
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
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: 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
...
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.
...
Tip | |||||||
---|---|---|---|---|---|---|---|
| |||||||
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:
The script can return a modified token / JSON userinfo as a string. Example:
|
Token
...
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.
Note |
---|
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:
...
Exchange Script
This script is called whenever a token exchange is requested.
Default: None
JSON key is script.tokenexchange
Tip | ||
---|---|---|
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.
Variables
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.
|
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.
Note |
---|
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
...
Code Block | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
| "oauth2.clients
| ||||||||||
"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": [{], "namevalid_grant_types": "Sample" [ "authorization_code", "implicit", "hybrid", "refresh_token" ], "clientallowed_idscopes": "https://www.example.com/",[ "openid", "profile", "descriptionoffline_access": "Example client"], "clientrefreshtoken_validity_secretseconds": "secret"86400, "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 - 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.
...
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.
Info | ||
---|---|---|
| ||
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
...