Skip to content

Commit b89d426

Browse files
committed
Remove logout model
1 parent 8750834 commit b89d426

File tree

6 files changed

+62
-141
lines changed

6 files changed

+62
-141
lines changed

oauth2_provider/admin.py

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@
1010
get_grant_model,
1111
get_id_token_admin_class,
1212
get_id_token_model,
13-
get_logout_token_admin_class,
14-
get_logout_token_model,
1513
get_refresh_token_admin_class,
1614
get_refresh_token_model,
1715
)
@@ -53,14 +51,6 @@ class IDTokenAdmin(admin.ModelAdmin):
5351
list_select_related = ("application", "user")
5452

5553

56-
class LogoutTokenAdmin(admin.ModelAdmin):
57-
list_display = ("user", "application", "id_token")
58-
raw_id_fields = ("user",)
59-
search_fields = ("user__email",) if has_email else ()
60-
list_filter = ("application",)
61-
list_select_related = ("application", "user", "id_token")
62-
63-
6454
class RefreshTokenAdmin(admin.ModelAdmin):
6555
list_display = ("token", "user", "application")
6656
raw_id_fields = ("user", "access_token")
@@ -72,19 +62,16 @@ class RefreshTokenAdmin(admin.ModelAdmin):
7262
access_token_model = get_access_token_model()
7363
grant_model = get_grant_model()
7464
id_token_model = get_id_token_model()
75-
logout_token_model = get_logout_token_model()
7665
refresh_token_model = get_refresh_token_model()
7766

7867
application_admin_class = get_application_admin_class()
7968
access_token_admin_class = get_access_token_admin_class()
8069
grant_admin_class = get_grant_admin_class()
8170
id_token_admin_class = get_id_token_admin_class()
82-
logout_token_admin_class = get_logout_token_admin_class()
8371
refresh_token_admin_class = get_refresh_token_admin_class()
8472

8573
admin.site.register(application_model, application_admin_class)
8674
admin.site.register(access_token_model, access_token_admin_class)
8775
admin.site.register(grant_model, grant_admin_class)
8876
admin.site.register(id_token_model, id_token_admin_class)
89-
admin.site.register(logout_token_model, logout_token_admin_class)
9077
admin.site.register(refresh_token_model, refresh_token_admin_class)

oauth2_provider/checks.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ def validate_token_configuration(app_configs, **kwargs):
1313
oauth2_settings.ACCESS_TOKEN_MODEL,
1414
oauth2_settings.ID_TOKEN_MODEL,
1515
oauth2_settings.REFRESH_TOKEN_MODEL,
16-
oauth2_settings.LOGOUT_TOKEN_MODEL,
1716
)
1817
)
1918

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Django 5.2 on 2025-06-06 12:42
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('oauth2_provider', '0012_add_token_checksum'),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name='application',
15+
name='backchannel_logout_uri',
16+
field=models.URLField(blank=True, help_text='Backchannel Logout URI where logout tokens will be sent', null=True),
17+
),
18+
]

oauth2_provider/migrations/0013_application_backchannel_logout_uri_logouttoken.py

Lines changed: 0 additions & 35 deletions
This file was deleted.

oauth2_provider/models.py

Lines changed: 44 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from django.conf import settings
1313
from django.contrib.auth.hashers import identify_hasher, make_password
1414
from django.core.exceptions import ImproperlyConfigured
15-
from django.db import IntegrityError, models, router, transaction
15+
from django.db import models, router, transaction
1616
from django.urls import reverse
1717
from django.utils import timezone
1818
from django.utils.translation import gettext_lazy as _
@@ -636,6 +636,48 @@ def revoke(self):
636636
"""
637637
self.delete()
638638

639+
def send_backchannel_logout_request(self, ttl=timedelta(minutes=10)):
640+
"""
641+
Send a token to
642+
"""
643+
try:
644+
assert oauth2_settings.OIDC_BACKCHANNEL_LOGOUT_ENABLED, "Backchannel logout is not enabled"
645+
assert self.application.backchannel_logout_url is not None, (
646+
"URL for backchannel logout not provided by client"
647+
)
648+
649+
issued_at = timezone.now()
650+
expiration_date = issued_at + ttl
651+
652+
claims = {
653+
"iss": oauth2_settings.OIDC_ISS_ENDPOINT,
654+
"sub": str(self.user.id),
655+
"aud": str(self.application.client_id),
656+
"iat": int(issued_at.timestamp()),
657+
"exp": int(expiration_date.timestamp()),
658+
"jti": self.jti,
659+
"events": {"http://schemas.openid.net/event/backchannel-logout": {}},
660+
}
661+
662+
# Standard JWT header
663+
header = {"typ": "logout+jwt", "alg": self.application.algorithm}
664+
# RS256 consumers expect a kid in the header for verifying the token
665+
if self.application.algorithm == AbstractApplication.RS256_ALGORITHM:
666+
header["kid"] = self.application.jwk_key.thumbprint()
667+
668+
token = jwt.JWT(
669+
header=json.dumps(header, default=str),
670+
claims=json.dumps(claims, default=str),
671+
)
672+
token.make_signed_token(self.application.jwk_key)
673+
674+
headers = {"Content-Type": "application/x-www-form-urlencoded"}
675+
data = {"logout_token": token.serialize()}
676+
response = requests.post(self.application.backchannel_logout_uri, headers=headers, data=data)
677+
response.raise_for_status()
678+
except (AssertionError, requests.RequestException) as exc:
679+
raise BackchannelLogoutRequestError(str(exc))
680+
639681
@property
640682
def scopes(self):
641683
"""
@@ -657,71 +699,6 @@ class Meta(AbstractIDToken.Meta):
657699
swappable = "OAUTH2_PROVIDER_ID_TOKEN_MODEL"
658700

659701

660-
class AbstractLogoutToken(models.Model):
661-
TTL = timedelta(minutes=10)
662-
663-
id = models.BigAutoField(primary_key=True)
664-
user = models.ForeignKey(
665-
settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="%(app_label)s_%(class)s"
666-
)
667-
application = models.ForeignKey(oauth2_settings.APPLICATION_MODEL, on_delete=models.CASCADE)
668-
id_token = models.OneToOneField(oauth2_settings.ID_TOKEN_MODEL, null=True, on_delete=models.SET_NULL)
669-
created = models.DateTimeField(auto_now_add=True)
670-
671-
@property
672-
def expires(self):
673-
return self.created + self.TTL
674-
675-
@property
676-
def jwt_claim_dictionary(self):
677-
claims = {
678-
"iss": oauth2_settings.OIDC_ISS_ENDPOINT,
679-
"sub": str(self.user.id),
680-
"aud": str(self.application.client_id),
681-
"iat": int(self.created.timestamp()),
682-
"exp": int(self.expires.timestamp()),
683-
"events": {"http://schemas.openid.net/event/backchannel-logout": {}},
684-
}
685-
686-
if self.id_token and self.id_token.jti:
687-
claims["jti"] = str(self.id_token.jti)
688-
689-
return claims
690-
691-
def get_serialized_jwt(self):
692-
# Standard JWT header
693-
header = {"typ": "logout+jwt", "alg": self.application.algorithm}
694-
# RS256 consumers expect a kid in the header for verifying the token
695-
if self.application.algorithm == AbstractApplication.RS256_ALGORITHM:
696-
header["kid"] = self.application.jwk_key.thumbprint()
697-
698-
token = jwt.JWT(
699-
header=json.dumps(header, default=str),
700-
claims=json.dumps(self.jwt_claim_dictionary, default=str),
701-
)
702-
token.make_signed_token(self.application.jwk_key)
703-
return token.serialize()
704-
705-
def send_backchannel_logout_request(self):
706-
if not oauth2_settings.OIDC_BACKCHANNEL_LOGOUT_ENABLED:
707-
return
708-
try:
709-
headers = {"Content-Type": "application/x-www-form-urlencoded"}
710-
data = {"logout_token": self.get_serialized_jwt()}
711-
response = requests.post(self.application.backchannel_logout_uri, headers=headers, data=data)
712-
response.raise_for_status()
713-
except (AssertionError, requests.RequestException) as exc:
714-
raise BackchannelLogoutRequestError(str(exc))
715-
716-
class Meta:
717-
abstract = True
718-
719-
720-
class LogoutToken(AbstractLogoutToken):
721-
class Meta(AbstractLogoutToken.Meta):
722-
swappable = "OAUTH2_PROVIDER_LOGOUT_TOKEN_MODEL"
723-
724-
725702
def get_application_model():
726703
"""Return the Application model that is active in this project."""
727704
return apps.get_model(oauth2_settings.APPLICATION_MODEL)
@@ -742,11 +719,6 @@ def get_id_token_model():
742719
return apps.get_model(oauth2_settings.ID_TOKEN_MODEL)
743720

744721

745-
def get_logout_token_model():
746-
"""Return the IDToken model that is active in this project."""
747-
return apps.get_model(oauth2_settings.LOGOUT_TOKEN_MODEL)
748-
749-
750722
def get_refresh_token_model():
751723
"""Return the RefreshToken model that is active in this project."""
752724
return apps.get_model(oauth2_settings.REFRESH_TOKEN_MODEL)
@@ -776,12 +748,6 @@ def get_id_token_admin_class():
776748
return id_token_admin_class
777749

778750

779-
def get_logout_token_admin_class():
780-
"""Return the LogoutToken admin class that is active in this project."""
781-
logout_token_admin_class = oauth2_settings.LOGOUT_TOKEN_ADMIN_CLASS
782-
return logout_token_admin_class
783-
784-
785751
def get_refresh_token_admin_class():
786752
"""Return the RefreshToken admin class that is active in this project."""
787753
refresh_token_admin_class = oauth2_settings.REFRESH_TOKEN_ADMIN_CLASS
@@ -812,7 +778,6 @@ def batch_delete(queryset, query):
812778
access_token_model = get_access_token_model()
813779
refresh_token_model = get_refresh_token_model()
814780
id_token_model = get_id_token_model()
815-
logout_token_model = get_logout_token_model()
816781
grant_model = get_grant_model()
817782
REFRESH_TOKEN_EXPIRE_SECONDS = oauth2_settings.REFRESH_TOKEN_EXPIRE_SECONDS
818783

@@ -840,11 +805,6 @@ def batch_delete(queryset, query):
840805
else:
841806
logger.info("refresh_expire_at is %s. No refresh tokens deleted.", refresh_expire_at)
842807

843-
logout_token_query = models.Q(created__lt=now - logout_token_model.TTL)
844-
logout_tokens = logout_token_model.objects.filter(logout_token_query)
845-
logout_tokens_delete_no = batch_delete(logout_tokens, logout_token_query)
846-
logger.info("%s Expired logout tokens deleted", logout_tokens_delete_no)
847-
848808
access_token_query = models.Q(refresh_token__isnull=True, expires__lt=now)
849809
access_tokens = access_token_model.objects.filter(access_token_query)
850810

@@ -957,11 +917,6 @@ def send_backchannel_logout_requests(user):
957917
id_tokens = IDToken.objects.filter(application__backchannel_logout_uri__isnull=False, user=user)
958918
for id_token in id_tokens:
959919
try:
960-
logout_token = LogoutToken.objects.create(
961-
user=user, application=id_token.application, id_token=id_token
962-
)
963-
logout_token.send_backchannel_logout_request()
920+
id_token.send_backchannel_logout_request()
964921
except BackchannelLogoutRequestError as exc:
965922
logger.warn(str(exc))
966-
except IntegrityError:
967-
logger.warn(f"Logout for {id_token} already exists")

oauth2_provider/settings.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
APPLICATION_MODEL = getattr(settings, "OAUTH2_PROVIDER_APPLICATION_MODEL", "oauth2_provider.Application")
3131
ACCESS_TOKEN_MODEL = getattr(settings, "OAUTH2_PROVIDER_ACCESS_TOKEN_MODEL", "oauth2_provider.AccessToken")
3232
ID_TOKEN_MODEL = getattr(settings, "OAUTH2_PROVIDER_ID_TOKEN_MODEL", "oauth2_provider.IDToken")
33-
LOGOUT_TOKEN_MODEL = getattr(settings, "OAUTH2_PROVIDER_LOGOUT_TOKEN_MODEL", "oauth2_provider.LogoutToken")
3433
GRANT_MODEL = getattr(settings, "OAUTH2_PROVIDER_GRANT_MODEL", "oauth2_provider.Grant")
3534
REFRESH_TOKEN_MODEL = getattr(settings, "OAUTH2_PROVIDER_REFRESH_TOKEN_MODEL", "oauth2_provider.RefreshToken")
3635

@@ -62,14 +61,12 @@
6261
"APPLICATION_MODEL": APPLICATION_MODEL,
6362
"ACCESS_TOKEN_MODEL": ACCESS_TOKEN_MODEL,
6463
"ID_TOKEN_MODEL": ID_TOKEN_MODEL,
65-
"LOGOUT_TOKEN_MODEL": LOGOUT_TOKEN_MODEL,
6664
"GRANT_MODEL": GRANT_MODEL,
6765
"REFRESH_TOKEN_MODEL": REFRESH_TOKEN_MODEL,
6866
"APPLICATION_ADMIN_CLASS": "oauth2_provider.admin.ApplicationAdmin",
6967
"ACCESS_TOKEN_ADMIN_CLASS": "oauth2_provider.admin.AccessTokenAdmin",
7068
"GRANT_ADMIN_CLASS": "oauth2_provider.admin.GrantAdmin",
7169
"ID_TOKEN_ADMIN_CLASS": "oauth2_provider.admin.IDTokenAdmin",
72-
"LOGOUT_TOKEN_ADMIN_CLASS": "oauth2_provider.admin.LogoutTokenAdmin",
7370
"REFRESH_TOKEN_ADMIN_CLASS": "oauth2_provider.admin.RefreshTokenAdmin",
7471
"REQUEST_APPROVAL_PROMPT": "force",
7572
"ALLOWED_REDIRECT_URI_SCHEMES": ["http", "https"],

0 commit comments

Comments
 (0)