Skip to content

Authentication

Overview

The system uses a hybrid authentication model: - Access tokens: Short-lived JWT (15 minutes) - Refresh tokens: Long-lived stored tokens (7 days)

Token Flow

1. User logs in with email/password
2. Server validates credentials
3. Server returns: {access_token, refresh_token, expires_in}
4. Client stores tokens securely

5. Client sends requests with: Authorization: Bearer <access_token>
6. Server validates access token on each request

7. When access token expires:
   - Client sends refresh_token to /auth/refresh
   - Server returns new access_token

Token Structure

Access Token (JWT)

{
  "sub": "user_uuid",
  "tenant_id": "tenant_uuid",
  "role": "courier",
  "exp": 1715944800,
  "iat": 1715943600
}

Refresh Token

Stored in database:

refresh_token_hash: bcrypt_hash
expires_at: datetime
revoked: boolean

Password Storage

  • Algorithm: bcrypt with salt
  • Rounds: 12 (configurable)
  • Never store plain passwords

Auth Endpoints

Endpoint Method Description
/api/v1/auth/register POST Register new user
/api/v1/auth/login POST Login and get tokens
/api/v1/auth/refresh POST Refresh access token
/api/v1/auth/logout POST Revoke refresh token
/api/v1/auth/verify-email POST Verify email address
/api/v1/auth/reset-password POST Reset password flow

Security Considerations

  1. Token Storage: Use secure storage on client (Keychain/iOS, EncryptedSharedPreferences/Android)
  2. Token Expiry: Short access token lifespan limits exposure if token is leaked
  3. Logout: Properly revoke refresh tokens on logout
  4. Password Reset: Expire reset codes after use or timeout

JWT Payload Hardening

Sensitive fields are blocked from JWT payloads to prevent token tampering:

# JWTService blocks sensitive fields
class JWTService:
    SENSITIVE_FIELDS = frozenset([
        "password",
        "hashed_password",
        "password_hash",
        "secret",
        "api_key",
    ])

    def create_access_token(self, data: Dict[str, Any]) -> str:
        for field in self.SENSITIVE_FIELDS:
            if field in data:
                raise ValueError(f"Cannot include sensitive field '{field}' in JWT payload")

This ensures that passwords, API keys, and other secrets are never accidentally included in JWT token payloads, preventing exposure through token inspection or logging.

Ver también: Authorization · OWASP · Rate Limiting