Enable cross-device authentication by scanning QR codes with mobile apps.
QR code verification uses app credentials to enable cross-device authentication. This method leverages public key cryptography where private keys are securely stored on the user’s device.The Mobile SDK is used for two key steps:
Registering a mobile device for QR code verification by adding a credential. This step creates a new public/private key pair.
Responding to an authentication request by approving or rejecting a challenge. This step uses the device’s private key to sign a message which is verified on the server using the public key.
The Web SDK is used to initiate the challenge in a browser by generating a QR code.
The Mobile SDK is used to respond to the challenge in a mobile app by scanning the QR code.Initialize both SDKs using your tenant ID from the API keys page and your API URL.
import { Authsignal } from "@authsignal/browser";const authsignal = new Authsignal({ tenantId: "YOUR_TENANT_ID", baseUrl: "YOUR_API_URL",});
This token will be used to authorize enrolling a new authentication method on their mobile device.
The add:authenticators scope is required to enroll a new authentication factor for an existing user.
This scope should only be used when the user is in an already authenticated state.
For more information on using scopes safely refer to our documentation on authenticator binding.
You may optionally track an action from your backend if you want to create a QR code challenge that can only be completed by a specific user or run rules on the action for adaptive MFA.
If you want to start a QR code challenge that can be claimed and completed by any user, skip to step 2.Track an action from your backend using our Server SDK.
Return the token to your frontend and call the Web SDK’s setToken method.
authsignal.setToken(response.token);
When creating a QR code challenge after tracking an action, the custom data and action code must
be provided in the track request and cannot be overridden in the challenge request from the
frontend.
Use our Web SDK to initiate a QR code challenge.Initiate QR code challengeUse the qrCode.challenge() method to initiate a challenge.
The SDK will handle all the complexity of managing the challenge lifecycle, including automatic refreshing and state updates.By default the SDK will use WebSocket connections.
If your environment does not support WebSockets, you can fall back to polling using REST API calls by setting polling to true.
const { data } = await authsignal.qrCode.challenge({ action: "signIn", onStateChange: (state, token) => { switch (state) { case "claimed": // User has scanned the QR code - blur/hide it console.log("QR code claimed by user"); break; case "approved": // Challenge approved - validate the token on your backend if (token) { validateChallengeOnBackend(token); // Continue with the application flow } break; case "rejected": // Challenge rejected - show error and offer retry console.log("Challenge rejected by user"); break; } }, onRefresh: ({ challengeId, expiresAt }) => { // Update your QR code display with the new challengeId updateQRCode(challengeId); }, // Optional: Add context that will be shown on the user's device. Only available for anonymous challenges. custom: { deviceInfo: "Terminal A", }, refreshInterval: 540000, // 9 minutes (default) polling: false, // Use WebSocket for real-time updates (default)});// Display the QR code using the challengeIdif (data?.challengeId) { displayQRCode(data.challengeId);}
State changes
1
Initial state
After calling qrCode.challenge(), the QR code is ready to be scanned. Display it to the user.
2
Claimed
When onStateChange is called with state: "claimed", the user has scanned the QR code. You can
use this state change to indicate progress in your UI.
3
Approved
When onStateChange is called with state: "approved" and a token, the user has approved the
challenge. Pass the token to your backend for validation.
4
Rejected
When onStateChange is called with state: "rejected", the user has declined the challenge. Show an error message and provide a way to retry.
Refresh a challenge (optional)If you need to manually refresh a challenge you can use the qrCode.refresh() method:
Once the user scans the QR code, use the mobile sdk Claim Challenge method to set the user attempting to complete the challenge.The Claim Challenge method will return some context about the desktop or kiosk initiating the challenge such as ip address, location, user agent and custom data.
This data can be shown to the user to help them decide if they want to approve or decline the challenge.
To display custom data points passed into the initial track request, mark the relevant custom
data points as public.
Present a dialog to allow the user to review the challenge context and approve or decline the challenge by calling the mobile sdk Update Challenge method.
Once the challenge is approved, the onStateChange callback will return a token that should be passed to your backend to validate the challenge for the action and complete the authentication flow.