mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-24 17:31:09 -05:00
- 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.
217 lines
6.5 KiB
Markdown
217 lines
6.5 KiB
Markdown
# 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/)
|