Auth0 provides a built-in enrollment and authentication flow using Universal Login. However, if you want to create your own user interface, you can use the MFA API to accomplish it. You can enroll and challenge users using push notifications with the Guardian application or SDK using the MFA API.

Prerequisites

Before you can use the MFA APIs, you’ll need to enable the MFA grant type for your application. Go to Auth0 Dashboard > Applications > Advanced Settings > Grant Types and select MFA.

Enroll with push

Get MFA token

Depending on when you are triggering enrollment, you can obtain an for using the MFA API in different ways:

Enroll authenticator

Make a POST request to the MFA Associate endpoint to enroll the user’s authenticator. The bearer token required by this endpoint is the MFA token obtained in the previous step. To enroll with push, set the authenticator_types parameter to [oob] and the oob_channels parameter to [auth0].
curl --request POST \
  --url 'https://{yourDomain}/mfa/associate' \
  --header 'authorization: Bearer MFA_TOKEN' \
  --header 'content-type: application/json' \
  --data '{ "authenticator_types": ["oob"], "oob_channels": ["auth0"] }'
If successful, you receive a response like this:
{
    "authenticator_type": "oob",
    "barcode_uri": "otpauth://totp/tenant:user?enrollment_tx_id=qfjn2eiNYSjU3xID7dBYeCBSrdREWJPY&base_url=tenan",
    "recovery_codes": [
        "ALKE6EJZ4853BJYLM2DM2WU7"
    ],
    "oob_channel": "auth0",
    "oob_code": "Fe26.2...SYAg"
}
If you get a User is already enrolled error, the user already has an MFA factor enrolled. Before associating another factor with the user, you must challenge the user with the existing factor. If this is the first time the user is associating an authenticator, you’ll notice the response includes recovery_codes. Recovery codes are used to access the user’s account in the event that they lose access to the account or device used for their second-factor authentication. These are one-time usable codes, and new ones are generated as necessary.

Confirm push enrollment

To confirm the enrollment, the end user will need to scan a QR code with the barcode_uri in the Guardian application, within the next 5 minutes. Once that is done, the Guardian application will notify Auth0 that the user enrolled successfully. To know if that happened, poll the Auth0 with the oob_code returned by the MFA Associate endpoint call.
curl --request POST \
  --url 'https://{yourDomain}/oauth/token' \
  --header 'authorization: Bearer {mfaToken}' \
  --header 'content-type: application/x-www-form-urlencoded' \
  --data grant_type=http://auth0.com/oauth/grant-type/mfa-oob \
  --data 'client_id={yourClientId}' \
  --data 'client_secret={yourClientSecret}' \
  --data 'mfa_token={mfaToken}' \
  --data 'oob_code={oobCode}'
If the user has not scanned the code, it will return an authorization_pending response, indicating that you need to call oauth_token again in a few seconds:
{
    "error": "authorization_pending",
    "error_description": "Authorization pending: please repeat the request in a few seconds."
}
If the call was successful, you’ll receive a response in the following format, containing the access token:
{
  "id_token": "eyJ...i",
  "access_token": "eyJ...i",
  "expires_in": 600,
  "scope": "openid profile",
  "token_type": "Bearer"
}
At this point, the authenticator is fully associated and ready to be used, and you have the authentication tokens for the user. You can check at any point to verify whether an authenticator has been confirmed by calling the MFA Authenticators endpoint. If the authenticator is confirmed, the value returned for active is true.

Challenge with push

Get MFA token

Get an MFA token following the steps described in Authenticate With Resource Owner Password Grant and MFA.

Retrieve enrolled authenticators

To challenge the user, you need the authenticator_id for the factor you want to challenge. You can list all enrolled authenticators using the MFA Authenticators endpoint:
curl --request GET \
  --url 'https://{yourDomain}/mfa/authenticators' \
  --header 'authorization: Bearer MFA_TOKEN' \
  --header 'content-type: application/json'
You will get a list of authenticators with the following format:
[
    {
        "id": "recovery-code|dev_Ahb2Tb0ujX3w7ilC",
        "authenticator_type": "recovery-code",
        "active": true
    },
    {
        "id": "push|dev_ZUla9SQ6tAIHSz6y",
        "authenticator_type": "oob",
        "active": true,
        "oob_channel": "auth0",
        "name": "user's device name"
    },
    {
        "id": "totp|dev_gJ6Y6vpSrjnKeT67",
        "authenticator_type": "otp",
        "active": true
    }
]
When users enroll with push, they also get enrolled in OTP, as Guardian supports challenging with OTP for scenarios where the user does not have connectivity.

Challenge user with push

To trigger a push challenge, POST to the MFA Challenge endpoint using the corresponding authenticator_id and the mfa_token.
curl --request POST \
  --url 'https://{yourDomain}/mfa/challenge' \
  --data '{ "client_id": "{yourClientId}",  "client_secret": "{yourClientSecret", "challenge_type": "oob", "authenticator_id": "push|dev_ZUla9SQ6tAIHSz6y", "mfa_token": "{mfaToken}" }'

Complete authentication using received code

If success, you receive the following response:
{
    "challenge_type": "oob",
    "oob_code": "Fe26...jGco"
}
Your application must start polling the OAuth0 Token endpoint until the user accepts the push notification.
curl --request POST \
  --url 'https://{yourDomain}/oauth/token' \
  --header 'content-type: application/x-www-form-urlencoded' \
  --data grant_type=http://auth0.com/oauth/grant-type/mfa-oob \
  --data 'client_id={yourClientId}' \
  --data 'client_secret={yourClientSecret}' \
  --data 'mfa_token={mfaToken}' \
  --data 'oob_code={oobCode}'
The call can return one of the following results:
ResultDescription
authorization_pendingError: If the challenge has not been accepted or rejected.
slow_downError: If the polling is too frequent.
access_token and refresh_tokenIf the challenge has been accepted; polling should be stopped at this point.
invalid_grantError: If the challenge has been rejected: polling should be stopped at this point.

Learn more