Skip to main content

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:

  1. Client generates random code_verifier
  2. Client generates code_challenge = SHA256(code_verifier)
  3. Authorization request includes code_challenge
  4. Token exchange includes code_verifier
  5. 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

  1. Keep dependencies updated:

    pip install --upgrade sva-oauth-client
  2. Monitor security advisories:

    • Check GitHub for security updates
    • Subscribe to security notifications
  3. 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