# Authentication {#authentication}



## Auth Methods {#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 {#jwt-token-structure}


```python
{
  "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 {#authentication-flow}


```
Client                    Backend                     Redis
  │                         │                          │
  │  POST /account/login    │                          │
  │────────────────────────>│                          │
  │                         │                          │
  │  ├─ Verify credentials  │                          │
  │  ├─ Generate JWT        │                          │
  │  ├─ Store refresh token─│─────────────────────────>│
  │                         │                          │
  │<────────────────────────│                          │
  │  { access_token,        │                          │
  │    refresh_token,       │                          │
  │    expires_in }         │                          │
```


## Login Examples {#login-examples}



### Password Login {#password-login}


```bash
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 {#passkey-login}


```python
# WebAuthn registration
POST /api/v1/account/passkey/register
# Returns challenge for browser WebAuthn API

POST /api/v1/account/passkey/login
# Verifies authenticator assertion
```


### Magic Link {#magic-link}


```python
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 JWT
```


## RBAC Permission Checks {#rbac-permission-checks}


Admin status comes from **permissions list**, not a boolean field:

```python
# 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)  # DO
```


## TokenData Model {#tokendata-model}


```python
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!
```
