Skip to main content

MFA Complete Example

This example shows a complete MFA sign-in flow implemented with the LoginID Web SDK. It covers session creation, step-by-step MFA decision handling, and completing authentication using passkeys or other factor methods.

Prerequisites

  • Create an application to obtain a base URL. The SDK uses this base URL to interact with the LoginID authentication service.
  • Create an API key with at least the external-verify scope. You’ll need this to request authorization tokens from your backend.

Setup SDK

npm i @loginid/websdk3

Import and initialize an instance:

import { LoginIDMfa } from "@loginid/websdk3";

const lid = new LoginIDMfa({
baseUrl: process.env.LOGINID_BASE_URL,
});

MFA Flow

This example shows a complete MFA sign-in flow using the LoginID Web SDK. The flow is built incrementally. Each section below maps directly to a step in the final example.

1. Start an MFA Session

Begin the sign-in flow using a user identifier (for example, a username, email or phone number).

await mfa.beginFlow(username);

This creates an MFA session on the client and prepares the next authentication step.

2. Determine the Next MFA Action

After starting (or continuing) a session, ask the SDK what action should happen next.

const { nextAction } = mfa.getMfaSessionDetails();

nextAction indicates which recommended authentication option to present to the user:

  • passkey:auth – authenticate with an existing passkey
  • passkey:reg – register a new passkey
  • external – use a fallback or external authentication method

3. Perform Passkey Authentication or Registration

When the next action is passkey-based, call performAction with the appropriate value.

const result = await mfa.performAction("passkey:auth");
// or
const result = await mfa.performAction("passkey:reg");

If MFA is complete, the result will contain an accessToken.

4. Perform External / Fallback Authentication

For external authentication, your application performs its own authentication and passes an authorization token to the SDK once successfully authenticated.

const result = await mfa.performAction("external", {
payload: authToken,
});

The SDK validates the external step and advances the MFA session.

For more information on external authentication and how to set it up, see External Authentication.

5. Completing the Flow

After each action:

If an accessToken is returned, MFA is complete

Otherwise, read nextAction again and continue the flow

if (result.accessToken) {
// MFA complete - verify accessToken in your backend
}

Full Example

tip

The MFA flow does not need to live in a single component.
Session state is persisted in localStorage, allowing you to spread steps across multiple views.

import { useState } from "react";
import { LoginIDMfa } from "@loginid/websdk3";

const mfa = new LoginIDMfa({
baseUrl: process.env.REACT_APP_LOGINID_BASE_URL,
});

export const Signin = ({ setUserContext }) => {
const [username, setUsername] = useState("");
const [error, setError] = useState("");
const [showAuth, setShowAuth] = useState<
"passkey:auth" | "passkey:reg" | "external" | null
>(null);

/**
* Ask the SDK what the next MFA step is
*/
const decideNextStep = () => {
try {
// Step 2: Read `nextAction` from MFA session
const { nextAction } = mfa.getMfaSessionDetails();
setShowAuth(nextAction);
} catch (err) {
console.warn("Failed to read MFA session", err);
setShowAuth("external");
}
};

/**
* Start the MFA sign-in flow
*/
const startSignin = async () => {
setError("");

try {
// Step 1: Begin MFA session
await mfa.beginFlow(username);

decideNextStep();
} catch (err) {
setError("Failed to start sign-in");
console.warn(err);
}
};

/**
* Perform passkey authentication or registration
*/
const handleMfaAction = async (
action: "passkey:auth" | "passkey:reg"
) => {
setError("");

try {
// Step 3: Perform the selected MFA action
const result = await mfa.performAction(action);

if (result.accessToken) {
// MFA completed successfully — verify accessToken in your backend
setUserContext({ username });
return;
}

// Step 4: Decide next step if needed
decideNextStep();
} catch (err) {
setError("MFA step failed");
console.warn(err);
}
};

/**
* Perform external / fallback authentication
*/
const handleExternalAuth = async () => {
setError("");

try {
// Example: client completes its own fallback authentication
// and retrieves an authToken from its backend
const { authToken } = await response.json();

// Step 3: Perform the selected MFA action
const result = await mfa.performAction("external", {
payload: authToken,
});

if (result.accessToken) {
// MFA completed successfully — verify accessToken in your backend
setUserContext({ username });
return;
}

// Step 4: Decide next step if needed
decideNextStep();
} catch (err) {
setError("External authentication failed");
console.warn(err);
}
};

return (
<div>
<h2>Sign in</h2>

{/* Initial username input */}
{!showAuth && (
<>
<input
value={username}
onChange={(e) => setUsername(e.target.value)}
placeholder="Username"
/>

<button onClick={startSignin}>
Continue
</button>
</>
)}

{/* Passkey authentication */}
{showAuth === "passkey:auth" && (
<button onClick={() => handleMfaAction("passkey:auth")}>
Sign in with Passkey
</button>
)}

{/* Passkey registration */}
{showAuth === "passkey:reg" && (
<button onClick={() => handleMfaAction("passkey:reg")}>
Create Passkey
</button>
)}

{/* External / fallback authentication */}
{showAuth === "external" && (
<button onClick={handleExternalAuth}>
Continue with fallback authentication
</button>
)}

{error && <div style={{ color: "red" }}>{error}</div>}
</div>
);
};