WebAuthn quick start: Web SDK
Get started with WebAuthn APIs using our Platform SDK. This describes how to quickly integrate WebAuthn registration and authentication into your application.
Note
This guide describes integrating using the new Platform SDK. If you've already integrated using the older WebAuthn SDK, you can find that guide here.
Sample flows
Here are samples of some flows that implement WebAuthn biometric login using our Platform SDK. These are simplified representations to help you get to know Mosaic APIs in context. There are many different ways to implement this and this is just an example.
New credential registration
This flow represents a user registering new WebAuthn credentials. Mosaic APIs are shown in pink along with the relevant integration steps.
After initializing the SDK, the client validates that the device supports WebAuthn. Once the user is ready to register their credential, the SDK tells the browser to create credentials, which may require the user to verify (e.g., via TouchID). Once completed, the user's credential is registered and can be used going forward to log in.
Single device authentication
This flow represents an existing user authenticating using registered credentials which exist on the device. Mosaic APIs are shown in pink along with the relevant integration steps.
The SDK is initialized. Once the user is ready to authenticate, the SDK tells the browser to get credentials, which may require the user to verify (e.g., via TouchID). Once completed, a user access token is returned to your client backend and the user is logged in.
Step 1: Configure your app
To integrate with Mosaic, you'll need an application. From the Applications page, create a new application or use an existing one. The application settings provide client credentials that you'll need for API calls. These credentials are automatically generated when your application is created.
Note
-
Redirect URI is required, although it's not used in the WebAuthn flow. If you're not using any other login method (which do require redirection), enter your website URL here instead. Use HTTPS (e.g.,
https://login.example.com
), unless using a local environment (e.g., http://localhost ). - Enable public sign-up if you manage users using an external system (e.g., external identity provider) or if you want to quickly test WebAuthn registration and authentication without first logging in using a different authentication method.
Step 2: Configure auth method
From the Authentication methods page, configure the WebAuthn login method for your application. For Relying party ID, add your application's domain (e.g., example.com
). This is the domain to which WebAuthn credentials are registered and used to authenticate. By default, Mosaic will accept any origin that matches this domain, which includes all subdomains.
NOTE
To restrict usage to specific subdomains, toggle the Relying Party Origins and add one or more subdomains of the Relying Party ID to the Web Origins. Ensure to provide the full URL.
TIP
For testing and development, configure the login method for your local environment by setting the RP ID to localhost
and the RP origin to http://localhost:(PORT)
(e.g., http://localhost:1234
).
Step 3: Initialize the SDK
You can install the Platform SDK by adding a script tag to the relevant pages of your application:
<!-- Load the latest SDK version within 1.6 range
See changelog for details and update version if necessary -->
<script type="text/javascript" src="https://platform-websdk.transmitsecurity.io/platform-websdk/1.6.x/ts-platform-websdk.js" defer="true" id="ts-platform-script"></script>
Configure the SDK by calling the initialize()
SDK method, passing the client ID obtained in Step 1. For example:
await window.tsPlatform.initialize({
clientId: '[CLIENT_ID]',
webauthn: {serverPath: 'https://api.transmitsecurity.io'}
});
Note
Configure the SDK to work with a different cluster by setting serverPath
to https://api.eu.transmitsecurity.io
(for EU) or https://api.ca.transmitsecurity.io
for (CA).
Step 4: Register credentials
Before users can login with WebAuthn, they'll need to register WebAuthn credentials. This assumes that the registration flow occurs only after the end-user has logged in to your website (or at least performed an authentication process that verifies their identity), regardless of whether this is done using another Mosaic authentication method or using an authentication external to Mosaic. Credentials can be registered for existing Mosaic users or for new Mosaic users.
To implement a WebAuthn registration flow, you'll need to:
- Check that the device supports WebAuthn
- Register the credentials on the device
- Register the credentials for the Mosaic user
1. Check for WebAuthn support
Before initiating WebAuthn registration, use the isPlatformAuthenticatorSupported()
SDK call to check if the device supports WebAuthn.
const isBiometricsSupported = await window.tsPlatform.webauthn.isPlatformAuthenticatorSupported();
2. Register credential on device
When the end-user requests to register biometrics, call the register()
SDK call as shown below. Username is required and additional optional properties like display name are supported. This will prompt the user for biometrics. If successful, it returns a result encoded as a base64 string, which is required to complete the registration via your backend.
Note
USERNAME
is a verified username, which should be a representation of the user in your system (such as their email, phone number, or another verifiable username known to the user like account ID).
// Registers WebAuthn credentials on the device and returns an encoded result
// that should be passed to your backend to complete the registration flow
const encodedResult = await window.tsPlatform.webauthn.register('[USERNAME]');
3. Complete registration
Once WebAuthn credentials have been registered on the device (via the SDK), they will need to be registered in Mosaic to the relevant user. This is done by taking the encodedResult
parameter returned by the SDK (in step 4.2) and passing it to a backend API. This step varies based on whether the end-user logged into your website using a Mosaic authentication method or using an external one, as described below.
User is logged-in via Transmit
Complete WebAuthn credential registration for a user that is currently logged-in using a different Mosaic authentication method. This registration API must be called from the backend using the user access token returned upon login. If successful, the credential will be registered for the user that corresponds to the authorization token.
const resp = await fetch(
`https://api.transmitsecurity.io/cis/v1/auth/webauthn/register`, // Use api.eu.transmitsecurity.io for EU, api.ca.transmitsecurity.io for CA
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer [TOKEN]' // User access token from Mosaic login
},
body: JSON.stringify({
webauthn_encoded_result: '[ENCODED_RESULT]' // Returned by register() SDK call
})
}
);
const data = await resp.json();
console.log(data);
User isn't logged-in via Transmit
Complete WebAuthn credential registration for an end-user that logged into your website using a non-Mosaic authentication method. This registration API must be called from the backend using a client access token (See Get client access tokens). If successful, the credentials will be registered for the user corresponding to the external user ID in the request. If no user is found, a new user will be created.
Note
- While an end-user may be logged-in to your website, if they didn't perform a Mosaic authentication (i.e., there is no active Mosaic session for the user), they are considered a logged-out Mosaic user.
- While an end-user may be registered to your website, if they've never logged in via Mosaic (or created as a Mosaic user), they are considered an existing user in your system but a new user in Mosaic.
const resp = await fetch(
`https://api.transmitsecurity.io/cis/v1/auth/webauthn/external/register`, // Use api.eu.transmitsecurity.io for EU, api.ca.transmitsecurity.io for CA
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer [TOKEN]'// Client access token
},
body: JSON.stringify({
webauthn_encoded_result: '[ENCODED_RESULT]', // Returned by register() SDK call
external_user_id: '[EXTERNAL_USER_ID]'// Identifier of the user in your system
})
}
);
const data = await resp.json();
console.log(data);
Step 5: Authenticate user
Once WebAuthn credentials are registered for a user, they can use them to authenticate. Users are identified by their username, which can either be passed in the authentication request or can be inferred from the credential selected by the user. Once authentication is requested, a credential selection list is displayed to the user. You can choose to display this list as a modal native to the browser or using an autofill experience.
To implement a WebAuthn authentication flow, you'll need to:
1. Authenticate on device
WebAuthn APIs offer two distinct flows to lead to passkey authentication: the modal passkey credential flow, where users to manually enter identifiers, and the autofill passkey credential flow, that embeds passkey authentication into username collection UI fields for a seamless authentication experience.
Note
Steps below describe how to implement authentication using a modal experience for selecting credentials. To add support for autofill, see Next Steps.
To initiate the modal passkey credential flow, use the authenticate.modal()
SDK call. The username
parameter is optional as it's only used to fetch credentials matching to the username. If the username is not provided, all the credentials for this app in this device will be displayed.
// Authenticates WebAuthn credentials on the device and returns an encoded result
// that should be passed to your backend to complete the authentication flow
const webauthnEncodedResult = await window.tsPlatform.webauthn.authenticate.modal("USERNAME"); // Optional username
2. Complete authentication
Once the user has authenticated on the device via the SDK, call the authentication endpoint via your backend with the encodedResult
parameter returned by the SDK (in step 5.1) and a client access token (See Get client access tokens).
If successful, ID and access tokens will be returned. The user tokens will identify the user via user_id
. If set for the user, it will also include any other verified alias (such as email
or phone number
) and the external user ID (external_user_id
). These tokens should be validated as described here. For more, see the Token Reference.
const resp = await fetch(
`https://api.transmitsecurity.io/cis/v1/auth/webauthn/authenticate`, // Use api.eu.transmitsecurity.io for EU, api.ca.transmitsecurity.io for CA
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer [TOKEN]' // Client access token
},
body: JSON.stringify({
webauthn_encoded_result: '[ENCODED_RESULT]' // Returned by authenticate.modal() SDK call
})
}
);
const data = await resp.json();
console.log(data);
Full code examples
Here's an example of a registration flow for a logged-in Mosaic user:
<html>
<head>
<!-- Load the latest SDK version within 1.6 range
See changelog for details and update version if necessary -->
<script type="text/javascript" src="https://platform-websdk.transmitsecurity.io/platform-websdk/1.6.x/ts-platform-websdk.js" defer="true" id="ts-platform-script"></script>
<script>
// Set init params
const config = {
clientId: "CLIENT_ID",
serverPath: "https://api.transmitsecurity.io", // Use api.eu.transmitsecurity.io for EU, api.ca.transmitsecurity.io for CA
}
async function init() {
// Check if WebAuthn is supported on the current device
if (!window.tsPlatform.webauthn.isPlatformAuthenticatorSupported()) {
alert("WebAuthn is not supported on the current device");
}
}
async function onRegister() {
// Collect user input
const username = document.getElementById("username").value;
// Initialize the SDK
console.log("Initializing SDK");
await window.tsPlatform.initialize({
clientId: config.clientId,
webauthn: {serverPath: config.serverPath}
});
// Register the credential on the device
console.log("Registering the credential on the device");
const webauthnEncodedResult = await window.tsPlatform.webauthn.register(username);
// Send registration result to backend to complete registration with Transmit
console.log("Registering the credential in TransmitSecurity");
await fetch("https://YOUR_BACKEND/webauthn/register", {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({webauthnEncodedResult})
});
alert(`User ${username} registered successfully!`);
}
</script>
</head>
<body onload="init()">
<div>
Username<br/>
<input id="username"/>
</div>
<button id="register" onclick="onRegister()">Register WebAuthn</button>
</body>
</html>
Here's an example of an authentication flow using a modal experience:
<html>
<head>
<!-- Load the latest SDK version within 1.6 range
See changelog for details and update version if necessary -->
<script type="text/javascript" src="https://platform-websdk.transmitsecurity.io/platform-websdk/1.6.x/ts-platform-websdk.js" defer="true" id="ts-platform-script"></script>
<script>
// Set init params
const config = {
clientId: "CLIENT_ID",
serverPath: "https://api.transmitsecurity.io", // Use api.eu.transmitsecurity.io for EU, api.ca.transmitsecurity.io for CA
}
// Assumes device is already registered so no need to check for WebAuthn support
async function onAuthenticate() {
// Collect user input
const username = document.getElementById("username").value;
// Initialize the SDK
console.log("Initializing SDK");
await window.tsPlatform.initialize({
clientId: config.clientId,
webauthn: {serverPath: config.serverPath}
});
// Perform biometric authentication on device
console.log("Display available credentials and sign the challenge");
const webauthnEncodedResult = await window.tsPlatform.webauthn.authenticate.modal(username);
// Send authentication result to backend to obtains user tokens from Transmit
console.log("Finish authentication and fetch the access token");
await fetch("https://YOUR_BACKEND/webauthn/authenticate", {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({webauthnEncodedResult})
});
alert(`User ${username} logged in successfully!`);
}
</script>
</head>
<body>
<div>
Username<br/>
<input id="username"/>
</div>
<button id="authenticate" onclick="onAuthenticate()">Login</button>
</body>
</html>
Next steps
For an even more seamless authentication, you can opt to enable autofill passkey credential flows. Follow these steps:
1. Initiate autofill flow
A prerequisite for enabling passkey autofill is browser support. When loading the page, it is necessary to run the following function that verifies if autofill is supported.
const isAutofillSupported = await window.tsPlatform.webauthn.isAutofillSupported();
Note
To handle scenarios where the browser does not support passkey autofill, to ensure passkey authentication, enable the execution of the modal passkey credential flow.
In case of successful response, initiate the authenticate.autofill.activate(handlers)
function to allow passkey credential autofill. The handlers
parameter is mandatory as it's used to invoke the success/failure handlers after completing the authentication. The username
parameter is optional as it's only used to fetch credentials matching to the username. If no username is provided, authentication occurs without relying on any user information, and credentials are automatically inferred from the passkey entered by the user.
// Activate the passkey autofill
window.tsPlatform.webauthn.authenticate.autofill.activate({
onSuccess: handleSuccessfulPasskeyValidation, // Handle successful authentication
onError: handleAutofillError, // Handle error or passkey cancellation
});
async function handleSuccessfulPasskeyValidation({webauthnEncodedResult}) {
// Add code here that sends the encoded result to your backend to complete the authentication flow
}
async function handleAutofillError(error) {
if (error.errorCode === 'autofill_authentication_aborted') return; // Authentication canceled by user
console.log(error);
}
2. Enable autofill in the UI
After activating passkey credential autofill, ensure to enable the autocomplete="webauthn"
option on your user input field. Here's an example.
<input type="text" size="40" placeholder="user@example.com" autocomplete="username webauthn" />
3. Abort autofill requests
Enabling autofill on your login page corresponds to initiating a WebAuthn authentication flow server-side. Consequently, the registration of passkey credentials and any other login methods will result in an error. If you have incorporated passkey registration or other login flows into the same page, ensure to deactivate (abort
) autofill for each specific UI element that trigger those flows (e.g., "Register a new passkey" or "Login with OTP" buttons).
// Authenticate user with email OTP
async function verifyOtp(email, otpCode) {
// Abort login page autofill, in case of successful login and redirect
await window.tsPlatform.webauthn.authenticate.autofill.abort();
try {
// Send the OTP code and email to the server...
}
Note
In the event of authentication failure with other login methods, consider reactivating the autofill to make the option available again and ensure a more accommodating experience for users.