Next.js Supabase V3 (#463)
Version 3 of the kit: - Radix UI replaced with Base UI (using the Shadcn UI patterns) - next-intl replaces react-i18next - enhanceAction deprecated; usage moved to next-safe-action - main layout now wrapped with [locale] path segment - Teams only mode - Layout updates - Zod v4 - Next.js 16.2 - Typescript 6 - All other dependencies updated - Removed deprecated Edge CSRF - Dynamic Github Action runner
This commit is contained in:
committed by
GitHub
parent
4912e402a3
commit
7ebff31475
302
docs/api/otp-api.mdoc
Normal file
302
docs/api/otp-api.mdoc
Normal file
@@ -0,0 +1,302 @@
|
||||
---
|
||||
status: "published"
|
||||
label: "OTP API"
|
||||
order: 5
|
||||
title: "OTP API | Next.js Supabase SaaS Kit"
|
||||
description: "Generate and verify one-time passwords for secure operations in MakerKit. Use the OTP API for account deletion, ownership transfers, and other high-risk actions."
|
||||
---
|
||||
|
||||
The OTP API generates and verifies one-time passwords for secure operations like account deletion, ownership transfers, and email verification. It uses Supabase for secure token storage with automatic expiration and verification tracking.
|
||||
|
||||
{% sequence title="How to use the OTP API" description="Learn how to use the OTP API in Makerkit" %}
|
||||
|
||||
[OTP API - What is it for?](#otp-api---what-is-it-for)
|
||||
|
||||
[Installation](#installation)
|
||||
|
||||
[Basic Usage](#basic-usage)
|
||||
|
||||
[Server Actions](#server-actions)
|
||||
|
||||
[Verification UI Component](#verification-ui-component)
|
||||
|
||||
[API Reference](#api-reference)
|
||||
|
||||
[Database Schema](#database-schema)
|
||||
|
||||
[Best Practices](#best-practices)
|
||||
|
||||
[Example Use Cases](#example-use-cases)
|
||||
|
||||
{% /sequence %}
|
||||
|
||||
It is used for various destructive actions in the SaaS Kit, such as deleting
|
||||
accounts, deleting teams, and deleting users. However, you can use it for a
|
||||
variety of other purposes as well, such as:
|
||||
|
||||
- Your custom destructive actions
|
||||
- oAuth account connections
|
||||
- etc.
|
||||
|
||||
## OTP API - What is it for?
|
||||
|
||||
The OTP package offers:
|
||||
|
||||
- **Secure Token Generation**: Create time-limited tokens with configurable expiration
|
||||
- **Email Delivery**: Send OTP codes via email with customizable templates
|
||||
- **Verification UI**: Ready-to-use verification form component
|
||||
- **Token Management**: Revoke, verify, and check token status
|
||||
|
||||
## Installation
|
||||
|
||||
If you're using Makerkit, this package is already included. For manual installation:
|
||||
|
||||
```bash
|
||||
pnpm add @kit/otp
|
||||
```
|
||||
|
||||
## Basic Usage
|
||||
|
||||
### Creating and Sending an OTP
|
||||
|
||||
To create and send an OTP, you can use the `createToken` method:
|
||||
|
||||
```typescript
|
||||
import { createOtpApi } from '@kit/otp/api';
|
||||
import { getSupabaseServerClient } from '@kit/supabase/server-client';
|
||||
|
||||
// Create the API instance
|
||||
const client = getSupabaseServerClient();
|
||||
const api = createOtpApi(client);
|
||||
|
||||
// Generate and send an OTP email
|
||||
await api.createToken({
|
||||
userId: user.id,
|
||||
purpose: 'email-verification',
|
||||
expiresInSeconds: 3600, // 1 hour
|
||||
metadata: { redirectTo: '/verify-email' }
|
||||
});
|
||||
|
||||
// Send the email with the OTP
|
||||
await api.sendOtpEmail({
|
||||
email: userEmail,
|
||||
otp: token.token
|
||||
});
|
||||
```
|
||||
|
||||
### Verifying an OTP
|
||||
|
||||
To verify an OTP, you can use the `verifyToken` method:
|
||||
|
||||
```typescript
|
||||
// Verify the token
|
||||
const result = await api.verifyToken({
|
||||
token: submittedToken,
|
||||
purpose: 'email-verification'
|
||||
});
|
||||
|
||||
if (result.valid) {
|
||||
// Token is valid, proceed with the operation
|
||||
const { userId, metadata } = result;
|
||||
// Handle successful verification
|
||||
} else {
|
||||
// Token is invalid or expired
|
||||
// Handle verification failure
|
||||
}
|
||||
```
|
||||
|
||||
## Server Actions
|
||||
|
||||
The package includes a ready-to-use server action for sending OTP emails:
|
||||
|
||||
```typescript
|
||||
import { sendOtpEmailAction } from '@kit/otp/server/server-actions';
|
||||
|
||||
// In a form submission handler
|
||||
const result = await sendOtpEmailAction({
|
||||
email: userEmail,
|
||||
purpose: 'password-reset',
|
||||
expiresInSeconds: 1800 // 30 minutes
|
||||
});
|
||||
|
||||
if (result.success) {
|
||||
// OTP was sent successfully
|
||||
} else {
|
||||
// Handle error
|
||||
}
|
||||
```
|
||||
|
||||
**Note:** The `email` parameter is only used as verification mechanism, the actual email address being used is the one associated with the user.
|
||||
|
||||
## Verification UI Component
|
||||
|
||||
The package includes a ready-to-use OTP verification form:
|
||||
|
||||
```tsx
|
||||
import { VerifyOtpForm } from '@kit/otp/components';
|
||||
|
||||
function MyVerificationPage() {
|
||||
return (
|
||||
<VerifyOtpForm
|
||||
purpose="password-reset"
|
||||
email={userEmail}
|
||||
onSuccess={(otp) => {
|
||||
// Handle successful verification
|
||||
// Use the OTP for verification on the server
|
||||
}}
|
||||
CancelButton={
|
||||
<Button variant="outline" onClick={handleCancel}>
|
||||
Cancel
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `createOtpApi(client)`
|
||||
|
||||
Creates an instance of the OTP API.
|
||||
|
||||
**Parameters**:
|
||||
- `client`: A Supabase client instance
|
||||
- **Returns**: OTP API instance with the following methods:
|
||||
|
||||
### `api.createToken(params)`
|
||||
|
||||
Creates a new one-time token.
|
||||
|
||||
**Parameters**:
|
||||
- `params.userId` (optional): User ID to associate with the token
|
||||
- `params.purpose`: Purpose of the token (e.g., 'password-reset')
|
||||
- `params.expiresInSeconds` (optional): Token expiration time in seconds (default: 3600)
|
||||
- `params.metadata` (optional): Additional data to store with the token
|
||||
- `params.description` (optional): Description of the token
|
||||
- `params.tags` (optional): Array of string tags
|
||||
- `params.scopes` (optional): Array of permission scopes
|
||||
- `params.revokePrevious` (optional): Whether to revoke previous tokens with the same purpose (default: true)
|
||||
|
||||
**Returns**:
|
||||
```typescript
|
||||
{
|
||||
id: string; // Database ID of the token
|
||||
token: string; // The actual token to send to the user
|
||||
expiresAt: string; // Expiration timestamp
|
||||
revokedPreviousCount: number; // Number of previously revoked tokens
|
||||
}
|
||||
```
|
||||
|
||||
### `api.verifyToken(params)`
|
||||
|
||||
Verifies a one-time token.
|
||||
|
||||
**Parameters**:
|
||||
- `params.token`: The token to verify
|
||||
- `params.purpose`: Purpose of the token (must match the purpose used when creating)
|
||||
- `params.userId` (optional): User ID for additional verification
|
||||
- `params.requiredScopes` (optional): Array of required permission scopes
|
||||
- `params.maxVerificationAttempts` (optional): Maximum allowed verification attempts
|
||||
|
||||
**Returns**:
|
||||
```typescript
|
||||
{
|
||||
valid: boolean; // Whether the token is valid
|
||||
userId?: string; // User ID associated with the token (if valid)
|
||||
metadata?: object; // Metadata associated with the token (if valid)
|
||||
message?: string; // Error message (if invalid)
|
||||
scopes?: string[]; // Permission scopes (if valid)
|
||||
purpose?: string; // Token purpose (if valid)
|
||||
}
|
||||
```
|
||||
|
||||
### `api.revokeToken(params)`
|
||||
|
||||
Revokes a token to prevent its future use.
|
||||
|
||||
**Parameters**:
|
||||
- `params.id`: ID of the token to revoke
|
||||
- `params.reason` (optional): Reason for revocation
|
||||
|
||||
**Returns**:
|
||||
```typescript
|
||||
{
|
||||
success: boolean; // Whether the token was successfully revoked
|
||||
}
|
||||
```
|
||||
|
||||
### `api.getTokenStatus(params)`
|
||||
|
||||
Gets the status of a token.
|
||||
|
||||
**Parameters**:
|
||||
- `params.id`: ID of the token
|
||||
|
||||
**Returns**:
|
||||
```typescript
|
||||
{
|
||||
exists: boolean; // Whether the token exists
|
||||
purpose?: string; // Token purpose
|
||||
userId?: string; // User ID associated with the token
|
||||
createdAt?: string; // Creation timestamp
|
||||
expiresAt?: string; // Expiration timestamp
|
||||
usedAt?: string; // When the token was used (if used)
|
||||
revoked?: boolean; // Whether the token is revoked
|
||||
revokedReason?: string; // Reason for revocation (if revoked)
|
||||
verificationAttempts?: number; // Number of verification attempts
|
||||
lastVerificationAt?: string; // Last verification attempt timestamp
|
||||
lastVerificationIp?: string; // IP address of last verification attempt
|
||||
isValid?: boolean; // Whether the token is still valid
|
||||
}
|
||||
```
|
||||
|
||||
### `api.sendOtpEmail(params)`
|
||||
|
||||
Sends an email containing the OTP code.
|
||||
|
||||
**Parameters**:
|
||||
- `params.email`: Email address to send to
|
||||
- `params.otp`: OTP code to include in the email
|
||||
|
||||
**Returns**: Promise that resolves when the email is sent
|
||||
|
||||
## Database Schema
|
||||
|
||||
The package uses a `nonces` table in your Supabase database with the following structure:
|
||||
|
||||
- `id`: UUID primary key
|
||||
- `client_token`: Hashed token sent to client
|
||||
- `nonce`: Securely stored token hash
|
||||
- `user_id`: Optional reference to auth.users
|
||||
- `purpose`: Purpose identifier (e.g., 'password-reset')
|
||||
- Status fields: `expires_at`, `created_at`, `used_at`, etc.
|
||||
- Audit fields: `verification_attempts`, `last_verification_at`, etc.
|
||||
- Extensibility fields: `metadata`, `scopes`
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use Specific Purposes**: Always use descriptive, specific purpose identifiers for your tokens.
|
||||
2. **Short Expiration Times**: Set token expiration times to the minimum necessary for your use case.
|
||||
3. **Handle Verification Failures**: Provide clear error messages when verification fails.
|
||||
4. **Secure Your Tokens**: Never log or expose tokens in client-side code or URLs.
|
||||
|
||||
## Example Use Cases
|
||||
|
||||
- Email verification
|
||||
- Two-factor authentication
|
||||
- Account deletion confirmation
|
||||
- Important action verification
|
||||
|
||||
Each use case should use a distinct purpose identifier. The purpose will
|
||||
always need to match the one used when creating the token.
|
||||
|
||||
When you need to assign a specific data to a token, you can modify the
|
||||
purpose with a unique identifier, such as `email-verification-12345`.
|
||||
|
||||
## Related documentation
|
||||
|
||||
- [Authentication API](/docs/next-supabase-turbo/api/authentication-api) - User authentication and session handling
|
||||
- [Team Account API](/docs/next-supabase-turbo/api/team-account-api) - Team management for ownership transfers
|
||||
- [Email Configuration](/docs/next-supabase-turbo/emails/email-configuration) - Configure email delivery for OTP codes
|
||||
- [Server Actions](/docs/next-supabase-turbo/data-fetching/server-actions) - Use OTP verification in server actions
|
||||
Reference in New Issue
Block a user