Authentication Management¶
Complete guide to staff authentication, multi-tenant login, token management, and password reset flows in the Reserva platform.
Overview¶
The authentication system provides comprehensive identity and access management for staff users with:
- Dual-Mode Login - Tenant-specific and central authentication
- Multi-Tenant Support - Automatic tenant selection and access control
- JWT Token Management - Access and refresh token lifecycle
- Secure Password Reset - Token-based password recovery
- Role-Based Access - Permissions based on user roles (SUPER_ADMIN, TENANT_ADMIN, OUTLET_MANAGER, STAFF)
- Session Context - User profile and tenant context retrieval
Key Concepts:
- Tenant-Specific Login = Direct login with tenant slug (e.g.,
my-salon.app.com) - Central Login = Login without tenant, select from available tenants
- Access Token = Short-lived token for API requests (15-60 minutes)
- Refresh Token = Long-lived token to obtain new access tokens (7-30 days)
- Token Rotation = Security practice of issuing new refresh tokens after use
User Roles and Permissions¶
The platform supports four staff roles with hierarchical permissions:
| Role | Access Level | Permissions | Tenant Access |
|---|---|---|---|
| SUPER_ADMIN | System-wide | Full access to all tenants and system settings | All tenants |
| TENANT_ADMIN | Tenant-level | Manage outlets, staff, services, appointments, customers | Assigned tenants |
| OUTLET_MANAGER | Outlet-level | Manage outlet operations, staff, appointments, customers | Assigned tenants |
| STAFF | Individual | View/manage own appointments, view customers and services | Assigned tenants |
Permission Matrix¶
SUPER_ADMIN Permissions:
read:all,write:all,delete:alladmin:users,admin:tenants,admin:system
TENANT_ADMIN Permissions:
read:tenant,write:tenantadmin:outlets,admin:staff,admin:servicesread:appointments,write:appointmentsread:customers,write:customersread:reports,admin:settings
OUTLET_MANAGER Permissions:
read:outlet,write:outletread:appointments,write:appointmentsread:customers,write:customersread:staff,write:staffread:services,write:servicesread:reports
STAFF Permissions:
read:appointments,write:appointmentsread:customers,read:servicesread:profile,write:profile
Staff Login¶
Authenticate staff users with flexible dual-mode login support.
Endpoint¶
Portal: 🟧 STAFF Authentication: None (public endpoint)
Request Body¶
Parameters:
email(required) - User email addresspassword(required) - User passwordtenant_slug(optional) - Tenant identifier for tenant-specific login
Response Scenarios¶
Scenario 1: Direct Login (Single Tenant or Tenant Specified)¶
Request with tenant_slug:
Response (200 OK):
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer",
"user": {
"id": "507f1f77bcf86cd799439011",
"email": "manager@example.com",
"first_name": "John",
"last_name": "Doe",
"role": "OUTLET_MANAGER",
"avatar_url": "https://storage.example.com/avatars/john.jpg",
"last_login": "2025-10-07T14:30:00Z"
},
"tenant": {
"id": "507f1f77bcf86cd799439010",
"name": "Beauty Studio",
"slug": "beauty-studio",
"logo_url": "https://storage.example.com/logos/beauty-studio.png",
"theme_color": "#FF6B6B"
},
"access_type": "SINGLE",
"permissions": [
"read:outlet",
"write:outlet",
"read:appointments",
"write:appointments",
"read:customers",
"write:customers",
"read:staff",
"write:staff",
"read:services",
"write:services",
"read:reports"
],
"subscription_id": "507f1f77bcf86cd799439014",
"paper_id_enabled": true
}
Scenario 2: Multi-Tenant Selection Required¶
Request without tenant_slug (user has multiple tenants):
Response (200 OK):
{
"requires_tenant_selection": true,
"user": {
"id": "507f1f77bcf86cd799439012",
"email": "admin@example.com",
"first_name": "Jane",
"last_name": "Smith",
"role": "TENANT_ADMIN",
"tenant_ids": [
"507f1f77bcf86cd799439010",
"507f1f77bcf86cd799439013",
"507f1f77bcf86cd799439014"
],
"is_active": true
},
"available_tenants": [
{
"id": "507f1f77bcf86cd799439010",
"name": "Beauty Studio Downtown",
"slug": "beauty-studio-downtown"
},
{
"id": "507f1f77bcf86cd799439013",
"name": "Beauty Studio Uptown",
"slug": "beauty-studio-uptown"
},
{
"id": "507f1f77bcf86cd799439014",
"name": "Spa Wellness Center",
"slug": "spa-wellness"
}
]
}
Scenario 3: Super Admin with No Tenants (System Administration)¶
Request (SUPER_ADMIN with no tenants in system):
Response (200 OK):
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer",
"user": {
"id": "507f1f77bcf86cd799439015",
"email": "superadmin@platform.com",
"first_name": "Super",
"last_name": "Admin",
"role": "SUPER_ADMIN",
"avatar_url": null,
"last_login": "2025-10-07T14:35:00Z"
},
"tenant": {
"id": "507f1f77bcf86cd799439015",
"name": "System Administration",
"slug": "system"
},
"access_type": "ALL",
"permissions": [
"read:all",
"write:all",
"delete:all",
"admin:users",
"admin:tenants",
"admin:system"
],
"subscription_id": null,
"paper_id_enabled": null
}
Access Type Values¶
SINGLE- User has access to one tenantMULTIPLE- User has access to multiple tenantsALL- Super admin with system-wide access
Response Fields¶
Authentication Response includes:
access_token- JWT token for API authentication (expires in 15-60 minutes)refresh_token- Token for obtaining new access tokens (expires in 7-30 days)token_type- Always "bearer"user- User profile information (id, email, name, role, avatar, last_login)tenant- Current tenant context (id, name, slug, branding)access_type- User's tenant access level (SINGLE, MULTIPLE, ALL)permissions- Array of permission strings based on rolesubscription_id- Tenant's active subscription ID (ObjectId, null for system admins)paper_id_enabled- Whether Paper ID payment integration is enabled for the tenant (boolean, null if not configured)
Subscription ID Usage:
The subscription_id field allows frontend applications to:
- Fetch complete subscription details when needed
- Check subscription plan tier (FREE, PRO, ENTERPRISE)
- Verify subscription status and expiration
- Enable/disable features based on subscription limits
- Display plan information in user interface
Example: Use GET /api/v1/subscriptions/{subscription_id} to fetch full subscription details.
Paper ID Usage:
The paper_id_enabled field allows frontend applications to:
- Determine if online payment is available for the tenant
- Show/hide payment options in booking flow
- Enable Paper ID payment gateway integration
- Display appropriate payment method UI
Values:
- true - Paper ID payment integration is enabled and configured
- false - Paper ID integration is disabled
- null - Paper ID is not configured for the tenant
Error Responses¶
401 Unauthorized - Invalid Credentials:
401 Unauthorized - Account Deactivated:
403 Forbidden - Invalid Tenant:
403 Forbidden - No Tenant Access:
403 Forbidden - No Active Tenants:
Business Rules¶
-
Tenant-Specific Mode:
-
Validates tenant exists and is active
- Verifies user has access to specified tenant
-
Returns direct login response with tokens
-
Central Mode:
-
SUPER_ADMIN gets access to all active tenants
- Other roles get only assigned tenants
-
Auto-selects if user has exactly one tenant
-
Security:
-
Account locks after failed authentication attempts
- Validates account active status
-
Returns consistent error messages
-
Token Claims:
-
Access token includes: user_id, email, role, tenant_id, type
- Refresh token includes: user_id, type
- Tokens expire based on configuration
Complete Tenant Selection¶
Complete the multi-tenant login process by selecting a specific tenant.
Endpoint¶
Portal: 🟧 STAFF Authentication: None (requires valid credentials)
Request Body¶
{
"email": "admin@example.com",
"password": "SecurePass123!",
"tenant_slug": "beauty-studio-downtown"
}
Parameters:
email(required) - User email addresspassword(required) - User passwordtenant_slug(required) - Selected tenant slug from available tenants
Response¶
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer",
"user": {
"id": "507f1f77bcf86cd799439012",
"email": "admin@example.com",
"first_name": "Jane",
"last_name": "Smith",
"role": "TENANT_ADMIN",
"avatar_url": "https://storage.example.com/avatars/jane.jpg",
"last_login": "2025-10-07T14:40:00Z"
},
"tenant": {
"id": "507f1f77bcf86cd799439010",
"name": "Beauty Studio Downtown",
"slug": "beauty-studio-downtown",
"logo_url": "https://storage.example.com/logos/beauty-downtown.png",
"theme_color": "#4A90E2"
},
"access_type": "MULTIPLE",
"permissions": [
"read:tenant",
"write:tenant",
"admin:outlets",
"admin:staff",
"admin:services",
"read:appointments",
"write:appointments",
"read:customers",
"write:customers",
"read:reports",
"admin:settings"
],
"subscription_id": "507f1f77bcf86cd799439016",
"paper_id_enabled": true
}
Business Rules¶
-
Security:
-
Re-authenticates credentials (prevents token replay attacks)
- Validates tenant exists and is active
-
Verifies user has access to selected tenant
-
Access Control:
-
SUPER_ADMIN can select any active tenant
- Other roles can only select assigned tenants
-
Account must be active and not locked
-
Token Generation:
-
Generates new access and refresh tokens
- Includes tenant context in token claims
- Sets appropriate permissions based on role
Error Responses¶
Same as /login endpoint.
Verify Tenant¶
Check if a tenant exists and is accessible for login.
Endpoint¶
Portal: 🟧 STAFF Authentication: None (public endpoint)
Path Parameters¶
slug(required) - Tenant slug to verify
Response¶
Success (200 OK):
{
"valid": true,
"tenant": {
"name": "Beauty Studio",
"slug": "beauty-studio"
},
"message": "Tenant is valid and accessible"
}
Not Found (200 OK):
Use Cases¶
-
Login Form Validation:
-
Verify tenant slug before showing login form
- Display tenant name in login UI
-
Prevent login attempts for invalid tenants
-
Multi-Tenant Subdomain:
-
Validate subdomain matches tenant slug
- Show appropriate branding and theme
- Redirect invalid slugs to central login
Get Current User Profile¶
Retrieve authenticated user's profile and session context.
Endpoint¶
Portal: 🟧 STAFF Authentication: Required (JWT access token)
Response¶
{
"user": {
"id": "507f1f77bcf86cd799439011",
"email": "manager@example.com",
"first_name": "John",
"last_name": "Doe",
"role": "OUTLET_MANAGER",
"is_active": true,
"last_login": "2025-10-07T14:30:00Z"
},
"tenant": {
"id": "507f1f77bcf86cd799439010",
"name": "Beauty Studio",
"slug": "beauty-studio"
},
"permissions": [
"read:outlet",
"write:outlet",
"read:appointments",
"write:appointments",
"read:customers",
"write:customers",
"read:staff",
"write:staff",
"read:services",
"write:services",
"read:reports"
],
"session": {
"expires_at": 1728316200,
"tenant_context": true
}
}
Response Fields¶
User Object:
id- User unique identifieremail- User email addressfirst_name- User first namelast_name- User last namerole- User role (SUPER_ADMIN, TENANT_ADMIN, OUTLET_MANAGER, STAFF)is_active- Account active statuslast_login- Last login timestamp (ISO 8601)
Tenant Object (if in tenant context):
id- Tenant unique identifiername- Tenant business nameslug- Tenant URL slug
Session Object:
expires_at- Token expiration Unix timestamptenant_context- Boolean indicating if logged in with tenant
Use Cases¶
-
UI Personalization:
-
Display user name and avatar
- Show current tenant context
-
Apply role-based UI elements
-
Permission Checks:
-
Determine available features
- Show/hide admin sections
-
Enable/disable action buttons
-
Session Validation:
-
Check token expiration
- Refresh token before expiry
- Handle session timeout
Error Responses¶
401 Unauthorized - Invalid Token:
401 Unauthorized - Expired Token:
Refresh Access Token¶
Obtain a new access token using a valid refresh token.
Endpoint¶
Portal: 🟧 STAFF Authentication: None (requires valid refresh token)
Request Body¶
Parameters:
refresh_token(required) - Valid refresh token from login
Response¶
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer"
}
Token Rotation¶
The endpoint implements token rotation for enhanced security:
- Validates provided refresh token
- Generates new access token
- Generates new refresh token (rotation)
- Old refresh token is invalidated
Security Benefits:
- Prevents refresh token reuse
- Limits damage from token theft
- Detects compromised tokens
Business Rules¶
-
Validation:
-
Refresh token must be valid and not expired
- Token must have
type: "refresh"claim -
User account must still be active
-
Token Generation:
-
New access token maintains user context
- New refresh token replaces old one
-
Original session context is preserved
-
Limitations:
-
Tenant context NOT preserved (limitation in current implementation)
- Users may need to re-select tenant after refresh
- Production systems should store session context separately
Error Responses¶
401 Unauthorized - Invalid Token:
401 Unauthorized - User Deactivated:
401 Unauthorized - Expired Token:
Recommended Flow¶
// Check token expiration before API calls
if (tokenExpiresIn < 5 minutes) {
await refreshToken()
}
// Or use axios interceptor
axios.interceptors.response.use(
response => response,
async error => {
if (error.response.status === 401) {
await refreshToken()
// Retry original request
}
}
)
Request Password Reset¶
Initiate password reset process for user account.
Endpoint¶
Portal: 🟧 STAFF Authentication: None (public endpoint)
Request Body¶
Parameters:
email(required) - User email address
Response¶
Always returns success (200 OK):
{
"message": "If an account with this email exists, you will receive password reset instructions.",
"success": true
}
Security Features¶
-
Email Enumeration Protection:
-
Always returns success message
- Prevents attackers from discovering valid emails
-
Consistent response time regardless of email existence
-
Token Generation:
-
32-byte URL-safe random token
- SHA-256 hashed before storage
-
1-hour expiration window
-
Account Validation:
-
Email sent only to valid, active accounts
- Skips locked or deleted accounts
- Global search (not tenant-specific)
Business Rules¶
-
Rate Limiting:
-
Recommended: 3 requests per email per hour
- Prevents abuse and spam
-
Should be implemented at API gateway level
-
Email Delivery:
-
Reset email contains secure token link
- Link format:
{FRONTEND_URL}/reset-password?token={token} -
Email includes expiration time and security notice
-
Token Storage:
-
Token hash stored in user record
- Expiration timestamp tracked
- Old tokens automatically invalidated
Implementation Note¶
Current implementation stores reset tokens in user records. Production systems should:
- Use separate
password_reset_tokenscollection - Implement token cleanup for expired tokens
- Add audit logging for reset requests
- Send email notifications (currently TODO)
Confirm Password Reset¶
Complete password reset using secure reset token.
Endpoint¶
Portal: 🟧 STAFF Authentication: None (requires valid reset token)
Request Body¶
{
"token": "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz",
"new_password": "NewSecurePassword123!"
}
Parameters:
token(required) - Reset token from email linknew_password(required) - New password (must meet security requirements)
Response¶
Success (200 OK):
Password Requirements¶
Based on platform security policy:
- Minimum 8 characters
- At least one uppercase letter
- At least one lowercase letter
- At least one number
- At least one special character
- Cannot be same as previous password
Business Rules¶
-
Token Validation:
-
Token must exist and match hash
- Must not be expired (< 1 hour old)
- User account must be active
-
Token consumed after successful reset
-
Account Actions:
-
Password hashed with bcrypt
- Reset token cleared
- Account unlocked if previously locked
- Login attempts counter reset
-
Account activated (if deactivated)
-
Security:
-
Single-use tokens
- Automatic account unlock
- Password history validation (recommended)
- Audit log entry (recommended)
Error Responses¶
400 Bad Request - Invalid Token:
422 Validation Error - Weak Password:
{
"detail": [
{
"loc": ["body", "new_password"],
"msg": "Password must contain at least one uppercase letter",
"type": "value_error"
}
]
}
500 Internal Server Error:
Login Flow Examples¶
Flow 1: Direct Tenant Login¶
Client API
| |
| POST /auth/login |
| {email, password, |
| tenant_slug} |
|----------------------->|
| |
| LoginResponse |
| {access_token, |
| refresh_token, ...} |
|<-----------------------|
| |
| GET /auth/me |
| Authorization: Bearer |
|----------------------->|
| |
| User profile |
|<-----------------------|
Flow 2: Multi-Tenant Selection¶
Client API
| |
| POST /auth/login |
| {email, password} |
|----------------------->|
| |
| MultiTenantResponse |
| {requires_selection, |
| available_tenants} |
|<-----------------------|
| |
| [User selects tenant] |
| |
| POST /complete-login |
| {email, password, |
| tenant_slug} |
|----------------------->|
| |
| LoginResponse |
| {access_token, ...} |
|<-----------------------|
Flow 3: Token Refresh¶
Client API
| |
| [Access token expires]|
| |
| POST /auth/refresh |
| {refresh_token} |
|----------------------->|
| |
| TokenRefreshResponse |
| {new access_token, |
| new refresh_token} |
|<-----------------------|
| |
| [Continue with new |
| access token] |
Flow 4: Password Reset¶
Client API Email
| | |
| POST /password-reset/ | |
| request {email} | |
|----------------------->| |
| | |
| Success response | |
|<-----------------------| |
| | |
| | Reset email with token|
| |----------------------->|
| | |
| [User clicks link] | |
| | |
| POST /password-reset/ | |
| confirm {token, | |
| new_password} | |
|----------------------->| |
| | |
| Success response | |
|<-----------------------| |
| | |
| [User can now login] | |
Subscription Plan Limitations¶
Authentication endpoints are NOT limited by subscription plans. All authentication features are available to all tenants regardless of plan (FREE, PRO, ENTERPRISE).
However, user management features may be limited:
| Feature | FREE | PRO | ENTERPRISE |
|---|---|---|---|
| Staff Users | Max 5 per outlet | Max 50 per outlet | Unlimited |
| Multi-Outlet Access | 1 outlet | 10 outlets | Unlimited |
| SUPER_ADMIN Role | ❌ Platform-level only | ❌ Platform-level only | ❌ Platform-level only |
| TENANT_ADMIN Role | ✅ | ✅ | ✅ |
| OUTLET_MANAGER Role | ✅ | ✅ | ✅ |
| STAFF Role | ✅ | ✅ | ✅ |
Note: Attempting to create users beyond plan limits will fail at the user creation endpoint, not at authentication.
See Subscription Management for plan details and User Management for user creation limits.
Best Practices¶
For Frontend Developers¶
✅ DO:
- Store tokens in httpOnly cookies or secure storage (never localStorage)
- Implement automatic token refresh before expiration
- Clear tokens on logout
- Handle multi-tenant selection gracefully
- Show appropriate UI based on user role and permissions
- Validate tenant slug before login form submission
❌ DON'T:
- Store tokens in localStorage (XSS vulnerability)
- Hard-code tenant slugs in production
- Skip token expiration checks
- Ignore permission lists
- Cache user profile without refresh mechanism
For Backend Developers¶
✅ DO:
- Always validate tenant access for non-SUPER_ADMIN users
- Implement rate limiting on login and password reset
- Log authentication events for security audit
- Use secure password hashing (bcrypt with cost factor 12+)
- Invalidate old tokens on password reset
- Store session context for proper token refresh
❌ DON'T:
- Skip account status validation
- Return different error messages for valid/invalid emails
- Allow unlimited login attempts
- Store passwords in plain text or weak hashes
- Skip token type validation on refresh
- Allow refresh tokens without rotation
Security Checklist¶
- [ ] Implement rate limiting (3-5 attempts per 15 minutes)
- [ ] Add CAPTCHA after failed login attempts
- [ ] Log all authentication events
- [ ] Monitor for brute force attacks
- [ ] Implement IP-based blocking for suspicious activity
- [ ] Use HTTPS for all authentication endpoints
- [ ] Set secure token expiration times (access: 15-60 min, refresh: 7-30 days)
- [ ] Implement token rotation on refresh
- [ ] Add audit logging for password resets
- [ ] Send email notifications for security events
API Reference Summary¶
| Endpoint | Method | Purpose | Authentication |
|---|---|---|---|
/auth/login |
POST | Staff login with dual-mode support | None |
/auth/complete-login |
POST | Complete multi-tenant login | None (requires credentials) |
/auth/tenant/{slug}/verify |
GET | Verify tenant exists | None |
/auth/me |
GET | Get current user profile | Required (JWT) |
/auth/refresh |
POST | Refresh access token | None (requires refresh token) |
/auth/password-reset/request |
POST | Request password reset | None |
/auth/password-reset/confirm |
POST | Confirm password reset | None (requires reset token) |
Related Documentation¶
- User Management - Creating and managing staff users
- Subscription Management - Plan limits and features
- Tenant Management - Creating and configuring tenants
- Outlet Management - Managing physical locations
Next Steps:
- Review user roles and permissions for your use case
- Implement dual-mode login flow (tenant-specific or central)
- Set up token refresh mechanism
- Configure password reset email templates
- Implement proper token storage in frontend
- Add rate limiting and security monitoring
For integration examples and code snippets, refer to the platform API documentation and SDKs.