118 lines
7.5 KiB
Text
118 lines
7.5 KiB
Text
[[_action_token_spi]]
|
|
== Action Token SPI
|
|
|
|
An action token is a special instance of Json Web Token (JWT) that permits its bearer to perform some actions, e. g. to
|
|
reset a password or validate e-mail address. They are usually sent to users in form of a link that points to an endpoint
|
|
processing action tokens for a particular realm.
|
|
|
|
{project_name} offers four basic token types allowing the bearer to:
|
|
|
|
* Reset credentials
|
|
* Confirm e-mail address
|
|
* Execute required action(s)
|
|
* Confirm linking of an account with account in external identity provider
|
|
|
|
In addition to that, it is possible to implement any functionality that initiates or modifies authentication session
|
|
using action token SPI, details of which are described in the text below.
|
|
|
|
[[_action_token_anatomy]]
|
|
=== Anatomy of Action Token
|
|
|
|
Action token is a standard Json Web Token signed with active realm key where the payload contains several fields:
|
|
|
|
* `typ` - Identification of the action (e.g. `verify-email`)
|
|
* `iat` and `exp` - Times of token validity
|
|
* `sub` - ID of the user
|
|
* `azp` - Client name
|
|
* `iss` - Issuer - URL of the issuing realm
|
|
* `aud` - Audience - list containing URL of the issuing realm
|
|
* `asid` - ID of the authentication session (_optional_)
|
|
* `nonce` - Random nonce to guarantee uniqueness of use if the operation can only be executed once (_optional_)
|
|
|
|
In addition, an action token can contain any number of custom fields serializable into JSON.
|
|
|
|
=== Action Token Processing
|
|
|
|
When an action token is passed to a {project_name} endpoint
|
|
`_KEYCLOAK_ROOT_/auth/realms/master/login-actions/action-token` via `key` parameter, it is validated and a proper action
|
|
token handler is executed. The processing always takes place in a context of an authentication session, either a fresh
|
|
one or the action token service joins an existing authentication session (details are described below). The action token
|
|
handler can perform actions prescribed by the token (often it alters the authentication session) and results into a HTTP
|
|
response (e.g. it can continue in authentication or display an information/error page). These steps are detailed below.
|
|
|
|
1. *Basic action token validation.* Signature and time validity is checked, and action token handler is determined based
|
|
on `typ` field.
|
|
|
|
2. [[determining-auth-sess]]*Determining authentication session.* If the action token URL was opened in browser with
|
|
existing authentication session, and the token contains authentication session ID matching the authentication session
|
|
from the browser, action token validation and handling will attach this ongoing authentication session. Otherwise,
|
|
action token handler creates a fresh authentication session that replaces any other authentication session present at
|
|
that time in the browser.
|
|
|
|
3. *Token validations specific for token type.* Action token endpoint logic validates that the user (`sub` field) and
|
|
client (`azp`) from the token exist, are valid and not disabled. Then it validates all custom validations defined in the
|
|
action token handler. Furthermore, token handler can request this token be single-use. Already used tokens would then be
|
|
rejected by action token endpoint logic.
|
|
|
|
4. *Performing the action.* After all these validations, action token handler code is called that performs the actual
|
|
action according to parameters in the token.
|
|
|
|
5. *Invalidation of single-Use tokens.* If the token is set to single-use, once the authentication flow finishes, the
|
|
action token is invalidated.
|
|
|
|
=== Implement Your Own Action Token and its Handler
|
|
|
|
==== How to Create an Action Token
|
|
|
|
As action token is just a signed JWT with few mandatory fields (see <<_action_token_anatomy,Anatomy of action token>>
|
|
above), it can be serialized and signed as such using Keycloak's `JWSBuilder` class. This way has been already
|
|
implemented in `serialize(session, realm, uriInfo)` method of `org.keycloak.authentication.actiontoken.DefaultActionToken`
|
|
and can be leveraged by implementors by using that class for tokens instead of plain `JsonWebToken`.
|
|
|
|
==== Packaging Classes and Deployment
|
|
|
|
To plug your own action token and its handler, you need to implement few interfaces on server side:
|
|
|
|
* `org.keycloak.authentication.actiontoken.ActionTokenHandler` - actual handler of action token for a particular
|
|
action (i.e. for a given value of `typ` token field).
|
|
+
|
|
The central method in that interface is `handleToken(token, context)` which defines actual operation executed upon
|
|
receiving the action token. Usually it is some alteration of authentication session notes but generally it can be
|
|
arbitrary. This method is only called if all verifiers (including those defined in `getVerifiers(context)`) have
|
|
succeeded, and it is guaranteed that the `token` would be of the class returned by `getTokenClass()` method.
|
|
+
|
|
To be able to determine whether the action token was issued for the current authentication session as described in
|
|
<<determining-auth-sess,Item 2 above>>, method for extracting authentication session ID has to be declared in
|
|
`getAuthenticationSessionIdFromToken(token, context)` method. The implementation in `DefaultActionToken` returns the
|
|
value of `asid` field from the token if it is defined. Note that you can override that method to return current
|
|
authentication session ID regardless of the token - that way you can create tokens that would step into the ongoing
|
|
authentication flow before any authentication flow would be started.
|
|
+
|
|
If the authentication session from the token does not match the current one, the action token handler would be asked to
|
|
start a fresh one by calling `startFreshAuthenticationSession(token, context)`. It can throw a `VerificationException`
|
|
(or better its more descriptive variant `ExplainedTokenVerificationException`) to signal that would be forbidden.
|
|
+
|
|
The token handler also determines via method `canUseTokenRepeatedly(token, context)` whether the token would be
|
|
invalidated after it is used and authentication completes. Note that if you would have a flow utilizing multiple action
|
|
token, only the last token would be invalidated. In that case, you should use
|
|
`org.keycloak.models.ActionTokenStoreProvider` in action token handler to invalidate the used tokens manually.
|
|
+
|
|
Default implementation of most of the `ActionTokenHandler` methods is the
|
|
`org.keycloak.authentication.actiontoken.AbstractActionTokenHander` abstract class in `keycloak-services` module. The
|
|
only method that needs to be implemented is `handleToken(token, context)` that performs the actual action.
|
|
|
|
* `org.keycloak.authentication.actiontoken.ActionTokenHandlerFactory` - factory that instantiates action token
|
|
handler. Implementations have to override `getId()` to return value that must match precisely the value of `typ`
|
|
field in the action token.
|
|
+
|
|
Note that you have to register the custom `ActionTokenHandlerFactory` implementation as explained in the
|
|
<<_providers,Service Provider Interfaces>> section of this guide.
|
|
|
|
* If the action token you are implementing contains any custom fields that should be serializabled to JSON fields, you
|
|
should consider implementing a descendant of `org.keycloak.representations.JsonWebToken` class that would implement
|
|
`org.keycloak.models.ActionTokenKeyModel` interface. In that case, you can take advantage of the existing
|
|
`org.keycloak.authentication.actiontoken.DefaultActionToken` class as it already satisfies both these conditions,
|
|
and either use it directly or implement its child, the fields of which can be annotated with appropriate Jackson
|
|
annotations, e.g. `com.fasterxml.jackson.annotation.JsonProperty` to serialize them to JSON.
|
|
|
|
|