Authentication
Authentication flow covering JWT access/refresh tokens, OAuth2, WebAuthn passkeys, magic links, and RBAC permission checks
Auth Methods
MQUAD supports four authentication methods:
| Method | Endpoint | Security |
|---|---|---|
| Password | POST /api/v1/account/login | BCrypt hashed |
| Magic Link | POST /api/v1/account/login/magic-link | Email-based token |
| Passkey | POST /api/v1/account/login/passkey | WebAuthn/FIDO2 |
| OAuth2 | Via backend social providers | OAuth2 standard |
JWT Token Structure
{
"sub": "user-uuid",
"tenant_id": "tenant-uuid-or-null", # null = super admin
"role": "admin|editor|viewer",
"permissions": ["survey:read", "survey:write"],
"exp": 1715673600,
"iat": 1715587200
}Authentication Flow
Client Backend Redis
│ │ │
│ POST /account/login │ │
│────────────────────────>│ │
│ │ │
│ ├─ Verify credentials │ │
│ ├─ Generate JWT │ │
│ ├─ Store refresh token─│─────────────────────────>│
│ │ │
│<────────────────────────│ │
│ { access_token, │ │
│ refresh_token, │ │
│ expires_in } │ │Login Examples
Password Login
curl -X POST http://localhost:8000/api/v1/account/login \
-H "Content-Type: application/json" \
-d '{"email": "admin@example.com", "password": "Test@123!"}'Passkey Login
# WebAuthn registration
POST /api/v1/account/passkey/register
# Returns challenge for browser WebAuthn API
POST /api/v1/account/passkey/login
# Verifies authenticator assertionMagic Link
POST /api/v1/account/email_magic_link_login
# Sends email with one-time link
GET /api/v1/account/email_magic_link_validate?token=<token>
# Validates token and returns JWTRBAC Permission Checks
Admin status comes from permissions list, not a boolean field:
# WRONG — never do this
is_super_admin = token_data.is_super_admin # DON'T
# RIGHT — always check permissions
from common.rbac_helpers import is_super_admin
is_super_admin(token_data.permissions) # DOTokenData Model
class TokenData(BaseModel):
sub: str # User ID
tenant_id: Optional[str] = None # Tenant ID (null for super admin)
permissions: List[str] # Permission strings
# NO is_super_admin: bool field!