8000 Step up authentication for saml by rmartinc · Pull Request #44185 · keycloak/keycloak · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading 8000
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions common/src/main/java/org/keycloak/common/Profile.java
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ public enum Feature {
CLIENT_SECRET_ROTATION("Client Secret Rotation", Type.PREVIEW),

STEP_UP_AUTHENTICATION("Step-up Authentication", Type.DEFAULT),
STEP_UP_AUTHENTICATION_SAML("Step-up Authentication Saml", Type.PREVIEW, Feature.STEP_UP_AUTHENTICATION),

CLIENT_AUTH_FEDERATED("Authenticates client based on assertions issued by identity provider", Type.PREVIEW),

Expand Down
3 changes: 3 additions & 0 deletions docs/documentation/release_notes/topics/26_6_0.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ When using the {project_name} Operator, set the update strategy to `Auto` to ben

For more details on the Operator configuration, see the https://www.keycloak.org/operator/rolling-updates[Avoiding downtime with rolling updates] guide.

= Step-up authentication for SAML (preview)

The feature `step-up-authentication-saml` extends the step-up authentication to include the SAML protocol and clients. This feature is in preview mode. Additional information is available in the link:{adminguide_link}#_step-up-authentication-saml[{adminguide_name}].

= Java 25 support

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ Finally, change the bindings.
.Browser login with step-up mechanism
image:images/authentication-step-up-flow.png[Authentication step up flow]

.Request a certain authentication level
.Request a certain authentication level with OpenID Connect
To use the step-up mechanism, you specify a requested level of authentication (LoA) in your authentication request. The `claims` parameter is used for this purpose:

[source,subs=+attributes]
Expand Down Expand Up @@ -426,6 +426,88 @@ Note when the login request initiates a request with the `claims` parameter requ
one of the specified levels. If it is not able to return one of the specified levels (For example if the requested level is unknown or bigger than configured conditions
in the authentication flow), then {project_name} will throw an error.

[[_step-up-authentication-saml]]
==== Step-up authentication for SAML

:tech_feature_name: Step-up Authentication for SAML
:tech_feature_id: step-up-authentication-saml
include::../../topics/templates/techpreview.adoc[]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This adds the "preview" warning in the documentation, however the feature is enabled as "experimental" in this PR. It seems the options are:

  1. Mark the feature as "preview" instead of "experimental" . Which IMO is OK as it is small feature and isolated

  2. Update this documentation to mark as feature as "Experimental" . It should be either some "hand-written warning" or a template for experimental features (Something like include::../../topics/templates/techexperimental.adoc ).

My vote is (1) ATM .

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to preview.


For the SAML protocol, the step-up authentication uses the `<AuthnContextClassRef>` element (Authentication Context Class Reference or ACR) to map the Level of Authentication (LoA). This element is a URI reference that identifies an authentication context declaration. The LoA is requested by the client in the SAML request via the `<RequestedAuthnContext>` element.

[source,xml,subs=+attributes]
----
<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" AssertionConsumerServiceURL="https://sp.example.com/" Destination="https://localhost:8543/realms/demo/protocol/saml" ForceAuthn="false" ID="1cc6ba19-b34d-48d5-ac44-3c4b2598b6c3" IssueInstant="2025-12-10T16:29:41.177Z" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Version="2.0">
<saml:Issuer>https://sp.example.com/</saml:Issuer>
<samlp:NameIDPolicy Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient"/>
<samlp:RequestedAuthnContext>
<saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:TimeSyncToken</saml:AuthnContextClassRef>
<saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef>
</samlp:RequestedAuthnContext>
</samlp:AuthnRequest>
----

The request asks for a set of authentication contexts that will be evaluated in order by {project_name} until one of them can be satisfied. This means that {project_name} knows that URI declaration and there is a level defined in the mapping that is sufficient for the request. If none of the specified contexts can be satisfied, then {project_name} returns a `<Response>` message with a second-level `<StatusCode>` of `urn:oasis:names:tc:SAML:2.0:status:NoAuthnContext`.

[source,xml,subs=+attributes]
----
<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Destination="https://sp.example.com/" ID="ID_70625c64-004a-44cc-b338-74802f0cc37d" InResponseTo="b935f66a-8185-40b6-990c-bd5f4971773e" IssueInstant="2025-12-10T17:00:46.894Z" Version="2.0">
<saml:Issuer>https://localhost:8543/auth/realms/demo</saml:Issuer>
<samlp:Status>
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Responder">
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:NoAuthnContext"/>
</samlp:StatusCode>
</samlp:Status>
</samlp:Response>
----

If {project_name} can satisfy the request, the step-up authentication flow is called to authenticate the user with the mapped level. The SAML response, using the `<AuthnStatement>` element inside the successful response, will contain again the `<AuthnContextClassRef>` with the context finally applied.

[source,xml,subs=+attributes]
----
<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Destination="https://sp.example.com/" ID="ID_079be770-ace4-4a0b-bdad-2042267a9bb9" InResponseTo="1cc6ba19-b34d-48d5-ac44-3c4b2598b6c3" IssueInstant="2025-12-10T16:29:41.228Z" Version="2.0">
<saml:Issuer>https://localhost:8543/auth/realms/demo</saml:Issuer>
<samlp:Status>
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
</samlp:Status>
<saml:Assertion xmlns="urn:oasis:names:tc:SAML:2.0:assertion" ID="ID_0278d50c-03c2-4ae0-accd-16161577faa5" IssueInstant="2025-12-10T16:29:41.228Z" Version="2.0">
<saml:Issuer>https://localhost:8543/auth/realms/demo</saml:Issuer>
<saml:Subject>
<saml:NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient">G-b1e11e8a-c002-47bc-8060-64f6f11fe267</saml:NameID>
<saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
<saml:SubjectConfirmationData InResponseTo="1cc6ba19-b34d-48d5-ac44-3c4b2598b6c3" NotOnOrAfter="2025-12-10T16:34:39.228Z" Recipient="https://sp.example.com/"/>
</saml:SubjectConfirmation>
</saml:Subject>
<saml:Conditions NotBefore="2025-12-10T16:29:39.228Z" NotOnOrAfter="2025-12-10T16:30:39.228Z">
<saml:AudienceRestriction>
<saml:Audience>https://sp.example.com/</saml:Audience>
</saml:AudienceRestriction>
</saml:Conditions>
<saml:AuthnStatement AuthnInstant="2025-12-10T16:29:41.229Z" SessionIndex="pZJ1Scsa1MyQ2KkesFEbedPq::56687d60-82d9-49bd-8609-dbc38153e55f" SessionNotOnOrAfter="2025-12-11T02:29:41.229Z">
<saml:AuthnContext>
<saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:TimeSyncToken</saml:AuthnContextClassRef>
</saml:AuthnContext>
</saml:AuthnStatement>
</saml:Assertion>
</samlp:Response>
----

For more information about the processing rules defined in the SAML standard for the `<RequestedAuthnContext>` element, see Section 3.3.2.2.1 Element `<RequestedAuthnContext>` of the *Assertions and Protocols for the OASIS Security Assertion Markup Language (SAML) V2.0* (`saml-core-2.0-os.pdf` document). The complete SAML v2.0 OASIS Standard set (PDF format) and schema files are available in the link:https://www.oasis-open.org/standard/saml/[Security Assertion Markup Language (SAML) v2.0] page. Note that the spec also defines a comparison (exact, minimum, better and maximum), that complicates what Level of Authentication will be finally selected in {project_name}.

When the `step-up-authentication-saml` feature is enabled, the <<_mapping-acr-to-loa-realm,ACR to Level of Authentication (LoA) Mapping>> is a table with three values: the OpenID Connect ACR, the SAML URI context and the Level of Authentication (LoA). The mapping can be defined for both client types. It can also be overridden at client level, but, in this case, only the URI and the LoA are present. The minimum ACR value which is allowed for the client can also be defined in the configuration. The step-up authentication options for SAML clients are placed in the **Advanced** tab, section **Advanced Settings**. See chapter <<_client-saml-configuration,Creating a SAML client>> for details.

In summary, when the step-up authentication is configured for SAML, {project_name} will process the specified context level in the SAML request and the minimum ACR allowed for the client (if they are present) to obtain the LoA (integer level) that should be reached in the authentication. If there is no available level that satisfies the request, the error is returned per specification. If the URI/LoA mapping returns a level that satisfies the request, the authentication flow is started, enforcing that Level of Authentication to be reached.

In order to maintain backwards compatibility, {project_name} does not return an error and continues adding the previous ACR `urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified` to the response in the following situations:

* The new feature `step-up-authentication-saml` is not enabled in {project_name}.
* The SAML client does not define any mapping between context URI and LoA. Use the client mapping instead of the general realm mapping when you just need to apply the step-up for some specific SAML clients.
* The `AuthnContextClassRef mapper` is not executed. This mapper is provided by a new default client scope `AuthnContextClassRef` which is now added to SAML clients by default. It is in charge of adding the resulting `<AuthnContextClassRef>` to the response.
+
NOTE: For new realms created with the `step-up-authentication-saml` feature enabled, the mapper and the client scope `AuthnContextClassRef` are automatically created and assigned to SAML clients. But, for exiting realms, if you want to use this preview feature, the client scope and the mapper should be created and assigned to the client manually. When the feature is promoted to supported, the migration will also create the client scope for existing realms if the feature is not disabled at startup.
+
* The LoA calculated at request time is not achieved by the authentication flow. For example, if the authentication flow used for authentication is not a step-up flow, or there is a misconfiguration between the URI/LoA mapping and the final level reached in the step-up authentication flow.

[[_registration-rc-client-flows]]
==== Registration or Reset credentials requested by client

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,3 +180,7 @@ This tab has many fields for specific situations. Some fields are covered in ot
=== Advanced settings

*Assertion Lifespan*:: Specific client lifespan set in the SAML assertion conditions. After that time the assertion will be invalid. If not specified the realm *Access Token Lifespan* is used. The `SessionNotOnOrAfter` attribute is not modified and continue using the *SSO Session Max* time defined at realm level.

*ACR to LoA Mapping*:: Define which ACR (Authentication Context Class Reference) value is mapped to which LoA (Level of Authentication). The ACR for SAML is an URI, whereas the LoA must be numeric. This mapping overrides the <<_mapping-acr-to-loa-realm,ACR to Level of Authentication (LoA) Mapping>> defined at realm level. Only present if <<_step-up-authentication-saml,Step-up authentication for SAML>> feature is enabled.

*Minimum ACR Value*:: Minimum ACR to be enforced by Keycloak. If the resulting authentication context for the request is as strong as this ACR the request is valid, otherwise Keycloak returns the `NoAuthnContext` status error. Only present if <<_step-up-authentication-saml,Step-up authentication for SAML>> feature is enabled.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ The acr claim can be requested in the `claims` or `acr_values` parameter sent in

Mapping can be also specified at the client level in case that particular client needs to use different values than realm. However, a best practice is to stick to realm mappings.

.ACR to LoA mapping
image:images/realm-oidc-map-acr-to-loa.png[alt="ACR to LoA mapping"]

For further details see <<_step-up-flow,Step-up Authentication>> and https://openid.net/specs/openid-connect-core-1_0.html#acrSemantics[the official OIDC specification].

If the feature for <<_step-up-authentication-saml,Step-up authentication for SAML>> is enabled, the ACR to LoA mapping is a table with three values. The new URI column is the URI that will map the SAML authentication context class reference to the numeric LoA. This new column is necessary if you want to use step-up authentication with the SAML protocol.

.ACR/URI to LoA mapping
image:images/realm-oidc-map-acr-uri-to-loa.png[alt="ACR/URI to LoA mapping"]
Original file line number Diff line number Diff line change
Expand Up @@ -1445,6 +1445,7 @@ deleteConfirmGroup_other=Are you sure you want to delete these groups?
scopePermissions.users.manage-description=Policies that decide if an administrator can manage all users in the realm
defaultACRValuesHelp=Default values to be used as voluntary ACR in case that there is no explicit ACR requested by 'claims' or 'acr_values' parameter in the OIDC request.
minimumACRValueHelp=Minimum ACR to be enforced by Keycloak. Overrides lower ACRs explicitly requested by 'acr_values' or 'claims', unless they are marked as essential.
minimumACRValueSamlHelp=Minimum ACR to be enforced by Keycloak. If the resulting authentication context for the request is as strong as this ACR the request is valid, otherwise Keycloak returns the NoAuthnContext status error.
membershipAttributeType=Membership attribute type
eventTypes.PUSHED_AUTHORIZATION_REQUEST.name=Pushed authorization request
included.client.audience.tooltip=The Client ID of the specified audience client will be included in the audience (aud) field of the token. If the token includes audiences, the specified value is added to them. It will not override existing audiences.
Expand All @@ -1463,6 +1464,15 @@ prompts.select_account=Select account
defaultACRValues=Default ACR Values
minimumACRValue=Minimum ACR Value
valueError=A value must be provided.
loa=LoA
uri=URI
acr=ACR
loaError=Invalid LoA
uriError=Invalid URI
acrError=Invalid ACR
loaPlaceholder=Type a LoA
uriPlaceholder=Type an URI
acrPlaceholder=Type an ACR
noConsents=No consents
orderChangeSuccessUserFed=Successfully changed the priority order of user federation providers
noUsersEmptyStateDescriptionContinued=to find them. Users that already have this role as an effective role cannot be added here.
Expand Down Expand Up @@ -2792,6 +2802,8 @@ key=Key
email=Email
groupDeleted_other=Groups deleted
acrToLoAMappingHelp=Define which ACR (Authentication Context Class Reference) value is mapped to which LoA (Level of Authentication). The ACR can be any value, whereas the LoA must be numeric.
acrToLoAMappingSamlHelp=Define which ACR (Authentication Context Class Reference) value is mapped to which LoA (Level of Authentication). The ACR for SAML is an URI, whereas the LoA must be numeric.
acrToLoAMappingRealmSamlHelp=Define which ACR (Authentication Context Class Reference) value is mapped to which LoA (Level of Authentication). The ACR can be any value and is used in OpenID Connect, the URI is the authentication context for SAML and it is an URI, finally the LoA must be numeric.
uploadFile=Upload JSON file
loginActionTimeoutHelp=Max time a user has to complete login related actions like update password or configure totp. This is recommended to be relatively long, such as 5 minutes or more.
identityProviders=Identity providers
Expand Down
Loading
Loading
0