-
Notifications
You must be signed in to change notification settings - Fork 392
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Support cookie authentication (#4662)
Co-authored-by: Kyle Johnson <[email protected]>
- Loading branch information
Showing
25 changed files
with
452 additions
and
67 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -94,6 +94,7 @@ | |
"rest_framework.authtoken", | ||
# Used for managing api keys | ||
"rest_framework_api_key", | ||
"rest_framework_simplejwt.token_blacklist", | ||
"djoser", | ||
"django.contrib.sites", | ||
"custom_auth", | ||
|
@@ -254,6 +255,7 @@ | |
REST_FRAMEWORK = { | ||
"DEFAULT_PERMISSION_CLASSES": ["rest_framework.permissions.IsAuthenticated"], | ||
"DEFAULT_AUTHENTICATION_CLASSES": ( | ||
"custom_auth.jwt_cookie.authentication.JWTCookieAuthentication", | ||
"rest_framework.authentication.TokenAuthentication", | ||
"api_keys.authentication.MasterAPIKeyAuthentication", | ||
), | ||
|
@@ -416,19 +418,6 @@ | |
|
||
MEDIA_URL = "/media/" # unused but needs to be different from STATIC_URL in django 3 | ||
|
||
# CORS settings | ||
|
||
CORS_ORIGIN_ALLOW_ALL = True | ||
FLAGSMITH_CORS_EXTRA_ALLOW_HEADERS = env.list( | ||
"FLAGSMITH_CORS_EXTRA_ALLOW_HEADERS", default=["sentry-trace"] | ||
) | ||
CORS_ALLOW_HEADERS = [ | ||
*default_headers, | ||
*FLAGSMITH_CORS_EXTRA_ALLOW_HEADERS, | ||
"X-Environment-Key", | ||
"X-E2E-Test-Auth-Token", | ||
] | ||
|
||
DEFAULT_FROM_EMAIL = env("SENDER_EMAIL", default="[email protected]") | ||
EMAIL_CONFIGURATION = { | ||
# Invitations with name is anticipated to take two arguments. The persons name and the | ||
|
@@ -826,6 +815,16 @@ | |
"user_create": USER_CREATE_PERMISSIONS, | ||
}, | ||
} | ||
SIMPLE_JWT = { | ||
"AUTH_TOKEN_CLASSES": ["rest_framework_simplejwt.tokens.SlidingToken"], | ||
"SLIDING_TOKEN_LIFETIME": timedelta( | ||
minutes=env.int( | ||
"COOKIE_AUTH_JWT_ACCESS_TOKEN_LIFETIME_MINUTES", | ||
default=10 * 60, | ||
) | ||
), | ||
"SIGNING_KEY": env.str("COOKIE_AUTH_JWT_SIGNING_KEY", default=SECRET_KEY), | ||
} | ||
|
||
# Github OAuth credentials | ||
GITHUB_CLIENT_ID = env.str("GITHUB_CLIENT_ID", default="") | ||
|
@@ -907,8 +906,6 @@ | |
SENTRY_API_KEY = env("SENTRY_API_KEY", default=None) | ||
AMPLITUDE_API_KEY = env("AMPLITUDE_API_KEY", default=None) | ||
ENABLE_FLAGSMITH_REALTIME = env.bool("ENABLE_FLAGSMITH_REALTIME", default=False) | ||
USE_SECURE_COOKIES = env.bool("USE_SECURE_COOKIES", default=True) | ||
COOKIE_SAME_SITE = env.str("COOKIE_SAME_SITE", default="none") | ||
|
||
# Set this to enable create organisation for only superusers | ||
RESTRICT_ORG_CREATE_TO_SUPERUSERS = env.bool("RESTRICT_ORG_CREATE_TO_SUPERUSERS", False) | ||
|
@@ -1038,6 +1035,24 @@ | |
|
||
DISABLE_INVITE_LINKS = env.bool("DISABLE_INVITE_LINKS", False) | ||
PREVENT_SIGNUP = env.bool("PREVENT_SIGNUP", default=False) | ||
COOKIE_AUTH_ENABLED = env.bool("COOKIE_AUTH_ENABLED", default=False) | ||
USE_SECURE_COOKIES = env.bool("USE_SECURE_COOKIES", default=True) | ||
COOKIE_SAME_SITE = env.str("COOKIE_SAME_SITE", default="none") | ||
|
||
# CORS settings | ||
|
||
CORS_ORIGIN_ALLOW_ALL = env.bool("CORS_ORIGIN_ALLOW_ALL", not COOKIE_AUTH_ENABLED) | ||
CORS_ALLOW_CREDENTIALS = env.bool("CORS_ALLOW_CREDENTIALS", COOKIE_AUTH_ENABLED) | ||
FLAGSMITH_CORS_EXTRA_ALLOW_HEADERS = env.list( | ||
"FLAGSMITH_CORS_EXTRA_ALLOW_HEADERS", default=["sentry-trace"] | ||
) | ||
CORS_ALLOWED_ORIGINS = env.list("CORS_ALLOWED_ORIGINS", default=[]) | ||
CORS_ALLOW_HEADERS = [ | ||
*default_headers, | ||
*FLAGSMITH_CORS_EXTRA_ALLOW_HEADERS, | ||
"X-Environment-Key", | ||
"X-E2E-Test-Auth-Token", | ||
] | ||
|
||
# use a separate boolean setting so that we add it to the API containers in environments | ||
# where we're running the task processor, so we avoid creating unnecessary tasks | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
from rest_framework.request import Request | ||
from rest_framework_simplejwt.authentication import JWTAuthentication | ||
from rest_framework_simplejwt.tokens import Token | ||
|
||
from custom_auth.jwt_cookie.constants import JWT_SLIDING_COOKIE_KEY | ||
from users.models import FFAdminUser | ||
|
||
|
||
class JWTCookieAuthentication(JWTAuthentication): | ||
def authenticate_header(self, request: Request) -> str: | ||
return f'Cookie realm="{self.www_authenticate_realm}"' | ||
|
||
def authenticate(self, request: Request) -> tuple[FFAdminUser, Token] | None: | ||
if raw_token := request.COOKIES.get(JWT_SLIDING_COOKIE_KEY): | ||
validated_token = self.get_validated_token(raw_token) | ||
return self.get_user(validated_token), validated_token | ||
return None |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
JWT_SLIDING_COOKIE_KEY = "jwt" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
from django.conf import settings | ||
from rest_framework.response import Response | ||
from rest_framework_simplejwt.tokens import SlidingToken | ||
|
||
from custom_auth.jwt_cookie.constants import JWT_SLIDING_COOKIE_KEY | ||
from users.models import FFAdminUser | ||
|
||
|
||
def authorise_response(user: FFAdminUser, response: Response) -> Response: | ||
sliding_token = SlidingToken.for_user(user) | ||
response.set_cookie( | ||
JWT_SLIDING_COOKIE_KEY, | ||
str(sliding_token), | ||
httponly=True, | ||
secure=settings.USE_SECURE_COOKIES, | ||
samesite=settings.COOKIE_SAME_SITE, | ||
) | ||
return response |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
from typing import Any | ||
from urllib.parse import urlparse | ||
|
||
from core.helpers import get_current_site_url | ||
from corsheaders.signals import check_request_enabled | ||
from django.dispatch import receiver | ||
from django.http import HttpRequest | ||
|
||
|
||
@receiver(check_request_enabled) | ||
def cors_allow_current_site(request: HttpRequest, **kwargs: Any) -> bool: | ||
# The signal is expected to only be dispatched: | ||
# - When `settings.CORS_ORIGIN_ALLOW_ALL` is set to `False`. | ||
# - For requests with `HTTP_ORIGIN` set. | ||
origin_url = urlparse(request.META["HTTP_ORIGIN"]) | ||
current_site_url = urlparse(get_current_site_url(request)) | ||
return ( | ||
origin_url.scheme == current_site_url.scheme | ||
and origin_url.netloc == current_site_url.netloc | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
from djoser.views import TokenDestroyView | ||
from rest_framework.request import Request | ||
from rest_framework.response import Response | ||
from rest_framework_simplejwt.tokens import SlidingToken | ||
|
||
from custom_auth.jwt_cookie.constants import JWT_SLIDING_COOKIE_KEY | ||
|
||
|
||
class JWTSlidingTokenLogoutView(TokenDestroyView): | ||
def post(self, request: Request) -> Response: | ||
response = super().post(request) | ||
if isinstance(jwt_token := request.auth, SlidingToken): | ||
jwt_token.blacklist() | ||
response.delete_cookie(JWT_SLIDING_COOKIE_KEY) | ||
return response |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.