mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-24 21:31:09 -05:00
Add secret management guide, client-side performance monitoring, and search accessibility enhancements
- Introduced a comprehensive Secret Management Guide detailing best practices, secret classification, development setup, production management, rotation procedures, and emergency protocols. - Implemented a client-side performance monitoring script to track various metrics including page load performance, paint metrics, layout shifts, and memory usage. - Enhanced search accessibility with keyboard navigation support for search results, ensuring compliance with WCAG standards and improving user experience.
This commit is contained in:
216
docs/architecture/adr-005-authentication-approach.md
Normal file
216
docs/architecture/adr-005-authentication-approach.md
Normal file
@@ -0,0 +1,216 @@
|
||||
# ADR-005: Authentication Approach
|
||||
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
|
||||
## Context
|
||||
|
||||
ThrillWiki needs to authenticate users for:
|
||||
- Web browsing (session-based)
|
||||
- API access (token-based)
|
||||
- Social login (Google, Discord)
|
||||
|
||||
We needed an authentication approach that would:
|
||||
- Support multiple authentication methods
|
||||
- Provide secure token handling for API
|
||||
- Enable social authentication
|
||||
- Work seamlessly with Django + HTMX architecture
|
||||
|
||||
## Decision
|
||||
|
||||
We implemented a **Hybrid Authentication System** using django-allauth for social auth and djangorestframework-simplejwt for API tokens.
|
||||
|
||||
### Authentication Methods
|
||||
|
||||
| Context | Method | Library |
|
||||
|---------|--------|---------|
|
||||
| Web browsing | Session-based | Django sessions |
|
||||
| API access | JWT tokens | djangorestframework-simplejwt |
|
||||
| Social login | OAuth2 | django-allauth |
|
||||
| Password reset | Email tokens | Django built-in |
|
||||
|
||||
### Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ User Request │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
│
|
||||
┌───────────────┼───────────────┐
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||||
│ Web Request │ │ API Request │ │ Social Auth │
|
||||
│ │ │ │ │ │
|
||||
│ Session Cookie │ │ Bearer Token │ │ OAuth Flow │
|
||||
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
||||
│ │ │
|
||||
└───────────────┼───────────────┘
|
||||
▼
|
||||
┌─────────────────────┐
|
||||
│ Django User │
|
||||
│ (Authenticated) │
|
||||
└─────────────────────┘
|
||||
```
|
||||
|
||||
### JWT Token Configuration
|
||||
|
||||
```python
|
||||
# backend/config/settings/rest_framework.py
|
||||
SIMPLE_JWT = {
|
||||
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=15),
|
||||
'REFRESH_TOKEN_LIFETIME': timedelta(days=7),
|
||||
'ROTATE_REFRESH_TOKENS': True,
|
||||
'BLACKLIST_AFTER_ROTATION': True,
|
||||
'ALGORITHM': 'HS256',
|
||||
'SIGNING_KEY': SECRET_KEY,
|
||||
'AUTH_HEADER_TYPES': ('Bearer',),
|
||||
}
|
||||
```
|
||||
|
||||
### Social Authentication
|
||||
|
||||
```python
|
||||
# backend/config/settings/third_party.py
|
||||
SOCIALACCOUNT_PROVIDERS = {
|
||||
'google': {
|
||||
'SCOPE': ['profile', 'email'],
|
||||
'AUTH_PARAMS': {'access_type': 'online'},
|
||||
},
|
||||
'discord': {
|
||||
'SCOPE': ['identify', 'email'],
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## Consequences
|
||||
|
||||
### Benefits
|
||||
|
||||
1. **Flexibility**: Multiple auth methods for different use cases
|
||||
2. **Security**: JWT with short-lived access tokens
|
||||
3. **User Experience**: Social login reduces friction
|
||||
4. **Standards-Based**: OAuth2 and JWT are industry standards
|
||||
5. **Django Integration**: Seamless with Django's user model
|
||||
|
||||
### Trade-offs
|
||||
|
||||
1. **Complexity**: Multiple auth systems to maintain
|
||||
2. **Token Management**: Must handle token refresh client-side
|
||||
3. **Social Provider Dependency**: Reliance on third-party OAuth providers
|
||||
|
||||
### Authentication Flow
|
||||
|
||||
#### Web Session Authentication
|
||||
|
||||
```
|
||||
1. User visits /login/
|
||||
2. User submits credentials
|
||||
3. Django validates credentials
|
||||
4. Session created, cookie set
|
||||
5. Subsequent requests include session cookie
|
||||
```
|
||||
|
||||
#### API JWT Authentication
|
||||
|
||||
```
|
||||
1. Client POST /api/v1/auth/login/
|
||||
{username, password}
|
||||
2. Server validates, returns tokens
|
||||
{access: "...", refresh: "..."}
|
||||
3. Client includes in requests:
|
||||
Authorization: Bearer <access_token>
|
||||
4. On 401, client refreshes:
|
||||
POST /api/v1/auth/token/refresh/
|
||||
{refresh: "..."}
|
||||
```
|
||||
|
||||
#### Social Authentication
|
||||
|
||||
```
|
||||
1. User clicks "Login with Google"
|
||||
2. Redirect to Google OAuth
|
||||
3. User authorizes application
|
||||
4. Google redirects with auth code
|
||||
5. Server exchanges code for tokens
|
||||
6. Server creates/updates user
|
||||
7. Session created
|
||||
```
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
### Session-Only Authentication
|
||||
|
||||
**Rejected because:**
|
||||
- Not suitable for mobile apps
|
||||
- Not RESTful for API access
|
||||
- CSRF complexity for API clients
|
||||
|
||||
### JWT-Only Authentication
|
||||
|
||||
**Rejected because:**
|
||||
- More complex for web browsing
|
||||
- Token storage in browser has security concerns
|
||||
- Session logout not immediate
|
||||
|
||||
### OAuth2 Server (Self-Hosted)
|
||||
|
||||
**Rejected because:**
|
||||
- Significant complexity for current needs
|
||||
- django-oauth-toolkit overkill
|
||||
- django-allauth sufficient for social auth
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### Permission Classes
|
||||
|
||||
```python
|
||||
# API views use JWT or session authentication
|
||||
REST_FRAMEWORK = {
|
||||
'DEFAULT_AUTHENTICATION_CLASSES': [
|
||||
'rest_framework_simplejwt.authentication.JWTAuthentication',
|
||||
'rest_framework.authentication.SessionAuthentication',
|
||||
],
|
||||
'DEFAULT_PERMISSION_CLASSES': [
|
||||
'rest_framework.permissions.IsAuthenticatedOrReadOnly',
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
### Custom User Model
|
||||
|
||||
```python
|
||||
# backend/apps/accounts/models.py
|
||||
class User(AbstractUser):
|
||||
email = models.EmailField(unique=True)
|
||||
display_name = models.CharField(max_length=50, blank=True)
|
||||
avatar_url = models.URLField(blank=True)
|
||||
email_verified = models.BooleanField(default=False)
|
||||
|
||||
USERNAME_FIELD = 'email'
|
||||
REQUIRED_FIELDS = ['username']
|
||||
```
|
||||
|
||||
### Email Verification
|
||||
|
||||
```python
|
||||
# Required before full access
|
||||
ACCOUNT_EMAIL_VERIFICATION = 'mandatory'
|
||||
ACCOUNT_EMAIL_REQUIRED = True
|
||||
ACCOUNT_AUTHENTICATION_METHOD = 'email'
|
||||
```
|
||||
|
||||
### Security Measures
|
||||
|
||||
1. **Password Hashing**: Django's PBKDF2 with SHA256
|
||||
2. **Token Blacklisting**: Invalidated refresh tokens stored
|
||||
3. **Rate Limiting**: Login attempts limited
|
||||
4. **HTTPS Required**: Tokens only sent over secure connections
|
||||
|
||||
## References
|
||||
|
||||
- [django-allauth Documentation](https://django-allauth.readthedocs.io/)
|
||||
- [djangorestframework-simplejwt](https://django-rest-framework-simplejwt.readthedocs.io/)
|
||||
- [OAuth 2.0 Specification](https://oauth.net/2/)
|
||||
- [JWT Best Practices](https://auth0.com/blog/jwt-security-best-practices/)
|
||||
Reference in New Issue
Block a user