A composable API for implementing multi-factor authentication.
The Multi-Factor Authentication (MFA) API is intended to be a composable, unopinionated set of endpoints that can be integrated into existing application/session management strategies.
The available types of authentication factors are:
totp
– Time-based one-time passwordsms
– One-time password via SMS messageThe MFA API is not intended to be used with the WorkOS SSO feature. It’s recommended to leverage the MFA features of the Identity Provider that is powering your SSO implementation.
In this guide, we’ll walk you through the process of enrolling new authentication factors for a user, and the challenge/verification process for existing authentication factors.
This guide will show you how to:
WorkOS offers native SDKs in several popular programming languages. Choose a language below to see instructions in your application’s language.
Don't see an SDK you need? Contact us to request an SDK!
To make calls to WorkOS, provide the API key and, in some cases, the client ID. Store these values as managed secrets, such as WORKOS_API_KEY
and WORKOS_CLIENT_ID
, and pass them to the SDKs either as environment variables or directly in your app’s configuration based on your preferences.
WORKOS_API_KEY='sk_example_123456789' WORKOS_CLIENT_ID='client_123456789'
Use the TOTP type when the user is using a third-party authenticator app such as Google Authenticator or Authy.
import { WorkOS } from '@workos-inc/node'; const workos = new WorkOS('sk_example_123456789'); const factor = await workos.mfa.enrollFactor({ type: 'totp', issuer: 'Foo Corp', user: 'alan.turing@example.com', });
The response returns a qr_code
and a secret. The qr_code
value is a base64 encoded data URI that is used to display the QR code in your application for enrollment with an authenticator application.
The secret
can be entered into some authenticator applications in place of scanning a QR code.
Use the SMS type when the user wants to receive one time passwords as SMS messages to their mobile device.
Phone number must be valid. An error will be returned for malformed or invalid phone numbers.
import { WorkOS } from '@workos-inc/node'; const workos = new WorkOS('sk_example_123456789'); const factor = await workos.mfa.enrollFactor({ type: 'sms', phoneNumber: '+15005550006', });
Now that we’ve successfully created an authentication factor, we’ll need to save the ID for later use. It’s recommended that you persist the factor ID in your own user model according to your application’s needs.
Next we’ll initiate the authentication process for the newly created factor which we’ll refer to as a challenge.
import { WorkOS } from '@workos-inc/node'; const workos = new WorkOS('sk_example_123456789'); const challenge = await workos.mfa.challengeFactor({ authenticationFactorId: 'auth_factor_01FVYZ5QM8N98T9ME5BCB2BBMJ', });
When challenging an SMS authentication factor, you can pass an optional SMS template to customize the SMS message that is sent to the end user. Use the {{code}}
token to inject the one time password into the message.
import { WorkOS } from '@workos-inc/node'; const workos = new WorkOS('sk_example_123456789'); const enrollResponse = await workos.mfa.challengeFactor({ authenticationFactorId: 'auth_factor_01FVYZ5QM8N98T9ME5BCB2BBMJ', smsTemplate: 'Your FooCorp is {{code}}.', });
Now that we’ve successfully challenged the authentication factor, we’ll need to save the challenge ID for the last step, challenge verification.
The last step in the authentication process is to verify the one time password provided by the end-user.
import { WorkOS } from '@workos-inc/node'; const workos = new WorkOS('sk_example_123456789'); const { challenge, valid } = await workos.mfa.verifyChallenge({ authenticationChallengeId: 'auth_challenge_01FVYZWQTZQ5VB6BC5MPG2EYC5', code: '123456', });
If the challenge is successfully verified valid
will return true
. Otherwise it will return false
and another verification attempt must be made.
{ "challenge": { "object": "authentication_challenge", "id": "auth_challenge_01FVYZWQTZQ5VB6BC5MPG2EYC5", "created_at": "2022-02-15T15:26:53.274Z", "updated_at": "2022-02-15T15:26:53.274Z", "expires_at": "2022-02-15T15:36:53.279Z", "authentication_factor_id": "auth_factor_01FVYZ5QM8N98T9ME5BCB2BBMJ" }, "valid": true }
If a challenge was already successfully verified, it cannot be used a second time. If further verification is needed in your application, create a new challenge.
{ "code": "authentication_challenge_previously_verified", "message": "The authentication challenge 'auth_challenge_01FVYZWQTZQ5VB6BC5MPG2EYC5' has already been verified." }
For SMS authentication factors, challenges are only available for verification for 10 minutes. After that they are expired and cannot be verified.
{ "code": "authentication_challenge_expired", "message": "The authentication challenge 'auth_challenge_01FVYZWQTZQ5VB6BC5MPG2EYC5' has expired." }
We’ve now successfully verified an end-user’s authentication factor. This authentication factor can now be used as a second factor of authentication in your application’s existing authentication strategy.
The ID of the authentication factor should be persisted in your application for future authentication challenges.