Skip to content
downpipes docs

Re-authenticate for a sensitive action

Holding the right capability is not always enough. A defined set of the engine’s routes want a fresh proof that you are still at the keyboard before they will act, so a browser left signed in on an unattended machine cannot repoint your archives, grant itself a role, or approve a dangerous change. This is step-up re-authentication, ASVS V7.5.1 and V7.5.3.

You will meet it as a prompt to re-verify part-way through an action you have already started. That is the gate working, not a session that has expired.

What is gated

Where your data goes. Set, add, edit or remove an archive destination, and change which is the default.

Where your telemetry goes. Set or remove the SIEM push destination, send a test event to it, set or remove the OTLP metrics push.

Who can sign in, and with what authority. Add, remove, enable or disable an identity-provider connection; roll over a SAML signing certificate; delete a passkey credential; register a new one; grant or revoke a role, a group-to-role mapping, or a custom role definition; revoke every sign-in factor an address holds.

That last one is the strictly larger sibling of deleting a passkey credential, and it is worth naming separately. Deleting a credential removes one. The offboarding revoke removes every credential for an address, plus their banked recovery codes, plus every invite issued to them that is still live, in a single call. It is the one route in this engine that destroys recovery codes, which cannot be re-derived, so plan an urgent offboarding around the prompt rather than meeting it mid-ceremony. See offboarding a member.

Keys. Install a key, add an operational key, rotate the break-glass key, switch to break-glass-only posture, retire the break-glass bearer token. Add the configuration recovery keys to an already-keyed engine. Re-sign a destination RUNLOG that does not verify, reset its anti-rollback floor, or relink one downpipe’s chain in it.

Recovery. Regenerate recovery codes.

Restores, retention and attestations. Approve a restore, apply a restore, apply or approve a retention prune, create an attested verification session.

Change control. Approve a pending config change; approve a pending dual-control owner action; turn the restore-approval or the update-approval policy on or off. A stale session proposing either policy change gets a step-up challenge before anything queues or changes, the same as the approve leg already needed.

Posture. Record a posture acceptance, and withdraw one. Both directions are gated, because quietly un-accepting a risk changes what the posture score reports just as surely as accepting it did.

Custody. Email a custody share to a recipient.

Detection config. Create, replace or delete a notification rule, and create, replace or delete a notification channel. Both directions are gated, and the reason is availability of evidence rather than confidentiality: a notify emission is redaction-safe, so repointing a channel steals nothing, but silencing an account is the move made before an attack rather than after it. Suppression needs no delete either, since a channel repointed at a sink nobody reads or a rule narrowed until it matches nothing goes quiet with every record still in place, so the set direction is gated as well as the delete. Setting the sign-in context policy, the owner opt-in notification for a sign-in from an unusual location, is gated in the same way.

Credential minting. Mint an owner-minted pull credential, in any of its three scopes. The route carries the same custody weight as granting a role, which is gated in the same way.

Sessions. Every way to end a session requires step-up: signing out a member’s sessions, signing out everyone, signing out your own other sessions, and signing out one of your sessions. The last two reach no session but your own, so a sign-in of any method within the last five minutes satisfies them. The engine gates them because a stale session that survived a compromise could otherwise sign the legitimate operator’s other tabs out unchallenged. The two that reach another member’s session hold the usual passkey rule.

The engine’s step-up gate is STEPUP_SUBS in engine/src/admin/router-core.ts, and a small number of routes call it directly outside that set. Treat the categories above, not a route count, as the source of truth.

Which sign-in methods are exempt, and why

A bare break-glass token is exempt. That token is all-or-nothing by design: anyone holding it already has every power the engine has, so demanding a second proof from it would add nothing while removing the one credential that works when everything else is broken.

The engine judges Cloudflare Access on its own sign-in time. An Access session has no passkey to assert with, so the engine reads the issued-at time of the verified Access assertion instead. A sign-in within the last five minutes passes. The engine refuses an older one, or an assertion with no issued-at time, with 401 { stepUpRequired: true, reauth: "access" }. The console does not open the passkey prompt for that answer: it tells you to sign in to Access again, then you retry (requireStepUp, engine/src/admin/router-core.ts; console/src/lib/errors.ts).

An Access request that carries an Origin header meets the same Origin rule as a cookie session. The Origin must equal CONSOLE_ORIGIN, or the engine answers 403. The rule covers the admin routes, the recovery-code routes and the passkey registration routes. An Access request with no Origin header, and the break-glass token, skip the check (resolveRegisterAuth, engine/src/admin/router-auth-flow.ts).

A first-party cookie session is not exempt, and that is the common case. The engine asks its scheduler whether that session was authenticated recently enough, or whether you have presented a valid single-use step-up token. The engine then consumes that token, so the same proof cannot be replayed.

What a 401 means here

On a gated route a 401 carrying stepUpRequired is the opening move, not a failure. The console catches it, runs the re-verification, and retries the original request. Nothing has gone wrong and you have not been signed out; a 401 on these routes is how the conversation starts.

That is worth knowing because it changes how you read your own audit and diagnostic records: a step-up 401 is not an authentication failure to investigate.

If you dismiss the prompt, or no prompt appears

A ceremony that does not finish leaves the original action undone. The console says which way it ended, because one of the endings needs a different remedy from the others: an operator with no passkey on their device cannot approve a prompt that never opens.

There are six endings you can be shown.

“The engine would not start the identity check.” The engine refused to open the ceremony, so no prompt was ever raised. Nothing was changed. This is the ending that is worth reading carefully, because it has two very different causes and the message reads the same for both. If it clears on a retry, it was transient. If it does not clear, and it does not clear for anybody, see the fleet-wide section below: the usual cause is a configuration gap on the engine and no amount of retrying will move it.

“That passkey was not accepted for the identity check.” A prompt opened, you answered it with a passkey, and the engine rejected the answer when it verified the assertion. Nothing was changed. Retry with a passkey you enrolled for this account, since a credential the engine does not hold for you fails at exactly this step.

“The passkey check was not completed.” The prompt was dismissed, or it timed out, or your device holds no passkey enrolled for you. Nothing was changed. Those look identical on purpose and cannot be separated: WebAuthn answers a dismissed prompt, a timed-out prompt and a device with no matching credential with a single indistinguishable error, so that no website can quietly test whether you hold a passkey. If you saw a prompt and dismissed it, retry and approve it. If no prompt appeared at all, this device has no passkey enrolled for you, and retrying will keep producing the same nothing.

“This action needs a passkey assertion and no passkey is enrolled for your account.” You signed in through an identity provider and hold no passkey, so there is nothing to assert with. Nothing was changed. A retry cannot succeed. Enrol a passkey from the sign-in screen first.

“Your browser refused the identity check.” The engine answered, but the browser refused the ceremony before it opened a prompt. Nothing was changed. The message makes no claim about which passkeys the device holds. Try another browser or device.

“The identity check could not be completed because the console did not get a usable answer from the engine.” The request to start or finish the check did not get a usable answer. Nothing changed, and your session is still active. Try again in a moment.

Getting a passkey onto a device that has none

The console’s Access and security tab lists the passkeys you already hold and lets you revoke one. You enrol a new passkey from the sign-in screen, in one of these ways:

  • With your recovery codes. Sign out and sign in with one of your banked recovery codes. While your address holds a role, the console takes you straight to passkey enrolment. A recovery code is both a way in and a way to re-enrol, which is what it is banked for. A session that starts with a recovery code does not satisfy step-up; step-up needs a passkey, either an existing passkey or one enrolled after recovery. Within five minutes of the recovery sign-in, enrolling a replacement passkey and signing out your own sessions work from the recovered session without a further assertion.
  • With an invite. Ask an owner to re-invite your address. The invite mints a single-use, email-bound registration link, and that link’s enrolment form is another entry point.

If you have no recovery code, ask an owner for an invite. See session management for where recovery codes come from and offboarding a member for what destroys them.

The gate fails closed, and what that looks like

If the engine cannot reach the component that answers the freshness question, the action is denied. Failing closed is the right choice for a control whose whole job is to stop an unattended session acting, but it produces a failure mode worth recognising.

If that component is up but broken, every gated action is refused with the same 401 stepUpRequired that a legitimate ceremony opens with. The symptom is “it keeps asking for my passkey and never accepts it”, on every sensitive action, for every user at once.

So the way to tell the two apart is the blast radius. One user, one action, and the prompt clears on a retry: that is the gate working. Every user, every gated action, and the prompt never clears: that is not your device or your passkey, and re-enrolling will not fix it. It is an engine-side fault. The engine records it as a distinct signal rather than as a run of authentication failures, and it belongs in a support bundle.

The other fleet-wide cause, and it is a one-line fix

A second fault produces the same “every gated action is refused for everybody” shape and is not a fault at all in the usual sense. A step-up ceremony has to be bound to an origin, and the engine takes that origin from its CONSOLE_ORIGIN setting. If CONSOLE_ORIGIN is not set, the engine refuses to open any ceremony and answers 501 with passkey_not_configured (engine/src/admin/router-account-session.ts), so every step-up gated action on that deployment fails, for every user, from the moment it is deployed.

Tell it apart from the fail-closed case by two things. It appears immediately on a new or reconfigured deployment rather than starting mid-life. And it travels with the setup error the console shows on ordinary reads, “Engine CONSOLE_ORIGIN is not set to this console”, because the two have the same single cause. If you are seeing that block error anywhere in the console, do not diagnose the passkey prompts separately: set CONSOLE_ORIGIN on the engine and both clear together.

One caveat on the console message while you do. The console reports this ending as the engine not starting the identity check and suggests trying again in a moment, which is the right advice for the transient version and the wrong advice here. Retrying a 501 never succeeds, because nothing about it is transient. See look up a console error message for the message as it appears and error and status codes for the 501 on the wire.

Where this fits

  • Session management covers session lifetime, which is what step-up measures freshness against.
  • Roles and capabilities covers the capability check, which runs independently: step-up never grants a permission you do not hold, it only asks you to prove you are present.

Last updated .