🚀 Stablecoin Accounts are live! Learn more
import {
useDynamicContext,
useSyncMfaFlow,
useRegisterPasskey,
useAuthenticatePasskeyMFA,
useMfa,
useGetPasskeys,
} from "@dynamic-labs/sdk-react-core";
export const MfaComponent = () => {
const { userWithMissingInfo } = useDynamicContext();
const { getRecoveryCodes } = useMfa();
const getPasskeys = useGetPasskeys();
const registerPasskey = useRegisterPasskey();
const authenticatePasskeyMFA = useAuthenticatePasskeyMFA();
useSyncMfaFlow({
// this handler will run in one of these three scenarios:
// 1. when user is required to register a passkey on login
// 2. when user has a registered passkey and is required to authenticate after login
// 3. when user is still pending recovery codes acknowledgement
handler: async () => {
// this checks if the user is pending registration or authentication of a passkey
if (userWithMissingInfo?.scope?.includes("requiresAdditionalAuth")) {
getPasskeys().then(async (passkeys) => {
// if the user has no passkeys, we need to register a new one
if (passkeys.length === 0) {
// here you can show a view to display while the user is prompted to register a passkey
// e.g. setView('passkey-registration')
await registerPasskey();
} else {
// if the user has a passkey, we need to authenticate it
// here you can show a view to display while the user is prompted to authenticate their passkey
// e.g. setView('passkey-authentication')
await authenticatePasskeyMFA({
createMfaToken: {
singleUse: true,
},
});
}
});
} else {
// if the user already has a passkey registered and authenticated,
// but is pending recovery codes acknowledgement
const recoveryCodes = await getRecoveryCodes();
// here you can show a view to display the recovery codes with a button to acknowledge them
// to acknowledge the recovery codes, you can call the `completeAcknowledgement()` function
// e.g. setView('recovery-codes', { recoveryCodes, onAck: completeAcknowledgement })
}
},
});
};
import { UserPasskey } from "@dynamic-labs/sdk-api-core";
import {
useAuthenticatePasskeyMFA,
useGetPasskeys,
useIsLoggedIn,
useMfa,
useRegisterPasskey,
} from "@dynamic-labs/sdk-react-core";
import { useCallback, useEffect, useState } from "react";
export const MfaComponent = () => {
// when the user is pending any MFA action, Dynamic will set `userWithMissingInfo`
// other wise it will be undefined and `user` will be defined instead
const isLogged = useIsLoggedIn();
const { getRecoveryCodes, completeAcknowledgement } = useMfa();
const getPasskeys = useGetPasskeys();
const registerPasskey = useRegisterPasskey();
const authenticatePasskeyMFA = useAuthenticatePasskeyMFA();
const [error, setError] = useState<string>();
const [userPasskeys, setUserPasskeys] = useState<UserPasskey[]>([]);
const [recoveryCodes, setRecoveryCodes] = useState<string[]>([]);
// refresh the user's passkeys
const refreshUserPasskeys = useCallback(async () => {
const passkeys = await getPasskeys();
setUserPasskeys(passkeys);
}, [getPasskeys]);
// fetch passkeys when the user is logged in
// so you can display them in your UI
useEffect(() => {
if (isLogged) {
refreshUserPasskeys();
}
}, [isLogged, refreshUserPasskeys]);
// register a new passkey
const handleRegisterPasskey = async () => {
// here you can show a view to display while the user is prompted to register a passkey
// e.g. setView('passkey-registration')
try {
await registerPasskey();
// if you want to refresh the user's passkeys after registering a new one
refreshUserPasskeys();
} catch (e) {
setError(e instanceof Error ? e.message : String(e));
}
};
const handleAuthenticatePasskey = async () => {
// here you can show a view to display while the user is prompted to authenticate their passkey
// e.g. setView('passkey-authentication')
try {
// if you pass in a createMfaToken option as shown below,
// `authenticatePasskeyMFA` will return a single use MFA token
const mfaAuthToken = await authenticatePasskeyMFA({
createMfaToken: {
singleUse: true,
},
});
console.log("mfaAuthToken", mfaAuthToken);
} catch (e) {
setError(e instanceof Error ? e.message : String(e));
}
};
const handleRecoveryCodes = async () => {
try {
const recoveryCodes = await getRecoveryCodes();
// here you can show a view to display the recovery codes with a button to acknowledge them
// to acknowledge the recovery codes, you can call the `completeAcknowledgement()` function
// e.g. setView('recovery-codes', { recoveryCodes, onAck: completeAcknowledgement })
} catch (e) {
setError(e instanceof Error ? e.message : String(e));
}
};
return (
<div className="headless-mfa-view">
<div className="headless-mfa-view__section">
<p>
<b>Passkeys</b>
</p>
{userPasskeys.map((passkey) => (
<div key={passkey.id}>
<p>Id: {passkey.id}</p>
<p>Alias: {passkey.alias}</p>
<p>Credential Id: {passkey.credentialId}</p>
<p>Origin: {passkey.origin}</p>
<p>Storage: {passkey.storage?.name}</p>
<p>Created At: {new Date(passkey.createdAt).toLocaleString()}</p>
<p>
Updated At:{" "}
{passkey.updatedAt
? new Date(passkey.updatedAt).toLocaleString()
: "N/A"}
</p>
</div>
))}
</div>
{error && <div className="headless-mfa__section error">{error}</div>}
{recoveryCodes && (
<div className="headless-mfa-view__section">
<p className="headless-mfa-view__codes">
Recovery Codes: {recoveryCodes.join(", ")}
</p>
</div>
)}
<div className="headless-mfa-view__section">
<button onClick={() => handleRegisterPasskey()}>
Register Passkey
</button>
</div>
<div className="headless-mfa-view__section">
<button onClick={() => handleAuthenticatePasskey()}>
Authenticate Passkey
</button>
</div>
<div className="headless-mfa-view__section">
<button onClick={() => handleRecoveryCodes()}>
Generate Recovery Codes
</button>
</div>
</div>
);
};
Was this page helpful?