diff --git a/ftw/oidcauth/browser/configure.zcml b/ftw/oidcauth/browser/configure.zcml
index 84d3a8a..8300fe7 100644
--- a/ftw/oidcauth/browser/configure.zcml
+++ b/ftw/oidcauth/browser/configure.zcml
@@ -11,4 +11,11 @@
permission="zope.Public"
/>
+
diff --git a/ftw/oidcauth/browser/oidc.py b/ftw/oidcauth/browser/oidc.py
index 20600a2..e3162d5 100644
--- a/ftw/oidcauth/browser/oidc.py
+++ b/ftw/oidcauth/browser/oidc.py
@@ -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
@@ -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:
@@ -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()
@@ -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()
diff --git a/ftw/oidcauth/plugin.py b/ftw/oidcauth/plugin.py
index b326733..55bf009 100644
--- a/ftw/oidcauth/plugin.py
+++ b/ftw/oidcauth/plugin.py
@@ -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",
@@ -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')
diff --git a/ftw/oidcauth/www/config.zpt b/ftw/oidcauth/www/config.zpt
index c07461b..53b5890 100644
--- a/ftw/oidcauth/www/config.zpt
+++ b/ftw/oidcauth/www/config.zpt
@@ -81,6 +81,13 @@ Error:
|
+
+ End Session Endpoint
+
+ |
+ |
+
Enable Auto Provisioning
|