superset icon indicating copy to clipboard operation
superset copied to clipboard

Endpoint /api/me/roles returns 401 "Not authorized" despite valid access token

Open larshelge opened this issue 2 years ago • 12 comments

I am running Superset 3.0.0 with Docker Compose. Trying to access the following API endpoint:

/api/me/roles

I have created an access token from /api/v1/security/login for a user with the Admin, Public and Gamma roles. I make a request for the mentioned API endpoint with the appropriate header.

Authorization: Bearer {access-token}
Accept: application/json

Various endpoints return 200 and a valid JSON payload, such as /api/v1/dashboard and /api/v1/chart. However, the /api/me/roles endpoint returns 401 "Not authorized". This blocks the embedded dashboard feature. Could this be a bug, or am I doing something wrong? I sense there is something special with this endpoint and authentication as it relates to the currently authenticated user.

The /api/me/roles endpoint returns the roles when I log in through the UI and load the endpoint in a web browser.

Expected results

I expect the roles to be returned from the API with 200 OK status.

Actual results

The endpoint returns 401 "Not authorized" despite the access token seemingly being valid.

Environment

(please complete the following information):

  • Chrome latest.
  • Linux Ubuntu 22.04.
  • Superset 3.0.0 on Docker Compose in production mode

Flags

WTF_CSRF_ENABLED = False
TALISMAN_ENABLED = False
ENABLE_CORS = True

larshelge avatar Oct 23 '23 20:10 larshelge

Still happening in 3.0.1

cbuffevant avatar Nov 01 '23 01:11 cbuffevant

You can try a fix around by hitting a get request to http://localhost:8088/login endpoint and sending the csrf_token obtained along with username and password as formData and then get the access token to request http://localhost:8088/api/v1/me/roles api

Issue addressed here #25876

siddhartha8916 avatar Dec 30 '23 02:12 siddhartha8916

Still happening in 3.1.1, also blocking usage of embedded dashboards. Unfortunately the workaround above won't work because the embed SDK won't do that for me

bryanjknight avatar Mar 14 '24 22:03 bryanjknight

Digging into this more, it seems /api/v1/security/login does not set a session cookie; hence, the /api/v1/me and underlying calls fail b/c there's no session cookie

bryanjknight avatar Mar 18 '24 14:03 bryanjknight

Digging into this more, it seems /api/v1/security/login does not set a session cookie; hence, the /api/v1/me and underlying calls fail b/c there's no session cookie

I am facing the same issue Normally, how can I get the session cookie? if /api/v1/security/login doesn't return it, is there any other way to login and get cookie? how did superset work if there is no session cookie?

EuphoriaCelestial avatar Mar 19 '24 03:03 EuphoriaCelestial

Still happening in 3.1.1, also blocking usage of embedded dashboards. Unfortunately the workaround above won't work because the embed SDK won't do that for me

So superset only returns the dashboard available to that user according to the role defined

siddhartha8916 avatar Mar 19 '24 16:03 siddhartha8916

Digging into this more, it seems /api/v1/security/login does not set a session cookie; hence, the /api/v1/me and underlying calls fail b/c there's no session cookie

I am facing the same issue Normally, how can I get the session cookie? if /api/v1/security/login doesn't return it, is there any other way to login and get cookie? how did superset work if there is no session cookie?

I had to do a hack (emphasis on hack: this is not production ready code, please review it b/c if done wrong it creates a security hole) to basically take a JWT, verify it, find the corresponding user, then login that user again. The result is a cookie getting set on response:

class CustomOAuthView(AuthOAuthView):

    @expose('/custom/session-by-jwt', methods=['GET'])
    def session_by_jwt(self, provider= None):
        # get the jwt in the request
        current_jwt = request.headers.get('Authorization').split(' ')[1]

        # decode the jwt to get the claims
        import jwt
        try:
            jwt_options = {
                'verify_signature': False, # TODO: get the public key from the jwks_uri to verify the signature
                'verify_exp': True,
                'verify_nbf': False,
                'verify_iat': True,
                'verify_aud': False
            }
            jwt_decoded = jwt.decode(jwt=str.encode(current_jwt), algorithms=['HS256'], options=jwt_options)

            # get the user from the jwt
            user = self.appbuilder.sm.find_user(email=jwt_decoded['sub'])

            # create a session cookie for the user
            login_user(user)
        except Exception as e:
            print(f"Exception: {e}")
            return "Invalid JWT"
        return "session_by_jwt"  

class CustomSsoSecurityManager(SupersetSecurityManager):

    authoauthview = CustomOAuthView

    def __init__(self, appbuilder):
        super(CustomSsoSecurityManager, self).__init__(appbuilder)

bryanjknight avatar Mar 19 '24 17:03 bryanjknight

Digging into this more, it seems /api/v1/security/login does not set a session cookie; hence, the /api/v1/me and underlying calls fail b/c there's no session cookie

I found out that if we set those flag in superset config: SESSION_COOKIE_HTTPONLY = False SESSION_COOKIE_SECURE = False

the /api/v1/security/login endpoint will return a cookie like this: image

is this the right cookie? if so, how to use it? I tried to add it in postman but it doesn't work image

image

EuphoriaCelestial avatar Mar 20 '24 02:03 EuphoriaCelestial

Ah, good find! Yeah, that's looks close, the one I have starts with a .. Digging into the superset_config.py in the main repo, I see this:

#
# Flask session cookie options
#
# See https://flask.palletsprojects.com/en/1.1.x/security/#set-cookie-options
# for details
#
SESSION_COOKIE_HTTPONLY = True  # Prevent cookie from being read by frontend JS?
SESSION_COOKIE_SECURE = False  # Prevent cookie from being transmitted over non-tls?
SESSION_COOKIE_SAMESITE: Literal["None", "Lax", "Strict"] | None = "Lax"
# Whether to use server side sessions from flask-session or Flask secure cookies
SESSION_SERVER_SIDE = False
# Example config using Redis as the backend for server side sessions
# from flask_session import RedisSessionInterface
#
# SESSION_SERVER_SIDE = True
# SESSION_USE_SIGNER = True
# SESSION_TYPE = "redis"
# SESSION_REDIS = Redis(host="localhost", port=6379, db=0)
#
# Other possible config options and backends:
# # https://flask-session.readthedocs.io/en/latest/config.html

Perhaps switch to server side sessions might help?

bryanjknight avatar Mar 20 '24 12:03 bryanjknight

Perhaps switch to server side sessions might help?

I tried, but still not working Any other idea?

EuphoriaCelestial avatar Mar 21 '24 07:03 EuphoriaCelestial

Hi all,

same problem here. Do I have to rollback on version 2 or do you think this problem may be fixed in a rather short time ?

Thx !!

YannGer avatar Mar 25 '24 19:03 YannGer

~~Hi,~~

~~I'm also seeing this on Superset 3.1.0 when trying to embed dashboards using the embedded sdk. Any updates?~~

~~Thanks in advance~~

Turns out guest token I was using wasn't formatted correct in which case the Embedded SDK successfully creates the dashboard just to fail on the subsequent call to /roles

mortenesbensen avatar May 23 '24 13:05 mortenesbensen

@mortenesbensen when you say it "wasn't formatted correctly" what was the issue? I think we're running into this same issue now.

jay-growflow avatar Jun 12 '24 19:06 jay-growflow

Hello,

I am new to Apache Superset and am working on integrating REST APIs for CRUD operations within my project. I understand that in order to interact with the Superset REST APIs, I need to obtain an API token. Could you please provide guidance on how to retrieve the API token either through the Superset UI or by sending HTTP requests?

For context, I have already configured Keycloak for authentication, and would appreciate any instructions or references to help me proceed.

Thank you for your assistance!

Parzi68 avatar Nov 13 '24 10:11 Parzi68

This issue has seen a lot of activity in the past, but has been silent for several months now. Is this still an issue in 4.1.2/5.0.0/master? Otherwise tempted to close as inactive/not-planned.

rusackas avatar Apr 22 '25 19:04 rusackas

@rusackas I'm still experiencing this in 4.1.1 and 5.0.0rc2. I'm using the sdk and successfully creating a guest token directly in my app signed with the secret set in configuration variable GUEST_TOKEN_JWT_SECRET. I've checked all the tokens and they are all valid and working and the embed partially works...it pulls lots of js and css files using the token but then the embed just shows "Something went wrong with embedded authentication. Check the dev console for details." Checking the dev console shows what others report: A 401 returned when calling to /api/v1/me/roles. I've tried many solutions and none have worked for me.

schollz avatar May 21 '25 14:05 schollz

active issue getting 401 with SDK on roles

magnetic5355 avatar May 23 '25 08:05 magnetic5355

@mortenesbensen I realize its a year later, but I was wondering if you could elaborate on the changes you made to allow the embedding to work? I've checked the validity of my tokens and they are verified to be valid, yet I cannot get embedding to not fail on the call to /roles.

And, to anyone else, I'm wondering if there is a workaround? Maybe not using the SDK ?

schollz avatar May 23 '25 20:05 schollz

@rusackas I resolved the issue, there were a couple of issues at hand in my case, due to my configuration and not due to Superset.

Firstly I needed to include the correct ROLE configuration variables in my config:

GUEST_ROLE_NAME = "Gamma"
PUBLIC_ROLE_LIKE = "Gamma"

Once they were in, I no longer got the 401 error. I did have a new error, though, related to the audience. I was using the guest token generated from the app:

{
  "user": {
    "username": "[email protected]",
    "first_name": "embedded",
    "last_name": "embedded"
  },
  "resources": [
    {
      "type": "dashboard",
      "id": "d73e7841-9342-4afd-8e29-b4a416a2498c"
    }
  ],
  "rls_rules": [],
  "iat": 1730883214,
  "exp": 1732956814,
  "aud": "superset",
  "type": "guest"
}

And the aud was specified as superset. So to get this to work I realized I needed to include that in the configuration as well:

GUEST_TOKEN_JWT_AUDIENCE = "superset"

After that, it is working as expected in 5.0.0rc2 (and probably 4.1.1 as well, but I haven't checked). But in the end it was a configuration error on my end.

schollz avatar Jun 03 '25 15:06 schollz

Apologies for the late reply. It's been a while since I worked on this so can't remember the exact issue, but I do remember that I had somehow formatted the guest token incorrectly (probably padded it with some extra characters or the like). What baffled me was that I got this error while other calls seemed to pass though

mortenesbensen avatar Jun 04 '25 10:06 mortenesbensen