Skip to content

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:all
  • admin:users, admin:tenants, admin:system

TENANT_ADMIN Permissions:

  • read:tenant, write:tenant
  • admin:outlets, admin:staff, admin:services
  • read:appointments, write:appointments
  • read:customers, write:customers
  • read:reports, admin:settings

OUTLET_MANAGER Permissions:

  • read:outlet, write:outlet
  • read:appointments, write:appointments
  • read:customers, write:customers
  • read:staff, write:staff
  • read:services, write:services
  • read:reports

STAFF Permissions:

  • read:appointments, write:appointments
  • read:customers, read:services
  • read:profile, write:profile

Staff Login

Authenticate staff users with flexible dual-mode login support.

Endpoint

POST /api/v1/auth/login

Portal: 🟧 STAFF Authentication: None (public endpoint)

Request Body

{
  "email": "admin@example.com",
  "password": "SecurePassword123!",
  "tenant_slug": "my-salon"
}

Parameters:

  • email (required) - User email address
  • password (required) - User password
  • tenant_slug (optional) - Tenant identifier for tenant-specific login

Response Scenarios

Scenario 1: Direct Login (Single Tenant or Tenant Specified)

Request with tenant_slug:

{
  "email": "manager@example.com",
  "password": "SecurePass123!",
  "tenant_slug": "beauty-studio"
}

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

{
  "email": "admin@example.com",
  "password": "SecurePass123!"
}

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

{
  "email": "superadmin@platform.com",
  "password": "SuperSecure123!"
}

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 tenant
  • MULTIPLE - User has access to multiple tenants
  • ALL - 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 role
  • subscription_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:

{
  "detail": "Invalid email or password, or account is locked"
}

401 Unauthorized - Account Deactivated:

{
  "detail": "Account is deactivated"
}

403 Forbidden - Invalid Tenant:

{
  "detail": "Invalid tenant or tenant not found"
}

403 Forbidden - No Tenant Access:

{
  "detail": "User does not have access to this tenant"
}

403 Forbidden - No Active Tenants:

{
  "detail": "User does not have access to any active tenants"
}

Business Rules

  1. Tenant-Specific Mode:

  2. Validates tenant exists and is active

  3. Verifies user has access to specified tenant
  4. Returns direct login response with tokens

  5. Central Mode:

  6. SUPER_ADMIN gets access to all active tenants

  7. Other roles get only assigned tenants
  8. Auto-selects if user has exactly one tenant

  9. Security:

  10. Account locks after failed authentication attempts

  11. Validates account active status
  12. Returns consistent error messages

  13. Token Claims:

  14. Access token includes: user_id, email, role, tenant_id, type

  15. Refresh token includes: user_id, type
  16. Tokens expire based on configuration

Complete Tenant Selection

Complete the multi-tenant login process by selecting a specific tenant.

Endpoint

POST /api/v1/auth/complete-login

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 address
  • password (required) - User password
  • tenant_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

  1. Security:

  2. Re-authenticates credentials (prevents token replay attacks)

  3. Validates tenant exists and is active
  4. Verifies user has access to selected tenant

  5. Access Control:

  6. SUPER_ADMIN can select any active tenant

  7. Other roles can only select assigned tenants
  8. Account must be active and not locked

  9. Token Generation:

  10. Generates new access and refresh tokens

  11. Includes tenant context in token claims
  12. 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

GET /api/v1/auth/tenant/{slug}/verify

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

{
  "valid": false,
  "message": "Tenant not found or inactive"
}

Use Cases

  1. Login Form Validation:

  2. Verify tenant slug before showing login form

  3. Display tenant name in login UI
  4. Prevent login attempts for invalid tenants

  5. Multi-Tenant Subdomain:

  6. Validate subdomain matches tenant slug

  7. Show appropriate branding and theme
  8. Redirect invalid slugs to central login

Get Current User Profile

Retrieve authenticated user's profile and session context.

Endpoint

GET /api/v1/auth/me

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 identifier
  • email - User email address
  • first_name - User first name
  • last_name - User last name
  • role - User role (SUPER_ADMIN, TENANT_ADMIN, OUTLET_MANAGER, STAFF)
  • is_active - Account active status
  • last_login - Last login timestamp (ISO 8601)

Tenant Object (if in tenant context):

  • id - Tenant unique identifier
  • name - Tenant business name
  • slug - Tenant URL slug

Session Object:

  • expires_at - Token expiration Unix timestamp
  • tenant_context - Boolean indicating if logged in with tenant

Use Cases

  1. UI Personalization:

  2. Display user name and avatar

  3. Show current tenant context
  4. Apply role-based UI elements

  5. Permission Checks:

  6. Determine available features

  7. Show/hide admin sections
  8. Enable/disable action buttons

  9. Session Validation:

  10. Check token expiration

  11. Refresh token before expiry
  12. Handle session timeout

Error Responses

401 Unauthorized - Invalid Token:

{
  "detail": "Invalid token format"
}

401 Unauthorized - Expired Token:

{
  "detail": "Token has expired"
}


Refresh Access Token

Obtain a new access token using a valid refresh token.

Endpoint

POST /api/v1/auth/refresh

Portal: 🟧 STAFF Authentication: None (requires valid refresh token)

Request Body

{
  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

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:

  1. Validates provided refresh token
  2. Generates new access token
  3. Generates new refresh token (rotation)
  4. Old refresh token is invalidated

Security Benefits:

  • Prevents refresh token reuse
  • Limits damage from token theft
  • Detects compromised tokens

Business Rules

  1. Validation:

  2. Refresh token must be valid and not expired

  3. Token must have type: "refresh" claim
  4. User account must still be active

  5. Token Generation:

  6. New access token maintains user context

  7. New refresh token replaces old one
  8. Original session context is preserved

  9. Limitations:

  10. Tenant context NOT preserved (limitation in current implementation)

  11. Users may need to re-select tenant after refresh
  12. Production systems should store session context separately

Error Responses

401 Unauthorized - Invalid Token:

{
  "detail": "Invalid token type"
}

401 Unauthorized - User Deactivated:

{
  "detail": "User not found or deactivated"
}

401 Unauthorized - Expired Token:

{
  "detail": "Invalid or expired refresh token"
}

// 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

POST /api/v1/auth/password-reset/request

Portal: 🟧 STAFF Authentication: None (public endpoint)

Request Body

{
  "email": "user@example.com"
}

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

  1. Email Enumeration Protection:

  2. Always returns success message

  3. Prevents attackers from discovering valid emails
  4. Consistent response time regardless of email existence

  5. Token Generation:

  6. 32-byte URL-safe random token

  7. SHA-256 hashed before storage
  8. 1-hour expiration window

  9. Account Validation:

  10. Email sent only to valid, active accounts

  11. Skips locked or deleted accounts
  12. Global search (not tenant-specific)

Business Rules

  1. Rate Limiting:

  2. Recommended: 3 requests per email per hour

  3. Prevents abuse and spam
  4. Should be implemented at API gateway level

  5. Email Delivery:

  6. Reset email contains secure token link

  7. Link format: {FRONTEND_URL}/reset-password?token={token}
  8. Email includes expiration time and security notice

  9. Token Storage:

  10. Token hash stored in user record

  11. Expiration timestamp tracked
  12. Old tokens automatically invalidated

Implementation Note

Current implementation stores reset tokens in user records. Production systems should:

  • Use separate password_reset_tokens collection
  • 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

POST /api/v1/auth/password-reset/confirm

Portal: 🟧 STAFF Authentication: None (requires valid reset token)

Request Body

{
  "token": "abc123def456ghi789jkl012mno345pqr678stu901vwx234yz",
  "new_password": "NewSecurePassword123!"
}

Parameters:

  • token (required) - Reset token from email link
  • new_password (required) - New password (must meet security requirements)

Response

Success (200 OK):

{
  "message": "Password has been reset successfully.",
  "success": true
}

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

  1. Token Validation:

  2. Token must exist and match hash

  3. Must not be expired (< 1 hour old)
  4. User account must be active
  5. Token consumed after successful reset

  6. Account Actions:

  7. Password hashed with bcrypt

  8. Reset token cleared
  9. Account unlocked if previously locked
  10. Login attempts counter reset
  11. Account activated (if deactivated)

  12. Security:

  13. Single-use tokens

  14. Automatic account unlock
  15. Password history validation (recommended)
  16. Audit log entry (recommended)

Error Responses

400 Bad Request - Invalid Token:

{
  "detail": "Invalid or expired reset 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:

{
  "detail": "Failed to update password"
}


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)


Next Steps:

  1. Review user roles and permissions for your use case
  2. Implement dual-mode login flow (tenant-specific or central)
  3. Set up token refresh mechanism
  4. Configure password reset email templates
  5. Implement proper token storage in frontend
  6. Add rate limiting and security monitoring

For integration examples and code snippets, refer to the platform API documentation and SDKs.