superset
superset copied to clipboard
403 Forbidden error when trying to embed a dashboard & view it using a guest user token
I am trying to embed a superset dashboard into a simple front-end application. However, when the embedded dashboard always throws 403: Forbidden
. I am pretty sure I have set the correct permissions and everything. I even tried accessing it using admin
user, still it fails.
How to reproduce the bug
The source for the application can be found here - https://github.com/adimyth/superset_embedded
superset
application
Added the following configuration in superset_config.py
FEATURE_FLAGS = {"ALERT_REPORTS": True, "EMBEDDED_SUPERSET": True}
SESSION_COOKIE_SAMESITE = None
ENABLE_PROXY_FIX = True
PUBLIC_ROLE_LIKE_GAMMA = True
CORS_OPTIONS = {
'supports_credentials': True,
'allow_headers': ['*'],
'resources': ['*'],
'origins': ['http://localhost:8088', 'http://localhost:8000']
}
This gave me the following dashboard it to embed into my application
frontend
application
<head>
<script src="https://unpkg.com/@preset-sdk/embedded"></script>
<style>
iframe {
height: 100%;
width: 100%;
border: none;
}
</style>
</head>
<body>
<p id="dashboard-container"> </p>
<script>
// 1. Request guest_token from our backend, which runs at localhost:8000 by default
async function fetchGuestTokenFromBackend() {
let response = await fetch('http://127.0.0.1:8000/guest-token');
let data = await response.json()
return data
}
// 2. Uses Preset Embedded SDK to embed the dashboard as iFrame
const myDashboard = presetSdk.embedDashboard({
id: "{{ DASHBOARD_ID }}",
supersetDomain: "{{ SUPERSET_DOMAIN }}",
mountPoint: document.getElementById("dashboard-container"),
fetchGuestToken: () => fetchGuestTokenFromBackend(),
dashboardUiConfig: { hideTitle: true, hideChartControls: true}
});
</script>
</body>
The frontend simply calls the /guest-token
from backend and passes it to the presetSdk.embedDashboard
backend
application
The /guest-token
endpoint simply calls 2 methods from the helper (superset.py) file
@app.get("/guest-token")
async def analytics_view(request: Request):
access_token = superset.authenticate()
guest_token = superset.get_guest_token_for_dashboard(
dashboard_id=DASHBOARD_ID, access_token=access_token
)
return guest_token
The helper has 2 methods - authenticate()
which authenticates the superset-admin user & get_guest_token_for_dashboard
which is used to generate access token for the guest user
import json
import os
import requests
from dotenv import load_dotenv
load_dotenv()
URL_AUTH = os.getenv("URL_AUTH")
URL_GUEST_TOKEN = os.getenv("URL_GUEST_TOKEN")
USERNAME = os.getenv("USERNAME")
FIRST_NAMER = os.getenv("FIRST_NAMER")
LAST_NAME = os.getenv("LAST_NAME")
def authenticate(
username="admin",
password="admin",
):
response = requests.post(
"http://localhost:8088/api/v1/security/login",
headers={
"Content-Type": "application/json",
"Accept": "application/json",
"Access-Control-Allow-Origin": "http://localhost:8000",
},
data=json.dumps(
{
"username": username,
"password": password,
"provider": "db"
}
),
)
return response.json()["access_token"]
def get_guest_token_for_dashboard(
dashboard_id,
access_token,
username=USERNAME,
first_name=FIRST_NAMER,
last_name=LAST_NAME,
):
response = requests.post(
URL_GUEST_TOKEN,
data=json.dumps(
{
"user": {
"username": username,
"first_name": first_name,
"last_name": last_name,
},
"resources": [
{
"type": "dashboard",
"id": dashboard_id,
}
],
"rls": [],
}
),
headers={
"Authorization": "Bearer " + access_token,
"Accept": "application/json",
"Content-Type": "application/json",
},
)
return response.json()["token"]
Finally we pass environment variables. Note that the DASHBOARD_ID
matches from the screenshot above. Also note that I am passing superset-admin creds to the embedded dashboard
URL_AUTH=http://localhost:8088/api/v1/security/login
URL_GUEST_TOKEN=http://localhost:8088/api/v1/security/guest_token/
USERNAME=admin
FIRST_NAMER=Supserset
LAST_NAME=Admin
DASHBOARD_ID=b0a944b2-4ab5-47b7-a31d-c3eca4c36397
SUPERSET_DOMAIN=http://localhost:8088/
Error
When loading the frontend application which has the embedded dashboard, it returns -
{"errors": [{"message": "403 Forbidden: You don't have the permission to access the requested resource. It is either read-protected or not readable by the server.", "error_type": "GENERIC_BACKEND_ERROR", "level": "error", "extra": {"issue_codes": [{"code": 1011, "message": "Issue 1011 - Superset encountered an unexpected error."}]}}]}
Expected results
I was expecting to see the actual dashboard in the frontend application
Actual results
What's surprising is that after decoding the guest token, we can clearly see that the user is superset admin & has access to the dashboard 🤯
Environment
(please complete the following information):
- browser type and version: Chrome
- superset version: Built from the latest code as of 29th Nov
- python version: 3.9
- node.js version: NA
- any feature flags active:
{"ALERT_REPORTS": True, "EMBEDDED_SUPERSET": True}
Checklist
Make sure to follow these steps before submitting your issue - thank you!
- [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.
Pinging @lilykuang here, but also sharing this on the Superset Slack workspace in the #embedding-superset
channel in case anyone there has insight. Let me know if you would like help connecting there as well to join the thread.
The first thing to try would be to remove ALL the allowed domains and leave the field empty.
Background: The allowed domains field is incorrectly labeled in the UI. What this field does is to check the "Referer" header and compare it against the values in this field, which is IMO a bit awkward.
Relevant discussion in Slack. https://apache-superset.slack.com/archives/C01EP56QGTS/p1663143592850569?thread_ts=1663071066.037159&cid=C01EP56QGTS
If that doesn't help, the next step is to investigate the Guest permissions configuration.
Hey! Thanks for the response! Yeah, I would like to join the apache superset slack channel to discuss this & other issues further
The first thing to try would be to remove ALL the allowed domains and leave the field empty.
Background: The allowed domains field is incorrectly labeled in the UI. What this field does is to check the "Referer" header and compare it against the values in this field, which is IMO a bit awkward.
Relevant discussion in Slack. https://apache-superset.slack.com/archives/C01EP56QGTS/p1663143592850569?thread_ts=1663071066.037159&cid=C01EP56QGTS
If that doesn't help, the next step is to investigate the Guest permissions configuration.
Adding the following in superset_config.py
seems to be working for me -
# Dashboard embedding
GUEST_ROLE_NAME = "Gamma"
GUEST_TOKEN_JWT_SECRET = "test-guest-secret-change-me"
GUEST_TOKEN_JWT_ALGO = "HS256"
GUEST_TOKEN_HEADER_NAME = "X-GuestToken"
GUEST_TOKEN_JWT_EXP_SECONDS = 300 # 5 minutes
I shall test this out & revert!
Yup! GUEST_ROLE_NAME = "Gamma"
will give the Guest Token all the required (and much more) Superset permissions in order to access the required Superset API functions.
The exact permissions to use for setting up your own custom role in Superset have been recently documented in a Github discussion here: https://github.com/apache/superset/discussions/18814#discussioncomment-4056030
For posterity, just linking a Slack conversation related to this issue here
Hi,
My Dashboard is getting embedding successfully in firefox, but on chrome it is giving error 400 for CSRF.
I have done the below config changes
SESSION_COOKIE_HTTPONLY = True # Prevent cookie from being read by frontend JS? SESSION_COOKIE_SECURE = True # Prevent cookie from being transmitted over non-tls? SESSION_COOKIE_SAMESITE = None # One of [None, 'None', 'Lax', 'Strict'] GUEST_ROLE_NAME = "Gamma" PUBLIC_ROLE_LIKE_GAMMA= True
ENABLE_PROXY_FIX = True ENABLE_CORS = True CORS_OPTIONS: Dict[Any, Any] = { 'supports_credentials': True, 'allow_headers': [''], 'resources':[''], 'origins': ['*'] }
Can anyone help with what am i missing?
I have added a blog to implement RLS with a custom role and limited permissions - https://medium.com/@vishalsadriya1224/embedding-apache-superset-dashboards-in-ruby-on-rails-and-react-a-role-level-security-guide-697da01676af
After set GUEST_ROLE_NAME = 'Gamma' you must to set permission in gama role to grant guest token
Hey! Thanks for the response! Yeah, I would like to join the apache superset slack channel to discuss this & other issues further
I have encountered the same problem. How can I solve it? Configuration is as follows # cors WTF_CSRF_ENABLED = False TALISMAN_ENABLED = False ENABLE_PROXY_FIX = True PUBLIC_ROLE_LIKE_GAMMA = True GUEST_ROLE_NAME = 'Gamma' TALISMAN_ENABLED = False ENABLE_CORS = True CORS_OPTIONS = { "supports_credentials": True, "allow_headers": "", "expose_headers": "", "resources": "", "origins": "" }