MFA with Auth0 Actions (updated code)
Tags: auth0, mfa, actions
Published: 2022-01-22 23:44:49

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.  

```javascript
exports.onExecutePostLogin = async (event, api) => {
// do stuff
}
```

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

```javascript
  const enabledMfa = event.user.app_metadata.guardian;
  if (!enabledMfa) {
  // have the user enable mfa
  }
```
If the user does not have the guardian flag set tell Actions to direct them through the Guardian setup flow.
```javascript
    api.multifactor.enable("guardian");
```
Set the guardian flag so the user isn't driven through MFA every time. 

```javascript
    api.user.setAppMetadata("guardian", true);
```


The final code for setting up MFA looks like this: 

```javascript
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);
  }
};
```


## 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. 

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

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

```javascript
  if (scopes.includes("mfa:required")) {
  // push the user to MFA
  }
```

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

```javascript
      api.multifactor.enable("guardian");
```
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.

```javascript
    api.accessToken.setCustomClaim(`https://kleeut.com:mfaTime`, Date.now());
```

The final code for the step up MFA Action looks like this: 
```javascript
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());
  }
};
```

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](https://github.com/KleeUT/StepUpMfaExample2021).