OTP Service Integration
Updated: 2026-02-22 - Added custom OTP message template usage details and examples
This guide covers the OTP (One-Time Password) service for secure SMS-based verification.
Overview
The OTP Service provides secure, rate-limited one-time password generation and verification via SMS. It's designed for multi-tenant environments with robust security controls.
Features
- Cryptographically secure 6-digit code generation using
RandomNumberGenerator - Automatic SMS delivery via existing SMS infrastructure
- Rate limiting (3 per phone number / 10 minutes, 100 per tenant / 1 minute)
- Configurable expiration (default 10 minutes)
- Multi-tenant isolation with tenant-scoped grains
- SHA-256 code hashing (never store plaintext)
- Constant-time verification to prevent timing attacks
API Endpoints
Send OTP
POST /api/v2/otp/send
Generates and sends an OTP to the specified phone number via SMS.
Request:
Code
Request (with custom message template):
Code
Response:
Code
Note: The actual OTP code is NOT returned in the API response. It is only sent via SMS to the specified phone number.
Verify OTP
POST /api/v2/otp/verify
Verifies an OTP code entered by the user.
Request:
Code
Response (Success):
Code
Response (Failure):
Code
Cancel OTP
POST /api/v2/otp/{id}/cancel
Cancels a pending OTP request. Only pending OTPs can be cancelled.
Response:
Code
Get OTP Details (Admin)
GET /api/v2/otp/{id}
Retrieves detailed information about an OTP request. Requires otp_admin permission.
Response:
Code
Authentication
All OTP endpoints require authentication via:
- Bearer Token (JWT) in the
Authorizationheader - API Key in the
X-API-Keyheader
The tenant context is automatically extracted from the authentication token.
Rate Limiting
The OTP service implements distributed rate limiting to prevent abuse:
| Limit Type | Max Requests | Window |
|---|---|---|
| Per Phone Number | 3 | 10 minutes |
| Per Tenant | 100 | 1 minute |
When rate limits are exceeded, the API returns HTTP 429 (Too Many Requests) with a Retry-After header indicating when to retry.
Error Handling
| Error Code | HTTP Status | Description |
|---|---|---|
invalid_phone | 400 | Invalid phone number format (must be E.164) |
rate_limit_exceeded | 429 | Too many requests. Try again later. |
invalid_code | 400 | Code is incorrect or has expired |
max_attempts_exceeded | 400 | Maximum verification attempts (3) exceeded |
otp_not_found | 404 | OTP request not found or expired |
tenant_forbidden | 403 | OTP does not belong to your organization |
OTP Purposes
The purpose field categorizes OTP usage:
Login- User authenticationPasswordReset- Password reset verificationTransactionVerification- Financial transaction confirmationAccountActivation- New account activationCustom- Other purposes
Message Templates
Default template:
Code
Custom templates can be provided per request using the customMessageTemplate field. Available placeholders:
{code}- The OTP code{minutes}- Expiration time in minutes
Template behavior notes:
- Placeholder replacement is case-sensitive (
{code}and{minutes}only) - Unknown placeholders are not expanded and will be sent as literal text
- Keep templates short and explicit to reduce SMS truncation risk
Security Considerations
- Never log or expose OTP codes in API responses or logs
- Short expiration (default 10 minutes) limits exposure window
- One-time use - Codes can only be verified once
- Maximum attempts - 3 verification attempts per OTP
- Rate limiting prevents brute force attacks
- Constant-time comparison prevents timing attacks
Architecture
Configuration
Configure via appsettings.json:
Code
Next Steps
- See OTP Examples for code samples
- See OTP Security Guide for security details