2026-04-02 18:39:35 +08:00
# 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`
2026-04-16 10:51:08 +08:00
- `AUTH_USER_NOT_FOUND`
Authorization error codes from `backend/src/v1/users/dependencies.py` :
- `AUTH_UNAUTHORIZED`
2026-04-02 18:39:35 +08:00
## 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" }
```
2026-04-28 17:31:24 +08:00
Optional fields:
``` json
{ "email" : "user@example.com" , "token" : "123456" , "language" : "zh-CN" , "timezone" : "Asia/Shanghai" }
```
2026-04-02 18:39:35 +08:00
Validation (backend):
- `email` must match `SUPABASE_EMAIL_PATTERN`
- `token` must be exactly 6 chars
2026-04-28 17:31:24 +08:00
- `language` (optional): max 20 chars, updates profile settings.preferences.language if provided
- `timezone` (optional): max 50 chars, updates profile settings.preferences.timezone if provided
2026-04-02 18:39:35 +08:00
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.