Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot autheticate on API. Get the tokens (JWT and csrf) but all responses are "401" #19525

Open
pedrohdemedeiros opened this issue Apr 5, 2022 · 22 comments
Labels
#bug Bug report

Comments

@pedrohdemedeiros
Copy link

pedrohdemedeiros commented Apr 5, 2022

I cannot use the API succefully for any request (except to get the secutiry tokens), always getting " Response 401" for any request.

How to reproduce the bug

On the docker container where the superset is running:

>>>
import requests

session = requests.session()

jwt_token = session.post(
    url='http://localhost:8088/api/v1/security/login',
    json={
    "username": "admin",
    "password": "admin",
    "refresh": False,
    "provider": "db"
    }
).json()["access_token"]

csrf_token = session.get(
    url='http://localhost:8088/api/v1/security/csrf_token/',
    headers={
        'Authorization': f'Bearer {jwt_token}',
    }
).json()["result"]

headers = {
    'accept': 'application/json',
    'Authorization': f'Bearer {jwt_token}',
    'X-CSRFToken': csrf_token,
}

#trying to use the "current user" request as a test
response = requests.get('http://localhost:8088/api/v1/me', headers=headers)

session.close()`

response

Expected results

{
"result": {
"email": "[email protected]",
"first_name": "Superset",
"id": 1,
"is_active": true,
"is_anonymous": false,
"last_name": "Admin",
"username": "admin"
}
}

Actual results

>>> response
<Response [401]>

Screenshots

Environment

(please complete the following information):

  • superset version: v1.0.0
  • python version: Python 3.8.12

Checklist

  • [ x] I have checked the superset logs for python stacktraces and included it here as text if there are any.
  • [x ] I have reproduced the issue with at least the latest released version of superset.
  • [ x] I have checked the issue tracker for the same issue and I haven't found one similar.
@pedrohdemedeiros pedrohdemedeiros added the #bug Bug report label Apr 5, 2022
@MM-Lehmann
Copy link

did you try with 1.4.2?

@MarcinZegar
Copy link

MarcinZegar commented Apr 18, 2022

Instead of

#trying to use the "current user" request as a test
response = requests.get('http://localhost:8088/api/v1/me', headers=headers)

you should try:

session.headers.update(headers)

response = session.get('http://localhost:8088/api/v1/me') 

@pedrohdemedeiros
Copy link
Author

First of all, thank you @MM-Lehmann and @MarcinZegar .

I've tried with 1.4.0 and most of my API problems were solved but one: I still cannot upload a CSV using it. I've tried a lot of different things, learned a lot about requests using python, curl and so on but it seens that it simply doesn't work.

Not a big problem since I've decided to update my database by MySQL directly (witch I had to learn also).

I will keep an eye on the next versions just to test if, someday, it will be possible

@rusackas
Copy link
Member

rusackas commented Jan 6, 2023

Closing this since most issues were resolved. It's also been a while since this thread was active! Hopefully things are working better in 1.5.x or 2.0.x but if there are still bugs being found, let us know and we can re-open this, no problem!

@rusackas rusackas closed this as completed Jan 6, 2023
@raghulprashath
Copy link

Instead of

#trying to use the "current user" request as a test
response = requests.get('http://localhost:8088/api/v1/me', headers=headers)

you should try:

session.headers.update(headers)

response = session.get('http://localhost:8088/api/v1/me') 

This didn't work now.
The code is accessing g.user from flask_login. The flask_login doesn't load the user when the request is sent from the api.
I think there should be a better solution for the new version of code.

@sazary
Copy link

sazary commented Feb 2, 2023

I think i'm experiencing the same issue with superset 2.0.1.

this is the script that I run to get jwt token, csrf token, and finally get my user's info:

const base_url = "https://<server_addr>/api/v1/";

async function get_csrf_token(access_token) {
  const res = await fetch(base_url   "security/csrf_token", {
    headers: {
      "Content-Type": "application/json",
      Authorization: "Bearer "   access_token,
    },
    method: "GET",
  });

  const complete_res = await res.json();
  return complete_res.result;
}

async function login() {
  const res = await fetch(base_url   "security/login", {
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      username: "user",
      password: "pass",
      provider: "db",
      refresh: true,
    }),
    method: "POST",
  });

  const complete_res = await res.json();
  return complete_res.access_token;
}

async function get_me(access_token, csrf_token) {
  const res = await fetch(base_url   "me", {
    credentials: "same-origin",
    headers: {
      "Content-Type": "application/json",
      Authorization: "Bearer "   access_token,
      "X-CSRFToken": csrf_token,
    },
    method: "GET",
  });

  return await res.json();
}

(async () => {
  const access_token = await login();
  console.log("res is ", access_token);

  const csrf_token = await get_csrf_token(access_token);
  console.log("res is ", csrf_token);

  const me_res = await get_me(access_token, csrf_token);
  console.log("me is ", me_res);
})();

when I run the script the result is:

$ node ./superset_api_test.js
res is  <jwt token>
res is  <csrf token>
me is  { message: 'Not authorized' }

and I think @raghulprashath is right. lines 35-63 of get_me method of superset.views.users.api.CurrentUserRestApi are like:

    @expose("/", methods=["GET"])
    @safe
    def get_me(self) -> Response:
        """Get the user object corresponding to the agent making the request
        ---
        get:
          description: >-
            Returns the user object corresponding to the agent making the request,
            or returns a 401 error if the user is unauthenticated.
          responses:
            200:
              description: The current user
              content:
                application/json:
                  schema:
                    type: object
                    properties:
                      result:
                        $ref: '#/components/schemas/UserResponseSchema'
            401:
              $ref: '#/components/responses/401'
        """
        try:
            if g.user is None or g.user.is_anonymous:
                return self.response_401()
        except NoAuthorizationError:
            return self.response_401()

        return self.response(200, result=user_response_schema.dump(g.user))

i can confirm that the same workflow works totally fine for version 1.2.0, albeit on a view other than /me

versions:

superset: 2.0.1
python: 3.8.16

@nicolaskodak
Copy link

nicolaskodak commented Mar 21, 2023

Still facing this issue.

versions

superset: v2021.41.0-4182-gdf91664 ( `git clone` -> `docker compose -f docker-compose-non-dev.yml up -d` )
Python 3.8.13

have tried

  • node ./superset_api_test.js as @sazary brought up. Result is { message: 'Not authorized' }
  • hit /api/v1/me with Bearer token obtained from hitting /api/v1/security/login with admin, no luck.

The superset service is launched in docker, with superset_config_docker.py as

SESSION_COOKIE_SAMESITE = None
ENABLE_PROXY_FIX = True
PUBLIC_ROLE_LIKE_GAMMA = True
FEATURE_FLAGS = {
  "EMBEDDED SUPERSET" : True
}
CORS_OPTIONS = {
  'supports_credentials': True,
  'allow_headers': ['*'],
  'resources' : ['*'],
  'origins': ['http://localhost:8088', 'http://localhost:8888']
}
CSRF_ENABLED = False
WTF_CSRF_ENABLED = False

Wonder if anyone could point me to possible solutions for this?

@raghulprashath
Copy link

Actually I was able to solve this problem.

import requests
# from bs4 import BeautifulSoup

username = "admin"
password = "admin"
session = requests.session()

login_form = session.post('http://localhost:8088/login')
# soup = BeautifulSoup(login_form.text, 'html.parser')
# csrf_token = soup.find('input',{'id':'csrf_token'})['value']
data = {
  'username': username,
  'password': password,
  # 'csrf_token': csrf_token
}
response = session.post('http://localhost:8088/login', data=data)
response = session.get('http://localhost:8088/api/v1/me')

This is a work around to make this api work.
Comment out the lines in above code, if you want CSRF token i.e iff you enabled the flag in config

@sazary
Copy link

sazary commented Apr 11, 2023

I can confirm that this issue still exists with the latest release. Expected behavior, what goes wrong and steps to reproduce are the same as my first report.

versions:

apache-superset==2.1.0
Python 3.8.16

@rusackas Could you please re open this issue?

@rusackas rusackas reopened this Apr 12, 2023
@partizaans
Copy link

partizaans commented May 29, 2023

After a couple of hours doing debugging it seems that I could resolve the problem with a temporary solution.
In my case, I was requesting POST:api/v1/chart/data using the JWT authentication method. For this endpoint we have permission_str=can_read and the class_permission_name=Chart.
somewhere in flask_appbuilder/security/decorators.py:84 we have:

            if current_app.appbuilder.sm.is_item_public(
                permission_str, class_permission_name
            ):

In my running superset instance, reading a chart was a public action but only some of the charts were actually public. So the decorator prevents execution of verify_jwt_in_request() before processing the request, because of can read on Chart is in permissions of the Public role.

Temporary Solution

On the superset UI I edited the public role and removed can read on Chart from its permissions.

@huamichaelchen
Copy link

I think the Referer header is missing could be the culprit?

Wrote a guide on it, hopefully, it helps someone 😄 Choose your preferred media 😝

https://huamichaelchen.substack.com/p/end-to-end-example-of-setting-up

https://medium.com/@huamichaelchen/end-to-end-example-of-setting-up-superset-embedded-dashboard-f72fc985559

@Krishnamohan1604
Copy link

Hi Guys,

We are facing similar issue while authenticating superset API request (Superset version - 2.0.1). I have tried all the solution listed here but unfortunately nothing worked for me. Do we have any update on the issue?

Thanks!

@larshelge
Copy link

This issue seems to be present in version 3.0.0 as well. Running with Docker Compose. Requests to /api/v1/me/roles is always returning 401. This seems to break the embed dashboard feature. It would be great if anyone on the Superset side could look into this.

@lucidprojects
Copy link

same issue on 3.0.2

@hhhonzik
Copy link

Temporary Solution

On the superset UI I edited the role of public and removed can read on Chart from its permissions.

We've just had this issue when upgrading 3.0.1 to 3.1.0rc3 and this solved it. Thanks @partizaans

@m1zzo
Copy link

m1zzo commented Jan 18, 2024

3.0.2 Roles was 401, but after turning Talisman on - all seems work. Except other then security API endpoints :(

@michael-s-molina
Copy link
Member

Closing this as it seems the issue was resolved in recent versions of Superset. Please reopen if that's not the case.

@EuphoriaCelestial
Copy link

EuphoriaCelestial commented Mar 21, 2024

@michael-s-molina Hi, I am using the lastest version of Superset, but still facing this issue, can we re-open it?
All settings are leave as default except FAB_ADD_SECURITY_API = True
I am able to login, get csrf_token and call every other api except /api/v1/me/, it return 401

@partizaans
Copy link

Closing this as it seems the issue was resolved in recent versions of Superset. Please reopen if that's not the case.

Faced the same problem after following the upgrade instruction to the latest stable version (4.0.2) and happened again.
@michael-s-molina

@lsfc02
Copy link

lsfc02 commented Aug 21, 2024

Closing this as it seems the issue was resolved in recent versions of Superset. Please reopen if that's not the case.

Faced the same problem after following the upgrade instruction to the latest stable version (4.0.2) and happened again. @michael-s-molina

Same thing here, did you manage to solve it?

@partizaans
Copy link

partizaans commented Aug 24, 2024

Closing this as it seems the issue was resolved in recent versions of Superset. Please reopen if that's not the case.

Faced the same problem after following the upgrade instruction to the latest stable version (4.0.2) and happened again. @michael-s-molina

Same thing here, did you manage to solve it?

Unfortunately no. Had to do some manual actions after upgrading was completed, described as the Temporary Solution here: #19525 (comment)

@julienbrodier
Copy link

Same here. Getting a 404 when calling /api/v1/dashboard/14

1. Possible Explanation:

  • When the API is called with a session cookie, the user is identified through Flask-Login using current_user, and the @with_dashboard decorator can retrieve the dashboard based on this user.
  • With a JWT, the decorator might not know how to access the same user context. If @with_dashboard relies on current_user, and current_user is not properly defined when using the JWT, the lookup may fail, resulting in a 404.

2. User Context (current_user)

  • In the case of session cookies, Flask-Login properly sets up current_user to identify the user. However, when using a JWT, verify_jwt_in_request() verifies the token but does not automatically update current_user used by Flask to reference the authenticated user.
  • If the subsequent code (such as the @with_dashboard decorator) uses current_user to access the dashboard, and current_user is not properly set with the JWT, the system may fail to find the dashboard, returning a 404.

3. Differences in Initialization of Global Variables (g or current_user)

  • Flask often uses global variables such as g.user or current_user to store the current user. When the call is made via JWT, these variables may not be correctly initialized, resulting in an inability to locate the resources.
  • Potential Solution: Make sure that current_user is correctly set after JWT authentication. You can do this by extending the JWT validation process to explicitly set current_user.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
#bug Bug report
Projects
None yet
Development

No branches or pull requests