authentik.providers.iframe_logout
Shared logout stages for SAML and OIDC providers
1"""Shared logout stages for SAML and OIDC providers""" 2 3from django.http import HttpResponse 4from rest_framework.fields import CharField, ListField 5 6from authentik.common.oauth.constants import PLAN_CONTEXT_OIDC_LOGOUT_IFRAME_SESSIONS 7from authentik.core.api.utils import PassiveSerializer 8from authentik.flows.challenge import Challenge, ChallengeResponse 9from authentik.flows.stage import ChallengeStageView 10from authentik.providers.saml.views.flows import PLAN_CONTEXT_SAML_LOGOUT_IFRAME_SESSIONS 11 12 13class LogoutURL(PassiveSerializer): 14 """Data for a single logout URL""" 15 16 url = CharField() 17 provider_name = CharField(required=False, allow_null=True) 18 binding = CharField(required=False, allow_null=True) 19 saml_request = CharField(required=False, allow_null=True) 20 saml_response = CharField(required=False, allow_null=True) 21 saml_relay_state = CharField(required=False, allow_null=True) 22 23 24class IframeLogoutChallenge(Challenge): 25 """Challenge for iframe logout""" 26 27 component = CharField(default="ak-provider-iframe-logout") 28 logout_urls = ListField(child=LogoutURL(), default=list) 29 30 31class IframeLogoutChallengeResponse(ChallengeResponse): 32 """Response for iframe logout""" 33 34 component = CharField(default="ak-provider-iframe-logout") 35 36 37class IframeLogoutStageView(ChallengeStageView): 38 """SAML and OIDC Logout stage that handles parallel iframe logout""" 39 40 response_class = IframeLogoutChallengeResponse 41 42 def get_challenge(self) -> Challenge: 43 """Generate iframe logout challenge for both SAML and OIDC""" 44 logout_urls = [] 45 46 if self.executor.plan: 47 saml_sessions = self.executor.plan.context.get( 48 PLAN_CONTEXT_SAML_LOGOUT_IFRAME_SESSIONS, [] 49 ) 50 oidc_sessions = self.executor.plan.context.get( 51 PLAN_CONTEXT_OIDC_LOGOUT_IFRAME_SESSIONS, [] 52 ) 53 54 logout_urls.extend(saml_sessions) 55 logout_urls.extend(oidc_sessions) 56 57 self.executor.plan.context.pop(PLAN_CONTEXT_SAML_LOGOUT_IFRAME_SESSIONS, None) 58 self.executor.plan.context.pop(PLAN_CONTEXT_OIDC_LOGOUT_IFRAME_SESSIONS, None) 59 else: 60 saml_sessions = [] 61 oidc_sessions = [] 62 63 return IframeLogoutChallenge( 64 data={ 65 "component": "ak-provider-iframe-logout", 66 "logout_urls": logout_urls, 67 } 68 ) 69 70 def challenge_valid(self, response: ChallengeResponse) -> HttpResponse: 71 """Iframe logout completed""" 72 return self.executor.stage_ok()
14class LogoutURL(PassiveSerializer): 15 """Data for a single logout URL""" 16 17 url = CharField() 18 provider_name = CharField(required=False, allow_null=True) 19 binding = CharField(required=False, allow_null=True) 20 saml_request = CharField(required=False, allow_null=True) 21 saml_response = CharField(required=False, allow_null=True) 22 saml_relay_state = CharField(required=False, allow_null=True)
Data for a single logout URL
Inherited Members
25class IframeLogoutChallenge(Challenge): 26 """Challenge for iframe logout""" 27 28 component = CharField(default="ak-provider-iframe-logout") 29 logout_urls = ListField(child=LogoutURL(), default=list)
Challenge for iframe logout
32class IframeLogoutChallengeResponse(ChallengeResponse): 33 """Response for iframe logout""" 34 35 component = CharField(default="ak-provider-iframe-logout")
Response for iframe logout
38class IframeLogoutStageView(ChallengeStageView): 39 """SAML and OIDC Logout stage that handles parallel iframe logout""" 40 41 response_class = IframeLogoutChallengeResponse 42 43 def get_challenge(self) -> Challenge: 44 """Generate iframe logout challenge for both SAML and OIDC""" 45 logout_urls = [] 46 47 if self.executor.plan: 48 saml_sessions = self.executor.plan.context.get( 49 PLAN_CONTEXT_SAML_LOGOUT_IFRAME_SESSIONS, [] 50 ) 51 oidc_sessions = self.executor.plan.context.get( 52 PLAN_CONTEXT_OIDC_LOGOUT_IFRAME_SESSIONS, [] 53 ) 54 55 logout_urls.extend(saml_sessions) 56 logout_urls.extend(oidc_sessions) 57 58 self.executor.plan.context.pop(PLAN_CONTEXT_SAML_LOGOUT_IFRAME_SESSIONS, None) 59 self.executor.plan.context.pop(PLAN_CONTEXT_OIDC_LOGOUT_IFRAME_SESSIONS, None) 60 else: 61 saml_sessions = [] 62 oidc_sessions = [] 63 64 return IframeLogoutChallenge( 65 data={ 66 "component": "ak-provider-iframe-logout", 67 "logout_urls": logout_urls, 68 } 69 ) 70 71 def challenge_valid(self, response: ChallengeResponse) -> HttpResponse: 72 """Iframe logout completed""" 73 return self.executor.stage_ok()
SAML and OIDC Logout stage that handles parallel iframe logout
response_class =
<class 'IframeLogoutChallengeResponse'>
43 def get_challenge(self) -> Challenge: 44 """Generate iframe logout challenge for both SAML and OIDC""" 45 logout_urls = [] 46 47 if self.executor.plan: 48 saml_sessions = self.executor.plan.context.get( 49 PLAN_CONTEXT_SAML_LOGOUT_IFRAME_SESSIONS, [] 50 ) 51 oidc_sessions = self.executor.plan.context.get( 52 PLAN_CONTEXT_OIDC_LOGOUT_IFRAME_SESSIONS, [] 53 ) 54 55 logout_urls.extend(saml_sessions) 56 logout_urls.extend(oidc_sessions) 57 58 self.executor.plan.context.pop(PLAN_CONTEXT_SAML_LOGOUT_IFRAME_SESSIONS, None) 59 self.executor.plan.context.pop(PLAN_CONTEXT_OIDC_LOGOUT_IFRAME_SESSIONS, None) 60 else: 61 saml_sessions = [] 62 oidc_sessions = [] 63 64 return IframeLogoutChallenge( 65 data={ 66 "component": "ak-provider-iframe-logout", 67 "logout_urls": logout_urls, 68 } 69 )
Generate iframe logout challenge for both SAML and OIDC
def
challenge_valid( self, response: authentik.flows.challenge.ChallengeResponse) -> django.http.response.HttpResponse:
71 def challenge_valid(self, response: ChallengeResponse) -> HttpResponse: 72 """Iframe logout completed""" 73 return self.executor.stage_ok()
Iframe logout completed