DEV Community

Cover image for MFA with Auth0 Actions (updated code)
Klee Thomas
Klee Thomas

Posted on

MFA with Auth0 Actions (updated code)

I was alerted recently that the code examples in the previous two posts is now outdated. In May of 2021 Auth0 moved Actions to General Availability and with that came some significant changes to the Actions API.

In this post I'll go over the code that is needed to get MFA and conditional MFA working in the GA version of Auth0 Actions.

Forcing enable MFA

The fist step is to ensure that everyone logging in has MFA enabled before they can proceed.

For this we'll need the base Action structure.

exports.onExecutePostLogin = async (event, api) => {
// do stuff
}
Enter fullscreen mode Exit fullscreen mode

Inside the structure we'll need to check if the guardian enabled flag is set on the user's app_metadata.

  const enabledMfa = event.user.app_metadata.guardian;
  if (!enabledMfa) {
  // have the user enable mfa
  }
Enter fullscreen mode Exit fullscreen mode

If the user does not have the guardian flag set tell Actions to direct them through the Guardian setup flow.

    api.multifactor.enable("guardian");
Enter fullscreen mode Exit fullscreen mode

Set the guardian flag so the user isn't driven through MFA every time.

    api.user.setAppMetadata("guardian", true);
Enter fullscreen mode Exit fullscreen mode

The final code for setting up MFA looks like this:

exports.onExecutePostLogin = async (event, api) => {
  const enabledMfa = event.user.app_metadata.guardian;
  if (!enabledMfa) {
    console.log("MFA not enrolled. Enrolling user.");
    // Require the user to do an MFA.
    api.multifactor.enable("guardian");
    // Update the user's metadata to represent that they've enabled MFA.
    api.user.setAppMetadata("guardian", true);
  }
};
Enter fullscreen mode Exit fullscreen mode

MFA on client request

For the scenario in the previous posts the goal is to have the client to request that the Authentication Server pushes the user through an MFA flow.

The first thing to do is to get the scopes from the request. These scopes are on the transaction object as the requested_scopes property. Both of these could be undefined so make sure to take that into account.

function extractScopesFromEvent(event) {
  return event.transaction ? event.transaction.requested_scopes || "" : "";
}
Enter fullscreen mode Exit fullscreen mode

Once we have the scopes we need to check if they include the mfa:required scope.

  if (scopes.includes("mfa:required")) {
  // push the user to MFA
  }
Enter fullscreen mode Exit fullscreen mode

If the scope is present we need to tell the Authentication Server to push the user through MFA.

      api.multifactor.enable("guardian");
Enter fullscreen mode Exit fullscreen mode

Then we add the current timestamp in to the access token so that the server can known the last time the user did an MFA check.

    api.accessToken.setCustomClaim(`https://kleeut.com:mfaTime`, Date.now());
Enter fullscreen mode Exit fullscreen mode

The final code for the step up MFA Action looks like this:

function extractScopesFromEvent(event) {
  return event.transaction ? event.transaction.requested_scopes || "" : "";
}

exports.onExecutePostLogin = async (event, api) => {
  const scopes = extractScopesFromEvent(event);
  if (scopes.includes("mfa:required")) {
    console.log("Requiring MFA");
    // Force the user to do a Guardian MFA.
    api.multifactor.enable("guardian");
    // Set to custom claim for when the MFA is complete.
    api.accessToken.setCustomClaim(`https://kleeut.com:mfaTime`, Date.now());
  }
};
Enter fullscreen mode Exit fullscreen mode

This is the updated code for the previous posts in this series. The working code and a full example with a client and a server can be found on GitHub.

Top comments (1)

Collapse
 
gautam_g_4040323c7d4e2f53 profile image
Gautam G

Thanks!