Bug description
DatabaseRestApi.get_list and DatasetRestApi.get_list return count:0 for JWT-authenticated Admin users in 6.0.1
Labels: bug, needs-triage
Bug description
When authenticating via the REST API using a JWT Bearer token (POST /api/v1/security/login), the /api/v1/database/ and /api/v1/dataset/ list endpoints always return count: 0 and an empty result array — even for users with the Admin role who have all_database_access and all_datasource_access permissions, and even when the objects exist in the metadata database.
The same user can see all objects correctly in the Superset UI (browser session). The issue is specific to JWT Bearer token authentication used by programmatic clients (MCP servers, provisioning scripts, FastAPI backends, etc.).
Steps to reproduce
- Start Superset 6.0.1 with default config (
WTF_CSRF_ENABLED = True)
- Create at least one database connection and one dataset via the UI
- Authenticate via the REST API:
import requests
resp = requests.post(
"http://localhost:8088/api/v1/security/login",
json={"username": "admin", "password": "admin", "provider": "db", "refresh": True}
)
token = resp.json()["access_token"]
headers = {"Authorization": f"Bearer {token}"}
- Call the database list endpoint:
r = requests.get("http://localhost:8088/api/v1/database/", headers=headers)
print(r.status_code, r.json()["count"])
# Output: 200 0
- Call the dataset list endpoint:
r = requests.get("http://localhost:8088/api/v1/dataset/", headers=headers)
print(r.status_code, r.json()["count"])
# Output: 200 0
Both return 200 OK with count: 0 and result: [], despite objects existing in the DB.
Root cause (identified)
DatabaseFilter.apply() in superset/databases/filters.py calls security_manager.can_access_all_databases(), which internally calls self.can_access("all_database_access", "all_database_access"), which reads g.user.
In Superset 6.0.1, when a request is authenticated via JWT Bearer token, flask-jwt-extended's user_lookup_loader sets g.user correctly — but only after the FAB before_request hook runs, which resets g.user to current_user (the Flask-Login anonymous proxy). As a result, g.user.is_anonymous returns True inside DatabaseFilter.apply(), causing can_access_all_databases() to return False.
When can_access_all_databases() returns False, the filter falls through to:
return query.filter(
or_(
self.model.perm.in_(database_perms),
self.model.database_name.in_(database_names),
)
)
However, the perm column was removed from the dbs table in Superset 6.x. This causes a silent failure that results in an empty result set rather than raising an exception.
Superset logs showing the silent failure:
ERROR:superset.views.error_handling:'encrypted_extra'
KeyError: 'encrypted_extra'
This secondary error occurs on GET /api/v1/database/_info because the extra JSON field in older dbs rows is missing the encrypted_extra key expected by 6.0.1.
Workaround
Add a before_request hook in superset_config.py that explicitly sets g.user from the JWT token before FAB's hook can reset it:
def FLASK_APP_MUTATOR(app):
from flask import g, request
@app.before_request
def _set_user_from_jwt():
auth_header = request.headers.get("Authorization", "")
if auth_header.startswith("Bearer "):
token = auth_header.split(" ", 1)[1]
try:
from flask_jwt_extended import decode_token
decoded = decode_token(token)
identity = decoded.get("sub")
if identity:
from superset import security_manager
user = security_manager.load_user(identity)
if user and user.is_active:
g.user = user
except Exception:
pass
After applying this workaround, both endpoints return the correct count and result.
Expected behavior
GET /api/v1/database/ and GET /api/v1/dataset/ should return all objects visible to the authenticated user when using JWT Bearer token authentication, consistent with browser session behavior.
Actual behavior
Both endpoints return count: 0 and result: [] for JWT-authenticated requests, regardless of the user's role or permissions.
Superset version
6.0.1
Python version
3.10
Browser
N/A (API client)
Additional context
- The
dbs table perm column referenced in DatabaseFilter was removed in 6.x. The filter's fallback branch silently fails when this column is missing.
- A secondary issue:
dbs rows created before 6.0.1 may be missing the encrypted_extra key in the extra JSON field, causing KeyError: 'encrypted_extra' on GET /api/v1/database/_info. Fix:
UPDATE dbs
SET extra = (extra::jsonb || '{"encrypted_extra": ""}')::text
WHERE extra NOT LIKE '%encrypted_extra%';
- Both issues compound to make the REST API completely non-functional for programmatic clients (MCP servers, provisioning scripts, FastAPI backends) that use JWT auth.
Checklist
Screenshots/recordings
No response
Superset version
master / latest-dev
Python version
3.10
Node version
I don't know
Browser
Not applicable
Additional context
No response
Checklist
Bug description
DatabaseRestApi.get_list and DatasetRestApi.get_list return count:0 for JWT-authenticated Admin users in 6.0.1
Labels:
bug,needs-triageBug description
When authenticating via the REST API using a JWT Bearer token (
POST /api/v1/security/login), the/api/v1/database/and/api/v1/dataset/list endpoints always returncount: 0and an emptyresultarray — even for users with theAdminrole who haveall_database_accessandall_datasource_accesspermissions, and even when the objects exist in the metadata database.The same user can see all objects correctly in the Superset UI (browser session). The issue is specific to JWT Bearer token authentication used by programmatic clients (MCP servers, provisioning scripts, FastAPI backends, etc.).
Steps to reproduce
WTF_CSRF_ENABLED = True)Both return
200 OKwithcount: 0andresult: [], despite objects existing in the DB.Root cause (identified)
DatabaseFilter.apply()insuperset/databases/filters.pycallssecurity_manager.can_access_all_databases(), which internally callsself.can_access("all_database_access", "all_database_access"), which readsg.user.In Superset 6.0.1, when a request is authenticated via JWT Bearer token,
flask-jwt-extended'suser_lookup_loadersetsg.usercorrectly — but only after the FABbefore_requesthook runs, which resetsg.usertocurrent_user(the Flask-Login anonymous proxy). As a result,g.user.is_anonymousreturnsTrueinsideDatabaseFilter.apply(), causingcan_access_all_databases()to returnFalse.When
can_access_all_databases()returnsFalse, the filter falls through to:However, the
permcolumn was removed from thedbstable in Superset 6.x. This causes a silent failure that results in an empty result set rather than raising an exception.Superset logs showing the silent failure:
This secondary error occurs on
GET /api/v1/database/_infobecause theextraJSON field in olderdbsrows is missing theencrypted_extrakey expected by 6.0.1.Workaround
Add a
before_requesthook insuperset_config.pythat explicitly setsg.userfrom the JWT token before FAB's hook can reset it:After applying this workaround, both endpoints return the correct
countandresult.Expected behavior
GET /api/v1/database/andGET /api/v1/dataset/should return all objects visible to the authenticated user when using JWT Bearer token authentication, consistent with browser session behavior.Actual behavior
Both endpoints return
count: 0andresult: []for JWT-authenticated requests, regardless of the user's role or permissions.Superset version
6.0.1
Python version
3.10
Browser
N/A (API client)
Additional context
dbstablepermcolumn referenced inDatabaseFilterwas removed in 6.x. The filter's fallback branch silently fails when this column is missing.dbsrows created before 6.0.1 may be missing theencrypted_extrakey in theextraJSON field, causingKeyError: 'encrypted_extra'onGET /api/v1/database/_info. Fix:Checklist
Screenshots/recordings
No response
Superset version
master / latest-dev
Python version
3.10
Node version
I don't know
Browser
Not applicable
Additional context
No response
Checklist