From edfa16cfa23c5005fcdd797aa8cd07522528362b Mon Sep 17 00:00:00 2001 From: Timon Engelke Date: Wed, 31 Jan 2024 15:52:44 +0100 Subject: [PATCH] Pass id_token_hint on logout --- src/simple_openid_connect/data.py | 16 ++++++++++++++-- .../integrations/django/models.py | 4 ++++ .../integrations/django/views.py | 14 +++++++++++--- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/simple_openid_connect/data.py b/src/simple_openid_connect/data.py index 541b6d8..8da67f2 100644 --- a/src/simple_openid_connect/data.py +++ b/src/simple_openid_connect/data.py @@ -4,8 +4,9 @@ import enum import logging import time -from typing import Any, Callable, List, Literal, Mapping, Optional, Union +from typing import Any, Callable, List, Literal, Mapping, Optional, Type, Union +from cryptojwt import JWK from pydantic import AnyHttpUrl, Extra, Field, root_validator from simple_openid_connect.base_data import OpenidBaseModel @@ -165,7 +166,7 @@ class IdToken(OpenidBaseModel): class Config: extra = Extra.allow - allow_mutation = False + allow_mutation = True iss: AnyHttpUrl "REQUIRED. Issuer Identifier for the Issuer of the response The iss value is a case sensitive URL using the https scheme that contains scheme, host, and optionally, port number and path components and no query or fragment components." @@ -200,6 +201,9 @@ class Config: sid: Optional[str] "OPTIONAL. Session ID - String identifier for a Session. This represents a Session of a User Agent or device for a logged-in End-User at an RP. Different sid values are used to identify distinct sessions at an OP. The sid value need only be unique in the context of a particular issuer. Its contents are opaque to the RP." + raw_token: Optional[str] + "The raw token received from the issuer." + def validate_extern( self, issuer: str, @@ -293,6 +297,14 @@ def validate_extern( "The session associated with this ID-Token was authenticated too far in the past", ) + @classmethod + def parse_jwt( + cls: Type["IdToken"], value: str, signing_keys: List[JWK] + ) -> "IdToken": + token = super().parse_jwt(value, signing_keys) + token.raw_token = value + return token + class JwtAccessToken(OpenidBaseModel): """ diff --git a/src/simple_openid_connect/integrations/django/models.py b/src/simple_openid_connect/integrations/django/models.py index c989c49..08dac15 100644 --- a/src/simple_openid_connect/integrations/django/models.py +++ b/src/simple_openid_connect/integrations/django/models.py @@ -116,6 +116,10 @@ def id_token(self) -> IdToken: def id_token(self, value: IdToken) -> None: self._id_token = value.json() + @property + def raw_id_token(self) -> Optional[str]: + return self.id_token.raw_token + def update_session(self, token_response: TokenSuccessResponse) -> None: self.scope = str(token_response.scope) self.access_token = token_response.access_token diff --git a/src/simple_openid_connect/integrations/django/views.py b/src/simple_openid_connect/integrations/django/views.py index 3e6debe..6f4403a 100644 --- a/src/simple_openid_connect/integrations/django/views.py +++ b/src/simple_openid_connect/integrations/django/views.py @@ -26,7 +26,7 @@ TokenSuccessResponse, ) from simple_openid_connect.integrations.django.apps import OpenidAppConfig -from simple_openid_connect.integrations.django.models import OpenidUser +from simple_openid_connect.integrations.django.models import OpenidSession logger = logging.getLogger(__name__) @@ -106,16 +106,24 @@ class LogoutView(View): """ def get(self, request: HttpRequest) -> HttpResponse: + session_id = request.session.get("openid_session") logout(request) client = OpenidAppConfig.get_instance().get_client(request) if settings.LOGOUT_REDIRECT_URL is not None: + openid_session = ( + OpenidSession.objects.get(id=session_id) if session_id else None + ) + logout_request = RpInitiatedLogoutRequest( post_logout_redirect_uri=request.build_absolute_uri( resolve_url(settings.LOGOUT_REDIRECT_URL) - ), - client_id=client.client_auth.client_id, + ) ) + if openid_session is not None and openid_session.raw_id_token is not None: + logout_request.id_token_hint = openid_session.raw_id_token + else: + logout_request.client_id = client.client_auth.client_id else: logout_request = None