GoodData.UI: SAML SSO Example
This article will guide you through a SAML SSO authentication example within a GoodData.UI application. We will use GoodData’s Accelerator Toolkit along with the Auth0 authentication service.
This is a simple, happy-path example to get you a general idea of how SSO works and how it can be implemented. There are many 3rd party SAML authentication services available and an infinite number of different use-cases. If you are unsure or get stuck, ask here in the Gooddata Community, or reach out to our support@gooddata.com.
If you already have an SSO working on your domain and can log in using the “Use organization login” button, you can skip forward to the GoodData.UI chapter to see the React/JavaScript implementation.
Before We Begin
Single sign-on (SSO) is an authentication method that enables users to securely authenticate with multiple applications and websites by using just one set of credentials.
GoodData supports two types of SSO authentication: a proprietary authentication mechanism based on PGP keys or an authentication mechanism based on SAML 2.0. PGP SSO is usually a better fit when you store your users within your own database, while SAML SSO is handy when you already have a 3rd party identity provider where your users are stored (like Auth0, Okta, or Salesforce).
This article is dedicated to the SAML use case. To learn more about PGP, please read the GoodData.UI: PGP SSO Example article, and check out this Single Sign-On Overview in our docs.
For the SAML SSO use case, we have to choose between a service-provider-initiated scenario and an identity-provider-initiated scenario. To learn the difference, read the SAML SSO with GoodData - Supported Scenarios.
This article describes a service-provider-initiated scenario, where GoodData is the service provider, and Auth0 is the identity provider.
Prerequisites
We need a GoodData domain, and so we will use https://zajic.on.gooddata.com. We will also need a workspace, preferably with some data in it, so that we can render an insight to prove that our SSO authentication works. Any data and any insight will do.
For SSO implementation in React/JavaScript, we will also need some basic knowledge of front-end development, and npm/Node and yarn tools installed on our machines.
We will also need to sign up for Auth0 as we will use the Auth0 SAML authentication service as our SSO provider.
Let’s Get Started
First, we need to set up an SSO on our hosted GoodData domain https://zajic.on.gooddata.com. When we have a working SSO login, we will then implement a trivial GoodData.UI application that utilizes this SSO authentication setup.
To get started with an SSO domain setup, please follow Setting up Auth0 Single Sign-On in GoodData’s official documentation. For other supported SAML authentication services, like Okta or Salesforce, see Supported SAML Authentication Services.
Once you are able to log in using the “Use organization login” button, you can proceed to the implementation of a custom GoodData.UI application. If the “Use organization login” button is not working for you, try the following troubleshooting tips.
Useful Tips & Troubleshooting
-
There is a difference between a domain admin and a workspace admin. Workspace admin is not good enough to configure a working SSO; you need domain admin access or help from support@gooddata.com to succeed. (SSO authentication then, of course, works for all correctly configured users, not only for admins.)
-
Make sure that you have some users defined inside the Auth0 service. The user you used to sign up for the Auth0 SSO provider is not enough, and you need to have some users defined inside your Auth0 account.
-
The user must be known to both the identity provider (in our case, Auth0) and the same user must also be invited to the GoodData workspace.
-
Each user must have the correct SSO provider assigned. See Setting up Auth0 Single Sign-On and API Reference - Modify authentication settings for a user. In GoodData’s Accelerator Toolkit app, it could look something like this. Keep in mind that you need to do this for each user only once; the following code is for reference only. This will be most likely handled by the user provisioning brick anyway.
const { sdk: backend } = useBackend(); backend .xhr .put('/gdc/account/profile/<profile-id>', { body: { "accountSetting": { "firstName": "Jiri", "lastName": "Zajic", "ssoProvider": "auth0-zajic.on.gooddata.com", "authenticationModes": [ "SSO", "PASSWORD" // optional for debugging ] } } }) .then(data => data.getData()) .then(result => console.log(result));
-
You may experience an error “400 BAD REQUEST We cannot log in user because the HTTP request doesn’t contain a valid SAML response.” This means that there is a missing signature in the SAML communication. Upon your request, our support team can skip the signature check on GoodData’s side so that you are able to move forward. If you wish to skip the signature check, please reach out to support@gooddata.com.
Whenever you need to report an error to our support, please copy-paste both the error ID and request ID whenever applicable. This will help resolve issues much faster.
GoodData.UI
We now have a working SSO on our domain, so let’s see how we can implement a custom front-end application using GoodData.UI with the SSO authentication logic in JavaScript.
We will first log in using the standard username/password credentials to create what could be a custom analytics application. Then, we will log out and finally implement the SSO login. The basic username/password access will remain the same and continue to work even once the SSO authentication is in place.
Username/Password Credentials
Let’s use GoodData’s Accelerator Toolkit to get started quickly. Assuming you already have npm/Node and yarn installed on your machine, simply run the following in your command line:
npx @gooddata/create-gooddata-react-app
? What is your application name? cgra-sso
? What is your hostname? I have a custom hostname
? Insert your hostname zajic.on.gooddata.com
? What is your application desired flavor? JavaScript
cd cgra-sso
yarn
start
You should now be running your GoodData.UI application, and you should see its Welcome screen. Navigate to src/constants.js
and specify your desired workspace identifier. Then navigate to src/routes/AppRouter.js
and delete the line that starts with /* DELETE THIS LINE */
to prevent any unnecessary redirects to the Welcome screen in the future.
You can now log in using standard username/password credentials to the workspace. This is not an SSO login yet, but let’s do this so that we can render an insight. We will then log out, implement the proper SSO login, and see if the insight still renders correctly.
After logging in, navigate to src/routes/Home.js
and add a random insight just to be able to tell if the execution works. In this example, we will add a Headline component:
import React from "react";
import { Headline } from "@gooddata/sdk-ui-charts";
import { newMeasure } from "@gooddata/sdk-model";
import Page from "../components/Page";
const Home = () => {
return (
<Page>
<Headline primaryMeasure={newMeasure('<metric-identifier>')} />
</Page>
);
};
export default Home;
If you can see the Headline component correctly rendered, let’s log out and move onto the SSO implementation.
SSO Authentication
The steps required to initiate the SSO login from our custom GoodData.UI application are described in Set Up Authentication and Single Sign-On, and in practice, it can look something like this:
const { sdk: backend } = useBackend();
const relayState = 'https://deployed-analytics-app.com/';
useEffect(() => {
backend.user.isLoggedIn().then(isLogged => {
if (isLogged) {
// proceed with rendering the rest of the app
} else {
// redirect to SSO provider login url
backend.user.initiateSamlSso(relayState);
}
});
}, [backend]);
Note that there are also
backend.user.login()
andbackend.user.loginSso()
methods in the SDK, but those only apply to the PGP SSO scenario, and they do not work with SAML SSO.
Unfortunately, a service-provider-initiated SSO cannot be tested when using a development proxy on a localhost due to proxy limitations. So, in order to make sure our code works, you must deploy your custom analytics application to a hosting of your choice and try it once deployed.
When you deploy your custom analytics application, do not forget to deal with cross-origin issues (CORS). This means that the domain from which you want to enable API calls must be listed as an allowed origin for your domain. To make your domain an allowed origin, read https://sdk.gooddata.com/gooddata-ui/docs/platform_cors.html and use the API for adding domains allowed for CORS access. In the code, it can look something like this (please note that you only need to do this once):
backend
.xhr
.put('/gdc/domains/zajic/securitySettings/allowedOrigins', {
body: {
"allowedOrigins": {
"items": [
"https://deployed-analytics-app.com"
]
}
}
})
.then(data => data.getData())
.then(result => console.log(result));
With the above, once your users visit https://deployed-analytics-app.com, they will be redirected to https://zajic.us.auth0.com/u/login?state=hKFo2…, and once they successfully authenticate with Auth0, they will be redirected back to https://deployed-analytics-app.com.
Logout
When implementing the logout functionality, there are typically two layers to consider: the application session layer and the Auth0 session layer.
To destroy the application session, you can simply call backend.user.logout()
. This logic is already part of the Accelerator Toolkit app and can be found in AuthContext
and AuthProvider
in src/contexts/Auth/context.js
. But every time a user gets redirected to the Auth0 login form, he will be automatically re-authenticated. To prevent this, we must invalidate the Auth0 session as well.
To destroy the Auth0 session, we must follow the Auth0 logout docs. We can simply update the logic in src/components/Auth/LogoutForm.js
and change the line logout().then(() => history.push("/login"));
to something like this:
const AUTH_DOMAIN = 'https://zajic.us.auth0.com';
const CLIENT_ID = 'wJtJSAm3wBsxSG0nQopkjkQnuQmxdFyS';
const RETURN_URL = 'https://deployed-analytics-app.com';
logout().then(() => window.location.assign(`${AUTH_DOMAIN}/v2/logout?client_id=${CLIENT_ID}&returnTo=${encodeURIComponent(RETURN_URL)}`));
Summary
In this article, we described how to set up a SAML SSO on your domain, and how to implement SSO authentication logic in React/JavaScript with GoodData.UI.