keycloak-scim/server_admin/topics/authentication/flows.adoc

261 lines
17 KiB
Text
Raw Normal View History

2016-05-27 15:23:34 +00:00
[[_authentication-flows]]
2016-05-18 01:57:04 +00:00
=== Authentication Flows
An _authentication flow_ is a container for all authentications, screens, and actions that must happen during login, registration, and other
2017-08-28 12:50:14 +00:00
{project_name} workflows.
2016-05-18 01:57:04 +00:00
If you go to the admin console `Authentication` left menu item and go to the `Flows` tab, you can view all the defined flows
in the system and what actions and checks each flow requires.
==== Built-in flows
{project_name} comes with a certain number of built-in flows. These flows cannot be modified, but the requirements can be modified to
suit your needs.
This section does a walk-through of the built-in browser login flow. In the
2019-01-23 18:46:58 +00:00
left drop-down list select `browser` to come to the screen shown below:
2016-05-18 01:57:04 +00:00
.Browser Flow
2017-08-28 12:50:14 +00:00
image:{project_images}/browser-flow.png[]
2016-05-18 01:57:04 +00:00
If you hover over the tooltip (the tiny question mark) to the right of the flow selection list, this will describe what the
flow is and does.
The `Auth Type` column is the name of authentication or action that will be executed. If an authentication is indented
this means it is in a sub-flow and may or may not be executed depending on the behavior of its parent. The `Requirement`
column is a set of radio buttons which define whether or not the action will execute. Let's describe what each radio
button means in this context:
2016-05-18 01:57:04 +00:00
Required::
This authentication execution must execute successfully. If the user doesn't have that type of authentication mechanism
2016-05-18 01:57:04 +00:00
configured and there is a required action associated with that authentication type, then a required action will be attached
to that account. For example, if you switch the `Browser - Conditional OTP` sub-flow to `Required`, users that don't have an OTP generator configured
2016-05-18 01:57:04 +00:00
will be asked to do so.
Alternative::
This means that at least one alternative authentication type must execute successfully at that level of the flow.
Disabled::
If disabled, the authentication type is not executed.
Conditional::
This requirement type can only be set on sub-flows, indicating that it will only be executed if its condition evaluates to true.
2016-05-18 01:57:04 +00:00
This is better described in an example. Let's walk through the `browser` authentication flow.
. The first authentication type is `Cookie`. When a user successfully logs in for the first time, a session cookie is set.
If this cookie has already been set, then this authentication type is successful. In this case,
since the cookie provider returned success and each execution at this level of the flow is _alternative_, no other execution is executed and this results in a successful login.
. The second execution of the flow looks at the `Kerberos` execution. This authenticator is disabled by default and will be skipped.
. The third execution is the `Identity Provider Redirector`. It can be configured through the `Actions` > `Config` link to automatically redirect to another IdP for <<_identity_broker, identity brokering>>.
. The next execution is a sub-flow called `Forms`. Since this sub-flow is marked as _alternative_ it will not be executed if the `Cookie` authentication type passed.
This sub-flow contains additional authentication type that needs to be executed.
The executions for this sub-flow are loaded and the same processing logic occurs.
. The first execution in the Forms sub-flow is the Username Password Form. This authentication type renders the username and password page.
It is marked as _required_ so the user must enter in a valid username and password.
. The second execution in the Forms sub-flow is a new sub-flow: the `Browser - Conditional OTP` sub-flow. Since this sub-flow is _conditional_, whether it is executed depends on the result of the
evaluation of the `Condition - User Configured` execution. If it is, the executions for this sub-flow are loaded and the same processing logic occurs
. The next execution is the `Condition - User Configured`. This checks if the other executions in the flow are configured for the user.
Meaning that the `Browser - Conditional OTP` sub-flow will only be executed if the user has an OTP credential configured.
. The final execution is the `OTP Form`. This is marked as _required_, but because of the setup in the _conditional_ subflow, it will only be run if the user
has an OTP credential set up. If he doesn't, the user will not see an OTP form.
==== Creating flows
This section explains in greater depth how flows work, and how to create your own flows. Note that there are important functionality and security
considerations when designing your own flow. A badly created flow could either let no one log in, let users in with less verification than you would
like, or simply result in an error.
To create a flow, you can either:
. Copy and then modify an existing flow. To do this select an existing flow (for example the `Browser` flow), and press the `Copy` button.
This will then ask you to set a name for the new flow, before creating it.
. Create a new flow from scratch. To do this press the `New` button. Since this is the more general case, we will use this for our example.
When creating a new flow, you will have to create a top level flow
.Create a top level flow
image:{project_images}/Create-top-level-flow.png[]
With the following options:
Alias::
The name of the flow.
Description::
The description you can set to the flow.
Top Level Flow Type::
The type of flow. the type `client` is used only for the authentication of clients (applications). For all other cases choose `generic`.
Once the flow is created, in addition to the `New` and `Copy` buttons, you now have, `Delete`, `Add execution` and `Add flow`.
.An empty new flow
image:{project_images}/New-flow.png[]
What a flow finally does is determined by the structure of the flow and sub-flows, the executions in those flows, and the requirements set on the
sub-flows and the executions.
Executions can be added with the `Add execution` button. Executions can have a wide variety of actions, from sending a reset email to validating an OTP. If you hover over the
tooltip (the tiny question mark) next to `Provider`, this will describe what the execution does.
.Adding an authentication execution
image:{project_images}/Create-authentication-execution.png[]
These can be divided into _automatic executions_ and _interactive executions_. _Automatic executions_ are similar to the `Cookie` execution, and will automatically
perform their action when they are encountered in the flow. _Interactive executions_ will halt the flow, usually to get some user input. Executions that execute
successfully will get the _success_ status. This is important, because this is part of whether a flow is successful or not. For example, an empty `Browser` flow
would not allow anyone to log in. For that it would need at least one execution that successfully evaluates, for example a `Username Password Form` that is corrected
filled and submitted.
Sub-flows can be added in top level flow with the `Add flow` button, which opens a `Create Execution Flow` page that is very similar to the `Create Top Level Form`
page. The only difference is that the `Flow Type` can be either `generic` (like before), or `form`. The `form` type is used to construct a sub-flow that generates
a single form for the user, like what is done for the built-in `Registration` flow. Sub-flows are a special type of execution that evaluate as successful
depending on how the executions they contain evaluate (and this includes the evaluation of their contained sub-flows). And the logic of this evaluation
depends on the Requirement of each execution and sub-flow.
Fully understanding this requires a more complete explanation of how requirements work when evaluating a flow, and this also applies to sub-flows:
Required::
For a flow to be evaluated as successful, all required elements in the flow must evaluate as successful. This means that all _Required_ elements in the flow
must be sequentially executed, from top to bottom, unless one the the elements causes the flow to fail. However, this is only true for the current flow.
Any _Required_ element within a sub-flow is only processed if that sub-flow is entered.
Alternative::
When a flow contains only _Alternative_ elements, only a single element must evaluate as successful for the flow to evaluate as successful.
Because the _Required_ flow elements within a flow are sufficient to mark a flow as successful, any _Alternative_ flow element within a flow
that contains _Required_ flow elements will never be executed. In this case, they are functionally _Disabled_.
Disabled::
Any _Disabled_ element is not evaluated and does not count to mark a flow as successful.
Conditional::
This requirement type can only be set on sub-flows. A _Conditional_ sub-flow can contain a "Condition" execution. These "Condition" executions must evaluate as
logical statements. If all "Condition" executions evaluate as _true_ then the _Conditional_ sub-flow acts as _Required_. If not, the _Conditional_ sub-flow
acts as _Disabled_. If no "Condition" execution is set, the _Conditional_ sub-flow acts as _Disabled_. If a flow contains "Condition" executions and is not set to
_Conditional_, the "Condition" executions are not evaluated, and can be considered functionally _Disabled_.
Note that after adding an execution, you should check that the Requirement is set to the correct value. Even if there is only a single possible Requirement, it
can happen that it is not set.
When constructing a flow, all elements added to the flow will have an `Actions` menu on the right-hand side. All elements added to the flow have a `Delete`
option in this menu to remove it from the flow. Executions can contain a `Config` menu option to configure the execution, as is the case for the
`Identity Provider Redirector`. Sub-flows can also have executions and sub-flows added to them, with their `Add execution` and `Add flow` menu options.
Finally, since the order of execution is important, you can move executions and sub-flows up and down within their respective flows with the up and down buttons
that are set to left of their name.
==== Creating a password-less browser login flow
To illustrate the creation of flows, this section describes the creation of a more advanced browser login flow. The purpose of this flow is to allow a
user to choose between logging in in a password-less manner using <<_webauthn, WebAuthn>>, and a two-factor authentication with password and OTP.
The flow to create is similar to the standard browser login, but diverges when reaching the username selection. Instead of copying the flow however, you'll be
creating the flow from the start:
* Select a realm, click on Authentication link
* Select "new", and give the new flow a distinctive Alias, i.e. "Browser Password-less"
* Select "Add execution", and using the drop-down select "Cookie". After pressing "Save", set its Requirement to _Alternative_.
* Select "Add execution", and using the drop-down select "Kerberos".
* Select "Add execution", and using the drop-down select "Identity Provider Redirector". After pressing "Save", set its Requirement to _Alternative_.
* Select "Add flow", and choose an representative Alias, e.g. "Forms". After pressing "Save", set its Requirement to _Alternative_.
.The common part with the browser flow
image:images/Passwordless-browser-login-common.png[]
* Using the `Actions` menu on the right-hand side of the "Forms" subflow, select "Add execution". Using the drop-down select
"Username Form". After pressing "Save", set its Requirement to _Required_.
The Username form is similar to "Browser" flow's Username Password Form, but only asks for a username, allowing a user to do perform a password-less login.
However, note that this inevitably allows a user enumeration attack on your {project_name} server. This is an unavoidable security risk for the convenience,
so the flow should make sure that an attacker cannot just have to guess a password to be able to enter.
* Using the `Actions` menu on the right-hand side of the "Forms" subflow, select "Add flow". Choose an representative Alias, e.g. "Authentication".
After pressing "Save", set its Requirement to _Required_.
* Using the `Actions` menu on the right-hand side of the "Authentication" subflow, select "Add execution". Using the drop-down select
"Webauthn Authenticator". After pressing "Save", set its Requirement to _Alternative_.
* Using the `Actions` menu on the right-hand side of the "Authentication" subflow, select "Add flow". Choose an representative Alias, e.g. "Password with OTP".
After pressing "Save", set its Requirement to _Alternative_.
* Using the `Actions` menu on the right-hand side of the "Password with OTP" subflow, select "Add execution". Using the drop-down select
"Password Form". After pressing "Save", set its Requirement to Required.
* Using the `Actions` menu on the right-hand side of the "Password with OTP" subflow, select "Add execution". Using the drop-down select
"OTP Form". After pressing "Save", set its Requirement to Required.
* In the "Bindings" menu, change the browser flow from "Browser" to "Browser Password-less"
The final flow that is produced is the following:
.A password-less browser login
image:images/Passwordless-browser-login.png[]
After entering the username, the way this flow works is the following:
* If the user has any WebAuthn credentials recorded, he will be able to use any of them to log in directly. This is the password-less login.
The user also has a drop-down allowing him to select "Password with OTP". He can do this because the "WebAuthn" execution and the "Password with OTP"
flow are set to _Alternative_. Were they set to _Required_ the user would have to enter WebAuthn, password, and OTP.
* If the user selects the "Password with OTP" from the drop-down, or if the user doesn't have any WebAuthn credentials, he will have to first enter his
password, and then his OTP. If the user has no OTP credential, he will be asked to record one.
It is important to note that since the WebAuthn execution is set to _Alternative_ instead of _Required_, this flow will never ask the user to register a WebAuthn credential. For a user
to have a Webauthn credential, he must have a required action added to him by an administrator. This is done first by making sure that that the `Webauthn Register`
required action is enabled in the realm (see the <<_webauthn,WebAuthn>> documentation), and then by setting the required action by using the `Credential Reset` part of a
user's <<user-credentials,Credentials>> management menu.
Creating a more advanced flow such as this one can have some subtle side effects. For example, if you were to enable the ability to reset the password
for the user, then this would be accessible from the password form. In the default "Reset Credentials" flow, the user has to enter his username. Since
he's already entered his username earlier in the "Browser Password-less" flow, this would be unnecessary for {project_name}, and a sub-optimal in terms of user
experience. To correct this, you could:
* Copy the "Reset Credentials" flow, setting its name to, for example "Reset Credentials for password-less"
* Use the `Actions` menu on the right-hand side of the "Choose user" execution, select "Delete"
* In the "Bindings" menu, change the reset credential flow from "Reset Credentials" to "Reset Credentials for password-less"
2017-08-28 12:50:14 +00:00
ifeval::[{project_community}==true]
=== Executions
Executions can be used
.Script Authenticator
A _script_ authenticator allows to define custom authentication logic via JavaScript.
Custom authenticators. In order to make use of this feature, it must be explicitly enabled:
[source]
----
bin/standalone.sh|bat -Dkeycloak.profile.feature.scripts=enabled
----
For more information, see the link:{installguide_profile_link}[{installguide_profile_name}] section.
Authentication scripts must at least provide one of the following functions:
`authenticate(..)` which is called from `Authenticator#authenticate(AuthenticationFlowContext)`
`action(..)` which is called from `Authenticator#action(AuthenticationFlowContext)`
2018-01-25 08:35:22 +00:00
Custom `Authenticator` should at least provide the `authenticate(..)` function.
The following script `javax.script.Bindings` are available for convenient use within script code.
`script`::
the `ScriptModel` to access script metadata
`realm`::
the `RealmModel`
`user`::
the current `UserModel`
`session`::
the active `KeycloakSession`
`authenticationSession`::
the current `AuthenticationSessionModel`
`httpRequest`::
the current `org.jboss.resteasy.spi.HttpRequest`
`LOG`::
a `org.jboss.logging.Logger` scoped to `ScriptBasedAuthenticator`
Note that additional context information can be extracted from the `context` argument passed
to the `authenticate(context)` `action(context)` function.
2018-02-08 21:09:26 +00:00
[source,javascript]
----
AuthenticationFlowError = Java.type("org.keycloak.authentication.AuthenticationFlowError");
function authenticate(context) {
LOG.info(script.name + " --> trace auth for: " + user.username);
if ( user.username === "tester"
&& user.getAttribute("someAttribute")
&& user.getAttribute("someAttribute").contains("someValue")) {
context.failure(AuthenticationFlowError.INVALID_USER);
return;
}
context.success();
}
----
2017-08-28 12:50:14 +00:00
endif::[]