I'm attempting to integrate a Superset dashboard into our angular application(V16). Unfortunately, when embedding the dashboard, it consistently returns a "403: Forbidden" error, even when using an admin account.
Superset Application Config
FEATURE_FLAGS = {
"ALERT_REPORTS": True,
"EMBEDDED_SUPERSET": True,
},
CORS_OPTIONS = {
"supports_credentials": True,
"origins": ["http://localhost:4200"],
"allow_headers": ["*"],
"resources":["*"],
},
PUBLIC_ROLE_LIKE_GAMMA = True
SESSION_COOKIE_SAMESITE = None
ENABLE_PROXY_FIX = True
GUEST_ROLE_NAME = "Gamma"`
Frontend Application
``` embedDashboard({ id: '', // given by the Superset embedding UI supersetDomain: '', mountPoint: document.getElementById('my-superset-container'), // html element in which iframe render fetchGuestToken: () => this.fetchSupersetData(),
debug: true,
});`
async fetchSupersetData() { try { const apiUrl = 'http://localhost:8081/users/me/superset-token';
const response = await fetch(apiUrl, {
method: 'GET'
});
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
const data = await response.text();
return data;
} catch (error) {
console.error('Error fetching Superset data:', error);
throw error;
}
}
The frontend simply calls the /superset-token from backend and passes it to the embedDashboard
### Backend Application
public String loginAndGetAccessToken() { var restTemplate = solutionsFactory.createNewRestTemplate();
String supersetUrl = "https://bi.cargoai.co/api/v1/security/login";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
var username = "";
var password = "";
String requestBody = "{\"username\": \"" + username + "\", \"password\": \"" + password + "\", \"provider\": \"db\", \"refresh\": true}";
HttpEntity<String> entity = new HttpEntity<>(requestBody, headers);
ResponseEntity<Superset> response = restTemplate.postForEntity(supersetUrl, entity, Superset.class);
if (response.getStatusCode().is2xxSuccessful()) {
return response.getBody().getAccessToken();
} else {
throw new RuntimeException("Authentication failed");
}
}
public CsrfTokenAndCookie getCsrfToken(String accessToken) {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + accessToken);
ResponseEntity<CsrfToken> response = restTemplate.exchange(SUPRESET_BASE + "/api/v1/security/csrf_token/", HttpMethod.GET, new HttpEntity<>(headers), CsrfToken.class);
return new CsrfTokenAndCookie(response.getBody().getResult(), response.getHeaders().getFirst("Set-Cookie"));
}
public String createGuestToken(String accessToken, String csrfToken, String cookies) {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + accessToken);
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Referer", SUPRESET_BASE + "/api/v1/security/csrf_token/");
headers.set("X-CSRFToken", csrfToken);
headers.set("Cookie", cookies);
String guestTokenEndpoint = SUPRESET_BASE + "/api/v1/security/guest_token/";
String dashboardId = "";
String requestBody = "{\"user\":{\"username\":\\",\"first_name\":\"\",\"last_name\":\"\"},\"resources\":[{\"type\":\"dashboard\",\"id\":\"" + dashboardId + "\"}],\"rls\":[]}";
HttpEntity<String> request = new HttpEntity<>(requestBody, headers);
ResponseEntity<GuestToken> response = restTemplate.exchange(guestTokenEndpoint, HttpMethod.POST, request, GuestToken.class);
return response.getBody().getToken();
}
it has 3 methods:
- loginAndGetAccessToken - Login user the admin creds and get access token
- getCsrfToken - To get csrf token
- createGuestToken - To get guest token(we also tried with admin account on the place of guest account)
**Controller**
@GetMapping("me/superset-token")
public ResponseEntity<String> getGuestToken() {
String accessToken = userService.loginAndGetAccessToken();
CsrfTokenAndCookie csrfToken = userService.getCsrfToken(accessToken);
String guestToken = userService.createGuestToken(accessToken, csrfToken.getCsrfToken(), csrfToken.getCookies());
return ResponseEntity.ok(guestToken);
}
### 403 Error
I'm able to fetch the guest token but 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'm expecting to see actual superset dashboard on my angular application
### Actual results
403 error when fetching this API `GET /embedded/<dashboard-id>`
`{"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."}]}}]}`
#### Screenshots
403 Error

Request & Response Headers

### Environment
(please complete the following information):
- browser type and version: Chrome
- superset version: ^0.1.0-alpha.10
- Java version: V17
- Superset version: 3.0.1 (latest)
- node.js version: V18
- any feature flags active: {
"ALERT_REPORTS": True,
"THUMBNAILS": True,
"THUMBNAILS_SQLA_LISTENERS": True,
'PRESTO_EXPAND_DATA': True,
"DASHBOARD_RBAC": True,
"LISTVIEWS_DEFAULT_CARD_VIEW": True,
"HORIZONTAL_FILTER_BAR": True,
"TAGGING_SYSTEM": 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.
### Additional information
We found this similar issue https://github.com/apache/superset/issues/22258 but the solution given didn't help. not sure what we are missing here.
**Comment From: pongsathorn-ph**
@MickJerin12 Please follow this config maybe resolve your problem.
**Maybe you forget config about TALISMAN**
**superset_config.py**
```python
ALLOW_ORIGINS = ['YOUR_BACKEND_URL','http://localhost:4200']
# Cross Origin Config
ENABLE_CORS = True
CORS_OPTIONS = {
'supports_credentials': True,
'allow_headers': ['*'],
'resources':['*'],
'origins': ALLOW_ORIGINS
}
# CSRF Config
WTF_CSRF_ENABLED = True
WTF_CSRF_TIME_LIMIT = 300 # A CSRF token that expires in 5 minutes
# Talisman Config
TALISMAN_ENABLED = True
TALISMAN_CONFIG = {
"content_security_policy": {
"frame-ancestors": ALLOW_ORIGINS
},
"force_https": False,
"force_https_permanent": False,
"frame_options": "ALLOWFROM",
"frame_options_allow_from": "*"
}
# Dashboard embedding
GUEST_ROLE_NAME = "YOUR_GUEST_ROLE_NAME"
GUEST_TOKEN_JWT_SECRET = "CUSTOM_GUEST_TOKEN_JWT_SECRET"
GUEST_TOKEN_JWT_EXP_SECONDS = 300 # 5 minutes
app.component.html
<div id="my-superset-container" style="height: 100vh;"></div>
app.component.ts
async ngOnInit(): Promise<void> {
const myDashboard = embedDashboard({
id: 'YOUR_DASHBOARD_EMBED_ID',
supersetDomain: 'YOUR_SUPERSET_DOMAIN',
mountPoint: document.getElementById('my-superset-container') as HTMLElement, // any html element that can contain an iframe
fetchGuestToken: async () => await this.supersetService.getGuestToken(),
dashboardUiConfig: {
// dashboard UI config: hideTitle, hideTab, hideChartControls, filters.visible, filters.expanded (optional)
hideTitle: false,
filters: {
expanded: false,
},
},
});
myDashboard.then((data) => {
const iframe = document.querySelector('iframe') as HTMLIFrameElement;
if (iframe !== null) {
iframe.style.width = '100%';
iframe.style.height = '100%';
iframe.style.border = 'none';
}
});
}
Comment From: Lau08
This config worked for me:
FEATURE_FLAGS = { "ALERT_REPORTS": True, "EMBEDDED_SUPERSET": True }
GUEST_ROLE_NAME = "Gamma"
GUEST_TOKEN_JWT_SECRET = "your-secret-here"
GUEST_TOKEN_JWT_AUDIENCE = "audi"
TALISMAN_ENABLED = False
OVERRIDE_HTTP_HEADERS = {
"X-Frame-Options": "ALLOWALL",
"Content-Security-Policy": "frame-ancestors 'self' http://localhost:5173"
}
ENABLE_CORS = True
And in the dashboard to embed in superset, copy the URL like this: http://localhost:5173 with the http://
This worked using a guest token, payload to get it from the Svelte application in my case, and using the SDK:
const payload = {
user: {
first_name: 'Embedded first_name',
last_name: 'Embedded last_name',
username: 'Embedded_user'
},
resources: [
{
id: SUPERSET_DASHBOARD_ID, //your dashboard id
type: 'dashboard'
}
],
rls_rules: [],
aud: "audi", // same as GUEST_TOKEN_JWT_AUDIENCE in superset
type: 'guest'
};
const token = encodeJwt(payload, SUPERSET_GUEST_SECRET);