serverpod_auth_sms
The recommended package for Serverpod SMS authentication.
Why Use This Package?
This package provides complete SMS authentication functionality with flexible storage options:
| What You Need | Just Use This Package |
|---|---|
| Hash-only storage (irreversible) | ✅ Use PhoneIdHashStore |
| Crypto storage (decryptable) | ✅ Use PhoneIdCryptoStore |
No manual hide Protocol, Endpoints |
✅ Handled automatically |
You do NOT need to import sub-packages separately. This package includes everything and lets you choose storage method via configuration:
// Just import this one package
import 'package:serverpod_auth_sms/serverpod_auth_sms.dart';
// Then choose your storage method:
final phoneIdStore = PhoneIdHashStore.fromPasswords(pod); // Hash-only
// OR
final phoneIdStore = PhoneIdCryptoStore.fromPasswords(pod); // Crypto (recommended)
Sub-packages (
_core,_hash,_crypto) are for advanced users only and require manualhide Protocol, Endpointsdirectives. Most users should just use this package.
Features
- SMS Registration - Register with phone number and verification code
- Verification Code Login - Login with phone + code (auto-register unregistered users)
- Phone Binding - Bind phone to existing accounts
- Provider-Agnostic - Works with any SMS provider via callbacks
Installation
Server Dependencies
# gen_server/pubspec.yaml
dependencies:
serverpod_auth_sms: ^0.1.6
serverpod_auth_sms_core_client: ^0.2.0 # Recommended: reuse shared password policy helper
# Optional: Tencent Cloud SMS integration (for China business)
tencent_sms_serverpod: ^0.2.0
Client Dependencies
# gen_client/pubspec.yaml
dependencies:
serverpod_auth_sms_core_client: ^0.2.0
# Add ONE of the following based on your storage choice:
serverpod_auth_sms_crypto_client: ^0.1.6 # For crypto storage
# serverpod_auth_sms_hash_client: ^0.1.6 # For hash storage
Quick Start
Step 1: Choose Storage Method
| Storage | When to Use | Configuration Required |
|---|---|---|
| Crypto (recommended) | Most cases - can verify AND retrieve phone numbers | phoneHashPepper + phoneEncryptionKey |
| Hash | Maximum privacy - can only verify, cannot retrieve | phoneHashPepper only |
Note: Crypto storage internally stores hash values too, so it covers all hash functionality while also supporting phone number decryption. If you're unsure, choose crypto - you can always verify phones (like hash), plus decrypt when needed (e.g., customer support, notifications).
Step 2: Configure passwords.yaml
# config/passwords.yaml
shared:
# ============================================
# Core SMS Authentication (Required)
# ============================================
# Pepper for hashing verification codes (required)
smsSecretHashPepper: 'your-random-string-for-verification-code-hashing'
# ============================================
# Phone Number Storage (Required)
# ============================================
# Pepper for hashing phone numbers (required for BOTH hash and crypto)
# WARNING: Cannot be changed after deployment - existing data won't match
phoneHashPepper: 'your-random-string-for-phone-hashing'
# AES-256 encryption key (required ONLY for crypto storage)
# Generate with: openssl rand -base64 32
# WARNING: Cannot be changed - leakage allows decryption of all phone numbers
phoneEncryptionKey: 'base64-encoded-32-byte-key'
# ============================================
# Tencent Cloud SMS (Optional - for China business)
# ============================================
tencentSmsSecretId: 'your-tencent-secret-id'
tencentSmsSecretKey: 'your-tencent-secret-key'
tencentSmsSdkAppId: '1400000000'
tencentSmsSignName: 'YourAppName'
tencentSmsRegion: 'ap-guangzhou' # Optional, defaults to ap-guangzhou
# Template configuration (choose ONE method):
# Method 1: Direct template ID
tencentSmsVerificationTemplateId: '123456'
# Method 2: Scene-specific templates
# tencentSmsVerificationTemplateNameLogin: 'LoginTemplate'
# tencentSmsVerificationTemplateNameRegister: 'RegisterTemplate'
# tencentSmsVerificationTemplateNameResetPassword: 'ResetTemplate'
# Method 3: CSV template mapping
# tencentSmsTemplateCsvPath: 'config/sms/templates.csv'
Step 3: Configure Server
import 'package:serverpod/serverpod.dart';
import 'package:serverpod_auth_idp_server/core.dart';
import 'package:serverpod_auth_sms_core_client/serverpod_auth_sms_core_client.dart'
show validateAuthPasswordPolicy;
import 'package:serverpod_auth_sms/serverpod_auth_sms.dart';
void run(List<String> args) async {
final pod = Serverpod(args, Protocol(), Endpoints());
// ========================================
// Choose ONE storage method:
// ========================================
// Option A: Crypto storage (RECOMMENDED)
// - Can verify phone numbers (like hash)
// - Can also decrypt to get original phone (for support, notifications, etc.)
final phoneIdStore = PhoneIdCryptoStore.fromPasswords(pod);
// Option B: Hash storage
// - Can only verify phone numbers
// - Cannot retrieve original phone (maximum privacy)
// final phoneIdStore = PhoneIdHashStore.fromPasswords(pod);
pod.initializeAuthServices(
tokenManagerBuilders: [JwtConfigFromPasswords()],
identityProviderBuilders: [
SmsIdpConfigFromPasswords(
phoneIdStore: phoneIdStore,
sendRegistrationVerificationCode: _sendSmsCode,
sendLoginVerificationCode: _sendSmsCode,
sendBindVerificationCode: _sendSmsCode,
passwordValidationFunction: validateAuthPasswordPolicy,
),
],
);
await pod.start();
}
// Implement your SMS sending logic
Future<void> _sendSmsCode(
Session session, {
required String phone,
required UuidValue requestId,
required String verificationCode,
required Transaction? transaction,
}) async {
// Call your SMS provider API here
await yourSmsClient.send(phone, verificationCode);
session.log('Sent code $verificationCode to $phone');
}
Recommended: use validateAuthPasswordPolicy from
serverpod_auth_sms_core_client directly so Server config, Flutter rule hints,
and tests share the same source of truth. If you need extra business rules,
wrap this helper instead of reimplementing the whole password policy.
With Tencent Cloud SMS (for China Business)
import 'package:serverpod/serverpod.dart';
import 'package:serverpod_auth_idp_server/core.dart';
import 'package:serverpod_auth_sms_core_client/serverpod_auth_sms_core_client.dart'
show validateAuthPasswordPolicy;
import 'package:serverpod_auth_sms/serverpod_auth_sms.dart';
import 'package:tencent_sms_serverpod/tencent_sms_serverpod.dart';
void run(List<String> args) async {
final pod = Serverpod(args, Protocol(), Endpoints());
// Create Tencent Cloud SMS client (credentials from passwords.yaml, other config passed directly)
final smsConfig = TencentSmsConfigServerpod.fromServerpod(
pod,
appConfig: TencentSmsAppConfig(
smsSdkAppId: '1400000000',
signName: 'YourSignName',
templateCsvPath: 'config/sms/templates.csv',
verificationTemplateNameLogin: 'Login',
verificationTemplateNameRegister: 'Register',
verificationTemplateNameResetPassword: 'ResetPassword',
),
);
final smsClient = TencentSmsClient(smsConfig);
// Use Chinese error messages: TencentSmsClient(smsConfig, localizations: const SmsLocalizationsZh())
final smsHelper = SmsAuthCallbackHelper(smsClient);
// Choose storage method (see Step 1)
final phoneIdStore = PhoneIdCryptoStore.fromPasswords(pod);
pod.initializeAuthServices(
tokenManagerBuilders: [JwtConfigFromPasswords()],
identityProviderBuilders: [
SmsIdpConfigFromPasswords(
phoneIdStore: phoneIdStore,
sendRegistrationVerificationCode: smsHelper.sendForRegistration,
sendLoginVerificationCode: smsHelper.sendForLogin,
sendBindVerificationCode: smsHelper.sendForBind,
passwordValidationFunction: validateAuthPasswordPolicy,
// Optional configurations:
requirePasswordOnUnregisteredLogin: true,
allowPhoneRebind: false,
verificationCodeLength: 6,
loginVerificationCodeLifetime: Duration(minutes: 10),
),
],
);
await pod.start();
}
Step 4: Create Endpoints
// lib/src/endpoints/sms_idp_endpoint.dart
import 'package:serverpod/serverpod.dart';
import 'package:serverpod_auth_sms/serverpod_auth_sms.dart';
class SmsIdpEndpoint extends SmsIdpBaseEndpoint {}
// lib/src/endpoints/phone_bind_endpoint.dart
class PhoneBindEndpoint extends SmsPhoneBindBaseEndpoint {}
// lib/src/endpoints/sms_auth_ui_endpoint.dart
class SmsAuthUiEndpoint extends SmsAuthUiBaseEndpoint {}
Database Migration
After adding dependencies, generate and apply migrations:
cd your_server_project
serverpod create-migration
dart bin/main.dart --apply-migrations
Tables created:
serverpod_auth_sms_account- User credentialsserverpod_auth_sms_account_request- Registration requestsserverpod_auth_sms_login_request- Login requestsserverpod_auth_sms_bind_request- Binding requestsserverpod_auth_sms_phone_id_cryptoorserverpod_auth_sms_phone_id_hash- Phone storage
Configuration Options
Feature Toggles
| Option | Description | Default |
|---|---|---|
enableRegistration |
Enable SMS registration | true |
enableLogin |
Enable verification code login | true |
enableBind |
Enable phone binding | true |
requirePasswordOnUnregisteredLogin |
Require password for auto-register | true |
allowPhoneRebind |
Allow changing bound phone | false |
Verification Code Settings
| Option | Description | Default |
|---|---|---|
verificationCodeLength |
Code length | 6 |
registrationVerificationCodeLifetime |
Registration code TTL | 10 min |
loginVerificationCodeLifetime |
Login code TTL | 10 min |
bindVerificationCodeLifetime |
Bind code TTL | 10 min |
Rate Limiting
| Option | Description | Default |
|---|---|---|
registrationVerificationCodeAllowedAttempts |
Max registration attempts | 5 |
loginVerificationCodeAllowedAttempts |
Max login attempts | 5 |
bindVerificationCodeAllowedAttempts |
Max bind attempts | 5 |
registrationRequestRateLimit |
Registration rate limit | 5/10min |
loginRequestRateLimit |
Login rate limit | 5/10min |
bindRequestRateLimit |
Bind rate limit | 5/10min |
SmsIdpConfigFromPasswords(
// ...
loginVerificationCodeAllowedAttempts: 5,
loginRequestRateLimit: SmsRateLimit(
maxAttempts: 5,
timeframe: Duration(minutes: 10),
),
)
Troubleshooting
Tencent Cloud Rate Limits
| Error Code | Meaning | Solution |
|---|---|---|
LimitExceeded.PhoneNumberOneHourLimit |
Hourly limit exceeded | Wait or adjust in console |
LimitExceeded.PhoneNumberDailyLimit |
Daily limit exceeded | Wait until next day |
LimitExceeded.PhoneNumberThirtySecondLimit |
30-second cooldown | Add frontend countdown |
Password Regex in Raw Strings
// ❌ Wrong - double escaping
if (!password.contains(RegExp(r'[\\W_]'))) return false;
// ✅ Correct
if (!password.contains(RegExp(r'[\W_]'))) return false;
Async SMS Callback
// ❌ Wrong - async without await causes "Session is closed" error
void _sendSms(Session session, {...}) {
smsClient.send(...); // Not awaited
}
// ✅ Correct
Future<void> _sendSms(Session session, {...}) async {
await smsClient.send(...);
}
Advanced: Individual Package Usage
Not recommended for most users. Only use if you have specific requirements.
If you must use individual packages (e.g., to avoid crypto dependencies when only using hash):
dependencies:
serverpod_auth_sms_core_server: ^0.1.6
serverpod_auth_sms_hash_server: ^0.1.6 # Or _crypto_server
// Must manually hide Protocol and Endpoints
import 'package:serverpod_auth_sms_core_server/serverpod_auth_sms_core_server.dart'
hide Protocol, Endpoints;
import 'package:serverpod_auth_sms_hash_server/serverpod_auth_sms_hash_server.dart'
hide Protocol, Endpoints;
Related Packages
- tencent_sms - Tencent Cloud SMS SDK
- tencent_sms_serverpod - Serverpod integration
License
MIT License