Token Management¶
JWT Configuration¶
| Token | Expiry | Rotation |
|---|---|---|
| access_token | 15 minutes | No |
| refresh_token | 7 days | On every use |
Token Payload¶
Token Storage (Backend)¶
Sessions stored in database with hashed refresh token:
class SessionModel(SQLModel, table=True):
id: UUID = Field(primary_key=True)
user_id: UUID = Field(foreign_key="users.id")
refresh_token_hash: str = Field(max_length=255)
expires_at: datetime
created_at: datetime
Token Storage (Frontend)¶
Tokens encrypted with Fernet/AES before storing in SharedPreferences:
# core/security/encryption.py
from cryptography.fernet import Fernet
class TokenEncryption:
def __init__(self, key: bytes):
self._fernet = Fernet(key)
def encrypt(self, token: str) -> str:
return self._fernet.encrypt(token.encode()).decode()
def decrypt(self, encrypted: str) -> str:
return self._fernet.decrypt(encrypted.encode()).decode()
Automatic Token Refresh¶
Frontend TokenManager handles automatic refresh:
# core/security/token_manager.py
class TokenManager:
def should_refresh_token(self) -> bool:
# Refresh 2 minutes before expiry
expires_at = self._get_token_expiry()
return (expires_at - datetime.now()).total_seconds() < REFRESH_BEFORE_EXPIRY_MINUTES * 60
Environment Variables¶
# Session & Token Security
SESSION_TIMEOUT_MINUTES=15
REFRESH_BEFORE_EXPIRY_MINUTES=2
ENCRYPT_TOKENS=true
# Optional encryption
TOKEN_ENCRYPTION_KEY=your-256bit-key
MACHINE_ID=unique-machine-id
APP_SECRET=your-app-secret
Security Measures¶
- Token Encryption: Fernet/AES for tokens in SharedPreferences
- HTTPS Enforcement: Block HTTP requests in production
- Session Timeout: 15 minutes default (
SESSION_TIMEOUT_MINUTES) - Automatic Refresh: Refresh 2 minutes before expiry
- Refresh Token Rotation: New refresh token on every use