Security Best Practices
Essential security practices for integrating SVA OAuth in production.
Configuration Security
Use Environment Variables
Never hardcode secrets in your code:
# ❌ Bad
SVA_OAUTH_CLIENT_SECRET = 'hardcoded-secret'
# ✅ Good
SVA_OAUTH_CLIENT_SECRET = os.getenv('SVA_OAUTH_CLIENT_SECRET')
Secure Session Configuration
# settings.py (Production)
# Use HTTPS
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
# HttpOnly cookies
SESSION_COOKIE_HTTPONLY = True
# SameSite protection
SESSION_COOKIE_SAMESITE = 'Lax'
# Secure session storage
SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'
Data Token Secret
- Use a strong, randomly generated secret
- Must match the secret configured in your SVA provider
- Store securely (environment variables, secret manager)
- Never commit to version control
Token Security
Token Storage
- Tokens are stored in Django session (server-side)
- Session cookies are HttpOnly (not accessible via JavaScript)
- Use secure session storage in production
Token Expiration
- Access tokens expire in 1 hour (default)
- Refresh tokens expire in 30 days (default)
- Data tokens expire in 5 minutes (default)
- Always handle token expiration gracefully
Token Refresh
The TokenRefreshMiddleware automatically refreshes tokens:
MIDDLEWARE = [
'django.contrib.sessions.middleware.SessionMiddleware',
# ... other middleware
'sva_oauth_client.middleware.TokenRefreshMiddleware',
]
PKCE (Proof Key for Code Exchange)
PKCE is automatically enabled and provides protection against authorization code interception.
How it works:
- Client generates random
code_verifier - Client generates
code_challenge = SHA256(code_verifier) - Authorization request includes
code_challenge - Token exchange includes
code_verifier - Provider verifies:
SHA256(code_verifier) == code_challenge
CSRF Protection
The state parameter provides CSRF protection:
# Automatically handled by the package
# State is generated, stored in session, and verified in callback
Data Token Verification
Always verify data token signatures:
from sva_oauth_client.utils import get_sva_claims
from sva_oauth_client.client import SVATokenError
try:
claims = get_sva_claims(request)
# Token signature and expiration are automatically verified
except SVATokenError:
# Token is invalid or expired
# User will be logged out automatically
HTTPS in Production
Always use HTTPS in production:
# settings.py (Production)
# Force HTTPS
SECURE_SSL_REDIRECT = True
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
# Secure cookies
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
Redirect URI Validation
Ensure redirect URIs match exactly:
- Protocol (http vs https)
- Domain
- Port (if specified)
- Path
# ✅ Correct
SVA_OAUTH_REDIRECT_URI = 'https://yourapp.com/oauth/callback/'
# ❌ Wrong (trailing slash mismatch)
SVA_OAUTH_REDIRECT_URI = 'https://yourapp.com/oauth/callback'
Error Handling
Never expose internal errors to users:
# ❌ Bad
except Exception as e:
return HttpResponse(f"Error: {str(e)}")
# ✅ Good
except SVATokenError:
messages.error(request, 'Authentication failed. Please try again.')
return redirect('sva_oauth_client:login')
Logging
Log security events:
import logging
logger = logging.getLogger(__name__)
@sva_oauth_required
def my_view(request):
try:
claims = get_sva_claims(request)
logger.info(f"User authenticated: {claims.get('email')}")
except SVATokenError:
logger.warning("Token validation failed")
raise
Input Validation
Always validate user input:
from django.core.exceptions import ValidationError
def validate_email(email):
if not email or '@' not in email:
raise ValidationError('Invalid email')
# Use validated data from claims
claims = get_sva_claims(request)
email = claims.get('email')
if email:
validate_email(email)
Rate Limiting
Implement rate limiting for OAuth endpoints:
from django.views.decorators.cache import cache_page
from django.views.decorators.vary import vary_on_headers
@cache_page(60 * 15) # Cache for 15 minutes
@vary_on_headers('Authorization')
def api_endpoint(request):
# Your endpoint
pass
Audit Logging
Log authentication events:
import logging
logger = logging.getLogger('oauth_audit')
@sva_oauth_required
def my_view(request):
claims = get_sva_claims(request)
logger.info(f"User {claims.get('email')} accessed {request.path}")
Regular Security Updates
-
Keep dependencies updated:
pip install --upgrade sva-oauth-client -
Monitor security advisories:
- Check GitHub for security updates
- Subscribe to security notifications
-
Review access logs:
- Monitor for suspicious activity
- Review authentication failures
Checklist
Before deploying to production:
- All secrets stored in environment variables
- HTTPS enabled and configured
- Secure session cookies configured
- Redirect URIs match exactly
- Error handling doesn't expose internal details
- Logging configured for security events
- Rate limiting implemented
- Data token verification enabled
- PKCE enabled (automatic)
- CSRF protection enabled (automatic)
Next Steps
- Learn about PKCE in detail
- Understand Data Token Verification
- Read about CSRF Protection