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
}
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
}
If the user does not have the guardian flag set tell Actions to direct them through the Guardian setup flow.
api.multifactor.enable("guardian");
Set the guardian flag so the user isn't driven through MFA every time.
api.user.setAppMetadata("guardian", true);
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);
}
};
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 || "" : "";
}
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
}
If the scope is present we need to tell the Authentication Server to push the user through MFA.
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.
api.accessToken.setCustomClaim(`https://kleeut.com:mfaTime`, Date.now());
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());
}
};
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)
Thanks!