Skip to main content
Sensitive actions — deleting important data, changing payment details, granting someone access, approving a payment — deserve a fresh confirmation even from a user who is already signed in. Veriqa does that with one tap in a messaging app instead of an SMS code. There are two ways to do it. Signing in again fits when you need to know the same person is still at the screen. Confirming the action fits when the person must see exactly what they confirm, such as the amount and recipient of a payment.

What you send

A new authorize request before the action itself, the same one you send for sign-in:

What you check

The auth_time claim in the id_token — the moment the confirmation completed. Freshness is your call: compare auth_time with the current time and decide whether it fits the window this operation deserves.
Do not trust your own cookie. The evidence of a fresh confirmation is auth_time from the new id_token, not the presence of a session in your application: a session only says the person signed in at some point.
The core does not read max_age. Sending the standard OIDC max_age=0 does no harm, but it does not affect Veriqa’s behavior — do not build logic on it. There is one freshness mechanism here: a new authorize plus the auth_time check.

Why this works

There is nothing to bypass: the issuer keeps no long-lived session. The only cookie between callback and authorize is single-use and is cleared as soon as the code is issued, so every fresh authorize request asks for confirmation in the channel again.

Going stronger: two different channels

One sensitive operation can be confirmed in two different trusted channels — WhatsApp plus email, for instance. To narrow a specific request to one channel, use acr_values:
Multiple values are separated by spaces, and the user then picks from the ones listed.

Enriching the profile along the way

A confirmation is a convenient moment to collect what you are missing: request scope=email or phone and the application receives a verified contact during the confirmation itself — no separate form, no separate verification. The exact claims depend on the channel.

Confirming a specific action

When the person must see what they confirm, your backend creates a server-to-server transaction: POST /api/transaction/confirmation with a declared action type and its slot values. The person gets a message in their messaging app that states the action, such as “Approve a payment of 42.00 EUR to ACME Ltd?”, and confirms it. To accept a confirmation only from the account owner, the backend sends expected_identities and checks matched_type in the result. The browser is not involved: the backend gets a token with client credentials and waits for the transaction outcome. The samples/dotnet/inproc/step-up sample is ready to run, and the others are on the Step-up, second factor, human-in-the-loop page. Fields and errors are in the Server-to-server confirmation API reference.