Documentation Index
Fetch the complete documentation index at: https://docs.dynamic.xyz/docs/llms.txt
Use this file to discover all available pages before exploring further.
Authenticator apps provide a time-based one-time password (TOTP) that can be used for authentication.
Prerequisites
- You need to have the Dynamic Client initialized.
- You need to have the authenticator apps MFA enabled in your environment’s settings in the Dynamic dashboard.
Registering a new TOTP device
Calling registerTotpMfaDevice will return a URI and a secret key for the TOTP device.
You can use the URI to display a QR code to the user, so they can scan it with their authenticator app to get a TOTP code.
Unlike passkeys, registering a TOTP device will not automatically authenticate it, so you need to call authenticateTotpMfaDevice after registering
a TOTP device for the user to complete an MFA challenge.
import { registerTotpMfaDevice } from '@dynamic-labs-sdk/client';
const register = async () => {
const { uri, secret } = await registerTotpMfaDevice();
console.log(uri, secret);
};
Doing MFA authentication with a TOTP code
Calling authenticateTotpMfaDevice will verify the TOTP code and complete the MFA challenge.
The authentication will be successful if the user enters a valid TOTP code for the registered TOTP device.
import { authenticateTotpMfaDevice } from '@dynamic-labs-sdk/client';
const onLogin = async () => {
await authenticateTotpMfaDevice({ code: '123456' });
};
With step-up authentication (recommended)
Pass requestedScopes to receive an elevated access token for sensitive actions:
import { authenticateTotpMfaDevice } from '@dynamic-labs-sdk/client';
const onExportPrivateKeyClick = async () => {
await authenticateTotpMfaDevice({
code: '123456',
requestedScopes: ['wallet:export'],
});
// Elevated token is now stored — the SDK attaches it automatically
await exportWaasPrivateKey(params);
};
With MFA token (deprecated)
createMfaTokenOptions is deprecated and will be removed in the next major version. Use requestedScopes instead. See migration guide.
import { authenticateTotpMfaDevice } from '@dynamic-labs-sdk/client';
const onExportPrivateKeyClick = async () => {
await authenticateTotpMfaDevice({
code: '123456',
createMfaTokenOptions: { singleUse: true },
});
await exportWaasPrivateKey(params);
};
Deleting a TOTP device
Calling deleteMfaDevice will delete a TOTP device associated with the authenticated user.
To delete a TOTP device, you first need to get the user to perfome an authentication challenge with that TOTP device, and then use the deleteMfaDevice function with that MFA token.
import { deleteMfaDevice } from '@dynamic-labs-sdk/client';
const delete = async () => {
// Authenticate with the TOTP device to be deleted
// Replace '123456' with the actual TOTP code the user enters
await authenticateTotpMfaDevice({
code: '123456',
createMfaTokenOptions: { singleUse: true },
});
// Get the MFA token from the dynamic client
const mfaToken = dynamicClient.mfaToken;
// Replace 'device-id' with the actual ID of the TOTP device you want to delete
// Replace 'mfa-auth-token' with the actual MFA authentication token
await deleteMfaDevice({ deviceId: 'device-id', mfaAuthToken: mfaToken });
};
Getting all registered TOTP devices for a user
Calling getMfaDevices will return all registered TOTP devices for the authenticated user.
Currently, only one TOTP device is supported per user.
import { getMfaDevices } from '@dynamic-labs-sdk/client';
const getUserMfaDevices = async () => {
const mfaDevices = await getMfaDevices();
console.log(mfaDevices);
};
React
The TOTP flow requires multi-step state: first register the device (get the URI/QR code), then verify with a code:
import { registerTotpMfaDevice, authenticateTotpMfaDevice } from '@dynamic-labs-sdk/client';
import { useState } from 'react';
function TotpEnrollment() {
const [qrUri, setQrUri] = useState('');
const [code, setCode] = useState('');
const [enrolled, setEnrolled] = useState(false);
const handleRegister = async () => {
const { uri } = await registerTotpMfaDevice();
setQrUri(uri);
};
const handleVerify = async (e) => {
e.preventDefault();
await authenticateTotpMfaDevice({ code });
setEnrolled(true);
};
if (enrolled) return <p>TOTP device enrolled successfully.</p>;
return (
<div>
{!qrUri ? (
<button onClick={handleRegister}>Set Up Authenticator App</button>
) : (
<form onSubmit={handleVerify}>
<p>Scan this QR code with your authenticator app:</p>
{/* render QR code from qrUri using a QR library */}
<input
value={code}
onChange={(e) => setCode(e.target.value)}
placeholder="Enter 6-digit code"
maxLength={6}
/>
<button type="submit">Verify</button>
</form>
)}
</div>
);
}