Skip to main content

Auth0

This guide will demonstrate how to integrate the Authsignal Prebuilt MFA page with Auth0 by creating a Custom Action for the Login / Post-Login trigger.

The end result will be to allow users to complete an MFA challenge after they login with their username and password, redirecting them from the Auth0 Universal Login Page to the Authsignal Prebuilt MFA page.

The integration follows Auth0’s official guidelines for redirecting users after login to do custom MFA. You can learn more here about how to Redirect with Actions in Auth0.

User flow

  1. The user enters their username and password in the Auth0 Universal Login page.
Auth0 login screen
  1. The user is redirected to the Authsignal Prebuilt MFA page to complete an MFA challenge.
Authsignal Prebuilt MFA page challenge screen
  1. The user is logged in if the challenge is completed successfully.

Integration steps

1. Create an action

In the Auth0 Dashboard, go to Actions → Library and select “Build Custom”. Then select the “Login / Post Login” trigger and give the action an appropriate name (for example “post-login-mfa”).

Screenshot of creating a custom action in Auth0

2. Add your secret

Now your action has been created, create a new secret called AUTHSIGNAL_SECRET and provide the value of your secret from the Api Keys section in the Authsignal Portal.

Screenshot of defining the AUTHSIGNAL_SECRET in Auth0

3. Add dependencies

Add the @authsignal/node dependency.

Screenshot of adding the Authsignal Node SDK as a dependency in Auth0

4. Add the action code

Add the following code snippet to the action.

const { handleAuth0ExecutePostLogin, handleAuth0ContinuePostLogin } = require("@authsignal/node");

exports.onExecutePostLogin = handleAuth0ExecutePostLogin;

exports.onContinuePostLogin = handleAuth0ContinuePostLogin;

5. Connect your action to the Login flow

Now connect your action in the Flows section of the Auth0 Dashboard by dragging it into the Login flow.

Auth0 login screen

6. Configure the action in Authsignal

Once the previous steps have been completed, the next time the Auth0 action is run you will see an action appear in the Authsignal Portal called “Auth0 Login”. You will need to set the default outcome for this action to CHALLENGE and save it.

Screenshot of setting the default action outcome in Auth0

That’s it! You’ve done everything that’s required to redirect users to the Authsignal Prebuilt MFA page on login.

Advanced options

Opting out for social or federated logins

You may want to avoid redirecting users to the Authsignal Prebuilt MFA page if they are using an external social login provider, for example Google or Facebook. You can easily do this by checking the connection on the event object.

const { handleAuth0ExecutePostLogin, handleAuth0ContinuePostLogin } = require("@authsignal/node");

/**
* @param {Event} event
* @param {PostLoginAPI} api
*/
exports.onExecutePostLogin = async (event, api) => {
// Only redirect for auth0 database connections, not social or federated
if (event.connection.strategy === "auth0") {
await handleAuth0ExecutePostLogin(event, api);
}
};

exports.onContinuePostLogin = handleAuth0ContinuePostLogin;

Using the server client directly

To allow for more customization and control (for example, requiring MFA only for some users based on rules and conditions) you can also use the Authsignal Server API directly from within your Auth0 Custom Action.

const { Authsignal, UserActionState } = require("@authsignal/node");

/**
* @param {Event} event
* @param {PostLoginAPI} api
*/
exports.onExecutePostLogin = async (event, api) => {
// Redirects are not possible for refresh token exchange
// https://auth0.com/docs/customize/actions/flows-and-triggers/login-flow/redirect-with-actions#refresh-tokens
if (event.transaction?.protocol === "oauth2-refresh-token") {
return;
}

const secret = event.secrets.AUTHSIGNAL_SECRET;
const userId = event.user.user_id;
const redirectUrl = `https://${event.request.hostname}/continue`;

const authsignal = new Authsignal({ secret });

const { state, url, isEnrolled } = await authsignal.track({
action: "auth0-login",
userId,
redirectUrl,
// optional
email: event.user.email,
ipAddress: event.request.ip,
userAgent: event.request.user_agent,
});

// Redirect to MFA page if a challenge is required
// Also redirect users not yet enrolled so they can enroll
if (state === "CHALLENGE_REQUIRED" || !isEnrolled) {
api.redirect.sendUserTo(url);
}
};

/**
* @param {Event} event
* @param {PostLoginAPI} api
*/
exports.onContinuePostLogin = async (event, api) => {
const secret = event.secrets.AUTHSIGNAL_SECRET;
const userId = event.user.user_id;

const authsignal = new Authsignal({ secret });

const payload = api.redirect.validateToken({
secret,
tokenParameterName: "token",
});

const result = await authsignal.getAction({
action: "auth0-login",
userId,
idempotencyKey: payload.other.idempotencyKey,
});

if (result && result.state !== "CHALLENGE_SUCCEEDED") {
api.access.deny("Access denied");
}
};