# How Logto's Verification Code System Works: Architecture and Implementation

> Discover how Logto's verification code system works. Learn about its secure passcode generation, rate limiting with Sentinel, and tenant policy validation for enhanced security.

- Repository: [Logto/logto](https://github.com/logto-io/logto)
- Tags: architecture
- Published: 2026-07-05

---

**Logto's verification code system uses a multi-layered pipeline that generates cryptographically secure 6-digit passcodes, enforces rate limits via Sentinel, and validates codes against configurable tenant policies with automatic expiration and retry limits.**

Logto's open-source identity platform provides a robust verification code system for secure email and SMS authentication. According to the logto-io/logto source code, this system spans the Management API, a dedicated passcode library, PostgreSQL persistence, and messaging connectors. The architecture follows a strict lifecycle: request generation, secure delivery, and policy-enforced verification.

## The Verification Code Lifecycle

The verification code system operates through four distinct phases implemented across multiple layers of the Logto architecture.

### Requesting a Verification Code

The Management API exposes the endpoint `POST /verification-codes` in [`packages/core/src/routes/verification-code.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/routes/verification-code.ts). Incoming requests undergo strict validation via `requestVerificationCodePayloadGuard`, which ensures the payload contains valid email or phone identifiers along with template specifications.

Before processing, the route invokes `withMessageRateGuard` to check the recipient against Sentinel activity logs under `VerificationCodeSend`. This rate-limiting mechanism prevents abuse by restricting how frequently codes can be dispatched to a single destination.

### Generating and Storing Passcodes

Code generation occurs in the passcode library at [`packages/core/src/libraries/passcode.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/libraries/passcode.ts). The `createPasscode` function first executes a cleanup operation, removing any existing unconsumed passcodes associated with the same interaction (`jti`) or identifier to ensure only one active code exists per session.

Randomization uses `customAlphabet('1234567890', 6)` to produce cryptographically secure 6-digit numeric strings. The system persists these through `insertPasscode` in [`packages/core/src/queries/passcode.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/queries/passcode.ts), which executes an `INSERT … RETURNING *` SQL statement to retrieve the stored record.

### Sending the Code

The `sendPasscode` function resolves the appropriate messaging connector based on the identifier type—SMS or Email. Located in the same passcode library, this method utilizes the connector's `sendMessage` implementation to deliver the code.

Logto enriches message templates via `buildVerificationCodeContext`, which gathers tenant-specific data including application details, organization context, and user information. This context supports dynamic template variables defined in [`packages/core/src/libraries/passcode.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/libraries/passcode.ts) lines 176-221.

### Verifying and Consuming Codes

Verification occurs at `POST /verification-codes/verify`, validated by `verifyVerificationCodePayloadGuard`. The `verifyPasscode` function queries unconsumed records by `jti` or identifier and enforces tenant-level verification code policies stored in the sign-in experience configuration.

Default policies defined in [`packages/schemas/src/consts/verification-code.ts`](https://github.com/logto-io/logto/blob/main/packages/schemas/src/consts/verification-code.ts) specify a **10-minute expiration** window and **10 maximum retry attempts**. Failed verifications throw specific `RequestError` instances such as `verification_code.expired` or `verification_code.code_mismatch`. Upon successful validation, `consumePasscode` marks the record as used to prevent replay attacks.

## Security Mechanisms

Logto implements defense-in-depth for verification code security through multiple layers.

### Rate Limiting and Sentinel Integration

The `withMessageRateGuard` middleware in the verification code routes integrates with Logto's Sentinel system to track send frequency. This prevents brute-force attacks by enforcing time-based restrictions on verification code dispatch operations.

### Tenant-Level Policy Configuration

Administrators customize verification behavior through the sign-in experience settings. The system queries `queries.signInExperiences.findDefaultSignInExperience()` to retrieve tenant-specific overrides for expiration duration and retry limits, falling back to `defaultVerificationCodePolicy` constants when custom values are unspecified.

## Implementation Examples

### Request a Verification Code via Management API

```http
POST /api/verification-codes
Content-Type: application/json

{
  "email": "alice@example.com",
  "templateId": "login"
}

```

This endpoint creates a passcode, stores it in the database, and triggers delivery to the specified email address.

### Verify the Received Code

```http
POST /api/verification-codes/verify
Content-Type: application/json

{
  "email": "alice@example.com",
  "verificationCode": "123456",
  "templateId": "login"
}

```

Successful verification returns `204 No Content`. The passcode is immediately consumed and cannot be reused.

### Using the Passcode Library Programmatically

```typescript
import { createPasscodeLibrary } from '@logto/core';
import queries from './queries';
import connectorLibrary from './connector-library';

const { createPasscode, sendPasscode, verifyPasscode } = createPasscodeLibrary(
  queries,
  connectorLibrary
);

// Generate and send
const passcode = await createPasscode(undefined, TemplateType.Generic, { email: 'bob@example.com' });
await sendPasscode(passcode);

// Later, verify user input
await verifyPasscode(undefined, TemplateType.Generic, '987654', { email: 'bob@example.com' });

```

## Summary

- **Logto's verification code system** spans API routes, a dedicated passcode library, and PostgreSQL persistence to manage secure one-time codes.
- **Passcode generation** uses cryptographically secure 6-digit randomization via `createPasscode` in [`packages/core/src/libraries/passcode.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/libraries/passcode.ts).
- **Rate limiting** via Sentinel's `withMessageRateGuard` prevents abuse during the request phase.
- **Policy enforcement** applies default 10-minute expiration and 10-attempt limits, configurable per tenant through sign-in experience settings.
- **Verification** queries unconsumed codes by interaction ID or identifier, with `consumePasscode` ensuring single-use semantics.

## Frequently Asked Questions

### How long do Logto verification codes remain valid?

By default, verification codes expire after **10 minutes** from creation. This duration is defined in [`packages/schemas/src/consts/verification-code.ts`](https://github.com/logto-io/logto/blob/main/packages/schemas/src/consts/verification-code.ts) within the `defaultVerificationCodePolicy` constant. Administrators can override this default through tenant-specific sign-in experience configurations.

### What happens if a user enters the wrong verification code multiple times?

Logto enforces a maximum retry limit of **10 attempts** per verification code. After exceeding this threshold, the `verifyPasscode` function in [`packages/core/src/libraries/passcode.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/libraries/passcode.ts) throws a `RequestError` with code `verification_code.code_mismatch` or similar, rendering the current code invalid and requiring a new code request.

### Can I customize the verification code expiration time?

Yes. While the system defaults to 10 minutes, you can configure tenant-level verification code policies through the sign-in experience settings. The system retrieves these custom values via `queries.signInExperiences.findDefaultSignInExperience()` and applies them during the `verifyPasscode` execution.

### How does Logto prevent brute-force attacks on verification codes?

The system implements **two-layer protection**: first, the `withMessageRateGuard` middleware restricts how frequently codes can be sent to a single email or phone number through Sentinel integration. Second, the verification phase enforces strict retry limits and automatic expiration, making automated brute-force attacks computationally infeasible within the valid timeframe.