Docs

Time-based One-time Password (TOTP)

Warning

Authenticator application and Backup codes must be enabled as multi-factor strategies in your Clerk settings for these methods to work. See the Multi-factor authentication section to learn more.

Clerk supports multi-factor authentication (MFA) using Time-based One-time Password (TOTP) as a second factor. TOTP is a widely used standard for generating one-time passwords that are valid for a short period of time.

These methods on the User object are related to TOTP functionality and allow you to generate and verify TOTP secrets, disable TOTP, and create backup codes for user authentication.

To see how all these methods work together, check out the comprehensive example.

createTOTP()

Generates a TOTP secret for a user that can be used to register the application on the user's authenticator app of choice. Note that if this method is called again (while still unverified), it replaces the previously generated secret.

function createTOTP(): Promise<TOTPResource>;

createTOTP() returns

TypeDescription
Promise<TOTPResource>This method returns a Promise that resolves to the newly created TOTP object.

verifyTOTP()

Verifies a TOTP secret after a user has created it. The user must provide a code from their authenticator app that has been generated using the previously created secret. This way, correct set up and ownership of the authenticator app can be validated.

function verifyTOTP(params: VerifyTOTPParams): Promise<TOTPResource>;
  • Name
    code
    Type
    string
    Description

    A 6 digit TOTP generated from the user's authenticator app.

verifyTOTP() returns

TypeDescription
Promise<TOTPResource>This method returns a Promise that resolves to the newly verified TOTP object.

disableTOTP()

Disables TOTP by deleting the user's TOTP secret.

function disableTOTP(): Promise<DeletedObject>;

disableTOTP() returns

TypeDescription
Promise<DeletedObject>This method returns a Promise that resolves to the reference to the deleted TOTP object.

createBackupCode()

Generates a fresh new set of backup codes for the user.

Every time the method is called, it will replace the previously generated backup codes.

Can only be created for the user if the user has another multi-factor authentication method enabled for their account, as backup codes are a fallback for when the user is unable to use their primary MFA method.

function createBackupCode(): Promise<BackupCodeResource>;

createBackupCode() returns

TypeDescription
Promise<BackupCodeResource>This method returns a Promise that resolves to the newly created backup code.

Types

TOTPResource

  • Name
    id
    Type
    string
    Description

    A unique identifier for this TOTP secret

  • Name
    secret?
    Type
    string
    Description

    The generated TOTP secret. Note: this is only returned to the client upon creation and cannot be retrieved afterwards.

  • Name
    uri?
    Type
    string
    Description

    A complete TOTP configuration URI including the Issuer, Account, etc that can be pasted to an authenticator app or encoded to a QR code and scanned for convenience. Just like the secret, the URI is exposed to the client only upon creation and cannot be retrieved afterwards.

  • Name
    verified
    Type
    boolean
    Description

    Whether this TOTP secret has been verified by the user by providing one code generated with it. TOTP is not enabled on the user unless they have a verified secret.

  • Name
    backupCodes?
    Type
    string[]
    Description

    A set of fresh generated Backup codes. Note that this will be populated if the feature is enabled in your instance and the user doesn't already have backup codes generated.

  • Name
    createdAt
    Type
    Date
    Description

    Creation date of the TOTP secret

  • Name
    updatedAt
    Type
    Date
    Description

    Update timestamp of the TOTP secret

  • Name
    id
    Type
    string
    Description

    A unique identifier for this TOTP secret

  • Name
    codes
    Type
    string[]
    Description

    The generated set of backup codes

  • Name
    createdAt
    Type
    Date
    Description

    Creation date of the TOTP secret

  • Name
    updatedAt
    Type
    Date
    Description

    Update timestamp of the TOTP secret

TOTP example

The following example demonstrates how to use the TOTP methods to create and verify a TOTP secret, disable TOTP, and create backup codes for a user. To ease the development process, the response or error message of a method will be displayed on the user interface.

The following example assumes:

For the following example, your HTML file should look like this:

index.html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Clerk + JavaScript App</title>
  </head>
  <body>
    <div id="app"></div>

    <p id="error-container" hidden>
      Error:
      <span id="error-message"></span>
    </p>

    <button id="create-totp">Create TOTP</button>
    <button id="disable-totp">Disable TOTP</button>
    <button id="create-backup-codes">Create Backup Codes</button>

    <div id="totp-container" hidden>
      <p id="enter-totp-secret">
        Enter the TOTP secret into your authenticator app:
        <span id="totp-secret"></span>
      </p>

      <label for="totp-code">
        Enter the code from your authenticator app
      </label>
      <input id="totp-code" type="text" />
      <button id="verify-totp">Verify TOTP</button>
    </div>

    <h2>Response:</h2>
    <pre id="response"></pre>

    <script type="module" src="/main.js"></script>
  </body>
</html>

And your JavaScript file should look like this:

main.js
import Clerk from '@clerk/clerk-js';

// Initialize Clerk with your Clerk publishable key
const clerk = new Clerk('YOUR_PUBLISHABLE_KEY');
await clerk.load();

if (clerk.user) {
  // Create and verify TOTP
  document.getElementById("create-totp")
    .addEventListener("click", async () => {
      clerk.user.createTOTP()
        .then((res) => {
          // Show the TOTP container with the secret and verify button
          document.getElementById("totp-container").removeAttribute("hidden");

          // Display the secret to the user
          document.getElementById("totp-secret").innerHTML = res.secret;

          // Add event listener to verify TOTP button
          document.getElementById("verify-totp")
            .addEventListener("click", async () => {
              const code = document.getElementById("totp-code").value;
              clerk.user.verifyTOTP({ code })
                .then((res) => {
                  document.getElementById("response").innerHTML =
                    JSON.stringify(res);
                })
                .catch((error) => {
                  document.getElementById("error-container").removeAttribute("hidden");
                  document.getElementById("error-message").innerHTML =
                    error.errors[0].longMessage;
                  console.log("An error occurred:", error.errors);
                });
            });
        })
        .catch((error) => {
          document.getElementById("error-container").removeAttribute("hidden");
          document.getElementById("error-message").innerHTML =
            error.errors[0].longMessage;
          console.log("An error occurred:", error.errors);
        });
  });

  // Disable TOTP
  document.getElementById("disable-totp")
    .addEventListener("click", async () => {
      clerk.user.disableTOTP()
        .then((res) => {
          document.getElementById("response").innerHTML = JSON.stringify(res);
        })
        .catch((error) => {
          document.getElementById("error-container").removeAttribute("hidden");
          document.getElementById("error-message").innerHTML =
            error.errors[0].longMessage;
          console.log("An error occurred:", error.errors);
        });
    });

  // Create backup codes
  document.getElementById("create-backup-codes")
    .addEventListener("click", async () => {
      clerk.user.createBackupCode()
        .then((res) => {
          document.getElementById("response").innerHTML = JSON.stringify(res);
        })
        .catch((error) => {
          document.getElementById("error-container").removeAttribute("hidden");
          document.getElementById("error-message").innerHTML =
            error.errors[0].longMessage;
          console.log("An error occurred:", error.errors);
        });
    });
} else {
  document.getElementById("app").innerHTML = `
    <div id="sign-in"></div>
  `;

  const signInDiv = document.getElementById("sign-in");

  clerk.mountSignIn(signInDiv);
}
index.html
  <div id="app"></div>

  <p id="error-container" hidden>
    Error:
    <span id="error-message"></span>
  </p>

  <button id="create-totp">Create TOTP</button>
  <button id="disable-totp">Disable TOTP</button>
  <button id="create-backup-codes">Create Backup Codes</button>

  <div id="totp-container" hidden>
    <p id="enter-totp-secret">
      Enter the TOTP secret into your authenticator app:
      <span id="totp-secret"></span>
    </p>

    <label for="totp-code">
      Enter the code from your authenticator app
    </label>
    <input id="totp-code" type="text" />
    <button id="verify-totp">Verify TOTP</button>
  </div>

  <h2>Response:</h2>
  <pre id="response"></pre>

  <!-- Initialize Clerk with your
  Clerk Publishable key and Frontend API URL -->
  <script
    async
    crossorigin="anonymous"
    data-clerk-publishable-key="YOUR_PUBLISHABLE_KEY"
    src="https://YOUR_FRONTEND_API_URL/npm/@clerk/clerk-js@latest/dist/clerk.browser.js"
    type="text/javascript"
  ></script>

  <script>
    window.addEventListener("load", async function () {
      await Clerk.load();
      if (Clerk.user) {
        // Create and verify TOTP
        document.getElementById("create-totp")
          .addEventListener("click", async () => {
            Clerk.user.createTOTP()
              .then((res) => {
                // Show the TOTP container with the secret and verify button
                document.getElementById("totp-container").removeAttribute("hidden");

                // Display the secret to the user
                document.getElementById("totp-secret").innerHTML = res.secret;

                // Add event listener to verify TOTP button
                document.getElementById("verify-totp")
                  .addEventListener("click", async () => {
                    const code = document.getElementById("totp-code").value;
                    Clerk.user.verifyTOTP({ code })
                      .then((res) => {
                        document.getElementById("response").innerHTML =
                          JSON.stringify(res);
                      })
                      .catch((error) => {
                        document.getElementById("error-container").removeAttribute("hidden");
                        document.getElementById("error-message").innerHTML =
                          error.errors[0].longMessage;
                        console.log("An error occurred:", error.errors);
                      });
                  });
              })
              .catch((error) => {
                document.getElementById("error-container").removeAttribute("hidden");
                document.getElementById("error-message").innerHTML =
                  error.errors[0].longMessage;
                console.log("An error occurred:", error.errors);
              });
        });

        // Disable TOTP
        document.getElementById("disable-totp")
          .addEventListener("click", async () => {
            Clerk.user.disableTOTP()
              .then((res) => {
                document.getElementById("response").innerHTML = JSON.stringify(res);
              })
              .catch((error) => {
                document.getElementById("error-container").removeAttribute("hidden");
                document.getElementById("error-message").innerHTML =
                  error.errors[0].longMessage;
                console.log("An error occurred:", error.errors);
              });
          });

        // Create backup codes
        document.getElementById("create-backup-codes")
          .addEventListener("click", async () => {
            Clerk.user.createBackupCode()
              .then((res) => {
                document.getElementById("response").innerHTML = JSON.stringify(res);
              })
              .catch((error) => {
                document.getElementById("error-container").removeAttribute("hidden");
                document.getElementById("error-message").innerHTML =
                  error.errors[0].longMessage;
                console.log("An error occurred:", error.errors);
              });
          });
      } else {
        document.getElementById("app").innerHTML = `
            <div id="sign-in"></div>
          `;

        const signInDiv = document.getElementById("sign-in");

        Clerk.mountSignIn(signInDiv);
      }
    });
  </script>

Feedback

What did you think of this content?