> For the complete documentation index, see [llms.txt](https://docs.openiam.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.openiam.com/administration-guide/security/step-up-authentication.md).

# Step-up authentication

Demand a stronger authentication method on one URL without changing everyday sign-in — how OpenIAM expresses assurance, how a rule reaches a path, what the user sees, how long a step-up lasts, and how

## What this is, and when you need it

Everyday sign-in is a password. Opening the Webconsole, approving a payment, or resetting somebody else's password should be more than that — but demanding a hardware key of everybody, all day, is how you end up with a help desk full of lockouts.

Step-up is the middle: the session a user already has is good enough for most of the estate, and one specific path asks for one more proof before it lets them through.

The mechanics of binding an authentication rule to a URL — creating the URI pattern, the `EXEC_ORDER`, the either/or relationship with the content provider's rules — are [Content providers and URI patterns](/administration-guide/access/content-providers-and-uri-patterns.md). This page assumes them and links rather than repeating. The rule graph itself is [The authentication model](/administration-guide/security/authentication-model.md), the individual factors are [Authentication providers and MFA](/administration-guide/security/authentication-providers-and-mfa.md), and branching on device, country or behaviour is [Adaptive authentication](/administration-guide/security/adaptive-authentication.md).

What OpenIAM means by assurance, how a rule reaches a path, the shipped rule worth copying, how long a step-up lasts, and what a user sees when they have not met the demand are all set out after the procedure. The worked example names every node, edge and value it needs, so start there.

## At a glance

|                          |                                                                                                                                                                                                                                                                                           |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **You will end up with** | Everyday sign-in unchanged, and one path — the Webconsole — additionally demanding either TOTP or a FIDO security key, re-demanded every 60 minutes, with a break-glass administrator on a route that does not carry the rule                                                             |
| **Where**                | **Policy → Authentication Rule** at `/webconsole/authentication-rule-list`, then the URI pattern list at `/webconsole/contentProviders`; `/webconsole/authenticationDebug` reports how a request was evaluated                                                                            |
| **You need first**       | TOTP and a FIDO key enrolled and each proven on its own, for your test identity and for the break-glass administrator; a count of how many people who reach the path have neither; and a second browser session held open on a route the rule will not match                              |
| **Takes**                | 60 to 120 minutes, including a one-minute wait for `org.openiam.auth.provider.sweep.time` after every edit to an already-cached rule                                                                                                                                                      |
| **Reversible**           | Partly. Removing the rule from the pattern restores the previous behaviour, but the path you are protecting is the path you would use to undo the change — a lockout is recovered by the break-glass deletion under [Rollback and recovery](#rollback-and-recovery), not from the console |

## Worked example: demand a second factor for the Webconsole only

Objective: everybody signs in with a password exactly as they do now. Reaching the Webconsole additionally requires **either** TOTP **or** a FIDO security key, re-demanded **every 60 minutes**. SelfService, the launchpad and every proxied application are untouched. A break-glass administrator keeps a route in that does not carry the rule.

{% hint style="danger" %}
This is the change class [Change configuration safely](/administration-guide/start/change-safely.md) calls unrecoverable: the path you are protecting is the one you would use to undo the change. Do steps 1 and 2 before anything else, and do not skip step 7.
{% endhint %}

{% stepper %}
{% step %}

### Confirm each factor works on its own, for you

Enrol TOTP and register a security key for your own test identity and for the break-glass administrator, and prove each one signs in by itself before it appears in any rule. See [Authentication providers and MFA](/administration-guide/security/authentication-providers-and-mfa.md). A factor that does not work alone will not start working inside a graph.

Then check coverage for the population: how many people who reach the Webconsole have neither a TOTP secret nor a registered authenticator? Those are the lockouts you are about to create.
{% endstep %}

{% step %}

### Establish the way back in

Open a second browser, signed in as an administrator on a route that will **not** carry the new rule — the SelfService content provider, or a Webconsole path you are deliberately leaving on the content provider's own rule. Confirm it works now. Keep it open for the whole exercise.
{% endstep %}

{% step %}

### Create the rule and save it once

**Policy → Authentication Rule → New** (`/webconsole/authentication-rule-list`). Name it `WEBCONSOLE_STEPUP`, describe it, save. The canvas needs the persisted rule id before it renders, so an empty graph on a brand-new rule is expected.
{% endstep %}

{% step %}

### Lay out four nodes and four edges

* `PASSWORD_AUTH` as the single **root** step. It is in the rule because the pattern's rules replace the content provider's — without it, the Webconsole would stop asking for a password at all.
* `TOTP_AUTH` and `WEB_AUTH` as **two children of the password step**.
* One `AUTHORIZATION_ACCEPT` terminal, reused by both — do not create two.

Edges: `PASSWORD_AUTH` → `TOTP_AUTH`, `PASSWORD_AUTH` → `WEB_AUTH`, `TOTP_AUTH` → `AUTHORIZATION_ACCEPT`, `WEB_AUTH` → `AUTHORIZATION_ACCEPT`. All four carry the `Y` result; there is no adaptive-risk node here, so there is no `N` branch to draw.

Set **lifetime** to `60` on both the `TOTP_AUTH` and the `WEB_AUTH` steps, and leave it empty on the password step so the password is not re-demanded every hour on top of everything else.

Read this graph the way OpenIAM walks it: before any second factor, both `TOTP_AUTH` and `WEB_AUTH` are outstanding, so the user is offered the choice; completing **either** one reaches the accept terminal through its own branch and the rule passes. That is what makes it an *or* even though both children hang off the same parent. Confirm that on your system before you widen the rule — it is one sign-in as a test user, and step 6 is where you do it.
{% endstep %}

{% step %}

### Attach it to one narrow path first

Not `/webconsole/*`. Pick a single Webconsole screen you can afford to break — for example `/webconsole/authenticationProviders` — create or open that URI pattern on the content provider serving the Webconsole, and add `WEBCONSOLE_STEPUP` at `EXEC_ORDER` `0`. The procedure is [Demand a different authentication on one URL](/administration-guide/gateway/protect-an-application.md#demand-a-different-authentication-on-one-url).

Every other Webconsole path still resolves to the content provider's `DEF_PASSWD_AUTH_RULE`, which is your way back in while you test.
{% endstep %}

{% step %}

### Test the whole table, in a private window

Six cases, each in a fresh private session as a test identity, not as yourself:

1. Sign in with a password and request an unprotected Webconsole screen — no prompt.
2. Request `/webconsole/authenticationProviders` — `/idp/auth-select` appears offering both methods.
3. Complete TOTP — you land on the screen you asked for.
4. Request it again straight away — no prompt; the approval is in the session.
5. Wait out the lifetime and request it again — prompted once more. This is the check that the lifetime is minutes.
6. As an identity with **neither** factor enrolled, request it — confirm you meet the loop described under [What the user experiences](#what-the-user-experiences), so you know what your help desk will be told.

Then ask the product what it thinks. `/webconsole/authenticationDebug` reports how a request for a given login was evaluated and names the rules that applied — it is the screen to open when the six cases above do not behave the way the rule says they should.
{% endstep %}

{% step %}

### Widen it only when every row of that table passed

To cover the console, add the rule to the `/webconsole/*` pattern. Before you save, satisfy yourself of three things: your break-glass administrator has one of the two factors enrolled and has used it; the second browser from step 2 is on a route that does **not** match `/webconsole/*`; and you know the rollback below without having to look it up.

Remember the sweep. A rule the cache has never seen takes effect at once; an *edit* to a rule already cached waits out `org.openiam.auth.provider.sweep.time`, `60000` ms. Re-test after a minute, not after a second.
{% endstep %}
{% endstepper %}

### The same shape, for a proxied application

A payment or approval path behind the Access Gateway is the identical exercise with two differences. The pattern is the application's path on its own content provider rather than a Webconsole path, and you must give **both** the screen and the path that performs the action a URI pattern carrying the rule — as the shipped password-reset patterns do. A step-up on `/payments/transfer` with `/payments/api/transfer` left open is decoration. Ask the application team which path its buttons submit to, and add a pattern for that too.

## Testing without locking yourself out

Four habits, in the order they save you.

**Read the evaluation before you trust the rule.** `/webconsole/authenticationDebug` reports how OpenIAM evaluates a request and which rules applied, and it changes nothing. It is the cheapest way to see whether a rule is in force without risking the session you are using.

**Two browsers, always.** One is the session under test, in a private window. The other is a break-glass administrator on a route the rule does not match, signed in *before* the change and left open. A single browser is how a five-minute change becomes a database recovery.

**One narrow pattern before the wildcard.** A rule attached to `/webconsole/*` applies to everyone that content provider serves, including you, including the screen where you would remove it.

**Test the negative cases deliberately.** The identity with no factor enrolled, the expired lifetime, the API path as well as the screen. Each is a real user tomorrow.

## Rollback and recovery

**While you can still reach the Webconsole.** Open the URI pattern and remove the rule from its **Authentication rules** list, then wait out `org.openiam.auth.provider.sweep.time` — `60000` ms — and re-test in a fresh private session. Removing the rule from the pattern restores the content provider's rules for that path, which is password-only sign-in unless you have changed it.

{% hint style="danger" %}
**Break-glass: you attached a step-up rule to the Webconsole and nobody can satisfy it.** There is no in-product way back: the screen that would detach the rule is behind the rule. This is a database change, outside the supported change path.

Find the attachment first, read-only:

{% code overflow="wrap" %}

```sql
SELECT URI_PATTERN_ID, RULE_ID, EXEC_ORDER
  FROM URI_RULE_XREF WHERE RULE_ID = '<YOUR_RULE_ID>';
```

{% endcode %}

Then make the narrowest write that restores a way in — delete that one row:

{% code overflow="wrap" %}

```sql
DELETE FROM URI_RULE_XREF
 WHERE URI_PATTERN_ID = '<PATTERN>' AND RULE_ID = '<YOUR_RULE_ID>';
```

{% endcode %}

Wait out the sixty-second provider sweep before retrying sign-in.

What this costs you: the delete bypasses the Webconsole, so it produces **no audit record** of the change and no validation of what you removed, and it is invisible to anyone reading the audit trail afterwards. Note it in your own change record. It does not repair the rule — fix that from the Webconsole once you are back in. If the content provider itself has lost its password rule as well, the recovery is the one in [Authentication providers and MFA](/administration-guide/security/authentication-providers-and-mfa.md#keep-a-working-way-in). The supported alternative, slower but auditable, is to have OpenIAM support or the deployment owner make the change.
{% endhint %}

**If instead the step-up is being demanded too often.** The lifetime is minutes: `60` is an hour, not a minute. Correct the value on the step, and remember that an *edit* to a cached rule waits out the sweep while a newly attached rule does not.

**If a user is stuck in a loop between the factor page and the destination.** They cannot perform the demanded method. Either enrol them in it, or add a second root to the rule that they can satisfy. Removing the rule for one person is not possible — a rule is attached to a path, not to a population; if one group must be exempt, that is an adaptive-risk branch on role membership, in [Adaptive authentication](/administration-guide/security/adaptive-authentication.md).

## Verification

| What to check                                                | How                                                                                                                                                                                                                                                                                                            |
| ------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| How a request is actually evaluated, and which rules applied | `/webconsole/authenticationDebug`                                                                                                                                                                                                                                                                              |
| The rule graph is complete                                   | Every root step must have a path to a step whose grouping is `AUTHORIZATION_ACCEPT`. The canvas at `/webconsole/authentication-rule` renders it                                                                                                                                                                |
| The rule is attached where you think                         | `SELECT * FROM URI_RULE_XREF WHERE RULE_ID='<rule>'` and `SELECT * FROM CP_RULE_XREF WHERE RULE_ID='<rule>'`                                                                                                                                                                                                   |
| The pattern is not silently overriding the content provider  | Any row in `URI_RULE_XREF` for the matched pattern means the content provider's rules are not consulted for that path                                                                                                                                                                                          |
| The step-up actually fires                                   | A fresh private session: sign in, request the protected path, and expect the method page or `/idp/auth-select`                                                                                                                                                                                                 |
| One factor is enough on an any-of rule                       | Complete one of the offered methods and confirm you reach the destination without being asked for the other                                                                                                                                                                                                    |
| The approval is remembered                                   | Request the protected path again immediately — no prompt                                                                                                                                                                                                                                                       |
| The lifetime is what you set                                 | Wait past it and request again — prompted. Under it — not prompted                                                                                                                                                                                                                                             |
| The shipped step-up bindings are intact                      | The eight shipped password-change patterns under [The shipped example to copy](#the-shipped-example-to-copy) each show `STEPUP_AUTH_RULE` in the pattern's **Authentication rules** at `/webconsole/contentProviders`                                                                                          |
| The lifetime values as stored                                | `SELECT ID, AUTH_LEVEL_GROUPING_ID, LIFETIME FROM RULE_AUTHENTICATION_STEP WHERE RULE_ID='<rule>'` — read-only, and the only place the per-step lifetime is plainly visible                                                                                                                                    |
| Audit                                                        | `LOGIN`, `DO_LOGIN`, and the factor's own action — `CONFIRM_SMS_OTP_TOKEN`, `AUTH_PUSH_COMPLETE`, `QUESTION_AUTHENTICATION`, `CAPTCHA_AUTHENTICATION`. Rule edits are `SAVE_AUTH_RULE` / `DELETE_AUTH_RULE`. See [Audit actions and retention](/administration-guide/reference/audit-actions-and-retention.md) |

## When it goes wrong

| Symptom                                                                                          | Cause                                                                                                                                               | Fix                                                                                                                                                 |
| ------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| The protected path stopped asking for a password too                                             | The pattern's rule replaced the content provider's, and your rule has no `PASSWORD_AUTH` step                                                       | Add the password step as the root of your rule                                                                                                      |
| Step-up configured, never prompts                                                                | The rule is not attached to the pattern that actually matched, or the edit is waiting on the sweep                                                  | Check `URI_RULE_XREF`; `/webconsole/authenticationDebug` shows which pattern matched. Only a rule id the cache has never seen refreshes immediately |
| Step-up prompts on every single request                                                          | The step's lifetime is very small, or the rule demands a level nothing in the flow records                                                          | Read the stored `LIFETIME`; remember the unit is minutes                                                                                            |
| Step-up never prompts again after the first time                                                 | The step has no lifetime, so the approval lasts as long as the session                                                                              | Set a lifetime on the step                                                                                                                          |
| `/idp/auth-select` appears when you expected one specific method                                 | More than one demanded level is outstanding                                                                                                         | Expected for an any-of rule. For a single method, give the rule one root                                                                            |
| The user completes the factor and is sent back to the factor page                                | They completed a method the rule does not name, or the postback URL was lost                                                                        | Compare the grouping on the page they were sent to with the rule's steps; check the postback parameter name on the content provider                 |
| A screen shows the sign-in page inside part of itself, rather than redirecting the whole browser | The interruption is a redirect rather than an authentication error, and the screen's own background request followed it                             | Not something you configure. It is the shape of the interruption; expect it on screens that load parts of themselves separately                     |
| `401` with nothing in the log about credentials                                                  | The rule path reached `AUTHORIZATION_DECLINE`, often because adaptive extra data was absent — *"Mandatory extra data don't present"* in the ESB log | [Adaptive authentication](/administration-guide/security/adaptive-authentication.md)                                                                |
| A user with no second factor cannot get past the step-up at all                                  | There is no downgrade path; the method page cannot be completed without enrolment                                                                   | Enrol them, or add a root they can satisfy. Check coverage before attaching a rule, not after                                                       |
| Setting **Min Auth Level** on a resource changes nothing                                         | Nothing enforces it                                                                                                                                 | Express the demand as an authentication rule on the URI pattern                                                                                     |
| A SAML sign-on interrupted by step-up loses the request                                          | An interstitial screen on a `/login/{randomId}` URL is not saved; a second factor is                                                                | [SAML identity provider](/administration-guide/federation/saml-identity-provider.md#how-an-interrupted-authnrequest-survives-authentication)        |
| The screen is protected but the action still works                                               | The path behind it has no URI pattern of its own                                                                                                    | Add one, as the shipped password-reset patterns do                                                                                                  |
| Rule name saves but the canvas is empty                                                          | The graph editor needs the persisted rule id                                                                                                        | Save, reopen, then build the graph                                                                                                                  |
| Administrator locked out                                                                         | The rule reached a step nobody can satisfy on a path that includes the Webconsole                                                                   | The break-glass delete above; keep the rule itself for investigation                                                                                |

## There is no assurance ladder, and that is the whole design

The most common mistake is to look for a number. OpenIAM does not have one.

An **authentication level** carries three things: an id such as `TOTP_AUTH`, a display name such as *TOTP 2-Factor Authentication*, and a `REQUIRES_AUTHENTICATION` flag saying whether reaching that step needs an already-authenticated session. **It carries no rank.** Nothing in the product compares one level against another, there is no "level 3 beats level 2", and no setting introduces one.

Two traces of an abandoned numeric scheme survive and will mislead you if you find them. The very first two levels seeded, `PASSWORD_AUTH` and `CERT_AUTH`, were given the numbers 1 and 2 back in 3.0.0.1; every level added since has none, and OpenIAM has not read the number for years. And a resource carries a **Min Auth Level** value which nothing enforces — it is stored, it is not exposed as a field in the Webconsole, and the two error codes that would report a failure against it, `URI_FEDERATION_AUTH_LEVEL_DOES_NOT_MEET_MIN_AUTH_LEVEL_ON_CP` and `URI_FEDERATION_AUTH_LEVEL_DOES_NOT_MEET_MIN_AUTH_LEVEL_ON_PATTERN`, are never returned. Neither of these facts can be confirmed from a console screen, because there is no screen for either; treat them as dead ends and do not design against them. If you need that attested for an audit, ask OpenIAM support.

**Assurance in OpenIAM is expressed as "which method", not "how strong".** You do not raise a level; you name a factor the session has not performed yet. Everything else on this page follows from that.

## How a session remembers what it has proved

The session cookie carries a list of **approved authentication levels** — the groupings this session has satisfied — and each entry may carry its own expiry timestamp. On every request, OpenIAM works out which levels the requested path demands, subtracts the ones already approved, and what is left is what the user is asked for. Entries whose expiry has passed are dropped from the list as it is read, so an expired approval is indistinguishable from never having done that factor.

The expiry comes from the **lifetime** on the rule step that demanded the level: `RULE_AUTHENTICATION_STEP.LIFETIME`, an integer added in 4.2.2.0, applied as **minutes** from the moment the factor is completed. A step with no lifetime grants an approval that lasts as long as the session itself.

That single field is what makes step-up *step-up* rather than just a second factor at sign-in. Set `LIFETIME` to `15` on a TOTP step protecting a payment path and the user is asked again a quarter of an hour later; leave it empty and one TOTP prompt at nine in the morning covers the whole day.

{% hint style="info" %}
The column carries no unit and no default, and nothing on the rule-editor canvas states one. Minutes is the unit the product applies. Confirm it once on your own release rather than taking it on trust: set `LIFETIME` to `2` on a step-up factor, satisfy it, and see whether the factor is demanded again after two minutes or after two of anything else. Record the result — this is a two-minute experiment and it settles the question for your deployment.
{% endhint %}

Two further consequences. The approved-level list survives token renewal, so a step-up is not lost because the session slid forward — see [Terminate a user's sessions](/administration-guide/security/session-management.md). And it does not survive a new sign-in, because a new session starts with a new list.

## How a path demands a level

By attaching an **authentication rule** containing that level's step to the URI pattern for the path, or to the content provider. That is the same mechanism as ordinary sign-in; step-up is not a separate feature with its own screen.

{% hint style="warning" %}
**A URI pattern's rules replace the content provider's rules — they do not add to them.** Attach a step-up rule to a pattern and that pattern stops honouring `DEF_PASSWD_AUTH_RULE` entirely. Your rule must therefore contain the password step as well as the step-up step, or the path becomes *easier* to reach than the rest of the site rather than harder. This trips up nearly everybody the first time. The full statement and the query that proves which side is in force are in [The authentication model](/administration-guide/security/authentication-model.md#how-a-request-selects-a-rule).
{% endhint %}

The binding procedure itself — creating the pattern, adding the rule and ordering it, and what a successful save does to the caches — is [Demand a different authentication on one URL](/administration-guide/gateway/protect-an-application.md#demand-a-different-authentication-on-one-url).

### The shipped example to copy

OpenIAM ships one step-up rule and uses it on eight paths, and it is the model worth imitating.

`STEPUP_AUTH_RULE`, named **Def. stepup auth** and described *"Rule for doing step-up authentication"*, arrived in 4.2.2.0. It is an **any-of**: ten sibling root steps, each edging straight to a single `AUTHORIZATION_ACCEPT` terminal. Satisfying any one of them satisfies the rule.

| Root grouping             | The page the user lands on           |
| ------------------------- | ------------------------------------ |
| `CERT_AUTH`               | `/idp/auth-cert`                     |
| `KERB_AUTH`               | — (resolved by the gateway, no page) |
| `PUSH_AUTH`               | `/idp/auth-push`                     |
| `SMS_AUTH`                | `/idp/auth-otp`                      |
| `VOICE_AUTH`              | `/idp/auth-otp`                      |
| `EMAIL_AUTH`              | `/idp/auth-otp`                      |
| `TOTP_AUTH`               | `/idp/auth-totp`                     |
| `WEB_AUTH` (FIDO)         | `/idp/auth-web`                      |
| `CHALLENGE_RESPONSE_AUTH` | `/idp/auth-questions`                |
| `CRIIPTO_AUTH`            | `/idp/auth-criipto`                  |

The full catalogue of levels, their display names, their pages and their `REQUIRES_AUTHENTICATION` values is in [Authentication levels, rules and capability flags](/administration-guide/security/authentication-providers-and-mfa/authentication-provider-reference.md#the-authentication-step-types). Open `STEPUP_AUTH_RULE` on **Policy → Authentication Rule** to see how many roots your release actually has.

The eight paths it protects out of the box are all password-changing paths — the case where a live session is not by itself good enough to let somebody set a new credential.

**These are URI pattern values, not calls for you to make.** Every one of them appears as a row in a content provider's pattern list at `/webconsole/contentProviders`, and that list is where you read them and where you would add one of your own. They are printed here so you can recognise them on that screen and so you can copy the pairing:

| URI pattern                                            | What it is         |                                                                       |
| ------------------------------------------------------ | ------------------ | --------------------------------------------------------------------- |
| `/idp/changePasswordManagedSystem`                     | Screen             | Change a managed-system password                                      |
| `/idp/rest/api/prov/changePasswordForManagedSystem`    | The path behind it | Where that screen's buttons submit                                    |
| `/selfservice/changePasswordManagedSystem`             | Screen             | The same, in SelfService — added to upgraded systems as of 2026.7.1.0 |
| `/selfservice/password-reset-screen`                   | Screen             | Reset another identity's password                                     |
| `/selfservice/rest/api/prov/resetPassword`             | The path behind it | Where that screen's buttons submit                                    |
| `/selfservice/bulk-password-reset`                     | Screen             | Bulk reset                                                            |
| `/selfservice/rest/api/prov/reset-bulk-password`       | The path behind it | Where that screen's buttons submit                                    |
| `/selfservice/rest/api/prov/admin-reset-bulk-password` | The path behind it | The administrative bulk reset                                         |

Note that the screens **and the paths behind them** are both patterns. That pairing is the lesson worth copying — protecting a screen and leaving the path behind it unprotected protects nothing, because anything that can reach the second one never meets the first. When you protect a screen of your own, add a pattern for what its buttons submit to as well.

Confirm the bindings on your own system at `/webconsole/contentProviders` → your provider → the pattern list: all eight patterns should show `STEPUP_AUTH_RULE` in **Authentication rules**.

## What the user experiences

| Situation                                                                      | What happens                                                                                                                                                                                                                                                                                                           |
| ------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Exactly one demanded level is outstanding                                      | The browser is redirected straight to that method's page, carrying `login=` and the postback parameter, and returns to the original URL afterwards                                                                                                                                                                     |
| More than one is outstanding                                                   | The browser goes to `/idp/auth-select` — *"Choose how you would like to verify your identity"* — and picks; satisfying one path through the rule to `AUTHORIZATION_ACCEPT` is enough                                                                                                                                   |
| The demanded level is already approved and unexpired                           | Nothing. The request proceeds; the user never sees a prompt                                                                                                                                                                                                                                                            |
| The rule path reaches `AUTHORIZATION_DECLINE`                                  | `401`, with nothing in the log about credentials. Usually missing adaptive extra data — [Adaptive authentication](/administration-guide/security/adaptive-authentication.md)                                                                                                                                           |
| The user cannot perform the method — no TOTP secret, no security key, no phone | They are sent to the method's page and cannot complete it. See the warning below                                                                                                                                                                                                                                       |
| An OAuth or SAML request is interrupted                                        | The parked request is replayed afterwards. For SAML, an interrupted `AuthnRequest` survives a second factor but not an interstitial screen on a `/login/{randomId}` URL — [SAML identity provider](/administration-guide/federation/saml-identity-provider.md#how-an-interrupted-authnrequest-survives-authentication) |

The interruption is a redirect, not an authentication error. That matters for a screen that loads parts of itself separately: the part follows the redirect and renders a sign-in page where the rest of the screen expected data. You do not configure this and cannot turn it off; recognise it, and read `/webconsole/authenticationDebug` rather than the broken screen.

The reasons OpenIAM gives for an interruption are worth knowing, because they appear in the log and on the debug screen: *"Auth type selection required"* for the step-up choice, *"Redirect required"* for a single outstanding factor, and, for an outright refusal, *"Not authenticated"*, *"URI federation declined authentication"* or *"Session expired (max active session time exceeded)"*.

{% hint style="danger" %}
**A demanded method the user cannot perform is a lockout, not a prompt.** OpenIAM sends the user to the method's page whether or not they are enrolled; there is no "you have no second factor registered, here is another way" fallback and no automatic downgrade. A TOTP step with no enrolled secret, a FIDO step with no registered authenticator, an SMS step with no phone number on the identity — each of these puts the user in a loop between the factor page and the destination.

Two defences, and use both. Make the rule an **any-of** with at least two roots the population can actually satisfy, as `STEPUP_AUTH_RULE` does. And check enrolment coverage before you attach the rule, not after: a report of identities with no TOTP secret and no registered authenticator is the prerequisite, not the post-mortem.
{% endhint %}

## Related

* [The authentication model](/administration-guide/security/authentication-model.md) — the rule graph, how a request selects a rule, and why a pattern's rules replace a content provider's
* [Demand a different authentication on one URL](/administration-guide/gateway/protect-an-application.md#demand-a-different-authentication-on-one-url) — the binding procedure this page assumes
* [Content providers and URI patterns](/administration-guide/access/content-providers-and-uri-patterns.md#three-inheritance-rules-that-surprise-people) — why a pattern's rules replace the content provider's rather than adding to them
* [Authentication providers and MFA](/administration-guide/security/authentication-providers-and-mfa.md) — every level, its page, and how to make each factor work on its own first
* [Adaptive authentication](/administration-guide/security/adaptive-authentication.md) — demanding more only on a new device, an unfamiliar country or a privileged role
* [Terminate a user's sessions](/administration-guide/security/session-management.md) — the approved-level list, and what a session is
* [Certificate authentication](/administration-guide/security/certificate-authentication.md) — `CERT_AUTH` as a step-up factor
* [SAML identity provider](/administration-guide/federation/saml-identity-provider.md#one-sso-endpoint-per-authentication-level-loginrandomid) — one SSO endpoint per authentication level, for federating partners at different assurance
* [Change configuration safely](/administration-guide/start/change-safely.md)

*Applies to OpenIAM 2026.8.1. Last reviewed 7 August 2026.*


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.openiam.com/administration-guide/security/step-up-authentication.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
