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

[SIP-75] Proposal for Embeddable Dashboards #17187

Closed
suddjian opened this issue Oct 21, 2021 · 37 comments
Closed

[SIP-75] Proposal for Embeddable Dashboards #17187

suddjian opened this issue Oct 21, 2021 · 37 comments
Labels
preset-io sip Superset Improvement Proposal

Comments

@suddjian
Copy link
Member

suddjian commented Oct 21, 2021

[SIP-75] Proposal for Embeddable Dashboards

📣 Status

Currently in alpha and under active development. You can activate the feature by turning on the EMBEDDED_SUPERSET feature flag. Embed dashboards into your application using the @superset-ui/embedded-sdk package on npm.

It is not advised to use this feature in production at this time! This will be subject to breaking changes from future PRs, and currently lacks important user-facing functionality.

As we continue development, relevant PRs will mention this issue, and be linked automatically by GitHub below. Check back with this page to stay up-to-date with progress. (you can also subscribe in the issue sidebar)

Motivation

Embedding Superset dashboards into a third-party application is currently only possible if the dashboard is made public, or if users authenticate through Superset. We would like to add a feature to integrate dashboards into third-party applications ("Host Apps") with authentication handled by the Host App.

There should be no need to log in to an integrated dashboard if the user is already logged in to the Host App.

Example use cases:

  • A storefront wants to offer engagement metrics to each of its vendors, integrated into the store management experience.
  • A bank wants to offer a curated financial wellness dashboard to account holders.

Superset currently allows for a variety of valid strategies for granting API access, which can include various types of API Keys, depending on the deployment. Rather than force a specific API Key system into Superset, we’ve designed this spec to be agnostic to those details and depend on the existing security manager features as much as possible.

Proposed Change

  1. Dashboard creator marks their dashboard as "embedded" in the dashboard UI. This design isn’t done yet, but it’ll likely be a modal or a new panel in the edit mode sidebar. Dashboard creator defines a list of allowed referrer domains. When a creator makes a dashboard embedded, documentation is displayed on how to actually embed the dashboard.

  2. Introduce a new endpoint POST /embedded-resource/, which returns an Embed Token that can be used to view the embedded resource. This token will be a JWT containing user info, row-level-security parameters (could be encoded either as a filter expression or as a RLS rule id), and grants of access to certain dashboards. It expires fairly quickly, so it must be refreshed periodically.

  3. A Superset SDK is available for the Host App frontend to use, which will render the embedded dashboard page in an iframe, and pass the Embed Token through to the iframe using postMessage. The Host App will pass a function to the SDK which fetches a new Embed Token, so when the token is about to expire, the SDK can get a new one.

  4. A new page, /embed/dashboard/:id will be used as the url for the iframe. This page acts as a wrapper around the dashboard. It receives the message from Host App containing the Embed Token, validates the sender of the message, and ensures that all requests use the token as auth.

  5. When a request is made using an Embed Token, security manager will use the information in the token to do its checks rather than looking up user, role and RLS info from the database.

Sequence diagram of the embedded dashboard auth flow:

Embedded Dashboard Access Control - Embed Token

New or Changed Public Interfaces

POST /embedded-resource/ endpoint

A new frontend SDK that will expose functions to embed a dashboard into the Host App

New dependencies

flask-jwt-extended for handling jwt embed tokens

Migration Plan and Compatibility

Will create new table embedded_dashboards containing a dashboard id reference, an allowlist of referrers, and any additional configurations of the embedded dashboard

Rejected Alternatives

OAuth login - requires users to sign in to the iframe, lower UX
Generating a url or token in the Host App - better to keep generation on Superset side to avoid exposing implementation details, and so that we don’t have to define a specific API key spec
Passing a signed url instead of an embed token - This makes revoking access more complex

@suddjian suddjian added the sip Superset Improvement Proposal label Oct 21, 2021
@nytai
Copy link
Member

nytai commented Oct 22, 2021

What are the security requirements of the POST /embedded-resource/ endpoint? ie, how do we ensure that this call is coming from a trusted source?

@suddjian
Copy link
Member Author

The usual authentication requirements for a Superset endpoint. If the caller is authenticated with the appropriate role, they are authorized. In Preset's case, a Preset API key would be used.

@suddjian
Copy link
Member Author

An alternative to this is issuing a "signing key" to the dashboard creator when they mark their dashboard as embeddable. Then rather than making an API call to Superset to get the signed url, the Host App would craft the embed url and sign it, and Superset would verify the signature. The signing key acts similarly to an API key but maybe we don't need a separate SIP for that if it's only part of this feature.

@wemod123
Copy link

wemod123 commented Oct 23, 2021

I Had been looking for this feature for a while and almost do not have choice except grafana,
We are embedding powerbi contents for statistical data analysis using powerbi embed API, and want to embed live data viz dashboard comes from direct query(powerbi direct query not powerful enough), if you are designing this kind of embed solution, powerbi embed API design may a good reference

AND I am very looking forward this feature, Great Proposal

@winterrobert
Copy link

This would be a great feature IMO.

@geido
Copy link
Member

geido commented Oct 25, 2021

It's definitely a +1. Great feature to have

@michael-s-molina
Copy link
Member

michael-s-molina commented Oct 26, 2021

@suddjian Thanks for writing this SIP. I have some broad questions to help with the SIP definition/scope:

Do we have any plans to support cross-window communication? One common requirement is to allow interactions in the host app to impact the embedded dashboard or the other way around. Some examples:

  • The host app already has a filter bar and wishes to apply these filters to the dashboard
  • A user selects a slice of a pie chart and the host app shows a detailed report about the data

Are we going to provide any sort of client/toolkit to our users to help with the embedding process?

How are we going to handle URL and JWT expiration? Are we going to have specific endpoints for that?

Should we also add a "How to Embed Superset" wiki page as one of the deliverables of this SIP?

@suddjian
Copy link
Member Author

suddjian commented Oct 27, 2021

Thanks for the feedback @michael-s-molina!

I've updated some of the implementation details to a flow that better supports expiration and revoking tokens - under this flow, refresh is triggered by the frontend sdk and access can be revoked by not refreshing the token.

Adding a wiki page is a great idea!

Adding cross-window messaging is definitely possible, but out of scope for this SIP. It could certainly be added later and seems like a great idea to me!

@suddjian
Copy link
Member Author

This SIP is approved! Thanks for the discussion, all. Closing it now.

@nytai
Copy link
Member

nytai commented Nov 12, 2021

This should be considered as part of this work
#17371

@gokhansari
Copy link

Thank you @suddjian for the proposal and your commits. It'll be life saver.
There is one thing that I would like to mention before you guys finalizing this feature.

Custom Url Parameters feature is one of the handy functionalities and It is not dispensable. When I check the master branch, for now It seems Embedded SDK only takes dashboardID parameter.
To be able to use {{ url_param('custom_variable') }} and reference It in dataset SQL queries, I think there should be an other SDK parameter like url_params: string as we all used to use a lot of url parameters.

Example from doc:

www.example.com/superset/sqllab?countrycode=ES&language=EN

SELECT count(*)
FROM ORDERS
WHERE country_code = '{{ url_param('countrycode') }}' AND language_code = '{{ url_param('language') }}' 

@suddjian
Copy link
Member Author

Thanks for the comment, @gokhansari. Rather than using URL parameters, we can use the message channel shared by the SDK and Superset-within-iframe to accomplish this.

@san-chang
Copy link

Hi @suddjian I'm trying to use the embedded SDK and integration the SDK into my project like this:

embedDashboard({
      id: "3", // given by the Superset embedding UI
      supersetDomain: "http://localhost:62421/superset",
      mountPoint: document.getElementById("superset-container"), // any html element that can contain an iframe
      fetchGuestToken: () => {setTimeout(() => "a token from swagger ui", 10) },
      debug: true,
    });

but the sdk inject an url http://localhost:62421/superset/dashboard/3/embedded in iframe which showing "page not found" am I doing wrong?

image

@san-chang
Copy link

I'm using the kubernetes deployment, and already set the EMBEDDED_SUPERSET to True in pythonpath/superset_config.py by modify the configOverrides value in my values.yaml

image

image

@san-chang
Copy link

Updates, I'm now able to use the embedded sdk by these settings:
image
image

@chrisjung-dev
Copy link

Updates, I'm now able to use the embedded sdk by these settings: image

Where did you put these values, @idears-org? I run superset from docker

@san-chang
Copy link

@campino2k
I'm using helm to deploy on my cluster, and these setting are placed in my values.yaml

# A dictionary of overrides to append at the end of superset_config.py - the name does not matter
# WARNING: the order is not guaranteed
configOverrides: 
  override:: |
    FEATURE_FLAGS = {
      "EMBEDDED_SUPERSET": True
    }
    ENABLE_PROXY_FIX = True
    PUBLIC_ROLE_LIKE = "Gamma"
    WTF_CSRF_ENABLED = False

It will automatically set the superset_config.py
There should be a way to set the same by using docker.

@chrisjung-dev
Copy link

Thanks. I inspected the config.py and found it also uses Environment Variables as overrides, so:

grafik

works like a charm.

@suddjian
Copy link
Member Author

suddjian commented Mar 30, 2022

Heads up for anyone using this feature. Some time soon (in a follow-up PR after #19364 is merged) there will be an update pushing out a breaking change.

Instead of using the Dashboard id, and any dashboard being embeddable via the sdk, dashboards will need to be configured to be embedded, and your app will need to use an Embedded Dashboard uuid. That uuid can be found through the dashboard's embed configuration screen. See that PR I mentioned for more info on that.

I'll post here again when that update goes out. I don't anticipate further breaking changes after this update.

@wemod123
Copy link

wemod123 commented May 16, 2022

Thanks. I inspected the config.py and found it also uses Environment Variables as overrides, so:

grafik

works like a charm.

@campino2k, Following the embed guideline I had always been getting 403 Error when embed a dashboard, Have you go through this feature E2E anyway?

@shenrie
Copy link

shenrie commented Jun 11, 2022

@suddjian I am curious about this new embedded feature. Does it require that all users are precreated on the SS application before a dashboard can be accessed from an iframe in the host app?

I have successfully embedded SS reports in our app using standard OAuth SSO. I had to write some custom python code in the values.yaml which the helm install injects into superset_config.py, but after that it works like a charm and does not require the user to be precreated in SS or to login to view reports.

I use a shared OAuth service provider, Keycloak in my case, which our app uses to request the JWT access token and operates no differently than when using Google or some other authentication provider for SSO capability. I simply have to pass the signed JWT as an access token parameter with each dashboard URL request, which of course contain the user's scoped info and appropriate roles, and our dashboards are protected using the SS dashboard role-based access feature. New users are created on the fly and SS roles are updated from the JWT token as needed, so very minimal ongoing coordination needed between our app and the SS app as our user base grows, which was an important requirement for our use case. We cannot precreate or have to manage users in SS beyond standard OAuth capacity.

I have attached a screen capture of our app showing an example SS report embedded in an iframe. We are still iterating on the UI and style of the SS reports for a better experience, but this demonstrates the point.

image

@fstn
Copy link

fstn commented Jun 25, 2022

Could we filter embedded data by userid?
I mean I would like to embed superset into my application and would like to display only data that are relative to the current login user.

@weitang2022
Copy link

weitang2022 commented Jul 20, 2022

Hi, I am able to embed superset into our website. It works fine as read-only dashboard. However, when I click "Enter fullscreen", or "Edit Dashboard" -> "Discard", it shows this error:

{"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."}]}}]}

"Edit chart" shows a blank page.

"CREATE NEW CHART" gives this error in the browser console:

Blocked opening 'https://superset.example.com/chart/add?dashboard_id=5' in a new window because the request was made in a sandboxed frame whose 'allow-popups' permission is not set.

I have given the guest role "Admin" and the rls is empty when getting the guest token. Most other menu links seem to work.

Please keep up the great work! Hopefully, these issue can be resolved soon.

@shenrie Do you mind sharing your changes to superset_config.py? I am interested to see how to hide the superset top navigation menu.

@cwegener
Copy link
Contributor

Could we filter embedded data by userid? I mean I would like to embed superset into my application and would like to display only data that are relative to the current login user.

I think it should work by using Jinja templating. There is a {{ current_user_id() }} macro available which should work just fine with the Guest Token that is used by the Embedded dashboard. https://superset.apache.org/docs/installation/sql-templating

@faycal-merouane
Copy link

Hi, I am able to embed superset into our website. It works fine as read-only dashboard. However, when I click "Enter fullscreen", or "Edit Dashboard" -> "Discard", it shows this error:

{"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."}]}}]}

"Edit chart" shows a blank page.

"CREATE NEW CHART" gives this error in the browser console:

Blocked opening 'https://superset.example.com/chart/add?dashboard_id=5' in a new window because the request was made in a sandboxed frame whose 'allow-popups' permission is not set.

I have given the guest role "Admin" and the rls is empty when getting the guest token. Most other menu links seem to work.

Please keep up the great work! Hopefully, these issue can be resolved soon.

@shenrie Do you mind sharing your changes to superset_config.py? I am interested to see how to hide the superset top navigation menu.

hey, can you provide me documentation to embed an iframe in my web application ? I'm trying to use the jwt from the security api but no result .

@weitang2022
Copy link

@faycal-merouane Here is an online instruction you can try:
https://medium.com/@khushbu.adav/embedding-superset-dashboards-in-your-react-application-7f282e3dbd88

@phamhuong7420
Copy link

phamhuong7420 commented Dec 1, 2022

image

I don't see this in my workspace. thanks for any help

@Gokuyamatos12
Copy link

Gokuyamatos12 commented Dec 30, 2022

image

I don't see this in my workspace. thanks for any help

@phamhuong7420 I think you need to set ["EMBEDDED_SUPERSET": True] in your configuration file for superset under default feature flags for the embed feature to be shown.

@benjaminlhai
Copy link

Regarding URL Parameters for template dashboards.

#17187 (comment)

Hi @suddjian

Thanks for the comment, @gokhansari. Rather than using URL parameters, we can use the message channel shared by the SDK and Superset-within-iframe to accomplish this.

Could you expand upon this or provide an example of how this might be achieved? I've looked all over for an example and can't find anything of value

@temuujinmo4
Copy link

temuujinmo4 commented Jan 20, 2023

Hi. returned {"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."}]}}]}

config on
EMBEDDED_SUPERSET": True

@cwegener
Copy link
Contributor

Hi. returned {"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."}]}}]}

config on EMBEDDED_SUPERSET": True

I think this discussion will help you. You need to configure a Superset Role on your Superset server that will be used to apply permissions for your embedded Guest Token:
#18814 (reply in thread)

@temuujinmo4
Copy link

Hi guys HELP ME. 403 Forbidden: You don't have the permission to access the requested resource. It is either read-protected or not readable by the server

My backend superset_conifg.py
PUBLIC_ROLE_LIKE_GAMMA = True
GUEST_ROLE_NAME = "Gamma"SQLALCHEMY_ECHO = True
SESSION_COOKIE_SAMESITE = None
ENABLE_PROXY_FIX = True
FEATURE_FLAGS = {
"EMBEDDED_SUPERSET": True
}

I got guest token, My Frontend:

<iframe id="superSet" width="100%" height="100%"></iframe>

getToken = () => {
$.ajax({
'type': 'POST',
'url': "**************",
'data': {},
'headers': {
'X-CSRFToken': "{{csrf_token}}",
},
'dataType': 'json',
'success': function (data) {
getGuestToken(data.access_token, data.refresh_token)
},
});
}

getGuestToken = (access_token, refresh_token) => {
    $.ajax({
        'type': 'POST',
        'url': "******",
        'data': {
            'access_token': access_token,
            'refresh_token': refresh_token
        },
        'headers': {
            'X-CSRFToken': "{{csrf_token}}",
        },
        'dataType': 'json',
        'success': function (data) {
            supersetEmbeddedSdk.embedDashboard({
                id: "*******************************",
                supersetDomain: "********,
                mountPoint: document.getElementById("superSet"),
                fetchGuestToken: () => data['token'],
            });
        },
    });
}

@chandrasekharjandhyam
Copy link

chandrasekharjandhyam commented Jul 10, 2023

@suddjian I am curious about this new embedded feature. Does it require that all users are precreated on the SS application before a dashboard can be accessed from an iframe in the host app?

I have successfully embedded SS reports in our app using standard OAuth SSO. I had to write some custom python code in the values.yaml which the helm install injects into superset_config.py, but after that it works like a charm and does not require the user to be precreated in SS or to login to view reports.

I use a shared OAuth service provider, Keycloak in my case, which our app uses to request the JWT access token and operates no differently than when using Google or some other authentication provider for SSO capability. I simply have to pass the signed JWT as an access token parameter with each dashboard URL request, which of course contain the user's scoped info and appropriate roles, and our dashboards are protected using the SS dashboard role-based access feature. New users are created on the fly and SS roles are updated from the JWT token as needed, so very minimal ongoing coordination needed between our app and the SS app as our user base grows, which was an important requirement for our use case. We cannot precreate or have to manage users in SS beyond standard OAuth capacity.

I have attached a screen capture of our app showing an example SS report embedded in an iframe. We are still iterating on the UI and style of the SS reports for a better experience, but this demonstrates the point.

image

Hi @shenrie ,

could you please provide the custom code that you have developed to use SSO and get JWT token for embedding, from official rest api documentation, login API does not support SSO based authentication.

@andrecamara
Copy link

Any updates on this? Does anyone have a reference on how to get the tokens for embedding a dashboard, when oauth is enabled?
Thanks in advance

@okayakdeniz
Copy link

Thanks for the comment, @gokhansari. Rather than using URL parameters, we can use the message channel shared by the SDK and Superset-within-iframe to accomplish this.

Could you please share about an introduction how to we use the message channel?

@ameen7626
Copy link

ameen7626 commented May 15, 2024

Hi everyone, so I have a different use case, I'm trying to embed a dashboard(lets say it is landing page) which contains link(by writing markdown in the description) to other dashboards so the landing page is getting embedded correctly but the issue I faced is when I try to access the other dashboard it is causing error the error is occurring at these api's /api/v1/dashboard

I have attached the error log, is there a option to provide multiple dashboard id to a guest token
const guest_token_body = JSON.stringify({
"resources": [
{
"type": "dashboard",
"id": "dashboardId1",
},
{
"type": "dashboard",
"id": "dashboarid2",
}
],
"rls": [],
"user": {
"username": "report-viewer",
"first_name": "report-viewer",
"last_name": "report-viewer",
}
});
I have tried adding like this but it's not working
image

Screen.sharing.-.2024-05-14.8_28_59.PM.mp4

@rusackas
Copy link
Member

Hi all... this SIP was passed and implemented forever ago. It's not really sustainable to pile on with more questions/bugs/requests, so I'm going to (politely as possible, I thank you all!) lock this thread, and encourage anyone seeing this to open either a new issue or discussion. Thanks!

@apache apache locked and limited conversation to collaborators May 15, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
preset-io sip Superset Improvement Proposal
Projects
Status: Implemented / Done
Development

No branches or pull requests