readme und docs von der tenant app hinzugefügt
This commit is contained in:
318
docs/prp/13-backend-authentication.md
Normal file
318
docs/prp/13-backend-authentication.md
Normal file
@@ -0,0 +1,318 @@
|
||||
# 13 - Backend Authentication Implementation
|
||||
|
||||
## Overview
|
||||
|
||||
This document outlines the authentication requirements and implementation details for the Fotospiel tenant backend. The system uses OAuth2 with PKCE (Proof Key for Code Exchange) for secure authorization, providing tenant-specific access tokens for API operations.
|
||||
|
||||
## Authentication Flow
|
||||
|
||||
### 1. Authorization Request
|
||||
- **Endpoint**: `POST /oauth/authorize`
|
||||
- **Method**: GET (redirect from frontend)
|
||||
- **Parameters**:
|
||||
- `client_id`: Fixed client ID for tenant-admin-app (`tenant-admin-app`)
|
||||
- `redirect_uri`: Frontend callback URL (e.g., `https://tenant-admin-app.example.com/auth/callback`)
|
||||
- `response_type`: `code`
|
||||
- `scope`: `tenant:read tenant:write` (tenant-specific scopes)
|
||||
- `state`: CSRF protection state parameter
|
||||
- `code_challenge`: PKCE challenge (SHA-256 hash of code verifier)
|
||||
- `code_challenge_method`: `S256`
|
||||
|
||||
**Response**: Redirect to frontend with authorization code and state parameters.
|
||||
|
||||
### 2. Token Exchange
|
||||
- **Endpoint**: `POST /oauth/token`
|
||||
- **Method**: POST
|
||||
- **Content-Type**: `application/x-www-form-urlencoded`
|
||||
- **Parameters**:
|
||||
- `grant_type`: `authorization_code` or `refresh_token`
|
||||
- `client_id`: `tenant-admin-app`
|
||||
- `code`: Authorization code (for initial exchange)
|
||||
- `redirect_uri`: Same as authorization request
|
||||
- `code_verifier`: PKCE verifier (plain text)
|
||||
- `refresh_token`: For token refresh (optional)
|
||||
|
||||
**Response** (JSON):
|
||||
```json
|
||||
{
|
||||
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||||
"refresh_token": "def50200...",
|
||||
"expires_in": 3600,
|
||||
"token_type": "Bearer",
|
||||
"scope": "tenant:read tenant:write"
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Token Refresh
|
||||
- **Endpoint**: `POST /oauth/token`
|
||||
- **Method**: POST
|
||||
- **Content-Type**: `application/x-www-form-urlencoded`
|
||||
- **Parameters**:
|
||||
- `grant_type`: `refresh_token`
|
||||
- `client_id`: `tenant-admin-app`
|
||||
- `refresh_token`: Current refresh token
|
||||
|
||||
**Response**: Same as token exchange (new access and refresh tokens).
|
||||
|
||||
### 4. Token Validation
|
||||
- **Endpoint**: `GET /api/v1/tenant/me`
|
||||
- **Method**: GET
|
||||
- **Authorization**: `Bearer {access_token}`
|
||||
- **Purpose**: Validate token and return tenant information
|
||||
|
||||
**Response** (JSON):
|
||||
```json
|
||||
{
|
||||
"tenant_id": "tenant-uuid",
|
||||
"tenant_name": "Event Photo Company",
|
||||
"credits": 150,
|
||||
"role": "admin",
|
||||
"email": "admin@eventphoto.com"
|
||||
}
|
||||
```
|
||||
|
||||
## Security Requirements
|
||||
|
||||
### PKCE Implementation
|
||||
- Validate `code_challenge_method` is `S256`
|
||||
- Store `code_challenge` and `code_verifier` association temporarily (Redis, 5-minute expiry)
|
||||
- Verify SHA-256 hash of `code_verifier` matches stored `code_challenge` during token exchange
|
||||
- Reject requests without valid PKCE parameters
|
||||
|
||||
### State Validation
|
||||
- Generate cryptographically secure state parameter
|
||||
- Store state in session or Redis with 5-minute expiry
|
||||
- Validate state parameter matches stored value during callback
|
||||
- Reject requests with state mismatch (CSRF protection)
|
||||
|
||||
### Token Security
|
||||
- Access tokens: JWT with 1-hour expiry, signed with RS256
|
||||
- Refresh tokens: Secure random 128-character strings, stored in database
|
||||
- Refresh tokens: Single-use or rotation (new refresh token with each refresh)
|
||||
- Rate limiting on token endpoints (100 requests/hour per IP)
|
||||
- IP binding for refresh tokens (optional for enhanced security)
|
||||
|
||||
### Scopes and Permissions
|
||||
- `tenant:read`: Access to tenant data (events, photos, members)
|
||||
- `tenant:write`: Create/update/delete tenant resources
|
||||
- `tenant:admin`: Full administrative access (user management, billing)
|
||||
- Token claims include `tenant_id` and `scope` for authorization
|
||||
|
||||
## Database Schema
|
||||
|
||||
### oauth_clients Table
|
||||
```sql
|
||||
CREATE TABLE oauth_clients (
|
||||
id VARCHAR(255) PRIMARY KEY,
|
||||
client_id VARCHAR(255) UNIQUE NOT NULL,
|
||||
client_secret VARCHAR(255),
|
||||
redirect_uris TEXT,
|
||||
scopes TEXT DEFAULT 'tenant:read tenant:write',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
);
|
||||
```
|
||||
|
||||
### oauth_codes Table (Temporary)
|
||||
```sql
|
||||
CREATE TABLE oauth_codes (
|
||||
id VARCHAR(255) PRIMARY KEY,
|
||||
client_id VARCHAR(255) NOT NULL,
|
||||
user_id VARCHAR(255) NOT NULL,
|
||||
code VARCHAR(255) UNIQUE NOT NULL,
|
||||
code_challenge VARCHAR(255) NOT NULL,
|
||||
state VARCHAR(255),
|
||||
redirect_uri VARCHAR(255),
|
||||
scope TEXT,
|
||||
expires_at TIMESTAMP NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
INDEX idx_code (code),
|
||||
INDEX idx_expires (expires_at)
|
||||
);
|
||||
```
|
||||
|
||||
### refresh_tokens Table
|
||||
```sql
|
||||
CREATE TABLE refresh_tokens (
|
||||
id VARCHAR(255) PRIMARY KEY,
|
||||
tenant_id VARCHAR(255) NOT NULL,
|
||||
token VARCHAR(255) UNIQUE NOT NULL,
|
||||
access_token VARCHAR(255),
|
||||
expires_at TIMESTAMP NOT NULL,
|
||||
scope TEXT,
|
||||
ip_address VARCHAR(45),
|
||||
user_agent TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
revoked_at TIMESTAMP NULL,
|
||||
INDEX idx_tenant (tenant_id),
|
||||
INDEX idx_token (token),
|
||||
INDEX idx_expires (expires_at)
|
||||
);
|
||||
```
|
||||
|
||||
### tenant_tokens Table (JWT Blacklist)
|
||||
```sql
|
||||
CREATE TABLE tenant_tokens (
|
||||
id VARCHAR(255) PRIMARY KEY,
|
||||
tenant_id VARCHAR(255) NOT NULL,
|
||||
jti VARCHAR(255) UNIQUE NOT NULL, -- JWT ID
|
||||
token_type VARCHAR(50) NOT NULL,
|
||||
expires_at TIMESTAMP NOT NULL,
|
||||
revoked_at TIMESTAMP NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
INDEX idx_jti (jti),
|
||||
INDEX idx_tenant (tenant_id),
|
||||
INDEX idx_expires (expires_at)
|
||||
);
|
||||
```
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### Authentication Endpoints
|
||||
| Endpoint | Method | Description | Authentication |
|
||||
|----------|--------|-------------|----------------|
|
||||
| `/oauth/authorize` | GET | Authorization request | None |
|
||||
| `/oauth/token` | POST | Token exchange/refresh | None |
|
||||
| `/api/v1/tenant/me` | GET | Validate token | Bearer Token |
|
||||
|
||||
### Protected Endpoints
|
||||
All tenant API endpoints require `Authorization: Bearer {access_token}` header.
|
||||
|
||||
#### Token Validation Middleware
|
||||
```javascript
|
||||
// Pseudocode
|
||||
function validateTenantToken(req, res, next) {
|
||||
const token = req.headers.authorization?.replace('Bearer ', '');
|
||||
if (!token) return res.status(401).json({ error: 'Missing token' });
|
||||
|
||||
try {
|
||||
const decoded = jwt.verify(token, process.env.JWT_SECRET);
|
||||
const { tenant_id, scope, exp, jti } = decoded;
|
||||
|
||||
// Check if token is blacklisted
|
||||
const blacklisted = await db.query('SELECT * FROM tenant_tokens WHERE jti = ? AND revoked_at IS NULL', [jti]);
|
||||
if (blacklisted.length > 0) {
|
||||
return res.status(401).json({ error: 'Token revoked' });
|
||||
}
|
||||
|
||||
// Check expiry
|
||||
if (Date.now() >= exp * 1000) {
|
||||
return res.status(401).json({ error: 'Token expired' });
|
||||
}
|
||||
|
||||
// Set tenant context
|
||||
req.tenant = { id: tenant_id, scope };
|
||||
next();
|
||||
} catch (error) {
|
||||
res.status(401).json({ error: 'Invalid token' });
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
### Backend (.env)
|
||||
```
|
||||
JWT_SECRET=your-super-secret-jwt-signing-key
|
||||
API_BASE_URL=https://api.fotospiel.com
|
||||
OAUTH_CLIENT_SECRET=your-oauth-client-secret
|
||||
DATABASE_URL=your-database-connection-string
|
||||
REDIS_URL=redis://localhost:6379
|
||||
```
|
||||
|
||||
### Frontend (.env)
|
||||
```
|
||||
VITE_API_URL=https://api.fotospiel.com
|
||||
VITE_OAUTH_CLIENT_ID=tenant-admin-app
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Common Error Responses
|
||||
```json
|
||||
// 400 Bad Request
|
||||
{
|
||||
"error": "invalid_request",
|
||||
"error_description": "Missing required parameter: code"
|
||||
}
|
||||
|
||||
// 401 Unauthorized
|
||||
{
|
||||
"error": "invalid_token",
|
||||
"error_description": "Token signature invalid"
|
||||
}
|
||||
|
||||
// 403 Forbidden
|
||||
{
|
||||
"error": "insufficient_scope",
|
||||
"error_description": "Scope tenant:write required"
|
||||
}
|
||||
```
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
### 1. PKCE Storage
|
||||
- Use Redis for temporary code_challenge storage (5-minute TTL)
|
||||
- Key format: `pkce:{code_challenge}:{client_id}`
|
||||
- Value: JSON with `code_verifier`, `user_id`, `redirect_uri`, `scope`
|
||||
|
||||
### 2. Refresh Token Rotation
|
||||
- Issue new refresh token with each refresh
|
||||
- Revoke old refresh token immediately
|
||||
- Limit refresh tokens per tenant to 5 active
|
||||
|
||||
### 3. Rate Limiting
|
||||
- Authorization requests: 10/minute per IP
|
||||
- Token exchanges: 5/minute per IP
|
||||
- Token validation: 100/minute per tenant
|
||||
|
||||
### 4. Logging and Monitoring
|
||||
- Log all authentication attempts (success/failure)
|
||||
- Monitor token usage patterns
|
||||
- Alert on unusual activity (multiple failed attempts, token anomalies)
|
||||
- Track refresh token usage for security analysis
|
||||
|
||||
### 5. Database Cleanup
|
||||
- Cron job to remove expired authorization codes (daily)
|
||||
- Remove expired refresh tokens (weekly)
|
||||
- Clean blacklisted tokens after expiry (daily)
|
||||
|
||||
## Testing Requirements
|
||||
|
||||
### Unit Tests
|
||||
- PKCE generation and validation
|
||||
- State parameter security
|
||||
- Token signing and verification
|
||||
- Scope validation middleware
|
||||
|
||||
### Integration Tests
|
||||
- Complete OAuth2 flow (authorize → token → validate)
|
||||
- Token refresh cycle
|
||||
- Error scenarios (invalid code, expired tokens, state mismatch)
|
||||
- Concurrent access testing
|
||||
|
||||
### Security Tests
|
||||
- CSRF protection validation
|
||||
- PKCE bypass attempts
|
||||
- Token replay attacks
|
||||
- Rate limiting enforcement
|
||||
|
||||
## Deployment Considerations
|
||||
|
||||
### 1. Secrets Management
|
||||
- Store JWT secret and OAuth client secret in secure vault (AWS Secrets Manager, HashiCorp Vault)
|
||||
- Rotate secrets every 90 days
|
||||
- Use different secrets for dev/staging/production
|
||||
|
||||
### 2. Certificate Management
|
||||
- Use Let's Encrypt or commercial SSL certificates
|
||||
- Rotate certificates before expiry
|
||||
- Enable HSTS headers
|
||||
|
||||
### 3. Monitoring
|
||||
- Track authentication success/failure rates
|
||||
- Monitor token expiry patterns
|
||||
- Alert on PKCE validation failures
|
||||
- Log all security-related events
|
||||
|
||||
This implementation provides secure, scalable authentication for the Fotospiel tenant system, following OAuth2 best practices with PKCE for public clients.
|
||||
Reference in New Issue
Block a user