# Implementing Passwordless Authentication with Passcodes in Logto: A Complete Technical Guide

> Implement passwordless authentication with passcodes in Logto. This guide shows how Logto securely delivers and verifies 6-digit passcodes via email or SMS for enhanced user access.

- Repository: [Logto/logto](https://github.com/logto-io/logto)
- Tags: how-to-guide
- Published: 2026-07-04

---

**Logto enables passwordless authentication by generating cryptographically secure 6-digit passcodes, delivering them via configurable email or SMS connectors, and verifying them against tenant-specific policies that enforce expiration windows and retry limits.**

Implementing passwordless authentication with passcodes in Logto provides users with a secure, frictionless sign-in experience without requiring memorized passwords. The `logto-io/logto` repository implements this flow through a tenant-aware **PasscodeLibrary** that handles code generation, message delivery via the ConnectorKit API, and strict policy enforcement. This guide examines the source code architecture and provides practical implementation examples for integrating passcode-based verification into your identity management workflows.

## Core Architecture of Logto's Passcode System

Logto's passcode implementation follows a modular architecture separating concerns between library logic, database queries, API routes, and frontend components.

### PasscodeLibrary Factory

The **`createPasscodeLibrary`** function in [[`packages/core/src/libraries/passcode.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/libraries/passcode.ts)](https://github.com/logto-io/logto/blob/master/packages/core/src/libraries/passcode.ts) serves as the central factory that exposes three core operations:

- **`createPasscode`** – Generates a random 6-digit numeric code and handles database cleanup of existing unconsumed passcodes for the same `jti` (session identifier) or user identifier.
- **`sendPasscode`** – Routes the generated code to the appropriate message connector (Email or SMS) based on whether the payload contains an `email` or `phone` field.
- **`verifyPasscode`** – Validates the submitted code against the stored record, checking expiration timestamps, retry counts, and identifier matches.

### Database Query Layer

Low-level database operations reside in [[`packages/core/src/queries/passcode.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/queries/passcode.ts)](https://github.com/logto-io/logto/blob/master/packages/core/src/queries/passcode.ts). These **transactions** ensure data consistency during the verification flow:

- `insertPasscode` – Creates the initial passcode record with `tryCount` initialized to zero.
- `findUnconsumedPasscodeByJtiAndIdentifier` – Retrieves active codes excluding already-verified entries.
- `consumePasscode` – Marks the passcode as used after successful verification.
- `increasePasscodeTryCount` – Increments the attempt counter during validation attempts.
- `deletePasscodesByIds` – Removes expired or consumed codes during cleanup operations.

### Management API Endpoints

The [[`packages/core/src/routes/verification-code.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/routes/verification-code.ts)](https://github.com/logto-io/logto/blob/master/packages/core/src/routes/verification-code.ts) file exposes two public endpoints secured by Sentinel rate-limiting:

- **`POST /verification-codes`** – Initiates the flow by invoking `createPasscode` and `sendPasscode`.
- **`POST /verification-codes/verify`** – Validates user input by calling `verifyPasscode`.

These routes return `204 No Content` on success or `400 Bad Request` with specific error codes (e.g., `verification_code.expired`, `verification_code.code_mismatch`) on failure.

## Security Policies and Validation Logic

Logto enforces **tenant-aware verification policies** that apply to all passcode operations within a specific tenant context.

### Policy Enforcement Mechanism

The `verifyPasscode` implementation retrieves the tenant's `verificationCodePolicy` configuration (or falls back to system defaults) and performs three critical checks:

1. **Expiration validation** – Compares `passcode.createdAt + expirationMs` against `Date.now()` to reject stale codes.
2. **Retry limiting** – Validates that `passcode.tryCount` remains below `maxTryCount` to prevent brute-force attacks.
3. **Identifier binding** – Ensures the email or phone number submitted during verification matches the original identifier stored with the passcode.

If any check fails, the library throws a `RequestError` with a machine-readable error code suitable for frontend localization.

### Message Connector Integration

The `sendPasscode` function dynamically selects connectors based on the identifier type. Any connector implementing the **ConnectorKit** API can handle delivery, including custom SMTP servers, Twilio SMS, or proprietary messaging services. The `buildVerificationCodeContext` helper enriches message templates with contextual data such as application name, organization details, and user metadata.

## Implementation Examples

### Requesting a Passcode via REST API

Send a `POST` request to create and dispatch a new passcode:

```bash
curl -X POST https://your-logto-instance.com/api/verification-codes \
  -H "Content-Type: application/json" \
  -d '{"email":"user@example.com"}' \
  -i

```

A successful request returns `204 No Content` and triggers the configured email connector to deliver the 6-digit code.

### Verifying a Received Passcode

Submit the user-entered code for validation:

```bash
curl -X POST https://your-logto-instance.com/api/verification-codes/verify \
  -H "Content-Type: application/json" \
  -d '{"email":"user@example.com","verificationCode":"123456"}' \
  -i

```

Success yields `204 No Content`. Invalid submissions return `400 Bad Request` with an error code such as `verification_code.invalid_passcode` or `verification_code.expired`.

### JavaScript SDK Integration

Use the Logto client SDK to handle passcode flows programmatically:

```javascript
import { LogtoClient } from '@logto/client';

const logto = new LogtoClient({
  endpoint: 'https://your-logto-instance.com',
  appId: 'your-app-id',
});

// Request passcode delivery
await logto.sendVerificationCode({ email: 'user@example.com' });

// Verify user input
await logto.verifyVerificationCode({
  email: 'user@example.com',
  verificationCode: '123456',
});

```

The SDK automatically surfaces `RequestError` instances as JavaScript exceptions with detailed error codes for error handling.

### React Component Integration

For custom sign-in experiences, import the official verification component:

```tsx
import VerificationCode from '@/packages/experience/src/shared/components/VerificationCode';

function SignInPage() {
  const [code, setCode] = useState('');
  
  return (
    <VerificationCode
      name="passcode"
      value={code}
      onChange={setCode}
      placeholder="Enter verification code"
    />
  );
}

```

The component automatically displays localized error messages (e.g., `error.invalid_passcode`) when bound to the Logto error state.

## Summary

- **Logto's passcode system** lives in [`packages/core/src/libraries/passcode.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/libraries/passcode.ts) and provides `createPasscode`, `sendPasscode`, and `verifyPasscode` functions.
- **Database operations** in [`packages/core/src/queries/passcode.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/queries/passcode.ts) handle atomic updates for try counts, consumption status, and cleanup.
- **Management API endpoints** at `/verification-codes` and `/verification-codes/verify` expose the functionality to clients with built-in rate limiting.
- **Security policies** enforce expiration windows, maximum retry attempts, and identifier binding at the tenant level.
- **Frontend integration** utilizes the `VerificationCode` React component and supports full localization via `packages/phrases-experience`.

## Frequently Asked Questions

### What is the default expiration time for passcodes in Logto?

Logto applies the `verificationCodePolicy` configuration defined at the tenant level, which specifies the expiration duration in milliseconds. If the tenant has not customized this policy, the system falls back to a default expiration window that ensures codes remain valid long enough for email/SMS delivery but short enough to mitigate replay attacks.

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

The `verifyPasscode` function in [`packages/core/src/libraries/passcode.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/libraries/passcode.ts) increments the `tryCount` field via `increasePasscodeTryCount` on each failed attempt and compares it against the `maxTryCount` policy limit. Once the threshold is exceeded, subsequent verification attempts automatically fail with a `verification_code.code_mismatch` error until a new passcode is generated.

### Can I customize the email template for passcode delivery?

Yes. The `sendPasscode` function passes the generated code to your configured message connector through the ConnectorKit API. You can define custom templates in your email connector configuration (such as SendGrid or SMTP), and the `buildVerificationCodeContext` helper can inject additional variables like application name or organization branding into the template context.

### What happens when a user requests multiple passcodes before using the first one?

The `createPasscode` implementation automatically deletes existing unconsumed passcodes for the same `jti` (session identifier) or user identifier before inserting a new record. This ensures only one active passcode exists per session, preventing confusion and reducing database clutter while maintaining security.