<!-- llms.txt: https://workos.com/llms.txt -->

# Multi-Factor Authentication

## Introduction

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 password
- `sms` – One-time password via SMS message (US only)

> The 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.

## What you'll build

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:

1. Create an Authentication Factor
2. Challenge the Authentication Factor
3. Verify the Challenge

## Before getting started

To get the most out of this guide, you'll need:

- A [WorkOS account](https://dashboard.workos.com/)

## API object definitions

[Authentication Factor](https://workos.com/docs/reference/mfa/factor)
: A factor of authentication that can be used in conjunction with a primary factor to provide multiple factors of authentication.

[Authentication Challenge](https://workos.com/docs/reference/mfa/challenge)
: A request for an Authentication Factor to be verified.

## (1) Create an Authentication Factor

We'll first need to enroll a new Authentication Factor.

### Install the WorkOS SDK

WorkOS offers native SDKs in several popular programming languages. Choose a language below to see instructions in your application's language.

Install the SDK using the command below.

:::code-group{title="Install the WorkOS SDK"}

```bash language="js" title="npm" tab="js-1"
npm install @workos-inc/node
```

```bash language="js" title="Yarn" tab="js-2"
yarn add @workos-inc/node
```

```bash language="ruby" title="Terminal" tab="ruby-5"
gem install workos
```

```rb language="ruby" title="Bundler" tab="ruby-6"
gem "workos"
```

```bash language="python"
pip install workos
```

```bash language="go"
go get github.com/workos/workos-go/v10
```

```bash language="php"
composer require workos/workos-php
```

```bash language="laravel"
composer require workos/workos-php-laravel
```

```xml language="java" title="Maven" tab="java-3"
<dependency>
  <groupId>com.workos</groupId>
  <artifactId>workos</artifactId>
  <version>{version}</version>
</dependency>
```

```groovy language="java" title="Gradle" tab="java-4"
dependencies {
  implementation 'com.workos:workos:VERSION'
}
```

```bash language="dotnet"
nuget install WorkOS.net
```

```elixir language="elixir"
def deps do
  [{:workos, "~> 3.1"}]
end
```

```bash language="rust"
cargo add workos
```

:::

### Set secrets

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.

```plain title="Environment variables"
WORKOS_API_KEY='sk_example_123456789'
WORKOS_CLIENT_ID='client_123456789'
```

### Enroll the Authentication Factor

- | Using TOTP

  Use the TOTP type when the user is using a third-party authenticator app such as Google Authenticator or Authy.

  :::code-group{title="Enroll Endpoint"}

  ```js language="js"
  import { WorkOS } from '@workos-inc/node';

  const workos = new WorkOS('sk_example_123456789');

  const factor = await workos.multiFactorAuth.enrollFactor({
    type: 'totp',
    issuer: 'Foo Corp',
    user: 'alan.turing@example.com',
  });
  ```

  ```rb language="ruby"
  # workos:manual - preserve the required TOTP labels
  require "workos"

  WorkOS.configure do |config|
    config.api_key = "sk_example_123456789"
  end

  WorkOS.client.multi_factor_auth.enroll_factor(
    type: "totp",
    totp_issuer: "Foo Corp",
    totp_user: "alan.turing@example.com"
  )
  ```

  ```py language="python"
  # workos:manual - preserve the required TOTP labels
  from workos import WorkOSClient

  client = WorkOSClient(api_key="sk_example_123456789", client_id="client_123456789")

  client.multi_factor_auth.enroll_factor(
      type="totp",
      totp_issuer="Foo Corp",
      totp_user="alan.turing@example.com",
  )
  ```

  ```go language="go"
  // workos:manual - preserve the required TOTP labels
  package main

  import (
  	"context"

  	"github.com/workos/workos-go/v10"
  )

  func main() {
  	client := workos.NewClient("sk_example_123456789")

  	totpIssuer := "Foo Corp"
  	totpUser := "alan.turing@example.com"
  	_, err := client.MultiFactorAuth().EnrollFactor(context.Background(), &workos.MultiFactorAuthEnrollFactorParams{
  		Type:       "totp",
  		TOTPIssuer: &totpIssuer,
  		TOTPUser:   &totpUser,
  	})
  	if err != nil {
  		// Handle the error according to your application's needs.
  		return
  	}
  }
  ```

  ```php language="php"
  <?php

  // workos:manual - preserve the required TOTP labels
  use WorkOS\WorkOS;

  $workos = new WorkOS(
      apiKey: "sk_example_123456789",
      clientId: "client_123456789",
  );

  $workos
      ->multiFactorAuth()
      ->enrollFactor(
          type: "totp",
          totpIssuer: "Foo Corp",
          totpUser: "alan.turing@example.com",
      );
  ```

  ```php language="laravel"
  <?php

  use WorkOS\Laravel\Facades\WorkOS;
  use WorkOS\Resource\AuthenticationFactorsCreateRequestType;

  $type = AuthenticationFactorsCreateRequestType::Totp;
  $totpIssuer = "Foo Corp";
  $totpUser = "alan.turing@example.com";

  $factor = WorkOS::multiFactorAuth()->enrollFactor(
      type: $type,
      totpIssuer: $totpIssuer,
      totpUser: $totpUser,
  );
  ```

  ```java language="java"
  // workos:manual - preserve the required TOTP labels in the current Java SDK
  import com.workos.WorkOS;
  import com.workos.types.AuthenticationFactorsCreateRequestType;

  WorkOS workos = new WorkOS("sk_example_123456789");

  workos.getMultiFactorAuth().enrollFactor(AuthenticationFactorsCreateRequestType.Totp,
      null,
      "Foo Corp",
      "alan.turing@example.com",
      null);
  ```

  ```cs language="dotnet"
  // workos:manual - preserve the required TOTP labels
  using WorkOS;

  var client = new WorkOSClient(new WorkOSOptions {
      ApiKey = "sk_example_123456789",
      ClientId = "client_123456789",
  });

  await client.MultiFactorAuth.EnrollFactorAsync(new MultiFactorAuthEnrollFactorOptions {
      Type = "totp",
      TotpIssuer = "Foo Corp",
      TotpUser = "alan.turing@example.com",
  });
  ```

  ```elixir language="elixir"
  # workos:manual - preserve the required TOTP labels
  client =
    WorkOS.client(
      api_key: "sk_example_123456789",
      client_id: "client_123456789"
    )

  WorkOS.MultiFactorAuth.enroll_factor(client, %{
    type: "totp",
    totp_issuer: "Foo Corp",
    totp_user: "alan.turing@example.com"
  })
  ```

  ```rust language="rust"
  // workos:manual - preserve required TOTP labels in the current Rust request model
  use workos::multi_factor_auth::EnrollFactorParams;
  use workos::{AuthenticationFactorsCreateRequest, AuthenticationFactorsCreateRequestType, Client};

  #[tokio::main]
  async fn main() -> Result<(), workos::Error> {
      let client = Client::builder()
          .api_key("sk_example_123456789")
          .client_id("client_123456789")
          .build();

      let _factor = client
          .multi_factor_auth()
          .enroll_factor(EnrollFactorParams::new(
              AuthenticationFactorsCreateRequest {
                  type_: AuthenticationFactorsCreateRequestType::Totp,
                  phone_number: None,
                  totp_issuer: Some("Foo Corp".into()),
                  totp_user: Some("alan.turing@example.com".into()),
                  user_id: None,
              },
          ))
          .await?;

      Ok(())
  }
  ```

  :::

  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](https://css-tricks.com/data-uris/) 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.

- | Using SMS

  Use the SMS type when the user wants to receive one time passwords as SMS messages to their mobile device.

  Phone number must be a valid US number. An error will be returned for malformed or invalid phone numbers.

  :::code-group{title="Enroll Endpoint"}

  ```js language="js"
  import { WorkOS } from '@workos-inc/node';

  const workos = new WorkOS('sk_example_123456789');

  const factor = await workos.multiFactorAuth.enrollFactor({
    type: 'sms',
    phoneNumber: '+15005550006',
  });
  ```

  ```rb language="ruby"
  require "workos"

  WorkOS.configure do |config|
    config.api_key = "sk_example_123456789"
  end

  WorkOS.client.multi_factor_auth.enroll_factor(
    type: "sms",
    phone_number: "+15005550006"
  )
  ```

  ```py language="python"
  from workos import WorkOSClient

  workos_client = WorkOSClient(
      api_key="sk_example_123456789", client_id="client_123456789"
  )

  factor_type = "sms"
  phone_number = "+15005550006"
  response = workos_client.mfa.enroll_factor(type=factor_type, phone_number=phone_number)
  ```

  ```go language="go"
  package main

  import (
  	"context"
  	workos "github.com/workos/workos-go/v10"
  )

  func main() {
  	client := workos.NewClient("sk_example_123456789")

  	enroll, err := client.MultiFactorAuth().EnrollFactor(
  		context.Background(),
  		&workos.MultiFactorAuthEnrollFactorParams{
  			Type:        workos.AuthenticationFactorsCreateRequestTypeSms,
  			PhoneNumber: workos.String("+15005550006"),
  		},
  	)
  	if err != nil {
  		// Handle the error...
  	}

  	_ = enroll
  }
  ```

  ```php language="php"
  <?php

  $workos = new \WorkOS\WorkOS();

  $type = \WorkOS\Resource\AuthenticationFactorsCreateRequestType::Sms;
  $phoneNumber = "+15005550006";

  $factor = $workos
      ->multiFactorAuth()
      ->enrollFactor(type: $type, phoneNumber: $phoneNumber);
  ```

  ```php language="laravel"
  <?php

  use WorkOS\Laravel\Facades\WorkOS;
  use WorkOS\Resource\AuthenticationFactorsCreateRequestType;

  $type = AuthenticationFactorsCreateRequestType::Sms;
  $phoneNumber = "+15005550006";

  $factor = WorkOS::multiFactorAuth()->enrollFactor(
      type: $type,
      phoneNumber: $phoneNumber,
  );
  ```

  ```java language="java"
  import com.workos.WorkOS;
  import com.workos.mfa.MfaApi.EnrollFactorOptions;
  import com.workos.mfa.models.Factor;

  WorkOS workos = new WorkOS("sk_example_123456789");

  EnrollFactorOptions options =
      EnrollFactorOptions.builder().type("sms").phoneNumber("+15005550006").build();

  Factor factor = workos.mfa.enrollFactor(options);
  ```

  ```cs language="dotnet"
  WorkOS.SetApiKey("sk_example_123456789");

  var mfaService = new MfaService();

  var options = new EnrollSmsFactorOptions("+15005550006");
  var factor = await mfaService.EnrollFactor(options);
  ```

  ```elixir language="elixir"
  client =
    WorkOS.client(
      api_key: "sk_example_123456789",
      client_id: "client_123456789"
    )

  WorkOS.MultiFactorAuth.enroll_factor(client, %{
    type: "sms",
    phone_number: "+15005550006"
  })
  ```

  ```rust language="rust"
  // workos:manual - target the current Rust SDK request model
  use workos::multi_factor_auth::EnrollFactorParams;
  use workos::{AuthenticationFactorsCreateRequest, AuthenticationFactorsCreateRequestType, Client};

  #[tokio::main]
  async fn main() -> Result<(), workos::Error> {
      let client = Client::builder()
          .api_key("sk_example_123456789")
          .client_id("client_123456789")
          .build();

      let _factor = client
          .multi_factor_auth()
          .enroll_factor(EnrollFactorParams::new(
              AuthenticationFactorsCreateRequest {
                  type_: AuthenticationFactorsCreateRequestType::Sms,
                  phone_number: Some("+15005550006".into()),
                  totp_issuer: None,
                  totp_user: None,
                  user_id: None,
              },
          ))
          .await?;

      Ok(())
  }
  ```

  :::

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.

## (2) Challenge the Authentication Factor

Next we'll initiate the authentication process for the newly created factor which we'll refer to as a challenge.

- | Create Authentication Challenge

  :::code-group{title="Challenge Endpoint"}

  ```js language="js"
  import { WorkOS } from '@workos-inc/node';

  const workos = new WorkOS('sk_example_123456789');

  const challenge = await workos.multiFactorAuth.challengeFactor({
    authenticationFactorId: 'auth_factor_01FVYZ5QM8N98T9ME5BCB2BBMJ',
  });
  ```

  ```rb language="ruby"
  require "workos"

  WorkOS.configure do |config|
    config.api_key = "sk_example_123456789"
  end

  WorkOS.client.multi_factor_auth.challenge_factor(id: "auth_factor_01FVYZ5QM8N98T9ME5BCB2BBMJ")
  ```

  ```py language="python"
  from workos import WorkOSClient

  client = WorkOSClient(api_key="sk_example_123456789", client_id="client_123456789")

  client.multi_factor_auth.challenge_factor(id_="auth_factor_01FVYZ5QM8N98T9ME5BCB2BBMJ")
  ```

  ```go language="go"
  // workos:manual - do not regenerate; see scripts/generate-sdk-snippets.ts
  package main

  import (
  	"context"

  	"github.com/workos/workos-go/v10"
  )

  func main() {
  	client := workos.NewClient("sk_example_123456789")

  	_, err := client.MultiFactorAuth().ChallengeFactor(context.Background(), "auth_factor_01FVYZ5QM8N98T9ME5BCB2BBMJ", &workos.MultiFactorAuthChallengeFactorParams{})
  	if err != nil {
  		// Handle the error according to your application's needs.
  		return
  	}
  }
  ```

  ```php language="php"
  <?php

  use WorkOS\WorkOS;

  $workos = new WorkOS(
      apiKey: "sk_example_123456789",
      clientId: "client_123456789",
  );

  $workos
      ->multiFactorAuth()
      ->challengeFactor(id: "auth_factor_01FVYZ5QM8N98T9ME5BCB2BBMJ");
  ```

  ```php language="laravel"
  <?php

  use WorkOS\Laravel\Facades\WorkOS;

  $authenticationFactorId = "auth_factor_01FXNWW32G7F3MG8MYK5D1HJJM";

  $challenge = WorkOS::multiFactorAuth()->challengeFactor(
      $authenticationFactorId,
  );
  ```

  ```java language="java"
  import com.workos.WorkOS;

  WorkOS workos = new WorkOS("sk_example_123456789");

  workos.multiFactorAuth.challengeFactor("auth_factor_01FVYZ5QM8N98T9ME5BCB2BBMJ");
  ```

  ```cs language="dotnet"
  using WorkOS;

  var client = new WorkOSClient(new WorkOSOptions {
      ApiKey = "sk_example_123456789",
      ClientId = "client_123456789",
  });

  await client.MultiFactorAuth.ChallengeFactorAsync("auth_factor_01FVYZ5QM8N98T9ME5BCB2BBMJ");
  ```

  ```elixir language="elixir"
  client =
    WorkOS.client(
      api_key: "sk_example_123456789",
      client_id: "client_123456789"
    )

  WorkOS.MultiFactorAuth.challenge_factor(
    client,
    "auth_factor_01FVYZ5QM8N98T9ME5BCB2BBMJ"
  )
  ```

  ```rust language="rust"
  // workos:manual - target the current Rust SDK request model
  use workos::multi_factor_auth::ChallengeFactorParams;
  use workos::{ChallengeAuthenticationFactor, Client};

  #[tokio::main]
  async fn main() -> Result<(), workos::Error> {
      let client = Client::builder()
          .api_key("sk_example_123456789")
          .client_id("client_123456789")
          .build();

      let _challenge = client
          .multi_factor_auth()
          .challenge_factor(
              "auth_factor_01FVYZ5QM8N98T9ME5BCB2BBMJ",
              ChallengeFactorParams::new(ChallengeAuthenticationFactor { sms_template: None }),
          )
          .await?;

      Ok(())
  }
  ```

  :::

- | Sending Custom SMS Message

  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.

  :::code-group{title="Challenge Endpoint"}

  ```js language="js"
  import { WorkOS } from '@workos-inc/node';

  const workos = new WorkOS('sk_example_123456789');

  const enrollResponse = await workos.multiFactorAuth.challengeFactor({
    authenticationFactorId: 'auth_factor_01FVYZ5QM8N98T9ME5BCB2BBMJ',
    smsTemplate: 'Your FooCorp is {{code}}.',
  });
  ```

  ```rb language="ruby"
  require "workos"

  workos = WorkOS::Client.new(api_key: "sk_example_123456789")

  challenge = workos.multi_factor_auth.challenge_factor(
    id: "auth_factor_01FZ4TS14D1PHFNZ9GF6YD8M1F",
    sms_template: "Your code is {{code}}"
  )
  ```

  ```py language="python"
  from workos import WorkOSClient

  workos_client = WorkOSClient(
      api_key="sk_example_123456789", client_id="client_123456789"
  )

  factor_id = "auth_factor_01FY7SABJNSPYR7CT052GNDQ49"
  message = "Your code is {{code}}"
  response = workos_client.mfa.challenge_factor(
      authentication_factor_id=factor_id, sms_template=message
  )
  ```

  ```go language="go"
  package main

  import (
  	"context"
  	workos "github.com/workos/workos-go/v10"
  )

  func main() {
  	client := workos.NewClient("sk_example_123456789")

  	challenge, err := client.MultiFactorAuth().ChallengeFactor(
  		context.Background(),
  		"auth_factor_01FVYZ5QM8N98T9ME5BCB2BBM",
  		&workos.MultiFactorAuthChallengeFactorParams{
  			SmsTemplate: workos.String("Your code is {{code}}"),
  		},
  	)
  	if err != nil {
  		// Handle the error...
  	}

  	_ = challenge
  }
  ```

  ```php language="php"
  <?php

  $workos = new \WorkOS\WorkOS();

  $authenticationFactorId = "auth_factor_01FXNWW32G7F3MG8MYK5D1HJJM";
  $smsTemplate = "Your code is {{code}}";

  $challenge = $workos
      ->multiFactorAuth()
      ->challengeFactor($authenticationFactorId, $smsTemplate);
  ```

  ```php language="laravel"
  <?php

  use WorkOS\Laravel\Facades\WorkOS;

  $authenticationFactorId = "auth_factor_01FXNWW32G7F3MG8MYK5D1HJJM";
  $smsTemplate = "Your code is {{code}}";

  $challenge = WorkOS::multiFactorAuth()->challengeFactor(
      $authenticationFactorId,
      $smsTemplate,
  );
  ```

  ```java language="java"
  import com.workos.WorkOS;
  import com.workos.mfa.MfaApi.ChallengeFactorOptions;
  import com.workos.mfa.models.Challenge;

  WorkOS workos = new WorkOS("sk_example_123456789");

  String authenticationFactorId = "auth_factor_01FY7SABJNSPYR7CT052GNDQ49";
  String smsTemplate = "Your code is {{code}}";

  ChallengeFactorOptions options = ChallengeFactorOptions.builder()
                                       .authenticationFactorId(authenticationFactorId)
                                       .smsTemplate(smsTemplate)
                                       .build();

  Challenge challenge = workos.mfa.challengeFactor(options);
  ```

  ```cs language="dotnet"
  WorkOS.SetApiKey("sk_example_123456789");

  var mfaService = new MfaService();

  var options = new ChallengeSmsFactorOptions("Your FooCorp is {{code}}.") {
      FactorId = "auth_factor_01FVYZ5QM8N98T9ME5BCB2BBMJ",
  };

  var challenge = await mfaService.ChallengeFactor(options);
  ```

  ```elixir language="elixir"
  client =
    WorkOS.client(
      api_key: "sk_example_123456789",
      client_id: "client_123456789"
    )

  WorkOS.MultiFactorAuth.challenge_factor(
    client,
    "auth_factor_01FY7SABJNSPYR7CT052GNDQ49",
    %{sms_template: "Your code is {{code}}"}
  )
  ```

  ```rust language="rust"
  // workos:manual - target the current Rust SDK request model
  use workos::multi_factor_auth::ChallengeFactorParams;
  use workos::{ChallengeAuthenticationFactor, Client};

  #[tokio::main]
  async fn main() -> Result<(), workos::Error> {
      let client = Client::builder()
          .api_key("sk_example_123456789")
          .client_id("client_123456789")
          .build();

      let _challenge = client
          .multi_factor_auth()
          .challenge_factor(
              "auth_factor_01FY7SABJNSPYR7CT052GNDQ49",
              ChallengeFactorParams::new(ChallengeAuthenticationFactor {
                  sms_template: Some("Your code is {{code}}".into()),
              }),
          )
          .await?;

      Ok(())
  }
  ```

  :::

Now that we've successfully challenged the authentication factor, we'll need to save the challenge ID for the last step, challenge verification.

## (3) Verify the Challenge

The last step in the authentication process is to verify the one time password provided by the end-user.

:::code-group{title="Verify Endpoint"}

```js language="js"
import { WorkOS } from '@workos-inc/node';

const workos = new WorkOS('sk_example_123456789');

const { challenge, valid } = await workos.multiFactorAuth.verifyChallenge({
  authenticationChallengeId: 'auth_challenge_01FVYZWQTZQ5VB6BC5MPG2EYC5',
  code: '123456',
});
```

```rb language="ruby"
require "workos"

WorkOS.configure do |config|
  config.api_key = "sk_example_123456789"
end

WorkOS.client.multi_factor_auth.verify_challenge(
  id: "auth_challenge_01FVYZ5QM8N98T9ME5BCB2BBMJ",
  code: "123456"
)
```

```py language="python"
from workos import WorkOSClient

client = WorkOSClient(api_key="sk_example_123456789", client_id="client_123456789")

client.multi_factor_auth.verify_challenge(
    id_="auth_challenge_01FVYZ5QM8N98T9ME5BCB2BBMJ", code="123456"
)
```

```go language="go"
package main

import (
	"context"

	"github.com/workos/workos-go/v10"
)

func main() {
	client := workos.NewClient("sk_example_123456789")

	_, err := client.MultiFactorAuth().VerifyChallenge(context.Background(), "auth_challenge_01FVYZ5QM8N98T9ME5BCB2BBMJ", &workos.MultiFactorAuthVerifyChallengeParams{
		Code: "123456",
	})
	if err != nil {
		// Handle the error according to your application's needs.
		return
	}
}
```

```php language="php"
<?php

use WorkOS\WorkOS;

$workos = new WorkOS(
    apiKey: "sk_example_123456789",
    clientId: "client_123456789",
);

$workos
    ->multiFactorAuth()
    ->verifyChallenge(
        id: "auth_challenge_01FVYZ5QM8N98T9ME5BCB2BBMJ",
        code: "123456",
    );
```

```php language="laravel"
<?php

use WorkOS\Laravel\Facades\WorkOS;

$authenticationChallengeId = "auth_challenge_01FXNX3BTZPPJVKF65NNWGRHZJ";
$code = "123456";

$response = WorkOS::multiFactorAuth()->verifyChallenge(
    $authenticationChallengeId,
    $code,
);
```

```java language="java"
import com.workos.WorkOS;
import com.workos.multifactorauth.MultiFactorAuthApi.VerifyChallengeOptions;

WorkOS workos = new WorkOS("sk_example_123456789");

VerifyChallengeOptions options = VerifyChallengeOptions.builder().code("123456").build();

workos.multiFactorAuth.verifyChallenge(
    "auth_challenge_01FVYZ5QM8N98T9ME5BCB2BBMJ", options);
```

```cs language="dotnet"
using WorkOS;

var client = new WorkOSClient(new WorkOSOptions {
    ApiKey = "sk_example_123456789",
    ClientId = "client_123456789",
});

await client.MultiFactorAuth.VerifyChallengeAsync("auth_challenge_01FVYZ5QM8N98T9ME5BCB2BBMJ",
                                                  new MultiFactorAuthVerifyChallengeOptions {
                                                      Code = "123456",
                                                  });
```

```elixir language="elixir"
client =
  WorkOS.client(
    api_key: "sk_example_123456789",
    client_id: "client_123456789"
  )

WorkOS.MultiFactorAuth.verify_challenge(
  client,
  "auth_challenge_01FVYZ5QM8N98T9ME5BCB2BBMJ",
  %{code: "123456"}
)
```

```rust language="rust"
// workos:manual - target the current Rust SDK request model
use workos::multi_factor_auth::VerifyChallengeParams;
use workos::{AuthenticationChallengesVerifyRequest, Client};

#[tokio::main]
async fn main() -> Result<(), workos::Error> {
    let client = Client::builder()
        .api_key("sk_example_123456789")
        .client_id("client_123456789")
        .build();

    let _challenge = client
        .multi_factor_auth()
        .verify_challenge(
            "auth_challenge_01FVYZ5QM8N98T9ME5BCB2BBMJ",
            VerifyChallengeParams::new(AuthenticationChallengesVerifyRequest {
                code: "123456".into(),
            }),
        )
        .await?;

    Ok(())
}
```

:::

### Verification Response

If the challenge is successfully verified `valid` will return `true`. Otherwise it will return `false` and another verification attempt must be made.

:::code-group{title="Response"}

```json language="json"
{
  "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
}
```

:::

### Already Verified Error

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-group{title="Response"}

```json language="json"
{
  "code": "authentication_challenge_previously_verified",
  "message": "The authentication challenge 'auth_challenge_01FVYZWQTZQ5VB6BC5MPG2EYC5' has already been verified."
}
```

:::

### Expired Error

For SMS authentication factors, challenges are only available for verification for 10 minutes. After that they are expired and cannot be verified.

:::code-group{title="Response"}

```json language="json"
{
  "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.
