Skip to content

Multi-factor authentication

Ask for a second factor after the first: a code by e-mail, an approval in the Authio app or a passkey, and step up when your app needs a stronger sign-in.

In Authio, multi-factor is not a switch on the account: it comes from the sign-in steps each client asks for. Put a second-factor step after the first one and the hosted login asks for it, skipping any factor the sign-in already presented.

This page covers the three second factors, when each one is asked, the Authio Authenticator app that answers push approvals, what to do when a phone is lost, and how your app can ask for a stronger sign-in.

The second factors

FactorStepWhat the person doesRecorded in amr
One-time code by e-mailmfaTypes the 6-digit code sent to the account’s e-mail address.otp
Push approvalpushTaps, in the Authio app, the number shown on the sign-in screen.mca, plus user when the phone verified the person
PasskeypasskeyUnlocks a passkey with the device’s fingerprint, face or screen lock.swk, plus user when the device verified the person
There is no SMS step. The Authio app can also hold time-based codes (TOTP) for other services, but those codes are not a sign-in step of the hosted login.

When the second factor is asked

The client’s steps decide, in the order they are listed. The first step that can answer the factor still owed is the one shown:

StepAsked of
mfaEvery sign-in that reaches it. The step cannot be Optional: a code that may be skipped is no factor at all.
push, PreferredAccounts with a paired phone. Others are offered to pair one, unless registration is Never.
push, RequiredEvery account. One without a phone has to pair before continuing, so registration has to allow it.
passkey, PreferredAccounts that already registered a passkey. See Passkeys.
passkey, RequiredEvery account.
Any step, OptionalNobody, unless an authorization request asks for a higher level with acr_values (see step-up).

A sign-in through an identity provider counts as one factor, whatever the provider did on its side, and the client’s second-factor steps apply after it. Single sign-on into another client is only offered when nothing that client requires is missing.

One-time code by e-mail

After the first factor, Authio sends a 6-digit code to the account’s e-mail address and the screen shows six digit boxes, filled by the browser’s one-time-code autofill where it is available. Didn’t get the code? Resend it sends a new one.

  • The same code is delivered to every phone paired with the account, where the Authio app shows it as Your sign-in code. The code never travels in the notification itself: the app fetches it over an authenticated connection while it is valid.
  • A wrong code counts toward the realm’s lockout and is recorded in the access log.
  • After a correct code the account is checked again — status, suspension, access to the client, sign-in address — before the session opens.

Push approval

Push approval uses the Authio Authenticator app for iOS and Android, called “the Authio app” on the sign-in screens. It defends against push fatigue — someone who has the password sending requests until the victim approves one to make the phone stop — with number matching:

  1. The screen shows a number

    After the first factor, the sign-in screen shows Tap this number in the Authio app with a two-digit number, and waits.

  2. Every paired phone is asked

    Each phone of the account receives a notification. The app shows the application, the IP address and browser of the attempt, and three numbers.

  3. The person taps the matching number

    Tapping the number is the approval. The app asks for biometrics or the screen lock when the phone has one, then signs the answer with a key created when it was paired. It wasn’t me denies the request.

  4. The sign-in continues by itself

    The screen sees the answer and carries on. Authio re-reads the approved request on the server before completing the sign-in; nothing the browser sends is taken as proof.

RuleValue
A request is valid for2 minutes
Numbers offered on the phoneThree distinct numbers from 10 to 99
Tapping the wrong numberDenies the request, with no second try
Requests per accountAt most 10 in 5 minutes, counted per account rather than per IP
Several paired phonesAll are asked with the same number; the first answer decides
ApprovalAccepted only with a valid signature from the phone’s own key

While waiting, the screen offers Ask my phone again, which sends a new request with a new number, and Use another way to sign in whenever another of the client’s steps can answer, such as the e-mail code. A notification that never arrives does not block the sign-in: the app also finds pending requests when it is opened.

Pairing a phone

Pairing is always started by the account holder. There are two places to do it:

  • During sign-in, when the push step offers or requires registration: the screen shows Set up your phone with a QR code to scan in the app, and Can’t scan it? reveals the same content as a line to paste. With registration Offer, Set it up later skips it.
  • From the account pages, on the Authio app tab, with Show the pairing code. See Account self-service.

A pairing code works once, for 5 minutes, and showing a new one retires the previous one. An account can have up to 5 active phones.

Lost phone or no access to the factor

  • On the sign-in screen, Use another way to sign in moves to the e-mail code when the client also has the mfa step.
  • When a passkey is owed and is not on the device, the screen offers to send a code by e-mail instead. See Passkeys.
  • The person removes a lost phone from the Authio app tab of the account pages, or from another paired phone. A removed phone stops approving anything at once.
  • An administrator removes it from the user’s Credentials tab in Users, or from Push Authenticator.

Phones are revoked, never deleted, so the history of what each one approved stays readable. Using a revoked phone again means pairing it again.

Step-up from your app

The ID token says how the person signed in: amr lists the methods, acr sums them up as a level, and auth_time says when. A single sign-on reports the original sign-in, never a fresh one.

acrMeaningExample
1One factorA password; a sign-in through Google
2More than one factorPassword and e-mail code; Google and a push approval
3Multi-factor where one factor resists phishingA passkey verified by the device, alone or after a password

When an action needs more than the current session proves, send the person through the authorization endpoint again with what you need:

  • acr_values=2 or acr_values=3 asks for a level. When the client’s own steps would not reach it, one Optional second-factor step the account can answer is asked as if it were Preferred.
  • max_age=300 asks for a sign-in no older than five minutes.
  • prompt=login asks for the credentials again whatever the session.
Step-up requestPlain text
https://authio.api.hyperio.tech/realms/acme/protocol/openid-connect/auth
  ?client_id=acme-web
  &response_type=code
  &redirect_uri=http://localhost:3000/api/auth/callback/authio
  &scope=openid profile email
  &state=STATE
  &code_challenge=CODE_CHALLENGE
  &code_challenge_method=S256
  &acr_values=2
  &max_age=300

acr_values never refuses a sign-in

The request is best effort, as OpenID Connect defines it. A level the client or the account cannot reach is not enforced: the sign-in completes with what it proved and the token states the level reached. The check is yours to make.
step-up.tsTypeScript
interface SignInClaims {
  acr?: string
  auth_time?: number
}

export function isStrongEnough(
  claims: SignInClaims,
  minimumAcr: number,
  maxAgeSeconds: number,
): boolean {
  const level = Number(claims.acr ?? '0')
  const ageSeconds = Math.floor(Date.now() / 1000) - (claims.auth_time ?? 0)

  return level >= minimumAcr && ageSeconds <= maxAgeSeconds
}

Watching push approval

Push Authenticator in the console shows, for a realm or all of them, every request sent to a phone and how it ended — approved, denied, unanswered or never delivered — with response times, the most active applications and the paired phones, including revoked ones and phones that cannot be notified. Requests that never reached a phone are highlighted, because neither the person nor the administrator would notice otherwise.

Next steps