Authentication
Configure Memory uses phone-based OTP (One-Time Password) authentication to identify users across applications.
Overview
The authentication flow:
1. Your app collects user's phone number
2. Call sendOtp() → User receives SMS with 6-digit code
3. User enters code → Call verifyOtp() → Receive token + userId
4. Use token for all subsequent API callsPhone Number Format
All phone numbers must be in E.164 format:
- Start with
+and country code - No spaces, dashes, or parentheses
- Examples:
+14155551234(US),+447911123456(UK)
typescript
// Correct
await client.auth.sendOtp('+14155551234');
// Wrong formats
await client.auth.sendOtp('415-555-1234'); // No country code
await client.auth.sendOtp('+1 415 555 1234'); // Has spaces
await client.auth.sendOtp('14155551234'); // Missing +Sending OTP
typescript
import { MemoryClient } from '@configure/memory-sdk';
const client = new MemoryClient('your-api-key');
try {
await client.auth.sendOtp('+14155551234');
console.log('OTP sent!');
} catch (error) {
console.error('Failed to send OTP:', error.message);
// Handle: invalid phone format, rate limiting, etc.
}python
from configure_memory import MemoryClient
client = MemoryClient("your-api-key")
try:
client.auth.send_otp("+14155551234")
print("OTP sent!")
except Exception as e:
print(f"Failed to send OTP: {e}")Rate Limiting
OTP requests are rate-limited to prevent abuse:
- 1 OTP per phone number per 60 seconds
- Max 5 OTPs per phone number per hour
Verifying OTP
typescript
try {
const { token, userId } = await client.auth.verifyOtp('+14155551234', '123456');
// Store these securely
console.log('Token:', token); // JWT valid for 30 days
console.log('User ID:', userId); // Stable user identifier
} catch (error) {
if (error.message === 'invalid_code') {
console.log('Wrong code, please try again');
} else if (error.message === 'code_expired') {
console.log('Code expired, request a new one');
} else {
console.error('Verification failed:', error.message);
}
}python
try:
result = client.auth.verify_otp("+14155551234", "123456")
# Store these securely
print(f"Token: {result.token}") # JWT valid for 30 days
print(f"User ID: {result.user_id}") # Stable user identifier
except Exception as e:
if "invalid_code" in str(e):
print("Wrong code, please try again")
elif "code_expired" in str(e):
print("Code expired, request a new one")
else:
print(f"Verification failed: {e}")OTP Code Details
- 6 digits
- Valid for 10 minutes
- Single use (consumed after verification)
- Case-insensitive (123456 = 123456)
Token Management
Token Properties
The JWT token returned by verifyOtp():
- Valid for: 30 days
- Contains: User ID, issue time, expiration
- Format: Standard JWT (can be decoded client-side)
Storing Tokens
Web (Browser):
typescript
// Store in localStorage or secure cookie
localStorage.setItem('memory_token', token);
localStorage.setItem('memory_user_id', userId);
// Retrieve for API calls
const token = localStorage.getItem('memory_token');Mobile (React Native):
typescript
import * as SecureStore from 'expo-secure-store';
// Store securely
await SecureStore.setItemAsync('memory_token', token);
await SecureStore.setItemAsync('memory_user_id', userId);
// Retrieve
const token = await SecureStore.getItemAsync('memory_token');Server-side:
typescript
// Store in session or encrypted database
req.session.memoryToken = token;
req.session.memoryUserId = userId;Token Expiration
Tokens expire after 30 days. Handle expiration gracefully:
typescript
async function makeAuthenticatedRequest(token: string, userId: string) {
try {
return await client.memory.getProfile(token, userId);
} catch (error) {
if (error.message.includes('token_expired') ||
error.message.includes('unauthorized')) {
// Token expired - re-authenticate user
throw new Error('Please log in again');
}
throw error;
}
}Token Refresh
Currently, tokens cannot be refreshed. When a token expires:
- Prompt user to re-authenticate
- Send new OTP and verify
Future versions may support token refresh.
User Identity
The userId returned by verifyOtp() is:
- Stable: Same phone → same userId (across all apps)
- UUID format:
550e8400-e29b-41d4-a716-446655440000 - Cross-app: User can connect to multiple apps with same ID
typescript
const { userId } = await client.auth.verifyOtp(phone, code);
// User's data is associated with this ID
const profile = await client.memory.getProfile(token, userId);Error Handling
Common authentication errors:
| Error | Cause | Solution |
|---|---|---|
otp_start_failed | Invalid phone format | Check E.164 format |
rate_limited | Too many OTP requests | Wait and retry |
invalid_code | Wrong OTP code | Ask user to re-enter |
code_expired | OTP expired (>10 min) | Request new OTP |
unauthorized | Invalid/expired token | Re-authenticate |
typescript
async function authenticate(phone: string, code: string) {
try {
return await client.auth.verifyOtp(phone, code);
} catch (error) {
switch (error.message) {
case 'invalid_code':
throw new Error('The code you entered is incorrect. Please try again.');
case 'code_expired':
throw new Error('This code has expired. Please request a new one.');
case 'rate_limited':
throw new Error('Too many attempts. Please wait a minute and try again.');
default:
throw new Error('Authentication failed. Please try again.');
}
}
}Security Best Practices
Never expose your API key in client-side code
- Use environment variables
- Proxy through your backend if needed
Store tokens securely
- Use secure storage APIs (SecureStore, Keychain)
- Never log tokens
Handle token expiration gracefully
- Check for auth errors on every request
- Provide smooth re-authentication flow
Validate phone numbers client-side
- Use libraries like
libphonenumber-js - Show clear format hints to users
- Use libraries like
Demo Mode
For development without real SMS:
typescript
// Use demo endpoint (development only)
const demoToken = await client.auth.getDemo();This returns a demo token for testing. Not available in production.
Complete Authentication Flow
typescript
import { MemoryClient } from '@configure/memory-sdk';
async function loginUser(phone: string) {
const client = new MemoryClient(process.env.CONFIGURE_API_KEY!);
// Step 1: Send OTP
console.log('Sending OTP to', phone);
await client.auth.sendOtp(phone);
// Step 2: Get code from user (your UI handles this)
const code = await promptUserForCode();
// Step 3: Verify
const { token, userId } = await client.auth.verifyOtp(phone, code);
// Step 4: Store credentials
await storeCredentials(token, userId);
// Step 5: User is now authenticated
const profile = await client.memory.getProfile(token, userId);
console.log('Welcome,', profile.user?.name || 'new user');
return { token, userId };
}