docs.rodeo

MDN Web Docs mirror

One-time passwords (OTP)

A one-time password (OTP), also known as one-time PIN, or one-time authorization code (OTAC) is a generated code that is specific to a single login attempt. The website either sends the code to the user in a separate channel, such as an email, or the user’s device independently generates the code. The user then enters the code on the site to log in.

Overview

Authentication flows with one-time passwords are based on something the user has (a phone, an email address, a secret stored in an authenticator app) rather than something they know (a password) or something they are (biometric information, such as a fingerprint).

One-time passwords can either be used in addition to traditional passwords, or they can replace them. Often they are used to confirm the user’s intentions, for example when making a payment.

Many OTPs are 6 digits long with a 1-in-a-million chance to guess correctly. This is much better than 4 digits with just 10,000 possible combinations. The security mechanism that OTPs rely on is the temporal component: OTPs are usually only valid once for a defined timeframe and invalidated after use. That’s why OTPs have a short expiration time (ideally ≤5 minutes; 30–120 seconds for stronger protection).

This article discusses three common implementations for one-time passwords: email, SMS, and time-based one-time passwords (TOTP). TOTP is considered the most secure delivery channel in this comparison.

Email OTP

In email-based OTP, during registration:

When the user asks to sign in:

There are two common approaches to delivering the code:

  1. The website sends a personalized one-time link to the user’s email address. When the user clicks the link, the website authenticates the user. The link is only valid for a few minutes and expires immediately after the user clicks it. This option can be highly convenient for the user. However, it requires the user to complete the process on the same device and in the same browser, which can be an issue when logging in from an in-app browser or a different device. Asking users to click links in emails also makes them more vulnerable to phishing attacks.

  2. The website sends a personalized one-time code to the user’s email address. The user is then asked to type the code into the website on their desired device and in their desired browser. This process can be slower and less convenient for the user but it offers greater flexibility as where to login and is considered more secure than using links in emails.

For a good user experience with all email-based OTP methods, it is important that users receive the OTP emails reasonably quickly.

SMS OTP

In SMS OTP, the user provides their cellphone number during registration, and at sign-in the website sends the one-time code to the phone in an SMS message.

A weakness of both email and SMS methods is that an attacker could intercept the message that contains the code. However, SMS is considered more vulnerable:

Also, users might be using a different SIM card or an eSIM when traveling and might have disabled their usual SIM card or SMS services to save costs, in which case they won’t receive any SMS one-time passwords.

Because of this, you should not use SMS OTP on its own to establish new sessions or for general authentication. Instead, if at all, only use it as a second factor or for confirming intentions (e.g., payments).

Autocompleting SMS codes

To make it easier for users to enter SMS codes into a site, and to reduce the likelihood of phishing attacks, the standard for origin-bound one-time codes delivered via SMS enables websites to support autocomplete for one-time code values.

To enable this, you need to format the SMS message like this:

Your verification code is 123456.

@www.example.com #123456

Then in your site’s login form, provide an {{HTMLElement("input")}}  element with the autocomplete=one-time-code attribute value set.

<form action="/verify-otp" method="POST">
  <input
    required
    type="text"
    autocomplete="one-time-code"
    inputmode="numeric"
    maxlength="6"
    pattern="\d{6}" />
  <input type="submit" />
</form>

The browser will automatically extract the code from the SMS, and if the origin given in the message matches the origin of the login form, it will autofill the <input> element with the code.

WebOTP API

The WebOTP API gives websites programmatic access to one-time codes delivered over SMS. However, it doesn’t have good cross-browser support, and unless you need programmatic access to the code, you don’t need to use this API. Using the standardized format and autocomplete=one-time-code should be enough for autocomplete to work across browsers.

TOTP

With time-based one-time passwords, the website does not send the sign-in code to the user. Instead, the website and the user are able to generate the same code independently of each other, based on the current time and a shared secret. To generate the code, the user must install an app on their device: this is called an authenticator app.

At registration time:

  1. The user installs an authenticator app, if they don’t have one already.

  2. The website:

    • Generates the shared secret.
    • Securely stores the secret, associating it with the user’s account.
    • Embeds the secret and some related metadata in an otpauth URI.
    • Encodes the URI as a QR code and invites the user to scan it.
  3. The authenticator app on the user’s device decodes the URI from the QR code and parses the URI, storing the secret and related metadata.

At sign-in time, the user provides the current code value, which the authenticator app calculates based on the secret and the current time. The website is able to perform the same calculation, and if the values match, the user can be signed in.

TOTP algorithm

The time-based one-time password (TOTP) algorithm is specified in {{rfc("6238")}} . It is an extension of the HMAC-Based One-Time Password Algorithm (HOTP), which is specified in {{rfc("4226")}} .

The algorithm creates one-time codes that are 6 digits long and which are only valid for a limited amount of time (usually 30 seconds). This means that unlike the other OTP systems we’ve described, TOTP implements time-based validity and automatic invalidation by design.

The secret key is a random value recommended to be at least 160 bits long.

You should use a well-regarded third-party package to implement TOTP, such as pyotp, for Python, or otpauth, for Node.

The otpauth URI format

The otpauth URI format is defined in this IETF draft.

For TOTP, the URI is formatted like this:

otpauth://totp/LABEL?secret=MQCHJLS6FJXT2BGQJ6QMG3WCAVUC2HJZ&issuer=My_Website

The LABEL component identifies the user: for example, it could be their username.

The URI includes a number of query string parameters, of which the most important are:

Authenticator apps

A large number of authenticator apps, both proprietary and open source, support TOTP. For example: Ente Auth, 2FAS, and Microsoft Authenticator.

Securing the secret

With TOTP the secret key must be stored securely in both the server and the client.

For the server, the considerations are similar to those for password storage: the server must store TOTP secrets in such a way that the attacker cannot access them, even if they get access to the server’s database.

For the client, the authenticator app should provide some degree of protection for the secret key.

Strengths and weaknesses

Compared with passwords, the biggest strength of OTP is that the user is not involved in creating or remembering secrets, so OTP is not vulnerable to guessing or credential stuffing attacks.

Weaknesses

Security aside, OTP has some usability issues:

OTP recommendations

OTP, and in particular TOTP, can be useful as an {{glossary("Multi-factor authentication", "additional authentication factor")}}  and for confirming user intentions, for example when making a payment. For general authentication purposes, it is better to use passkeys, which are more resistant to phishing attacks.

If you implement OTP, consider the following recommendations:

In this article

View on MDN