Skip to main content
Version: v2

Add email based OTP to your app

This guide describes how to add email based OTP (one-time password) to your app. OTP is an auto generated code that is sent to the user's email address that can only be used once for a single login. This can be used to prove that the user has access to the email address associated with the account in question. You will find that OTP email verification is used on the Hosted Web Authenticator for sign-up and new browser logins. This guide will help you implement your own OTP solution for your application using the Beyond Identity Embedded SDK.

Prerequisites

Add OTP to your app

Adding email based OTP to your app involves calling the following functions from the Beyond Identity Embedded SDK.

Use the following guides for language specific information:

1. Send the user an OTP

In your application, create a text field for the user to enter an email address. After the user enters an email address you will want to call the Embedded SDK function authenticateOtp with the current authenticate url and the user's email address. Calling this functions will enable Beyond Identity to send the user an email with a one-time password.

The result of calling this function will return an OtpChallengeResponse that contains a URL. This URL should be saved and used when calling redeemOtp or authenticateOtp in the same transaction.

danger

Once your user starts a transaction with OTP they will be unable to switch to passkey authentication in the same transaction. Passkey authentication using authenticate accepts the original URL generated by the /authorize endpoint and not an OtpChallengeResponse URL.

// Get the most recent authenticate URL.
const url = otpChallengeResponse.url || window.location.href;

let response = await embedded.authenticateOtp(url, email);

// Save OtpChallengeResponse to use the new authenticate URL later
setOtpChallengeResponse(response);
authenticate URL

The first authenticate URL is generated by the Beyond Identity API's /authorize endpoint in response to a standard OpenID Connect request from your app. However, once you start an authentication transaction you will need to keep track of the most recent URL. The generated URL is unique for each authentication request. It contains an encoded JWT token containing a challenge.

tip

To keep track of the most recent authentication URL, save OtpChallengeResponse each time it is returned from authenticateOtp or redeemOtp.

Use this new authenticate URL when calling redeemOtp or authenticateOtp in the same transaction.

2. Redeem the user's OTP

After calling the function authenticateOtp, the user should receive an email with a one-time password. They have 10 minutes to redeem thier OTP. To redeem that code for your user, you will need to call the function redeemOtp. Create a text field for your user to enter the code that was received in thier email. After the user has entered that code, you will want to call the Embedded SDK function redeemOtp with the current authenticate url and the user's OTP.

The result of calling this function will return either an AuthenticateResponse or an OtpChallengeResponse.

const response = await embeddedSdk.redeemOtp(
otpChallengeResponse.url,
otpValue
);

if ("redirectUrl" in response) {
// User has successfully authenticated!

// Either continue with logging the user in
let code = parseCode(from: response.redirectUrl)
let token = exchangeForToken(code)

// Or generating a passkey
let credentialBindingLink = generateCredentialBindingLink(from: response.passkeyBindingToken)
const isValidBindingLink = await embedded.isBindPasskeyUrl(
credentialBindingLink
);
if (isValidBindingLink) {
const response = await embedded.bindPasskey(credentialBindingLink);
}
}

if ("url" in response) {
// User has failed to redeem OTP, possilbly due to an incorrect code entry
// Save OtpChallengeResponse to use the new authenticate URL during retry.
setOtpChallengeResponse(response);
}

Successful authentication

Successful authentication will return an AuthenticateResponse. The AuthenticateResponse contains a redirectURL and a passkeyBindingToken. At this point you can either continue OIDC with a token exchange using the redirectURL, or redeem the passkeyBindingToken for a credentialBindingLink which can be used to bind a passkey for the authenticated user.

  1. To make a token exchange, parse the redirectURL provided by the AuthenticateResponse. The authorization code and the state parameter are attached to this URL. You can exchange the authorization code for an id token using your Beyond Identity Token Exchange Endpoint.

  2. To exchange a passkeyBindingToken for a credentialBindingLink, use the passkeyBindingToken as a bearer token in the credential-binding-jobs endpoint.

tip

OTP will create an identity associated with the email used in the Beyond Identity cloud if no other identity exists. A matching identity's username will match the associated email.


The credential binding job authorized with passkeyBindingToken will be associated with that authenticated identity.
/credential-binding-jobs
1
2
3
curl "https://auth-$(REGION).beyondidentity.com/v1/tenants/$(TENANT_ID)/realms/$(REALM_ID)/applications/$(APPLICATION_ID)/credential-binding-jobs" \
-X POST \
-H "Authorization: Bearer $(passkeyBindingToken)"

Failed authenitcation

Failed OTP authenitcation will return an OtpChallengeResponse that contains a URL. This URL should be saved and used when calling redeemOtp or authenticateOtp in the same transaction.

tip

It is recommended to limit the user to 3 OTP tries before starting a new transation. A new transaction would involve having the user re-enter an email address and begining with authenticateOtp.