Two-Factor Authentication via OXAPI

As of V. 4.0, accounts with two-factor authentication (2FA) enabled can also authenticate via OXAPI. Previously, 2FA and OXAPI were mutually exclusive. This page is aimed at developers connecting a headless frontend via OXAPI.

The functional basics of 2FA (shop-wide activation, activation by the customer, code validity, failed attempts) are described under Two-Factor Authentication and apply to OXAPI unchanged.

Scope

OXAPI 2FA applies to every account that authenticates via OXAPI — regardless of user rights. Even an admin user account with 2FA enabled goes through the challenge/OTP flow on OXAPI login, just like a customer account. The “admin backend” exception mentioned under Two-Factor Authentication concerns only the classic shop administration (/admin), not OXAPI.

Background

For an account with 2FA enabled, the standard OXAPI login (token / login queries from graphql-base) does not yet return a full access token, but first a challenge token. This token identifies the ongoing 2FA process (claims mfa_pending and mfa_exp) and is not issued together with a refresh token. Only after the one-time code (OTP) sent by email has been entered successfully does the client exchange the challenge token for a regular access token.

Without 2FA enabled, login behaves unchanged and returns a regular access token directly.

Login flow

Procedure

  1. Log in. The client calls the standard login (token/login) with username and password. For an account with 2FA enabled, the shop sends a 6-digit code to the stored email address and returns a challenge token.

  2. Use the challenge token. The client sends the challenge token as Authorization: Bearer <token> on all subsequent 2FA calls.

  3. Verify the code. The client passes the entered code to verifyTwoFactorToken or verifyTwoFactorLogin and, on success, receives a regular access token. The challenge token is consumed and cannot be reused.

  4. Optional: resend the code. If the code did not arrive, the client requests a new one with resendTwoFactorOtp.

Mutations

verifyTwoFactorToken

Verifies the code against the ongoing 2FA process and, on success, returns a regular access token (without a refresh token). Requires the challenge token as the bearer token.

mutation {
  verifyTwoFactorToken(otp: "123456")
}

verifyTwoFactorLogin

Like verifyTwoFactorToken, but additionally returns a refresh token — i.e. the same result as a regular login. Requires the challenge token as the bearer token.

mutation {
  verifyTwoFactorLogin(otp: "123456") {
    accessToken
    refreshToken
  }
}

resendTwoFactorOtp

Sends a new one-time code for the ongoing process. Requires the challenge token as the bearer token. The waiting period between two dispatches (60 seconds) applies here too; if it is not met, the mutation responds with an error.

mutation {
  resendTwoFactorOtp
}

setTwoFactorAuth

Enables or disables the 2FA preference of the logged-in customer. This mutation requires a regular access token (not the challenge token) and always acts on the logged-in user’s own account. The return value is the actually stored state.

mutation {
  setTwoFactorAuth(enabled: true)
}

Challenge token lifetime

The challenge token is valid for the smaller of the two values “API 2FA challenge lifetime” and “OTP code lifetime”. This ensures a code can never be redeemed against an already expired challenge token. Both values are maintained in the module settings.

Note

A code resent via resendTwoFactorOtp does not extend the challenge token’s validity. A code requested late can therefore expire before it can be redeemed — in that case the login must be restarted.

Error behavior

Situation

Message

Invalid or expired code

Invalid or expired two-factor code

Code resend requested too early (waiting period)

Please wait before requesting a new two-factor code

No valid challenge token (missing, expired, or no 2FA process)

Not a two-factor challenge token / Two-factor challenge has expired

Prerequisites

  • graphql-base is installed and activated. Without graphql-base, the token-issuing 2FA mutations are not available.