Additional authentication assurance without using step-up MFA
-
I need the user to prove they’re still the user before a sensitive action. Do I really have to wire up the Two-Factor APIs to use step-up MFA auth to do that?
-
You can use the linked APIs, and that’s the path most people find first. The step-up flow in the API docs works, but it means your app owns the challenge: start it, collect the code, handle the result. That’s a lot of surface area 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 than what people might expect, and it uses what you probably already have built. Since version 1.60 the
authorizeendpoint supports the standard OIDCpromptandmax_ageparameters.When your app hits a sensitive action, it doesn’t call any specific APIs. Instead, it can redirect the user to /oauth2/authorize with
prompt=loginand/or with amax_agesmall 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, plus one query parameter.max_age=0andprompt=loginend up in roughly the same place. I’d usemax_ageif you want a window like “re-auth if it’s been more than five minutes,” andprompt=loginif you want it every time.A successful redirect doesn’t prove anything by itself, because your API still has to decide whether the token it’s looking at came from a recent authentication. That’s what the
auth_timeclaim 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 anything older than your threshold. If it’s too old, send the user back throughauthorize. That’s basically themax_agehalf of RFC 9470, just without FusionAuth issuing theWWW-Authenticatechallenge for you. You’d write that 401 yourself.Now the 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 path. - 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 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”), try the redirect approach first. It’s less code, the hosted pages handle the UI, and you’re not maintaining a custom MFA challenge. If you need per-operation binding or proof of which factor was used, you’re back in the APIs.
-
D dan has marked this topic as solved