You can use the linked APIs, and that’s the option most people find first. The step-up flow in the API docs works, but means your app owns the challenge. It has to start it, collect the code, and handle the result. That’s a lot for what is basically “make the user log in again because they are about to do a sensitive thing.”
There’s another way that’s a little different, and it leverages login. Since version 1.60 the authorize endpoint supports the standard OIDC prompt and max_age parameters.
When your app hits a sensitive action, don't call any specific APIs. Instead, it can redirect the user to /oauth2/authorize with prompt=login and/or with a max_age small enough that their existing SSO session doesn’t satisfy it. FusionAuth shows the hosted login page, the user authenticates, and you get a fresh token back. Same code you already wrote for login, with one query parameter added.
Make sure you put the current page/location in the state so you can deliver the customer back to where they were. Here's a video on deep linking.
Using either max_age=0 and prompt=login end up in roughly the same place. I’d use max_age if you want a window like “re-auth if it’s been more than five minutes,” and prompt=login if you want it every time.
A successful redirect doesn’t prove everything. Your app still has to decide whether the token it’s looking at came from a recent authentication.
That’s what the auth_time claim is for. It’s a reserved claim in the JWT body (it moved there in 1.60, and lambdas can’t override it), so your resource server compares it against the clock and rejects beyond your threshold. If it’s too old, send the user back through authorize.
That’s basically the max_age half of RFC 9470, just without FusionAuth issuing the WWW-Authenticate challenge for you. You’d write that 401 yourself.
Caveats:.
This forces a re-login, not necessarily a stronger one. If the user re-authenticates with a password and nothing else, you’ve proven recency and nothing about strength. If you need the second factor on that re-auth, you need MFA to actually fire. A static policy of Required will do it. On Enterprise, the
MFA Requirement Lambda can make the decision per request.
There’s no acr-style “this was step-up” claim. You get “authenticated at time T,” not “authenticated with a second factor at time T.” If your auditors need the latter, this approach won’t give it to you cleanly, and falling back to the step-up auth API is a better option.
It’s a full redirect. For a banking-style “confirm this transfer” flow where bouncing the user to another page is a non-starter, the step-up API approach is still right.
This only works when you are using the hosted login pages, not the Login API.
The short version: if sensitive actions are coarse-grained (“view the billing page,” “change the email address”), the redirect approach might work for you. It’s less code, the hosted pages handle the UI, and you’re not maintaining a custom MFA challenge.
But if you need per-operation binding or proof of which factor was used, look at the step-up API.