One-Time Token Plugin

Secure verification tokens for sensitive operations

The one-time token plugin generates secure, single-use verification codes for sensitive operations like account deletion, organization deletion, and other high-security actions.

Overview

This plugin provides cryptographically secure 6-digit verification codes that:

  • Expire after 10 minutes
  • Can only be used once
  • Are stored as hashed values for security
  • Use Web Crypto API for secure random generation

Use Cases

The one-time token plugin is used for:

  • Account deletion verification
  • Organization deletion verification
  • Sensitive profile changes
  • Any operation requiring email-based confirmation

Configuration

Default Settings

SettingValueDescription
Expiration10 minutesToken validity period
StorageHashedTokens stored securely
Length6 digitsCode format (100000-999999)

Implementation

Location

packages/better-auth/src/plugins/one-time-token.ts

Token Generation

The plugin uses the Web Crypto API for cryptographically secure random number generation:

function generateSecureCode(): string {
const array = new Uint32Array(1);
crypto.getRandomValues(array);
// Generate 6-digit code using rejection sampling
// for uniform distribution
const code = 100000 + (array[0] % 900000);
return code.toString();
}

Plugin Configuration

import { oneTimeToken } from 'better-auth/plugins/one-time-token';
const oneTimeTokenPlugin = oneTimeToken({
expiresIn: 10, // 10 minutes
storeToken: 'hashed', // Store as hash
generateToken: generateSecureCode,
});

Development Mode

In development (NODE_ENV=development), generated codes are logged to the console:

[DEV] Verification code: 847293

This allows testing sensitive operations without checking email.

Usage Example

Delete Account Flow

// 1. Request verification code (sent via email)
await authClient.oneTimeToken.sendToken({
type: 'account-delete',
});
// 2. User receives email with 6-digit code
// 3. Verify code and complete action
await authClient.oneTimeToken.verifyToken({
token: '847293',
type: 'account-delete',
});

In the Application

The delete account dialog uses this flow:

// packages/account/ui/src/components/delete-account-dialog.tsx
// Step 1: Send verification email
const handleRequestCode = async () => {
await sendVerificationEmail();
setStep('verify');
};
// Step 2: Verify code and delete
const handleVerifyAndDelete = async (code: string) => {
await verifyCodeAndDeleteAccount(code);
};

Security Features

Cryptographic Security

  • Uses crypto.getRandomValues() for true randomness
  • Rejection sampling ensures uniform distribution
  • No predictable patterns in generated codes

Hashed Storage

Tokens are stored as hashed values in the database, preventing token theft even if the database is compromised.

Single Use

Each token can only be used once. After verification, the token is invalidated immediately.

Short Expiration

The 10-minute expiration window limits the attack surface for intercepted codes.

Error Handling

ErrorDescription
Token expiredCode was not used within 10 minutes
Token invalidCode doesn't match or was already used
Token not foundNo pending verification for this operation

Previous: Rate Limiting