You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@superset.apache.org by "byeonghyun-kim (via GitHub)" <gi...@apache.org> on 2025/09/26 06:07:30 UTC

Re: [I] Endpoint /api/me/roles returns 401 "Not authorized" despite valid access token [superset]

byeonghyun-kim commented on issue #25740:
URL: https://github.com/apache/superset/issues/25740#issuecomment-3336901186

   Maybe this issue is inactive, but I also ran into it when primarily using Keycloak as AUTH_OAUTH. It turns out that the JWT token from login only have 'sub' field, which is user id value itself. When calling /api/v1/me or other APIs, somehow this 'sub' based user lookup method does not work properly.
   When you call the API and check the logs in the WebUI, the API calls may show a 'None' user, meaning that fetching user from access token failed.
   With the help of GPT and inspecting the codebase, adding custom security manager did the trick. Now I can finally maintain Keycloak login session, but if I need to use DB login for the API, that still works as well.
   
   I'm currently using 4.1.2 version, and it works.
   Be sure that you turned on AUTH_API_LOGIN_ALLOW_MULTIPLE_PROVIDERS option as True, just in case of preventing collision of providers.
   
   ```
   import re, jwt
   from flask import current_app
   from superset.security import SupersetSecurityManager
   
   class ApiFirstSecurityManager(SupersetSecurityManager):
       def __init__(self, appbuilder):
           super().__init__(appbuilder)
   
           lm = self.lm
   
           @lm.request_loader
           def bearer_request_loader(req):
               """
               1) Authorization: Find jwt token with regex in Bearer <jwt> from authorization header and load user
               2) If user is empty or fail, return None → Flask-Login uses default location such as user_loader
               """
               auth = req.headers.get("Authorization", "")
               m = re.match(r"^\s*Bearer\s+(.+)$", auth, re.I)
               if not m:
                   return None
   
               token = m.group(1)
               secret = (current_app.config.get("SECRET_KEY")
                         or current_app.config.get("SUPERSET_SECRET_KEY"))
               alg = current_app.config.get("JWT_ALGORITHM", "HS256") # this may fallback to HS256 in default, 4.1.2 doesn't have 'JWT_ALGORITHM' config value
   
               try:
                   payload = jwt.decode(token, secret, algorithms=[alg])
               except Exception:
                   return None
   
               uid = payload.get("sub")
               username = payload.get("username")
   
               user = self.get_user_by_id(uid) if uid is not None else None
               if user is None and username:  # jwt token only have 'sub' as uid but just in case
                   user = self.find_user(username=username)
               return user
   
   ```
   Add this code to superset_config.py (you can separate code as another python file of course)
   and set CUSTOM_SECURITY_MANAGER = ApiFirstSecurityManager and re-deploy.
   Now the API using access token can search user itself.


-- 
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: notifications-unsubscribe@superset.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@superset.apache.org
For additional commands, e-mail: notifications-help@superset.apache.org