Skip to content

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 calls

Phone 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:

  1. Prompt user to re-authenticate
  2. 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:

ErrorCauseSolution
otp_start_failedInvalid phone formatCheck E.164 format
rate_limitedToo many OTP requestsWait and retry
invalid_codeWrong OTP codeAsk user to re-enter
code_expiredOTP expired (>10 min)Request new OTP
unauthorizedInvalid/expired tokenRe-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

  1. Never expose your API key in client-side code

    • Use environment variables
    • Proxy through your backend if needed
  2. Store tokens securely

    • Use secure storage APIs (SecureStore, Keychain)
    • Never log tokens
  3. Handle token expiration gracefully

    • Check for auth errors on every request
    • Provide smooth re-authentication flow
  4. Validate phone numbers client-side

    • Use libraries like libphonenumber-js
    • Show clear format hints to users

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 };
}

AI Memory Infrastructure