Skip to content

Commit

Permalink
When a user logs out they should also be logged out of the authentica…
Browse files Browse the repository at this point in the history
…tion provider 4teamwork#12

Implemented post provider logout callback handler 4teamwork#12
  • Loading branch information
wluyima committed Jul 3, 2024
1 parent 676ad67 commit 22e9dd8
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 0 deletions.
7 changes: 7 additions & 0 deletions ftw/oidcauth/browser/configure.zcml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,11 @@
permission="zope.Public"
/>

<browser:page
for="plone.app.layout.navigation.interfaces.INavigationRoot"
name="logout"
class=".oidc.OIDCLogoutView"
permission="zope.Public"
layer="ftw.oidcauth.interfaces.IOIDCLayer"
/>
</configure>
49 changes: 49 additions & 0 deletions ftw/oidcauth/browser/oidc.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
from Products.CMFPlone.browser.login.logout import LogoutView
from Products.Five import BrowserView
from ftw.oidcauth.browser.oidc_tools import OIDCClientAuthentication
from ftw.oidcauth.errors import OIDCBaseError
from six.moves.urllib.parse import parse_qsl, urlencode
from zExceptions import NotFound as zNotFound
from zope.component import getMultiAdapter
from zope.interface import implements
from zope.publisher.interfaces import IPublishTraverse
from zope.publisher.interfaces import NotFound
Expand All @@ -23,6 +26,8 @@ def publishTraverse(self, request, name):
if self.method is None:
if name == 'callback':
self.method = name
elif name == 'logout':
self.method = name
else:
raise NotFound(self, name, request)
else:
Expand All @@ -32,6 +37,8 @@ def publishTraverse(self, request, name):
def __call__(self):
if self.method == 'callback':
self.callback()
elif self.method == 'logout':
self.logout()
else:
raise zNotFound()

Expand All @@ -58,3 +65,45 @@ def set_error_response(self, status, message):
response.setHeader('Content-Type', 'text/plain')
response.setStatus(status, lock=1)
response.setBody(message, lock=1)

def logout(self):
p = OIDCClientAuthentication.get_oidc_plugin()
base_url = get_base_url(self.context, self.request)
original_redirect = self.request.get('redirect')
redirect = base_url
if original_redirect:
if original_redirect.startswith("http:") or original_redirect.startswith("https:"):
redirect = original_redirect
else:
redirect = base_url + original_redirect

logout_base_url = p.end_session_endpoint
params = {}
if "?" in p.end_session_endpoint:
url_parts = p.end_session_endpoint.split("?")
logout_base_url = url_parts[0]
params.update(dict(parse_qsl(url_parts[1])))
params["client_id"] = p.client_id
params["post_logout_redirect_uri"] = redirect
logout_url = "{}?{}".format(logout_base_url, urlencode(params))
self.request.response.redirect(logout_url)


class OIDCLogoutView(LogoutView):
def __call__(self):
if OIDCClientAuthentication.get_oidc_plugin().end_session_endpoint:
base_url = get_base_url(self.context, self.request)
next_ = self.request.get('next')
oidc_logout = base_url + '/oidc/logout'
if next_ is None or not next_.startswith(oidc_logout):
if next_:
oidc_logout = "{}?{}".format(oidc_logout, urlencode({'redirect': next_}))
redirect = "{}?{}".format(base_url + '/logout', urlencode({'next': oidc_logout}))
self.request.response.redirect(redirect)
return

super(OIDCLogoutView, self).__call__()


def get_base_url(context, request):
return getMultiAdapter((context, request), name="plone_portal_state").navigation_root_url()
2 changes: 2 additions & 0 deletions ftw/oidcauth/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ def __init__(self, id, title=None):
self.token_endpoint = None
self.user_endpoint = None
self.jwks_endpoint = None
self.end_session_endpoint = None
self._auto_provisioning_enabled = True
self.properties_mapping = json.dumps({
"userid": "sub",
Expand Down Expand Up @@ -228,6 +229,7 @@ def manage_updateConfig(self, REQUEST):
self.token_endpoint = REQUEST.form.get('token-endpoint')
self.user_endpoint = REQUEST.form.get('user-endpoint')
self.jwks_endpoint = REQUEST.form.get('jwks-endpoint')
self.end_session_endpoint = REQUEST.form.get('end-session-endpoint')
self._auto_provisioning_enabled = REQUEST.form.get('auto-provisioning-enabled')

roles = REQUEST.form.get('roles')
Expand Down
7 changes: 7 additions & 0 deletions ftw/oidcauth/www/config.zpt
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,13 @@ Error: <b tal:content="options/error_message">
<td><input type="text" name="jwks-endpoint" size="60"
tal:attributes="value context/jwks_endpoint"/></td>
</tr>
<tr valign="top">
<td><div class="form-label">End Session Endpoint</div>
<div class="form-help"></div>
</td>
<td><input type="text" name="end-session-endpoint" size="60"
tal:attributes="value context/end_session_endpoint"/></td>
</tr>
<tr valign="top">
<td><div class="form-label">Enable Auto Provisioning</div>
<div class="form-help"></div>
Expand Down

0 comments on commit 22e9dd8

Please sign in to comment.