core: db sessions (#2979)

* use db session backend

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* root: wrap session cookie in JWT and add useful claims

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* fix compatibility with tests

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* use standard session key for writing in sessions too

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
Jens L 2022-05-29 18:58:54 +02:00 committed by GitHub
parent 612163b82f
commit fb25b28976
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 31 additions and 2 deletions

View file

@ -1,4 +1,5 @@
"""Dynamically set SameSite depending if the upstream connection is TLS or not""" """Dynamically set SameSite depending if the upstream connection is TLS or not"""
from hashlib import sha512
from time import time from time import time
from typing import Callable from typing import Callable
@ -10,11 +11,14 @@ from django.http.request import HttpRequest
from django.http.response import HttpResponse from django.http.response import HttpResponse
from django.utils.cache import patch_vary_headers from django.utils.cache import patch_vary_headers
from django.utils.http import http_date from django.utils.http import http_date
from jwt import PyJWTError, decode, encode
from structlog.stdlib import get_logger from structlog.stdlib import get_logger
from authentik.lib.utils.http import get_client_ip from authentik.lib.utils.http import get_client_ip
LOGGER = get_logger("authentik.asgi") LOGGER = get_logger("authentik.asgi")
ACR_AUTHENTIK_SESSION = "goauthentik.io/core/default"
SIGNING_HASH = sha512(settings.SECRET_KEY.encode()).hexdigest()
class SessionMiddleware(UpstreamSessionMiddleware): class SessionMiddleware(UpstreamSessionMiddleware):
@ -35,6 +39,18 @@ class SessionMiddleware(UpstreamSessionMiddleware):
return True return True
return False 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: def process_response(self, request: HttpRequest, response: HttpResponse) -> HttpResponse:
""" """
If request.session was modified, or if the configuration is to save the 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 " "request completed. The user may have logged "
"out in a concurrent request, for example." "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( response.set_cookie(
settings.SESSION_COOKIE_NAME, settings.SESSION_COOKIE_NAME,
request.session.session_key, value,
max_age=max_age, max_age=max_age,
expires=expires, expires=expires,
domain=settings.SESSION_COOKIE_DOMAIN, domain=settings.SESSION_COOKIE_DOMAIN,

View file

@ -216,7 +216,8 @@ CACHES = {
DJANGO_REDIS_SCAN_ITERSIZE = 1000 DJANGO_REDIS_SCAN_ITERSIZE = 1000
DJANGO_REDIS_IGNORE_EXCEPTIONS = True DJANGO_REDIS_IGNORE_EXCEPTIONS = True
DJANGO_REDIS_LOG_IGNORED_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" SESSION_CACHE_ALIAS = "default"
# Configured via custom SessionMiddleware # Configured via custom SessionMiddleware
# SESSION_COOKIE_SAMESITE = "None" # SESSION_COOKIE_SAMESITE = "None"