feat: simplify auth flow - replace JWT with session cookies
Replace complex JWT + refresh token authentication with simple session-based auth using signed cookies. **Removed:** - JWT token service (jwt_service.py) - Refresh token store (refresh_store.py) - Refresh token model and database table - JWKS fetching and OIDC token verification - python-jose dependency **Added:** - Session service (session.py) with HMAC-SHA256 signed cookies - Auth dependencies module for shared auth logic - Session-based auth endpoints **Updated:** - All API endpoints to use session-based auth - Config: removed JWT settings, added SESSION_SECRET/SESSION_TTL_HOURS - Tests: rewritten for session-based flow - Frontend: no changes needed (already uses cookies) Quality gates: ruff ✓, mypy ✓, typecheck ✓, lint ✓
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
# Simplified Auth Flow Specification
|
||||
|
||||
## Requirements
|
||||
|
||||
### Functional Requirements
|
||||
|
||||
1. **OAuth2 Login**: Users authenticate via Authentik using standard OAuth2 flow
|
||||
2. **Session Management**: Authenticated users have a signed session cookie
|
||||
3. **User Sync**: User data (email, name, groups) synced from Authentik on login
|
||||
4. **Protected Routes**: API endpoints can require authentication
|
||||
5. **Logout**: Users can logout, clearing their session
|
||||
|
||||
### Non-Functional Requirements
|
||||
|
||||
1. **Simplicity**: Auth flow should be understandable in 5 minutes
|
||||
2. **Security**: Session cookies must be signed and httpOnly
|
||||
3. **Stateless**: No server-side session state (cookie contains all needed info)
|
||||
4. **Performance**: No token refresh overhead
|
||||
|
||||
## API Specification
|
||||
|
||||
### GET /auth/login
|
||||
Initiates OAuth2 login flow.
|
||||
|
||||
**Response**: 307 Redirect to Authentik authorize URL
|
||||
|
||||
### GET /auth/callback
|
||||
Handles OAuth2 callback from Authentik.
|
||||
|
||||
**Query Parameters**:
|
||||
- `code`: Authorization code
|
||||
- `state`: State parameter for CSRF protection
|
||||
|
||||
**Response**:
|
||||
- Success: 307 Redirect to frontend with session cookie set
|
||||
- Error: 400 Bad Request (invalid state or code)
|
||||
|
||||
### GET /auth/me
|
||||
Returns current authenticated user.
|
||||
|
||||
**Headers**: Requires session cookie
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"id": "uuid",
|
||||
"email": "user@example.com",
|
||||
"name": "User Name",
|
||||
"avatar_url": "..."
|
||||
}
|
||||
```
|
||||
|
||||
### POST /auth/logout
|
||||
Logs out current user.
|
||||
|
||||
**Response**: 200 OK with session cookie cleared
|
||||
|
||||
## Data Model
|
||||
|
||||
### User Model (existing, kept)
|
||||
```python
|
||||
class User:
|
||||
id: UUID
|
||||
email: str
|
||||
name: str
|
||||
authentik_id: str
|
||||
avatar_url: Optional[str]
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
```
|
||||
|
||||
### Session Cookie Format
|
||||
```
|
||||
session={signed_payload}; HttpOnly; Secure; SameSite=Lax
|
||||
```
|
||||
|
||||
Where signed_payload is:
|
||||
```json
|
||||
{
|
||||
"user_id": "uuid",
|
||||
"exp": 1234567890
|
||||
}
|
||||
```
|
||||
|
||||
Signed with HMAC-SHA256 using SESSION_SECRET.
|
||||
|
||||
## Security Considerations
|
||||
|
||||
1. **CSRF Protection**: State parameter in OAuth flow
|
||||
2. **Session Security**: Signed cookies prevent tampering
|
||||
3. **Cookie Attributes**: httpOnly, Secure, SameSite=Lax
|
||||
4. **Session Expiry**: Configurable TTL with automatic cleanup
|
||||
5. **Token Handling**: Authentik access token not exposed to client
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Authentication Errors
|
||||
- Missing session: 401 Unauthorized
|
||||
- Invalid session signature: 401 Unauthorized
|
||||
- Expired session: 401 Unauthorized (redirect to login)
|
||||
- Invalid OAuth state: 400 Bad Request
|
||||
- OAuth code exchange failure: 400 Bad Request
|
||||
|
||||
## Future Considerations
|
||||
|
||||
### Teams/Groups
|
||||
- Authentik groups available via userinfo endpoint
|
||||
- Can store group membership in user model
|
||||
- Team management can be built on top
|
||||
|
||||
### Session Persistence
|
||||
- Currently using signed cookies (stateless)
|
||||
- Can add Redis session store later if needed
|
||||
- No database changes required for upgrade
|
||||
Reference in New Issue
Block a user