authentik.providers.saml.native_logout
SAML Logout stages for automatic injection
1"""SAML Logout stages for automatic injection""" 2 3from django.http import HttpResponse 4from rest_framework.fields import BooleanField, CharField, ChoiceField 5from structlog.stdlib import get_logger 6 7from authentik.flows.challenge import Challenge, ChallengeResponse, HttpChallengeResponse 8from authentik.flows.stage import ChallengeStageView 9from authentik.providers.saml.models import SAMLBindings 10from authentik.providers.saml.views.flows import PLAN_CONTEXT_SAML_LOGOUT_NATIVE_SESSIONS 11 12LOGGER = get_logger() 13 14 15class NativeLogoutStageViewBase(ChallengeStageView): 16 """Base class for native browser logout stages with shared functionality""" 17 18 19class NativeLogoutChallenge(Challenge): 20 """Challenge for native browser logout""" 21 22 component = CharField(default="ak-provider-saml-native-logout") 23 provider_name = CharField(required=False) 24 is_complete = BooleanField(required=False, default=False) 25 26 post_url = CharField(required=False) 27 redirect_url = CharField(required=False) 28 29 saml_binding = ChoiceField(choices=SAMLBindings.choices, required=False) 30 saml_request = CharField(required=False) 31 saml_response = CharField(required=False) 32 saml_relay_state = CharField(required=False) 33 34 35class NativeLogoutChallengeResponse(ChallengeResponse): 36 """Response for native browser logout""" 37 38 component = CharField(default="ak-provider-saml-native-logout") 39 40 41class NativeLogoutStageView(NativeLogoutStageViewBase): 42 """Native browser logout stage that handles redirect chain and post logouts.""" 43 44 response_class = NativeLogoutChallengeResponse 45 46 def get_challenge(self, *args, **kwargs) -> Challenge: 47 """Generate challenge for next provider""" 48 pending = self.executor.plan.context.get(PLAN_CONTEXT_SAML_LOGOUT_NATIVE_SESSIONS, []) 49 if not pending: 50 self.executor.plan.context.pop(PLAN_CONTEXT_SAML_LOGOUT_NATIVE_SESSIONS, None) 51 return NativeLogoutChallenge( 52 data={ 53 "component": "ak-provider-saml-native-logout", 54 "is_complete": True, 55 } 56 ) 57 58 logout_data = pending.pop(0) 59 self.executor.plan.context[PLAN_CONTEXT_SAML_LOGOUT_NATIVE_SESSIONS] = pending 60 61 return NativeLogoutChallenge( 62 data={ 63 "component": "ak-provider-saml-native-logout", 64 **logout_data, 65 } 66 ) 67 68 def challenge_valid(self, response: ChallengeResponse) -> HttpResponse: 69 """Challenge completed""" 70 challenge = self.get_challenge() 71 72 if not challenge.is_valid(): 73 return self.executor.stage_invalid() 74 75 if challenge.initial_data.get("is_complete"): 76 return self.executor.stage_ok() 77 78 return HttpChallengeResponse(challenge)
LOGGER =
<BoundLoggerLazyProxy(logger=None, wrapper_class=None, processors=None, context_class=None, initial_values={}, logger_factory_args=())>
16class NativeLogoutStageViewBase(ChallengeStageView): 17 """Base class for native browser logout stages with shared functionality"""
Base class for native browser logout stages with shared functionality
20class NativeLogoutChallenge(Challenge): 21 """Challenge for native browser logout""" 22 23 component = CharField(default="ak-provider-saml-native-logout") 24 provider_name = CharField(required=False) 25 is_complete = BooleanField(required=False, default=False) 26 27 post_url = CharField(required=False) 28 redirect_url = CharField(required=False) 29 30 saml_binding = ChoiceField(choices=SAMLBindings.choices, required=False) 31 saml_request = CharField(required=False) 32 saml_response = CharField(required=False) 33 saml_relay_state = CharField(required=False)
Challenge for native browser logout
36class NativeLogoutChallengeResponse(ChallengeResponse): 37 """Response for native browser logout""" 38 39 component = CharField(default="ak-provider-saml-native-logout")
Response for native browser logout
42class NativeLogoutStageView(NativeLogoutStageViewBase): 43 """Native browser logout stage that handles redirect chain and post logouts.""" 44 45 response_class = NativeLogoutChallengeResponse 46 47 def get_challenge(self, *args, **kwargs) -> Challenge: 48 """Generate challenge for next provider""" 49 pending = self.executor.plan.context.get(PLAN_CONTEXT_SAML_LOGOUT_NATIVE_SESSIONS, []) 50 if not pending: 51 self.executor.plan.context.pop(PLAN_CONTEXT_SAML_LOGOUT_NATIVE_SESSIONS, None) 52 return NativeLogoutChallenge( 53 data={ 54 "component": "ak-provider-saml-native-logout", 55 "is_complete": True, 56 } 57 ) 58 59 logout_data = pending.pop(0) 60 self.executor.plan.context[PLAN_CONTEXT_SAML_LOGOUT_NATIVE_SESSIONS] = pending 61 62 return NativeLogoutChallenge( 63 data={ 64 "component": "ak-provider-saml-native-logout", 65 **logout_data, 66 } 67 ) 68 69 def challenge_valid(self, response: ChallengeResponse) -> HttpResponse: 70 """Challenge completed""" 71 challenge = self.get_challenge() 72 73 if not challenge.is_valid(): 74 return self.executor.stage_invalid() 75 76 if challenge.initial_data.get("is_complete"): 77 return self.executor.stage_ok() 78 79 return HttpChallengeResponse(challenge)
Native browser logout stage that handles redirect chain and post logouts.
response_class =
<class 'NativeLogoutChallengeResponse'>
47 def get_challenge(self, *args, **kwargs) -> Challenge: 48 """Generate challenge for next provider""" 49 pending = self.executor.plan.context.get(PLAN_CONTEXT_SAML_LOGOUT_NATIVE_SESSIONS, []) 50 if not pending: 51 self.executor.plan.context.pop(PLAN_CONTEXT_SAML_LOGOUT_NATIVE_SESSIONS, None) 52 return NativeLogoutChallenge( 53 data={ 54 "component": "ak-provider-saml-native-logout", 55 "is_complete": True, 56 } 57 ) 58 59 logout_data = pending.pop(0) 60 self.executor.plan.context[PLAN_CONTEXT_SAML_LOGOUT_NATIVE_SESSIONS] = pending 61 62 return NativeLogoutChallenge( 63 data={ 64 "component": "ak-provider-saml-native-logout", 65 **logout_data, 66 } 67 )
Generate challenge for next provider
def
challenge_valid( self, response: authentik.flows.challenge.ChallengeResponse) -> django.http.response.HttpResponse:
69 def challenge_valid(self, response: ChallengeResponse) -> HttpResponse: 70 """Challenge completed""" 71 challenge = self.get_challenge() 72 73 if not challenge.is_valid(): 74 return self.executor.stage_invalid() 75 76 if challenge.initial_data.get("is_complete"): 77 return self.executor.stage_ok() 78 79 return HttpChallengeResponse(challenge)
Challenge completed