Customer Authentication¶
Complete guide to customer registration, login, and session management in the Reserva platform.
Overview¶
The customer authentication system provides secure access control for end-users booking appointments through the platform.
Key Features:
- Customer Registration - Create new customer accounts with verification workflows
- Dual-Mode Login - Tenant-specific or central authentication
- Session Management - Secure token-based sessions with device tracking
- Account Security - Password hashing, verification codes, and logout controls
- Multi-Channel Support - Email or phone-based authentication
Authentication Flow:
- Registration → Customer creates account with email/phone
- Verification → Email/SMS codes sent (prepared for delivery)
- Login → Customer authenticates with credentials
- Session Created → JWT tokens issued for API access
- Logout → Session invalidated, tokens revoked
Register New Customer¶
Create a new customer account with automatic verification workflow preparation.
Endpoint¶
Authentication: None required (public endpoint)
Request Body¶
{
"email": "customer@example.com",
"password": "SecurePass123!",
"first_name": "John",
"last_name": "Doe",
"phone": "+1234567890",
"tenant_slug": "luxury-spa",
"marketing_consent": true
}
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
email |
string | Conditional | Customer email (required if no phone) |
password |
string | Yes | Account password (must meet security requirements) |
first_name |
string | Yes | Customer first name |
last_name |
string | Yes | Customer last name |
phone |
string | Conditional | Phone with country code (required if no email) |
tenant_slug |
string | Yes | Tenant identifier from URL or selection |
marketing_consent |
boolean | No | Opt-in for marketing communications |
Validation Rules:
- At least one of
emailorphonemust be provided - Email must be unique within the tenant
- Phone must be unique within the tenant
- Password must meet complexity requirements
- Tenant must exist and be active
Response¶
{
"customer_id": "507f1f77bcf86cd799439011",
"email": "customer@example.com",
"phone": "+1234567890",
"first_name": "John",
"last_name": "Doe",
"tenant_id": "507f1f77bcf86cd799439010",
"email_verification_required": true,
"phone_verification_required": true,
"message": "Registration successful. Please verify your email and phone number."
}
Registration Process¶
The registration flow follows these steps:
- Request Validation - Validate required fields and format
- Duplicate Check - Verify email/phone uniqueness within tenant
- Tenant Validation - Confirm tenant exists and is active
- Password Hashing - Securely hash password using bcrypt
- Code Generation - Generate verification codes:
- Email: 6-digit code, expires in 24 hours
- Phone: 6-digit code, expires in 15 minutes
- Account Creation - Create customer record with verification data
- Response - Return customer profile with verification requirements
Security Features:
- Passwords hashed with bcrypt before storage
- Verification codes are cryptographically secure
- Account starts as
is_active: trueby default - Email and phone marked as unverified initially
Error Responses¶
| Status Code | Description | Reason |
|---|---|---|
| 201 | Customer registered | Registration successful |
| 400 | Validation error | Invalid request format or missing required fields |
| 404 | Tenant not found | Invalid tenant_slug provided |
| 409 | Customer already exists | Email or phone already registered in tenant |
| 422 | Invalid request data | Schema validation failed |
Example: Email-Only Registration¶
curl -X POST http://localhost:8000/api/v1/customer/auth/register \
-H "Content-Type: application/json" \
-d '{
"email": "jane@example.com",
"password": "SecurePass456!",
"first_name": "Jane",
"last_name": "Smith",
"tenant_slug": "wellness-center"
}'
Response:
{
"customer_id": "507f1f77bcf86cd799439012",
"email": "jane@example.com",
"phone": null,
"first_name": "Jane",
"last_name": "Smith",
"tenant_id": "507f1f77bcf86cd799439010",
"email_verification_required": true,
"phone_verification_required": false,
"message": "Registration successful. Please verify your email."
}
Example: Phone-Only Registration¶
curl -X POST http://localhost:8000/api/v1/customer/auth/register \
-H "Content-Type: application/json" \
-d '{
"phone": "+6281234567890",
"password": "SecurePass789!",
"first_name": "Budi",
"last_name": "Santoso",
"tenant_slug": "jakarta-spa"
}'
Response:
{
"customer_id": "507f1f77bcf86cd799439013",
"email": null,
"phone": "+6281234567890",
"first_name": "Budi",
"last_name": "Santoso",
"tenant_id": "507f1f77bcf86cd799439010",
"email_verification_required": false,
"phone_verification_required": true,
"message": "Registration successful. Please verify your phone number."
}
Important Notes:
- Verification code delivery requires external service integration (not yet implemented)
- Customers can book appointments before verification (system limitation)
- Email addresses are automatically converted to lowercase
- Phone numbers must include country code (E.164 format recommended)
Customer Login¶
Authenticate customer with dual-mode login support (tenant-specific or central discovery).
Endpoint¶
Authentication: None required (public endpoint)
Request Body¶
Tenant-Specific Login (Recommended):
Central Login (Auto-Discovery):
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
email |
string | Conditional | Customer email (required if no phone) |
phone |
string | Conditional | Customer phone (required if no email) |
password |
string | Yes | Account password |
tenant_slug |
string | No | Tenant identifier for direct login |
Validation Rules:
- One of
emailorphonemust be provided - Password is required for all login attempts
- If
tenant_slugprovided, customer must belong to that tenant - If no
tenant_slug, system auto-discovers customer's tenant
Response¶
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer",
"expires_in": 3600,
"customer": {
"id": "507f1f77bcf86cd799439011",
"email": "customer@example.com",
"first_name": "John",
"last_name": "Doe",
"phone": "+1234567890",
"email_verified": false,
"phone_verified": false
},
"tenant": {
"id": "507f1f77bcf86cd799439010",
"name": "Luxury Spa & Wellness",
"slug": "luxury-spa",
"description": "Premium spa services in downtown",
"logo_url": "https://cdn.example.com/logos/luxury-spa.png",
"theme_color": "#8B4789"
},
"outlets": [
{
"id": "507f1f77bcf86cd799439020",
"name": "Downtown Branch",
"slug": "downtown",
"address": "123 Main Street, Downtown",
"phone": "+1234567800",
"business_hours": {
"monday": {"open": "09:00", "close": "21:00"},
"tuesday": {"open": "09:00", "close": "21:00"},
"wednesday": {"open": "09:00", "close": "21:00"},
"thursday": {"open": "09:00", "close": "21:00"},
"friday": {"open": "09:00", "close": "22:00"},
"saturday": {"open": "10:00", "close": "22:00"},
"sunday": {"open": "10:00", "close": "20:00"}
}
},
{
"id": "507f1f77bcf86cd799439021",
"name": "Uptown Branch",
"slug": "uptown",
"address": "456 Park Avenue, Uptown",
"phone": "+1234567801",
"business_hours": {
"monday": {"open": "10:00", "close": "20:00"},
"tuesday": {"open": "10:00", "close": "20:00"},
"wednesday": {"open": "10:00", "close": "20:00"},
"thursday": {"open": "10:00", "close": "20:00"},
"friday": {"open": "10:00", "close": "21:00"},
"saturday": {"open": "09:00", "close": "21:00"},
"sunday": {"open": "09:00", "close": "19:00"}
}
}
],
"preferred_outlet": {
"id": "507f1f77bcf86cd799439020",
"name": "Downtown Branch",
"slug": "downtown",
"address": "123 Main Street, Downtown",
"phone": "+1234567800",
"business_hours": {
"monday": {"open": "09:00", "close": "21:00"}
}
},
"paper_id_enabled": true
}
Login Modes¶
1. Tenant-Specific Login (Recommended)¶
Use Case: Customer logs in directly through tenant-branded portal
Process:
- Validate
tenant_slugexists and is active - Find customer by email/phone within tenant scope
- Verify password matches hashed password
- Check account is active
- Create session with tenant context
- Generate JWT tokens with tenant_id
- Return full context (customer, tenant, outlets)
Benefits:
- Faster authentication (no tenant discovery needed)
- Clear tenant context from start
- Better for branded customer portals
- Prevents cross-tenant login attempts
2. Central Login (Auto-Discovery)¶
Use Case: Customer logs in through central portal without specifying tenant
Process:
- Find customer by email/phone across all tenants
- Verify password matches
- Use customer's
tenant_idfield directly (one customer = one tenant) - Fetch full tenant information and active outlets
- Create session with customer's tenant context
- Generate JWT tokens with tenant_id
- Return full context (customer, tenant, outlets, preferred outlet)
Business Rule:
- System enforces one customer per tenant relationship
- Customer record contains
tenant_idfield used for auto-discovery - Returns same comprehensive response as tenant-specific login
- Multiple tenant associations not supported (business constraint)
Authentication Tokens¶
Access Token:
- Purpose: API authentication for protected endpoints
- Expiration: 1 hour (3600 seconds)
- Usage: Include in
Authorization: Bearer {token}header - Contains: customer_id, tenant_id, session_id
Refresh Token:
- Purpose: Renew access token without re-login
- Expiration: 30 days
- Usage: Submit to
/auth/refreshendpoint - Contains: customer_id, tenant_id, session_id, type: "refresh"
Payment Integration¶
Paper ID Enabled:
The paper_id_enabled field indicates whether Paper ID payment integration is available for the tenant:
true- Paper ID payment gateway is enabled and configuredfalse- Paper ID integration is disablednull- Paper ID is not configured for the tenant
Frontend Usage:
- Show/hide online payment options in booking flow
- Enable Paper ID payment method during checkout
- Display appropriate payment UI based on availability
- Fall back to other payment methods if not enabled
Session Management¶
When login succeeds, a session record is created:
{
"customer_id": "507f1f77bcf86cd799439011",
"tenant_id": "507f1f77bcf86cd799439010",
"status": "active",
"login_at": "2025-01-15T10:30:00Z",
"last_activity": "2025-01-15T10:30:00Z",
"expires_at": "2025-02-14T10:30:00Z",
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-01-15T10:30:00Z"
}
Session Properties:
- Status:
active,expired,invalidated - Duration: 30 days from login
- Activity Tracking: Updates on each API call
- Device Info: Tracks login source (future enhancement)
Error Responses¶
| Status Code | Description | Reason |
|---|---|---|
| 200 | Login successful | Valid credentials, session created |
| 400 | Bad request | Email or phone not provided |
| 401 | Invalid credentials | Wrong password or customer not found |
| 403 | Account deactivated | Customer account disabled |
| 404 | No tenant associations | Central login found no tenants |
| 404 | Tenant not found | Invalid tenant_slug |
| 422 | Invalid request data | Schema validation failed |
Example: Tenant-Specific Login¶
curl -X POST http://localhost:8000/api/v1/customer/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "customer@example.com",
"password": "SecurePass123!",
"tenant_slug": "luxury-spa"
}'
Example: Phone-Based Login¶
curl -X POST http://localhost:8000/api/v1/customer/auth/login \
-H "Content-Type: application/json" \
-d '{
"phone": "+6281234567890",
"password": "SecurePass789!",
"tenant_slug": "jakarta-spa"
}'
Example: Central Login (Auto-Discovery)¶
curl -X POST http://localhost:8000/api/v1/customer/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "customer@example.com",
"password": "SecurePass123!"
}'
Important Notes:
- Email addresses are case-insensitive (automatically lowercased)
- Failed login attempts are not rate-limited (TODO: implement rate limiting)
- Session cleanup for expired sessions runs periodically
- Preferred outlet determined by customer preferences or defaults to first outlet
Customer Logout¶
Securely log out customer with session invalidation and optional multi-device logout.
Endpoint¶
Authentication: Required (Bearer token)
Query Parameters¶
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
everywhere |
boolean | No | false |
If true, logout from all devices/sessions |
Request¶
No request body required.
Authentication token is extracted from the Authorization header:
Response¶
Logout Process¶
Single-Device Logout (Default)¶
Flow:
- Extract customer_id from JWT token
- Invalidate current session in database
- Mark session status as
invalidated - Set
invalidated_attimestamp - Set
invalidation_reason: "logout" - Return success confirmation
Result:
- Current session is invalidated
- Access and refresh tokens become invalid
- Other device sessions remain active
Current Limitation:
- Session ID not yet extracted from JWT payload
- Temporarily invalidates all customer sessions
- TODO: Extract session_id for precise single-session logout
Multi-Device Logout (everywhere=true)¶
Flow:
- Extract customer_id from JWT token
- Find all active sessions for customer
- Invalidate all sessions simultaneously
- Mark all with
invalidation_reason: "logout_all_devices" - Clear device tracking data
- Return success confirmation
Result:
- All customer sessions invalidated across devices
- All issued tokens become invalid immediately
- Customer must re-login on all devices
Session Invalidation Details¶
When a session is invalidated, the database record is updated:
{
"_id": "507f1f77bcf86cd799439030",
"customer_id": "507f1f77bcf86cd799439011",
"tenant_id": "507f1f77bcf86cd799439010",
"status": "invalidated",
"login_at": "2025-01-15T10:30:00Z",
"last_activity": "2025-01-15T14:45:00Z",
"expires_at": "2025-02-14T10:30:00Z",
"invalidated_at": "2025-01-15T15:00:00Z",
"invalidation_reason": "logout",
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-01-15T15:00:00Z"
}
Invalidation Reasons:
logout- Single-device logoutlogout_all_devices- Multi-device logoutpassword_changed- Password resetsecurity- Admin or security actionexpired- Session lifetime exceeded
Token Blacklisting¶
Current Status: Not yet implemented
Planned Enhancement:
- Add invalidated tokens to blacklist collection
- Check blacklist on each authenticated request
- Automatic cleanup of expired blacklist entries
- Prevents token reuse after logout
Error Responses¶
| Status Code | Description | Reason |
|---|---|---|
| 200 | Logout successful | Session(s) invalidated successfully |
| 401 | Not authenticated | Missing or invalid Bearer token |
Note: Logout always returns success, even if session was already invalid
Example: Single-Device Logout¶
curl -X POST http://localhost:8000/api/v1/customer/auth/logout \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
Response:
Example: Multi-Device Logout¶
curl -X POST "http://localhost:8000/api/v1/customer/auth/logout?everywhere=true" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
Response:
Security Considerations¶
Immediate Effect:
- Session invalidation is immediate in database
- Existing tokens fail validation on next request
- No grace period for logout
Best Practices:
- Client apps should clear local token storage after logout
- Implement logout confirmation for "everywhere" option
- Log security events for audit trail
- Consider implementing token blacklist for high-security needs
Future Enhancements:
- Token blacklisting for complete security
- Session-specific logout (requires JWT session_id extraction)
- Device fingerprinting for suspicious login detection
- Automatic logout on password change
- Admin-initiated forced logout
Refresh Access Token¶
Generate new access token using valid refresh token without requiring re-login.
Endpoint¶
Authentication: None required (refresh token validation)
Request Body¶
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
refresh_token |
string | Yes | Valid refresh token received from login |
Validation Rules:
- Refresh token must be valid and not expired
- Token must be of type "refresh" (not access token)
- Associated customer must still be active
- Customer account must not be deactivated
Response¶
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer",
"expires_in": 3600
}
Response Fields:
| Field | Type | Description |
|---|---|---|
access_token |
string | New JWT access token for API authentication |
token_type |
string | Always "bearer" |
expires_in |
integer | Token lifetime in seconds (3600 = 1 hour) |
Token Refresh Process¶
Flow:
- Client submits refresh token from previous login
- Decode and validate refresh token structure
- Verify token signature and expiration
- Extract customer_id, tenant_id from token payload
- Verify token type is "refresh" (not "access")
- Check customer still exists and is active
- Generate new access token with same scope
- Return new access token with expiration
Security Features:
- Token validation: Cryptographic signature verification
- Type checking: Ensures refresh token (not access token) is used
- Customer status: Verifies account is still active
- Expiration handling: Automatic token lifecycle management
- Session continuity: Maintains same customer/tenant context
When to Use Token Refresh¶
Use Cases:
- Access token expired (after 1 hour)
- Proactive refresh before expiration (recommended)
- Silent authentication without user interaction
- Mobile apps with long-running sessions
Best Practices:
- Refresh access token 5-10 minutes before expiration
- Store refresh token securely (encrypted storage)
- Never expose refresh token in URLs or logs
- Implement automatic retry on 401 errors
Error Responses¶
| Status Code | Description | Reason |
|---|---|---|
| 200 | Token refreshed | New access token generated |
| 401 | Invalid refresh token | Expired, malformed, or wrong token type |
| 401 | Customer not found or deactivated | Account no longer exists or is inactive |
| 422 | Invalid request data | Schema validation failed |
Example: Token Refresh¶
curl -X POST http://localhost:8000/api/v1/customer/auth/refresh \
-H "Content-Type: application/json" \
-d '{
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI1MDdmMWY3N2JjZjg2Y2Q3OTk0MzkwMTEiLCJ0ZW5hbnRfaWQiOiI1MDdmMWY3N2JjZjg2Y2Q3OTk0MzkwMTAiLCJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTczOTU4NzIwMH0.signature"
}'
Response:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI1MDdmMWY3N2JjZjg2Y2Q3OTk0MzkwMTEiLCJ0ZW5hbnRfaWQiOiI1MDdmMWY3N2JjZjg2Y2Q3OTk0MzkwMTAiLCJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNzM3MDAxNjAwfQ.new_signature",
"token_type": "bearer",
"expires_in": 3600
}
Important Notes:
- Refresh tokens remain valid for 30 days from login
- Refresh tokens are invalidated on logout
- Refresh tokens are invalidated on password change
- Store new access token and use for subsequent API calls
- Original refresh token remains valid for future refreshes
Client Implementation Example¶
JavaScript:
async function refreshAccessToken(refreshToken) {
try {
const response = await fetch('http://localhost:8000/api/v1/customer/auth/refresh', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
refresh_token: refreshToken
})
});
if (!response.ok) {
throw new Error('Token refresh failed');
}
const data = await response.json();
// Store new access token
localStorage.setItem('access_token', data.access_token);
return data.access_token;
} catch (error) {
// Redirect to login if refresh fails
window.location.href = '/login';
}
}
// Automatic refresh before expiration
setInterval(() => {
const refreshToken = localStorage.getItem('refresh_token');
if (refreshToken) {
refreshAccessToken(refreshToken);
}
}, 50 * 60 * 1000); // Refresh every 50 minutes (before 1-hour expiration)
Change Password¶
Change password for authenticated customer with current password verification.
Endpoint¶
Authentication: Required (Bearer token)
Request Body¶
{
"current_password": "CurrentPass123!",
"new_password": "NewSecurePass123!",
"logout_other_devices": true
}
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
current_password |
string | Yes | Current account password for verification |
new_password |
string | Yes | New password (must meet security requirements) |
logout_other_devices |
boolean | No | If true, invalidate all other sessions (default: false) |
Validation Rules:
- Current password must match stored password hash
- New password must meet security requirements:
- Minimum 8 characters (recommended)
- Mix of letters, numbers, and symbols
- Different from current password
- Customer must be authenticated with valid token
Response¶
Password Change Process¶
Flow:
- Extract customer info from JWT token
- Verify current password matches stored hash
- Validate new password meets security requirements
- Generate secure bcrypt hash for new password
- Update customer record with new password hash
- Optionally invalidate other device sessions
- Update timestamp for password change audit
- Return success confirmation
Security Features:
- Current password verification: Prevents unauthorized changes
- Strength validation: New password must meet complexity requirements
- Secure hashing: bcrypt with salt for new password
- Session management: Option to logout other devices
- Audit tracking: Password change timestamp recorded
- Current session preserved: User stays logged in on current device
Session Management Options¶
Keep Other Sessions Active (default)¶
{
"current_password": "CurrentPass123!",
"new_password": "NewSecurePass123!",
"logout_other_devices": false
}
Result:
- Password changed successfully
- Current session remains active
- Other device sessions remain active
- All sessions continue using new password
Logout Other Devices¶
{
"current_password": "CurrentPass123!",
"new_password": "NewSecurePass123!",
"logout_other_devices": true
}
Result:
- Password changed successfully
- Current session remains active
- All other sessions invalidated
- User must re-login on other devices with new password
Use Case: Enhanced security when password is compromised or suspected breach
Error Responses¶
| Status Code | Description | Reason |
|---|---|---|
| 200 | Password changed | Password updated successfully |
| 400 | Current password incorrect | Wrong current password provided |
| 401 | Not authenticated | Missing or invalid Bearer token |
| 422 | Invalid request data | Schema validation failed or weak password |
Example: Change Password (Keep Sessions)¶
curl -X POST http://localhost:8000/api/v1/customer/auth/change-password \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
-H "Content-Type: application/json" \
-d '{
"current_password": "OldPassword123!",
"new_password": "NewSecurePass456!",
"logout_other_devices": false
}'
Response:
Example: Change Password (Logout Everywhere)¶
curl -X POST http://localhost:8000/api/v1/customer/auth/change-password \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
-H "Content-Type: application/json" \
-d '{
"current_password": "OldPassword123!",
"new_password": "SuperSecure789!",
"logout_other_devices": true
}'
Response:
Best Practices¶
For Users:
✅ DO:
- Use strong, unique passwords
- Change password immediately if compromised
- Use
logout_other_devices: trueif security concern - Keep current password secure
- Choose password different from previous passwords
❌ DON'T:
- Share current password
- Reuse passwords across services
- Use weak or common passwords
- Change password unnecessarily (reduces security fatigue)
For Frontend Developers:
✅ DO:
- Validate password strength client-side
- Show password requirements clearly
- Confirm new password with re-entry field
- Explain
logout_other_devicesoption clearly - Provide visual feedback for password strength
- Show success message after change
- Keep user logged in on current session
❌ DON'T:
- Store passwords locally
- Submit without current password verification
- Skip password strength validation
- Force logout on current session
- Log passwords in console or network logs
Security Considerations¶
Password Security:
- New password is hashed with bcrypt (12 rounds)
- Current password verification prevents unauthorized changes
- Password change is logged for audit trail
- Old password hash is permanently replaced
Session Security:
- Current session always remains active
- Other sessions optionally invalidated for security
- Session invalidation is immediate in database
- Tokens become invalid on next API call
Future Enhancements:
- Password history tracking (prevent reuse of recent passwords)
- Password strength meter integration
- Two-factor authentication requirement for password change
- Email notification on password change
- Temporary account lock after failed attempts
- Password expiration policies (enterprise feature)
Important Notes:
- Current session is preserved even with
logout_other_devices: true - All sessions are invalidated on password reset (forgot password flow)
- Password changes are permanent and cannot be undone
- Consider implementing password confirmation field in frontend
- TODO: Extract session_id from JWT for precise session management
Security Features¶
Password Security¶
Hashing:
- Algorithm: bcrypt with salt
- Cost factor: 12 rounds (configurable)
- Automatic salting for each password
- Secure comparison to prevent timing attacks
Requirements:
- Minimum length: 8 characters (recommended)
- Complexity: Mix of letters, numbers, symbols
- No common passwords (not yet enforced)
- Password history tracking (not yet implemented)
Verification Codes¶
Email Verification:
- Length: 6 characters (alphanumeric)
- Expiration: 24 hours
- Generation: Cryptographically secure random
- Delivery: Email (requires external integration)
Phone Verification:
- Length: 6 digits (numeric)
- Expiration: 15 minutes
- Generation: Cryptographically secure random
- Delivery: SMS (requires external integration)
Session Security¶
Token Security:
- JWT with HMAC-SHA256 signature
- Tokens include expiration timestamps
- Refresh tokens for long-term access
- Session tracking in database
Session Limits:
- Access token: 1 hour lifetime
- Refresh token: 30 days lifetime
- Session: 30 days from login
- Automatic cleanup of expired sessions
Privacy Protection¶
Anti-Enumeration:
- Forgot password returns same message for any email
- No indication whether account exists
- Consistent response times to prevent timing attacks
Data Protection:
- Passwords never stored in plain text
- Verification codes cleared after use
- Sensitive fields excluded from logs
- GDPR compliance considerations
Integration with Other Systems¶
Subscription Plan Limitations¶
Customer Registration:
- Each customer registration counts toward subscription limits
- FREE plan: Limited customers per month (check plan limits)
- PRO/ENTERPRISE: Higher or unlimited customer registrations
Reference: See Subscription Management for plan details
Appointment Booking¶
After Login:
- Customer can browse services at outlets
- View availability grid for staff/services
- Book appointments (requires valid session)
- Receive booking confirmations
Reference: See Appointment Management
Customer Profile¶
Profile Management:
- Update personal information
- Set preferred outlet
- Manage notification preferences
- View booking history
- Track loyalty points
Reference: See Customer Management
Best Practices¶
For Frontend Developers¶
Registration Flow:
✅ DO:
- Validate password strength client-side
- Show password requirements clearly
- Provide feedback for duplicate email/phone
- Explain verification process
- Support both email and phone registration
- Validate phone number format (E.164)
❌ DON'T:
- Store passwords locally
- Skip email/phone validation
- Allow weak passwords
- Submit without tenant_slug
Login Flow:
✅ DO:
- Store tokens securely (HttpOnly cookies or secure storage)
- Implement automatic token refresh
- Handle 401 errors with re-login prompt
- Clear tokens on logout
- Use tenant-specific login when possible
- Show loading states during authentication
❌ DON'T:
- Store tokens in localStorage (XSS risk)
- Ignore token expiration
- Hard-code tenant_slug
- Expose refresh tokens to JavaScript
Session Management:
✅ DO:
- Refresh access token before expiration
- Implement activity timeout on frontend
- Provide "logout everywhere" option in settings
- Clear all local data on logout
- Show active session list to user
❌ DON'T:
- Continue using expired tokens
- Ignore 401 responses
- Store sensitive data in browser storage
For Backend Developers¶
Security:
✅ DO:
- Always hash passwords with bcrypt
- Validate tenant_slug exists
- Check account status before login
- Invalidate sessions on security events
- Log authentication attempts
- Implement rate limiting for login attempts
❌ DON'T:
- Store plain-text passwords
- Trust client-provided tenant_id
- Skip password verification
- Expose user enumeration
- Allow unlimited login attempts
Performance:
✅ DO:
- Index customers by email and phone
- Cache tenant lookups
- Use database connection pooling
- Implement session cleanup job
- Paginate session history queries
❌ DON'T:
- Query without indexes
- Create new DB connection per request
- Leave expired sessions in database
- Return full customer history in login response
Troubleshooting¶
Registration Issues¶
Problem: "Customer already exists" error
Causes:
- Email already registered in tenant
- Phone number already registered in tenant
- Case-insensitive email match
Solution:
- Check if customer exists with GET /api/v1/customer/profile
- Use forgot-password flow if customer forgot credentials
- Try different email or phone number
Problem: "Tenant not found" error
Causes:
- Invalid tenant_slug
- Tenant is soft-deleted
- Tenant is inactive
Solution:
- Verify tenant_slug spelling
- Check tenant is active in database
- Use correct tenant_slug from URL or selection
Login Issues¶
Problem: "Invalid credentials" error
Causes:
- Wrong password
- Email not matching (case-sensitive on input)
- Customer doesn't exist in specified tenant
- Account deactivated
Solution:
- Verify email/phone and password
- Try forgot-password flow
- Check tenant_slug is correct
- Contact support if account deactivated
Problem: "Access denied for this tenant" error
Causes:
- Customer belongs to different tenant
- Attempting cross-tenant login
Solution:
- Verify correct tenant_slug
- Use central login (without tenant_slug) to auto-discover
- Customer may need to register with this tenant
Problem: 500 Internal Server Error on central login
Causes:
- ~~Missing
get_customer_tenants()method (fixed in v1.2.0)~~ - Database connection issues
- Invalid customer record structure
Solution:
- Ensure customer record has valid
tenant_idfield - Check database connectivity
- Verify tenant exists and is not soft-deleted
- Review server logs for detailed error messages
Note: Central login now uses customer's tenant_id field directly instead of querying tenant associations
Session Issues¶
Problem: Token expired errors
Causes:
- Access token expired (1 hour limit)
- Session expired (30 days limit)
- Session invalidated by logout
Solution:
- Use refresh token to get new access token
- Re-login if refresh token expired
- Check session wasn't invalidated by admin
Problem: Logout not working
Causes:
- Invalid Bearer token in header
- Session already invalidated
- Token blacklist not checked (if enabled)
Solution:
- Verify token format in Authorization header
- Check token hasn't expired
- Review server logs for detailed error
API Reference Summary¶
| Endpoint | Method | Auth | Purpose | Key Response |
|---|---|---|---|---|
/customer/auth/register |
POST | None | Create customer account | Customer ID, verification requirements |
/customer/auth/login |
POST | None | Authenticate customer | JWT tokens, tenant context, outlets |
/customer/auth/logout |
POST | Bearer | Invalidate session(s) | Success confirmation |
/customer/auth/refresh |
POST | None | Refresh access token | New access token |
/customer/auth/change-password |
POST | Bearer | Change account password | Success confirmation |
Next Steps¶
After implementing authentication:
- Integrate verification: Set up email/SMS service for code delivery
- Implement refresh: Add token refresh endpoint usage
- Build profile: Allow customers to update their information
- Enable booking: Let authenticated customers book appointments
- Track activity: Monitor customer engagement and sessions
Related Documentation:
- Customer Management - Profile and preferences
- Appointment Management - Booking flow
- Subscription Management - Plan limitations
- Webhook Integration - Event notifications