Files
eryao/docs/protocols/auth/session-auth-protocol.md
T

145 lines
3.3 KiB
Markdown
Raw Normal View History

# Session Auth Protocol (Frontend <-> Backend)
This protocol defines login, refresh, and logout contract for Eryao Flutter app.
Protocol verification status:
- Last audited against backend source: `backend/src/v1/auth/router.py`, `backend/src/v1/auth/schemas.py`, `backend/src/v1/router.py`
- Frontend mapping source: `apps/lib/features/auth/data/apis/auth_api.dart`
- Current status: aligned
## Base URL
- Runtime injected by Flutter `--dart-define=BACKEND_URL=...`
- Fallback:
- Android emulator: `http://10.0.2.2:5775`
- Others: `http://localhost:5775`
## Transport
- Content type: `application/json`
- Error format: RFC7807 with extension fields `code`, `params`
## Route audit (current backend)
From `backend/src/v1/auth/router.py`:
- `POST /auth/otp/send` (204)
- `POST /auth/email-session` (200, `SessionResponse`)
- `POST /auth/sessions/refresh` (200, `SessionResponse`)
- `DELETE /auth/sessions` (204)
Gateway error codes from `backend/src/v1/auth/gateway.py`:
- `AUTH_SERVICE_UNAVAILABLE`
- `AUTH_TOO_MANY_REQUESTS`
- `AUTH_VERIFICATION_CODE_INVALID`
- `AUTH_REFRESH_TOKEN_INVALID`
- `AUTH_REFRESH_TOKEN_MISSING`
- `AUTH_USER_NOT_FOUND`
Authorization error codes from `backend/src/v1/users/dependencies.py`:
- `AUTH_UNAUTHORIZED`
## Frontend route mapping
- Send OTP: `POST /api/v1/auth/otp/send`
- Login with OTP: `POST /api/v1/auth/email-session`
- Refresh session: `POST /api/v1/auth/sessions/refresh`
- Logout: `DELETE /api/v1/auth/sessions`
## Payload contract
### Send OTP
Request:
```json
{ "email": "user@example.com" }
```
Validation (backend):
- `email` must match `SUPABASE_EMAIL_PATTERN`
Response: `204 No Content`
### Create session
Request:
```json
{ "email": "user@example.com", "token": "123456" }
```
Optional fields:
```json
{ "email": "user@example.com", "token": "123456", "language": "zh-CN", "timezone": "Asia/Shanghai" }
```
Validation (backend):
- `email` must match `SUPABASE_EMAIL_PATTERN`
- `token` must be exactly 6 chars
- `language` (optional): max 20 chars, updates profile settings.preferences.language if provided
- `timezone` (optional): max 50 chars, updates profile settings.preferences.timezone if provided
Response:
```json
{
"access_token": "...",
"refresh_token": "...",
"expires_in": 3600,
"token_type": "bearer",
"user": {
"id": "uuid",
"email": "user@example.com"
}
}
```
### Refresh session
Request:
```json
{ "refresh_token": "..." }
```
Validation (backend):
- `refresh_token` min length: 1
Response: same as create session.
### Logout
Request:
```json
{ "refresh_token": "..." }
```
Validation (backend):
- `refresh_token` min length: 1
Response: `204 No Content`
## Global auth state requirements
- Auth must be managed by single global `AuthBloc` instance.
- App startup must attempt refresh if refresh token exists.
- On refresh failure (`401` + `AUTH_REFRESH_TOKEN_INVALID`), clear local session and route to login.
- Login success must persist access token + refresh token + display email.
- Logout must call backend route then clear local session.
- For protected requests, when backend returns `401`, frontend network layer must trigger global auth invalidation callback (single chain), not feature-level token clearing.
## Login identity mode
- Current app version supports **email OTP login only**.
- Phone registration/login is removed from frontend flow.