Skip to content

Instantly share code, notes, and snippets.

@nicosabena
Created December 17, 2025 00:04
Show Gist options
  • Select an option

  • Save nicosabena/d1965eebccc2669eaf0ae2aadcb376e5 to your computer and use it in GitHub Desktop.

Select an option

Save nicosabena/d1965eebccc2669eaf0ae2aadcb376e5 to your computer and use it in GitHub Desktop.
How to request an MFA API from the browser using auth0-spa-js v2
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="https://cdn.auth0.com/js/auth0-spa-js/2.11/auth0-spa-js.production.js"></script>
<title>MFA API Test</title>
<style>
.hidden {
display: none;
}
</style>
</head>
<body>
<a id="homeurl" href="#">Home</a>
<h2>MFA API Test</h2>
<div id="loading">Initializing...</div>
<div id="buttons" class="hidden">
<h3>Log in</h3>
<div><button type="button" onclick="initialLogin()">Login</button></div>
<h3>Get MFA API token</h3>
<div><button type="button" onclick="getMFAToken()">Get MFA API token</button></div>
</div>
<hr>
<div id="logs"></div>
<script>
// Configuration
const auth0Config = {
tenantDomain: "YOUR_AUTH0_TENANT_DOMAIN", // e.g. acme.us.auth0.com
clientId: "YOUR_CLIENT_ID", // YOUR_CLIENT_ID
audience: "API_AUDIECE" // Your backend API audience (not the MFA API). Omit if not using one.
};
const thisPageUrl = "http://localhost:3000/";
// Get logs container
const logsContainer = document.querySelector('#logs');
// Logging utilities
const write = (content, isCode) => {
if (content === '---') {
logsContainer.innerHTML = `${logsContainer.innerHTML}<hr>`;
return;
}
const tag = isCode ? 'code' : 'p';
logsContainer.innerHTML = `${logsContainer.innerHTML}<${tag}>${content}</${tag}>`;
if (isCode) {
logsContainer.innerHTML += '<br>';
}
};
const writeCollapsible = (summary, details) => {
const summaryHtml = `<summary>${summary}</summary>`;
logsContainer.innerHTML = `${logsContainer.innerHTML}<details>${summaryHtml}<pre>${details}</pre></details>`;
};
let auth0Client;
const init = async () => {
auth0Client = new auth0.Auth0Client({
domain: auth0Config.tenantDomain,
clientId: auth0Config.clientId,
authorizationParams: {
redirect_uri: thisPageUrl,
scope: "openid profile email",
audience: "Test"
}
});
document.getElementById('loading').classList.add('hidden');
document.getElementById('buttons').classList.remove('hidden');
};
const writeAuthDetails = async (token, operation) => {
const authenticated = await auth0Client.isAuthenticated();
if (!token) {
write(`<strong>${operation} failed</strong>`);
return;
}
write(`<strong>${operation} successful</strong>`);
write('Access token:');
write(token, true);
// Decode and display JWT payload if it's a JWT token
if (token.startsWith('eyJ')) {
const accessTokenPayload = JSON.parse(atob(token.split('.')[1]));
write('Access token payload:');
writeCollapsible(JSON.stringify(accessTokenPayload), JSON.stringify(accessTokenPayload, null, 4));
}
};
const writeError = (error) => {
window.err = error; // For debugging
write('<strong>Error received</strong>');
write(`Error: ${error.error}\n`, true);
write(`Message: ${error.error_description}`, true);
};
// Perform the initial login and get a token for the regular API
window.initialLogin = async () => {
try {
await auth0Client.loginWithPopup();
const token = await auth0Client.getTokenSilently();
writeAuthDetails(token, "Login");
} catch (err) {
writeError(err);
}
};
window.getMFAToken = async () => {
try {
const mfaAuthorizationParams = {
audience: `https://${auth0Config.tenantDomain}/mfa/`,
scope: 'read:authenticators'
};
// Present a popup first in case the user needs to give consent, perform an MFA challenge,
// or do any other interactive action.
// loginWithPopup (and loginWithRedirect) will store the authorization results in the cache.
// Another option would be to try getTokenSilently() and do the popup if it fails.
await auth0Client.loginWithPopup({ authorizationParams: mfaAuthorizationParams });
// Now that the interaction is finished, we can retrieve the tokens.
// At this point, getTokenSilently() will be retrieving the tokens from the cache.
// It is important that the authorizationParams here include the same audience (the MFA API identifier) and scope.
const token = await auth0Client.getTokenSilently({ authorizationParams: mfaAuthorizationParams });
writeAuthDetails(token, "MFA Token");
} catch (error) {
writeError(error);
return;
}
};
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment