Files
holy-flavor-rater/backend/api/utils/cookie_auth.py
T
2026-05-02 11:14:02 +02:00

73 lines
2.5 KiB
Python

"""httpOnly-cookie JWT auth.
Reads `access_token` from a cookie set by the login/refresh endpoints. Falls
back to the Authorization header for backwards compatibility during rollout —
strip the fallback once all clients use cookies.
"""
from __future__ import annotations
from django.conf import settings
from drf_spectacular.extensions import OpenApiAuthenticationExtension
from rest_framework_simplejwt.authentication import JWTAuthentication
class CookieJWTAuthentication(JWTAuthentication):
def authenticate(self, request):
raw_token = request.COOKIES.get(settings.JWT_AUTH_COOKIE_ACCESS)
if raw_token is None:
return super().authenticate(request)
validated_token = self.get_validated_token(raw_token)
return self.get_user(validated_token), validated_token
class CookieJWTScheme(OpenApiAuthenticationExtension):
"""Make drf-spectacular treat CookieJWTAuthentication like the SimpleJWT scheme."""
target_class = "api.utils.cookie_auth.CookieJWTAuthentication"
name = "cookieAuth"
def get_security_definition(self, auto_schema): # type: ignore[no-untyped-def]
return {
"type": "apiKey",
"in": "cookie",
"name": settings.JWT_AUTH_COOKIE_ACCESS,
}
def set_jwt_cookies(response, access: str | None, refresh: str | None) -> None:
"""Attach access/refresh tokens to the response as httpOnly cookies."""
from datetime import timedelta
secure = settings.JWT_AUTH_COOKIE_SECURE
samesite = settings.JWT_AUTH_COOKIE_SAMESITE
path = settings.JWT_AUTH_COOKIE_PATH
if access:
access_lifetime: timedelta = settings.SIMPLE_JWT["ACCESS_TOKEN_LIFETIME"]
response.set_cookie(
settings.JWT_AUTH_COOKIE_ACCESS,
access,
max_age=int(access_lifetime.total_seconds()),
httponly=True,
secure=secure,
samesite=samesite,
path=path,
)
if refresh:
refresh_lifetime: timedelta = settings.SIMPLE_JWT["REFRESH_TOKEN_LIFETIME"]
response.set_cookie(
settings.JWT_AUTH_COOKIE_REFRESH,
refresh,
max_age=int(refresh_lifetime.total_seconds()),
httponly=True,
secure=secure,
samesite=samesite,
path=path,
)
def clear_jwt_cookies(response) -> None:
response.delete_cookie(settings.JWT_AUTH_COOKIE_ACCESS, path=settings.JWT_AUTH_COOKIE_PATH)
response.delete_cookie(settings.JWT_AUTH_COOKIE_REFRESH, path=settings.JWT_AUTH_COOKIE_PATH)