providers/oauth2, rac: make sure tokens are revoked after session deletion (#14011)
This commit is contained in:
committed by
GitHub
parent
a07fd8d54b
commit
c908d9e95e
@ -1,19 +1,29 @@
|
||||
from django.contrib.auth.signals import user_logged_out
|
||||
from django.db.models.signals import post_save
|
||||
from django.db.models.signals import post_save, pre_delete
|
||||
from django.dispatch import receiver
|
||||
from django.http import HttpRequest
|
||||
|
||||
from authentik.core.models import User
|
||||
from authentik.core.models import AuthenticatedSession, User
|
||||
from authentik.providers.oauth2.models import AccessToken, DeviceToken, RefreshToken
|
||||
|
||||
|
||||
@receiver(user_logged_out)
|
||||
def user_logged_out_oauth_access_token(sender, request: HttpRequest, user: User, **_):
|
||||
"""Revoke access tokens upon user logout"""
|
||||
def user_logged_out_oauth_tokens_removal(sender, request: HttpRequest, user: User, **_):
|
||||
"""Revoke tokens upon user logout"""
|
||||
if not request.session or not request.session.session_key:
|
||||
return
|
||||
AccessToken.objects.filter(
|
||||
user=user, session__session__session_key=request.session.session_key
|
||||
user=user,
|
||||
session__session__session_key=request.session.session_key,
|
||||
).delete()
|
||||
|
||||
|
||||
@receiver(pre_delete, sender=AuthenticatedSession)
|
||||
def user_session_deleted_oauth_tokens_removal(sender, instance: AuthenticatedSession, **_):
|
||||
"""Revoke tokens upon user logout"""
|
||||
AccessToken.objects.filter(
|
||||
user=instance.user,
|
||||
session__session__session_key=instance.session.session_key,
|
||||
).delete()
|
||||
|
||||
|
||||
@ -22,6 +32,6 @@ def user_deactivated(sender, instance: User, **_):
|
||||
"""Remove user tokens when deactivated"""
|
||||
if instance.is_active:
|
||||
return
|
||||
AccessToken.objects.filter(session__user=instance).delete()
|
||||
RefreshToken.objects.filter(session__user=instance).delete()
|
||||
DeviceToken.objects.filter(session__user=instance).delete()
|
||||
AccessToken.objects.filter(user=instance).delete()
|
||||
RefreshToken.objects.filter(user=instance).delete()
|
||||
DeviceToken.objects.filter(user=instance).delete()
|
||||
|
||||
@ -7,12 +7,13 @@ from dataclasses import asdict
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
|
||||
from authentik.core.models import Application
|
||||
from authentik.core.models import Application, AuthenticatedSession, Session
|
||||
from authentik.core.tests.utils import create_test_admin_user, create_test_cert, create_test_flow
|
||||
from authentik.lib.generators import generate_id
|
||||
from authentik.providers.oauth2.models import (
|
||||
AccessToken,
|
||||
ClientTypes,
|
||||
DeviceToken,
|
||||
IDToken,
|
||||
OAuth2Provider,
|
||||
RedirectURI,
|
||||
@ -20,6 +21,7 @@ from authentik.providers.oauth2.models import (
|
||||
RefreshToken,
|
||||
)
|
||||
from authentik.providers.oauth2.tests.utils import OAuthTestCase
|
||||
from authentik.root.middleware import ClientIPMiddleware
|
||||
|
||||
|
||||
class TesOAuth2Revoke(OAuthTestCase):
|
||||
@ -135,3 +137,86 @@ class TesOAuth2Revoke(OAuthTestCase):
|
||||
},
|
||||
)
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
def test_revoke_logout(self):
|
||||
"""Test revoke on logout"""
|
||||
self.client.force_login(self.user)
|
||||
AccessToken.objects.create(
|
||||
provider=self.provider,
|
||||
user=self.user,
|
||||
session=self.client.session["authenticatedsession"],
|
||||
token=generate_id(),
|
||||
auth_time=timezone.now(),
|
||||
_scope="openid user profile",
|
||||
_id_token=json.dumps(
|
||||
asdict(
|
||||
IDToken("foo", "bar"),
|
||||
)
|
||||
),
|
||||
)
|
||||
self.client.logout()
|
||||
self.assertEqual(AccessToken.objects.all().count(), 0)
|
||||
|
||||
def test_revoke_session_delete(self):
|
||||
"""Test revoke on logout"""
|
||||
session = AuthenticatedSession.objects.create(
|
||||
session=Session.objects.create(
|
||||
session_key=generate_id(),
|
||||
last_ip=ClientIPMiddleware.default_ip,
|
||||
),
|
||||
user=self.user,
|
||||
)
|
||||
AccessToken.objects.create(
|
||||
provider=self.provider,
|
||||
user=self.user,
|
||||
session=session,
|
||||
token=generate_id(),
|
||||
auth_time=timezone.now(),
|
||||
_scope="openid user profile",
|
||||
_id_token=json.dumps(
|
||||
asdict(
|
||||
IDToken("foo", "bar"),
|
||||
)
|
||||
),
|
||||
)
|
||||
session.delete()
|
||||
self.assertEqual(AccessToken.objects.all().count(), 0)
|
||||
|
||||
def test_revoke_user_deactivated(self):
|
||||
"""Test revoke on logout"""
|
||||
AccessToken.objects.create(
|
||||
provider=self.provider,
|
||||
user=self.user,
|
||||
token=generate_id(),
|
||||
auth_time=timezone.now(),
|
||||
_scope="openid user profile",
|
||||
_id_token=json.dumps(
|
||||
asdict(
|
||||
IDToken("foo", "bar"),
|
||||
)
|
||||
),
|
||||
)
|
||||
RefreshToken.objects.create(
|
||||
provider=self.provider,
|
||||
user=self.user,
|
||||
token=generate_id(),
|
||||
auth_time=timezone.now(),
|
||||
_scope="openid user profile",
|
||||
_id_token=json.dumps(
|
||||
asdict(
|
||||
IDToken("foo", "bar"),
|
||||
)
|
||||
),
|
||||
)
|
||||
DeviceToken.objects.create(
|
||||
provider=self.provider,
|
||||
user=self.user,
|
||||
_scope="openid user profile",
|
||||
)
|
||||
|
||||
self.user.is_active = False
|
||||
self.user.save()
|
||||
|
||||
self.assertEqual(AccessToken.objects.all().count(), 0)
|
||||
self.assertEqual(RefreshToken.objects.all().count(), 0)
|
||||
self.assertEqual(DeviceToken.objects.all().count(), 0)
|
||||
|
||||
@ -8,7 +8,7 @@ from django.db.models.signals import post_delete, post_save, pre_delete
|
||||
from django.dispatch import receiver
|
||||
from django.http import HttpRequest
|
||||
|
||||
from authentik.core.models import User
|
||||
from authentik.core.models import AuthenticatedSession, User
|
||||
from authentik.providers.rac.api.endpoints import user_endpoint_cache_key
|
||||
from authentik.providers.rac.consumer_client import (
|
||||
RAC_CLIENT_GROUP_SESSION,
|
||||
@ -32,6 +32,18 @@ def user_logged_out_session(sender, request: HttpRequest, user: User, **_):
|
||||
)
|
||||
|
||||
|
||||
@receiver(pre_delete, sender=AuthenticatedSession)
|
||||
def user_session_deleted(sender, instance: AuthenticatedSession, **_):
|
||||
layer = get_channel_layer()
|
||||
async_to_sync(layer.group_send)(
|
||||
RAC_CLIENT_GROUP_SESSION
|
||||
% {
|
||||
"session": instance.session.session_key,
|
||||
},
|
||||
{"type": "event.disconnect", "reason": "session_logout"},
|
||||
)
|
||||
|
||||
|
||||
@receiver(pre_delete, sender=ConnectionToken)
|
||||
def pre_delete_connection_token_disconnect(sender, instance: ConnectionToken, **_):
|
||||
"""Disconnect session when connection token is deleted"""
|
||||
|
||||
Reference in New Issue
Block a user