MFA Endpoints

API reference for all multi-factor authentication endpoints. These endpoints handle MFA verification during sign-in, TOTP and SMS enrollment, backup code generation, and MFA status checks.

All endpoints require Content-Type: application/json. Endpoints marked with Bearer JWT require a valid JWT in the Authorization header. See the MFA guide for flow diagrams and integration patterns.


POST/api/proxy/mfa/verify

Verify MFA

Complete MFA verification during sign-in. Exchanges a challenge token and MFA code for a JWT and refresh token.

Request body

  • Name
    challenge
    Type
    string
    Description

    The challenge token returned by primary authentication when MFA is required.

  • Name
    code
    Type
    string
    Description

    The MFA code. A 6-digit TOTP code, 6-digit SMS code, or an 8-character backup code.

  • Name
    method
    Type
    string
    Description

    The MFA method to verify against. One of: totp, sms, or backup_codes.

Response 200

{
  "token": "eyJhbGciOiJIUzI1NiIs...",
  "refresh_token": "rt_a1b2c3d4e5f6...",
  "user": {
    "id": "user_abc123",
    "email": "jane@example.com",
    "name": "Jane Doe",
    "email_verified": true
  }
}
  • Name
    token
    Type
    string
    Description

    A short-lived JWT (5-minute expiry).

  • Name
    refresh_token
    Type
    string
    Description

    A long-lived refresh token for session management. See Session Management.

  • Name
    user
    Type
    object
    Description

    The authenticated user object.

Error responses

StatusError
400Missing or invalid fields
401Invalid challenge, expired challenge, or wrong code
404MFA method not enrolled for user
415Invalid Content-Type
429Rate limited

Rate limits

  • 5 attempts per 15 minutes per IP address

Example request

curl -X POST https://auth.example.com/api/proxy/mfa/verify \
  -H "Content-Type: application/json" \
  -d '{
    "challenge": "ch_a1b2c3d4e5f6...",
    "code": "123456",
    "method": "totp"
  }'

Example response

{
  "token": "eyJhbGciOiJIUzI1NiIs...",
  "refresh_token": "rt_a1b2c3d4e5f6...",
  "user": {
    "id": "user_abc123",
    "email": "jane@example.com",
    "name": "Jane Doe",
    "email_verified": true
  }
}

POST/api/proxy/mfa/totp/setup

TOTP setup

Generate a TOTP secret and QR code for the user to scan with their authenticator app. Requires a valid JWT.

Authentication

Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

Response 200

{
  "secret": "JBSWY3DPEHPK3PXP",
  "qr_code": "data:image/png;base64,iVBORw0KGgo...",
  "uri": "otpauth://totp/AuthGate:jane@example.com?secret=JBSWY3DPEHPK3PXP&issuer=AuthGate"
}
  • Name
    secret
    Type
    string
    Description

    The TOTP secret in Base32 encoding. Display this for manual entry if the user cannot scan the QR code.

  • Name
    qr_code
    Type
    string
    Description

    A data URI containing a PNG image of the QR code. Display this as an <img> element.

  • Name
    uri
    Type
    string
    Description

    The otpauth:// URI encoded in the QR code. Can be used to generate a custom QR code.

Error responses

StatusError
401Missing or invalid JWT
409TOTP is already set up for this user
429Rate limited

POST/api/proxy/mfa/totp/verify-setup

Verify TOTP setup

Confirm TOTP setup by verifying a code from the user's authenticator app. This activates TOTP MFA for the user. Requires a valid JWT.

Authentication

Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

Request body

  • Name
    code
    Type
    string
    Description

    A 6-digit TOTP code from the user's authenticator app.

Response 200

{
  "success": true
}

Error responses

StatusError
400Missing or invalid code format
401Missing or invalid JWT, or incorrect TOTP code
404No pending TOTP setup found (call setup first)
415Invalid Content-Type
429Rate limited

Example request

curl -X POST https://auth.example.com/api/proxy/mfa/totp/verify-setup \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
  -H "Content-Type: application/json" \
  -d '{"code": "123456"}'

Example response

{
  "success": true
}

DELETE/api/proxy/mfa/totp

Remove TOTP

Remove TOTP MFA from the user's account. Requires a valid JWT and a current TOTP code for confirmation.

Authentication

Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

Request body

  • Name
    code
    Type
    string
    Description

    A current 6-digit TOTP code to confirm the removal.

Response 200

{
  "success": true
}

Error responses

StatusError
400Missing or invalid code format
401Missing or invalid JWT, or incorrect TOTP code
403Cannot remove TOTP when MFA policy is "required" and no other method is enrolled
404TOTP is not set up for this user
415Invalid Content-Type
429Rate limited

POST/api/proxy/mfa/sms/enable

Enable SMS MFA

Enable SMS as an MFA method for the user. The user must have a verified phone number on their account. Requires a valid JWT.

Authentication

Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

Response 200

{
  "success": true
}

Error responses

StatusError
400User has no verified phone number
401Missing or invalid JWT
409SMS MFA is already enabled for this user
429Rate limited

DELETE/api/proxy/mfa/sms

Remove SMS MFA

Remove SMS as an MFA method from the user's account. Requires a valid JWT.

Authentication

Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

Response 200

{
  "success": true
}

Error responses

StatusError
401Missing or invalid JWT
403Cannot remove SMS when MFA policy is "required" and no other method is enrolled
404SMS MFA is not enabled for this user
429Rate limited

POST/api/proxy/mfa/sms/send-code

Send SMS code

Send an MFA verification code via SMS during the challenge flow. This endpoint does not require a JWT — it uses the challenge token to identify the user.

Request body

  • Name
    challenge
    Type
    string
    Description

    The challenge token returned by primary authentication.

Response 200

{
  "success": true
}

Error responses

StatusError
400Missing challenge in request body
401Invalid or expired challenge token
404SMS MFA is not enabled for this user
415Invalid Content-Type
429Rate limited
500SMS delivery failed

POST/api/proxy/mfa/backup-codes/generate

Generate backup codes

Generate a new set of 10 backup codes. Any existing backup codes are invalidated. Requires a valid JWT.

Authentication

Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

Response 200

{
  "codes": [
    "a1b2c3d4",
    "e5f6g7h8",
    "i9j0k1l2",
    "m3n4o5p6",
    "q7r8s9t0",
    "u1v2w3x4",
    "y5z6a7b8",
    "c9d0e1f2",
    "g3h4i5j6",
    "k7l8m9n0"
  ]
}
  • Name
    codes
    Type
    string[]
    Description

    An array of 10 single-use backup codes. Each code is 8 alphanumeric characters.

Error responses

StatusError
401Missing or invalid JWT
403User must have at least one other MFA method enrolled before generating backup codes
429Rate limited

Example request

curl -X POST https://auth.example.com/api/proxy/mfa/backup-codes/generate \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

Example response

{
  "codes": [
    "a1b2c3d4",
    "e5f6g7h8",
    "i9j0k1l2",
    "m3n4o5p6",
    "q7r8s9t0",
    "u1v2w3x4",
    "y5z6a7b8",
    "c9d0e1f2",
    "g3h4i5j6",
    "k7l8m9n0"
  ]
}

GET/api/proxy/mfa/status

MFA status

Check the current MFA enrollment status for the authenticated user. Requires a valid JWT.

Authentication

Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

Response 200

{
  "enrolled": true,
  "methods": ["totp", "sms", "backup_codes"],
  "required": true
}
  • Name
    enrolled
    Type
    boolean
    Description

    Whether the user has any MFA method enrolled.

  • Name
    methods
    Type
    string[]
    Description

    The MFA methods currently enrolled. Possible values: totp, sms, backup_codes.

  • Name
    required
    Type
    boolean
    Description

    Whether the project's MFA policy requires MFA for all users.

Error responses

StatusError
401Missing or invalid JWT
429Rate limited

Example request

curl https://auth.example.com/api/proxy/mfa/status \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

Example response

{
  "enrolled": true,
  "methods": ["totp", "backup_codes"],
  "required": false
}

Was this page helpful?