From fb25b28976fde1207ccb3b31bd082a79a5be450d Mon Sep 17 00:00:00 2001 From: Jens L Date: Sun, 29 May 2022 18:58:54 +0200 Subject: [PATCH] core: db sessions (#2979) * use db session backend Signed-off-by: Jens Langhammer * root: wrap session cookie in JWT and add useful claims Signed-off-by: Jens Langhammer * fix compatibility with tests Signed-off-by: Jens Langhammer * use standard session key for writing in sessions too Signed-off-by: Jens Langhammer --- authentik/root/middleware.py | 30 +++++++++++++++++++++++++++++- authentik/root/settings.py | 3 ++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/authentik/root/middleware.py b/authentik/root/middleware.py index 62a1f4cc7..2e9133181 100644 --- a/authentik/root/middleware.py +++ b/authentik/root/middleware.py @@ -1,4 +1,5 @@ """Dynamically set SameSite depending if the upstream connection is TLS or not""" +from hashlib import sha512 from time import time from typing import Callable @@ -10,11 +11,14 @@ from django.http.request import HttpRequest from django.http.response import HttpResponse from django.utils.cache import patch_vary_headers from django.utils.http import http_date +from jwt import PyJWTError, decode, encode from structlog.stdlib import get_logger from authentik.lib.utils.http import get_client_ip LOGGER = get_logger("authentik.asgi") +ACR_AUTHENTIK_SESSION = "goauthentik.io/core/default" +SIGNING_HASH = sha512(settings.SECRET_KEY.encode()).hexdigest() class SessionMiddleware(UpstreamSessionMiddleware): @@ -35,6 +39,18 @@ class SessionMiddleware(UpstreamSessionMiddleware): return True return False + def process_request(self, request): + session_jwt = request.COOKIES.get(settings.SESSION_COOKIE_NAME) + # We need to support the standard django format of just a session key + # for testing setups, where the session is directly set + session_key = session_jwt if settings.TEST else None + try: + session_payload = decode(session_jwt, SIGNING_HASH, algorithms=["HS256"]) + session_key = session_payload["sid"] + except (KeyError, PyJWTError): + pass + request.session = self.SessionStore(session_key) + def process_response(self, request: HttpRequest, response: HttpResponse) -> HttpResponse: """ If request.session was modified, or if the configuration is to save the @@ -82,9 +98,21 @@ class SessionMiddleware(UpstreamSessionMiddleware): "request completed. The user may have logged " "out in a concurrent request, for example." ) + payload = { + "sid": request.session.session_key, + "iss": "authentik", + "sub": "anonymous", + "authenticated": request.user.is_authenticated, + "acr": ACR_AUTHENTIK_SESSION, + } + if request.user.is_authenticated: + payload["sub"] = request.user.uid + value = encode(payload=payload, key=SIGNING_HASH) + if settings.TEST: + value = request.session.session_key response.set_cookie( settings.SESSION_COOKIE_NAME, - request.session.session_key, + value, max_age=max_age, expires=expires, domain=settings.SESSION_COOKIE_DOMAIN, diff --git a/authentik/root/settings.py b/authentik/root/settings.py index 9098ae917..2f75491c2 100644 --- a/authentik/root/settings.py +++ b/authentik/root/settings.py @@ -216,7 +216,8 @@ CACHES = { DJANGO_REDIS_SCAN_ITERSIZE = 1000 DJANGO_REDIS_IGNORE_EXCEPTIONS = True DJANGO_REDIS_LOG_IGNORED_EXCEPTIONS = True -SESSION_ENGINE = "django.contrib.sessions.backends.cache" +SESSION_ENGINE = "django.contrib.sessions.backends.cached_db" +SESSION_SERIALIZER = "django.contrib.sessions.serializers.PickleSerializer" SESSION_CACHE_ALIAS = "default" # Configured via custom SessionMiddleware # SESSION_COOKIE_SAMESITE = "None"