CodamAIDocs
Topicdone

Catching up on a stronger sign-in (step-up)

What happens when an action asks for a stronger or more recent sign-in: from the refusal in the module through the new sign-in at Keycloak to the repeated request.

Variants
sign-in level missingsign-in too oldsign-in level unreachablepermission missingservice accountpage loaded on the server

What this is about

For most actions it is enough to be signed in. Some ask for more: salary data may only be read by someone who signed in with a second factor (for example a code from an app), and a template may only be approved by someone who signed in just now.

A step-up is catching up on exactly this “more”: the person signs in again, Keycloak asks only for the missing step, and the portal repeats the action. The person does not have to sign out and loses nothing of what they are looking at.

Two words first:

  • The sign-in level says how strongly someone signed in. There are exactly three, in ascending order: basic (signed in, by any means), mfa (with a second factor) and phishing-resistant (with a factor bound to the address, for example a passkey). A higher level always satisfies a lower one too.
  • The maximum age of the sign-in says how long ago the sign-in may have happened at most, in whole seconds.

A requirement is always a level and, if needed, a maximum age. More under The sign-in level.

An example, end to end

The model salary says: reading requires the level mfa. A person with the reader role signed in this morning with a password only and now opens the list of salaries.

sequenceDiagram
    participant B as Browser
    participant P as Sign-in window
    participant BFF as BFF
    participant F as Filter chain (CIAS)
    participant D as CDMS
    participant K as Keycloak
    B->>BFF: open the list "Salaries"
    BFF->>F: POST /api/rest/hr/salary/query with token
    F->>F: reads level basic and sign-in time from the token
    F->>D: request with RequestContext
    D->>D: role for reading? yes. Level mfa reached? no
    D-->>BFF: 401 step-up-required|mfa
    BFF-->>B: 401, key unchanged
    B->>B: dialog: "This action asks for a sign-in with a second factor."
    B->>P: click on "Sign in again" opens the window
    P->>K: sign-in with acr_values=mfa
    K->>P: asks only for the second factor
    K-->>BFF: new tokens with acr=mfa into the session cookie
    P-->>B: reports "done" and closes
    B->>BFF: the same request again
    BFF->>F: POST /api/rest/hr/salary/query with the new token
    F->>D: level mfa
    D-->>B: 200, the salaries

The same steps in words:

A step-up for a read request
  1. 1
    BFF→CDMS
    sends POST /api/rest/hr/salary/query with the person's access token
  2. 2
    Filter chain
    reads the sign-in level from the claim acr and the time of the sign-in from auth_time
    Both come from the token the BFF sent, not from the exchanged one. If acr is missing or holds an unknown value, basic applies. Both end up in the RequestContext.
  3. 3
    CDMS
    checks the role for reading first, then the sign-in
    The role is there. The level basic does not satisfy mfa.
  4. 4
    CDMS→BFF
    answers 401 with the key cias.authentication.step-up-required|mfa
    It adds the standard header WWW-Authenticate: Bearer error="insufficient_user_authentication", acr_values="mfa". For a search the whole request answers 401. No rows are silently filtered out.
  5. 5
    BFF→Browser
    passes the 401 on with the key unchanged as messageKey
  6. 6
    Frontend
    recognizes the key and shows a dialog: "This action asks for a sign-in with a second factor."
    If the action also asks for a maximum age, it adds something like "It must not be older than 5 min." If several requests fail at the same time, there is only one dialog and one sign-in.
  7. 7
    User
    clicks "Sign in again"
    This opens a small sign-in window (popup). A click is needed because browsers block a popup without one. If it is blocked anyway, the dialog says so and asks you to allow popups.
  8. 8
    Frontend→Keycloak
    signs the person in again in the window, with acr_values=mfa
  9. 9
    Keycloak
    already knows the session and asks only for the missing step, here the second factor
    The new token carries acr=mfa. The BFF puts it into its session cookie like any token.
  10. 10
    Frontend
    The window reports "done" to the portal and closes
    It reports over a channel inside the browser (BroadcastChannel codamai.step-up) that only works between pages of the same address.
  11. 11
    BFF→CDMS
    the same request again, now with the new token
  12. 12
    CDMS→Browser
    delivers the list
    Result: The person sees the salaries. The request was repeated exactly once.

This applies to every request of a portal, not just to this example. The portals handle a step-up in one single place that all their requests pass through. No individual page has to know about it.

The variants

How a step-up can end

When: The action asks for mfa or phishing-resistant, the person signed in more weakly.

The flow from the example. The portal sends the level as acr_values. Keycloak asks only for the step that is missing for the level. Someone who is already signed in at the level is not asked at all: the window opens and closes right away.

Result: The repeated request succeeds.

When: The action asks for a maximum age, for example 300 seconds, and the sign-in was longer ago.

  1. 1
    CDMS
    answers 401 with cias.authentication.step-up-required|basic|300
  2. 2
    Frontend
    shows "This action asks for a sign-in. It must not be older than 5 min."
  3. 3
    Frontend→Keycloak
    signs in again, with acr_values=basic and prompt=login
    prompt=login forces Keycloak to really sign in anew, even if the session is still valid. So the new sign-in is fresh in any case, and auth_time in the new token moves forward.
  4. 4
    BFF→CDMS
    repeats the request
    Result: The sign-in is younger than 300 seconds, the request succeeds.

When: The action asks for a level for which the sign-in flow in Keycloak has no step.

  1. 1
    Frontend→Keycloak
    signs in again, with acr_values=mfa
  2. 2
    Keycloak
    cannot produce the level and silently ignores the request
    The new token carries the level the flow reaches, here basic.
  3. 3
    CDMS
    refuses the repeated request again with 401
  4. 4
    Frontend
    does not ask again, but says: "The new sign-in did not reach that. The action was not carried out."
    Result: No loop. The refusal stays until someone extends the sign-in flow.

When: The person lacks the role for the action.

The module always checks the permission first and only then the sign-in. Without the role there is the usual 403, and nothing is asked about the sign-in. A stronger sign-in would not let the person in anyway.

Result: 403, no dialog

When: Another program calls with the token of its own service account (client credentials).

A service account cannot sign in again. Its token has the level basic and no sign-in time. In CDMS and in the administration of CIAS it is therefore exempt from every requirement. It is protected by its credentials and roles. This does not apply to approvals in CRMS: an approval is a person's signature, and a service account is not supposed to act there at all.

Result: CDMS and CIAS administration: the request goes through. CRMS approval: refusal.

When: The portal already loads the data of a page on the server on the first call.

On the server nobody can be asked. The refusal arrives in the browser as an error together with the page. As soon as the page is set up there, the portal opens the same dialog. After the new sign-in it loads the data of the page again.

Result: The page then shows the data.

If the person cancels the dialog, the refusal stands. The action is then not carried out.

Repeating a write request is safe. The sign-in check runs before anything is stored. One special case is the answer: after a create or update, CDMS returns the stored object, and that is a read. If the model asks for a level for reading, it applies here too. If CDMS refuses at this point, the change is rolled back with the refusal.

What decides the result

Permission present?Service account?Level enough?Maximum age kept?What happens
no–––403, nothing is asked about the sign-in
yesyes––goes through (except for an approval in CRMS)
yesnono–401 step-up-required|<level>, the portal asks
yesnoyesno401 step-up-required|<level>|<seconds>, the portal asks
yesnoyesyesThe action runs

“Maximum age kept?” only matters if the action asks for one. Without a maximum age the level is enough. The limit itself still counts as kept.

Who decides what

QuestionResponsible
Does this action ask for a stronger or more recent sign-in?the module, by its own rules: CDMS per model and operation, CRMS per approval stage and kind of document, CIAS for its administration
How strongly and when did the person sign in?the CIAS filter chain, from acr and auth_time of the token that was sent
What does the refusal look like?CIAS defines it: 401, a key and the standard header. Every module answers the same way
Which steps does the sign-in ask for?Keycloak, following the sign-in flow the operator set up
When is the person asked, and how often is the request repeated?the portal: one dialog, one new sign-in, exactly one repetition

CIAS does not sign anyone in itself and does not ask for any factor. It only gives the levels their names and makes sure that Keycloak writes exactly these names into acr.

Where the requirement is set

A requirement is set when the system is assembled and is fixed after that. In the running system there is no way to change it, neither through an interface nor through a user interface. Whoever could lower a requirement could switch the step-up off.

ModuleWhat can ask for a sign-inWhere it is setMore
CDMSevery model, per operation (create, read, update, delete). A sub-model inherits the requirement and can only tighten itin the model, while modeling in the hubSign-in level per model
CRMSevery approval stage of a template, plus final documents per template key, reading and changing separatelywhen the system is assembled in the hub, in the CRMS portal: approval stages in the area “Approval”, documents in the area “Sign-in”. The build writes them into the settings of the system; the document defaults can be overridden by an environment variableApproval of templates
CIASits own administration in five areas (users, groups, roles, tenants, registrations), reading and changing separatelywhen the system is assembled in the hub, in the CIAS portal in the area “Administration”. The build writes them into the settings of the system, which an environment variable can overrideSign-in level for the administration

Without a setting, nothing asks for more than basic and no maximum age. Every signed-in person satisfies that.

Every module declares to CIAS the levels it requires, just like its roles. At reconciliation CIAS compares them with the levels the sign-in flow in Keycloak can produce. See Modules declare their roles.

Traps

Next

Search