jharmon96 opened a new issue, #20339:
URL: https://github.com/apache/superset/issues/20339

   A clear and concise description of what the bug is.
   
   #### How to reproduce the bug
   
   Backchannel logout sent from Keycloak OIDC server is failing. Login/Logout 
via the UI both work fine. 
   The intention is to implement a Single Sign Out solution, where a user 
logging out of another client application will cause them to logout of 
Superset. 
   At the moment, when a user signs out of another application, Keycloak is 
configured to send a POST request to Superset with the goal being that the 
request logs the user out. 
   
   I have added the following to my superset_config.py:
   
   ```python
       from flask import redirect, request
       from flask_appbuilder.security.manager import AUTH_OID
       from superset.security import SupersetSecurityManager
       from flask_oidc import OpenIDConnect
       from flask_appbuilder.security.views import AuthOIDView
       from flask_login import login_user
       from urllib.parse import quote
       from flask_appbuilder.views import ModelView, SimpleFormView, expose
       import logging
       class AuthOIDCView(AuthOIDView):
         @expose('/login/', methods=['GET', 'POST'])
         def login(self, flag=True):
           sm = self.appbuilder.sm
           oidc = sm.oid
           @self.appbuilder.sm.oid.require_login
           def handle_login(): 
             user = sm.auth_user_oid(oidc.user_getfield('email'))
             if user is None:
               info = oidc.user_getinfo(['preferred_username',    
                     'given_name', 'family_name', 'email'])
               user = sm.add_user(info.get('preferred_username'),   
                     info.get('given_name'), info.get('family_name'),    
                     info.get('email'), sm.find_role('Gamma'))
             login_user(user, remember=False)
             return redirect(self.appbuilder.get_url_for_index)
           return handle_login()
         @expose('/logout/', methods=['GET', 'POST'])
         def logout(self):
           oidc = self.appbuilder.sm.oid
           oidc.logout()
           super(AuthOIDCView, self).logout()        
           redirect_url = request.url_root.strip('/') + 
self.appbuilder.get_url_for_login
           # return redirect(oidc.client_secrets.get('issuer') + 
'/protocol/openid-connect/logout?post_logout_redirect_uri=' +  
quote(redirect_url, safe=''))
           return redirect(oidc.client_secrets.get('issuer') + 
'/protocol/openid-connect/logout')
         @expose('/backchannel-logout/', methods=['GET', 'POST'])
         def backchannel_logout(self):
           oidc = self.appbuilder.sm.oid
           oidc.logout()
           super(AuthOIDCView, self).logout()        
           redirect_url = request.url_root.strip('/') + 
self.appbuilder.get_url_for_login
           return redirect(oidc.client_secrets.get('issuer') + 
'/protocol/openid-connect/logout')
       class OIDCSecurityManager(SupersetSecurityManager):
         authoidview = AuthOIDCView
         def __init__(self,appbuilder):
           super(OIDCSecurityManager, self).__init__(appbuilder)
           if self.auth_type == AUTH_OID:
             self.oid = OpenIDConnect(self.appbuilder.get_app)
      # Trying different variations here to get this to work
       WTF_CSRF_EXEMPT_LIST.append("backchannel_logout")
       WTF_CSRF_EXEMPT_LIST.append("backchannel-logout")
       WTF_CSRF_EXEMPT_LIST.append("superset.backchannel_ogout")
       WTF_CSRF_EXEMPT_LIST.append("superset.backchannel-logout")
   
       AUTH_TYPE = AUTH_OID
       OIDC_CLIENT_SECRETS='client_secret.json'
       OIDC_ID_TOKEN_COOKIE_SECURE = False
       OIDC_REQUIRE_VERIFIED_EMAIL = False
       OIDC_CLOCK_SKEW = 560
       OIDC_VALID_ISSUERS = 'https://auth.{{ .Values.global.domain }}/realms/{{ 
.Values.global.customerName }}'
       AUTH_USER_REGISTRATION = True
       AUTH_USER_REGISTRATION_ROLE = 'Gamma'
       CUSTOM_SECURITY_MANAGER = OIDCSecurityManager
       OIDC_INTROSPECTION_AUTH_METHOD = 'client_secret_post'
       OIDC_TOKEN_TYPE_HINT = 'access_token'
   
   ```
   
   ### Expected results
   
   I expect that the POST request made against '/backchannel-logout/' would not 
require a CSRF token.
   
   ### Actual results
   
   I get the following message in my Superset server logs:
   
   ```
   2022-06-09 22:39:19,480:INFO:flask_wtf.csrf:The CSRF token is missing.
   400 Bad Request: The CSRF token is missing.
   2022-06-09 22:39:19,480:WARNING:superset.views.base:400 Bad Request: The 
CSRF token is missing.
   10.244.0.133 - - [09/Jun/2022:22:39:19 +0000] "POST /backchannel-logout/ 
HTTP/1.1" 302 220 "-" "Apache-HttpClient/4.5.13 (Java/11.0.15)"
   ```
   
   #### Screenshots
   N/A
   
   ### Environment
   
   POST to '/backchannel-logout/' is being sent from a Keycloak server. 
   Latest Superset version
   Python 3.8
   
   ### Additional context
   
   Apologies if I am doing something dumb here, like simply getting the format 
of my WTF_CSRF_EXEMPT_LIST record wrong. I've been trying to get this to work 
for hours : /
   
   Also, I realize that the code under `def backchannel_logout(self):` may not 
work, but right now I'm just trying to get past this CSRF issue. 
   
   
   
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to