GoodData.UI: PGP SSO Example

This article will guide you through a PGP SSO authentication example. We will use GoodData’s Accelerator Toolkit along with the OpenPGP library. PGP SSO is GoodData’s custom SSO implementation based on exchanging PGP key pairs.

This is a simple example to get you started. When dealing with PGP keys, much care is needed on the security side. In this example, we expose our private PGP key and passphrase in the JavaScript code. This is done for testing and should not be done in production.

A prerequisite that must be addressed is the configuration of an SSO provider. The steps are explained here.

Once an SSO provider is set up, ensure that the user you will be using for testing has been provisioned with the SSO provider. The domain administrator can do this using this [API](' s-information).

User Settings

Make sure ssoProvider is set to the SSO provider configured by GoodData support. Make sure authenticationModes is set to an array with SSO, like the image above. If authenticationModes is set to an empty array, the user will inherit the setting from the domain configuration. A workspace with any analytical designer insight will also be needed.

Once the SSO provider and the test user have been configured, we can spin up a React application using the Accelerator Toolkit. Run the following command in Terminal:

npx @gooddata/create-gooddata-react-app

You will be prompted with some questions; be sure to set up your hostname correctly. My example here is using as the hostname:

? What is your application name? my-app
? What is your hostname? I have a custom hostname
? Insert your hostname:
? What is your application’s desired flavor? JavaScript

Next, we will cd into the application directory and add the OpenPGP library:

cd my-app
yarn add openpgp

After adding the necessary libraries, we can start the application:

yarn start

You will be met by a Welcome page from the Accelerator Toolkit:

Welcome Page

If a browser does not automatically pop up after starting the application, you can go to the application URL in the browser manually:


This example will use PGP SSO to authenticate against a GoodData backend. In order to test the example, you will need a workspace along with any visualization built in Analytical Designer. We will use the InsightView component to render this visualization upon successful authentication.

Let’s make a few changes to the application before we get to the SSO code:

In /src/constants.js:

Double-check that the backend is set to the domain that you will be working with and set the workspace variable to the workspace identifier you will be working with.

In /src/routes/AppRouter.js:

Find the line that says DELETE THIS LINE, and delete it. This removes the redirect to the welcome page and sets up Home as the default landing page. We will be doing our work on the Home page.

Copy and paste the code below to /src/routes/Home.js:

import React, { useState, useEffect } from "react";
import { useBackend } from "../contexts/Auth";

import Page from "../components/Page";
import { InsightView } from "@gooddata/sdk-ui-ext";
import * as openpgp from 'openpgp';

// GoodData pubic key used to encrypt request
const publicKeyArmored = "-----BEGIN PGP PUBLIC KEY BLOCK-----\\n\\nmQENBExqdtABCAD4YZcUozx4vLtPWFbcpQt6/iBZQAs98d0JUGNy0szVQ2Ydm3zV\\nvDqdxUxNkTK8/BRxTZ3i4C+D7nmQm2Zn3eByUNszxLLLKgpxtGQ2ntWNfKwpPyuk\\nJkqjVrPvH3Naxn/jrm/hNZTq2DRWwqc33+XJbVGX7Br9ZeTTI3logp8PDN9jJrvE\\nDKGO+hwfI+6tsu1O5/zzAUfMRuqOK+N2Dj+CBE6HTqhRLPkU0UMVcuBXDUBWvjFV\\nLc8GW5kM0RzPTEx/iFk/o0mEM7Gzv6SxUDCx+kpAbOLuKl8Ke1ThFlm48mIPyXan\\nhtVjkyjEi/TxdAR1Swj+c7MoSqmQW52CqMi1ABEBAAG0JEdvb2REYXRhIFNTTyA8\\nc2VjdXJpdHlAZ29vZGRhdGEuY29tPokBVQQTAQgAPwIbAwYLCQgHAwIGFQgCCQoL\\nBBYCAwECHgECF4AWIQTcrzX+SYX1KdvbqZ6HeyxHIENB9QUCXZWz3QUJGpE+jQAK\\nCRCHeyxHIENB9bGRCADdRFspn40QebppVvH0AWaStkhHif3/F0MlHjRV/6sOW/5l\\nGGFupn+kpTxQPRnMFf+Bd0zPTg0/ZbzwVatWItJA8mDkSgCGMaOqALgMNPJBfySG\\nLvIlajdB6ThSqKUZVJ7UKWInYEILZj1j/WeD1IWfSWd/yLLpVAvRie2bcJMcZCJc\\nD9rJAviFwOGElWflqi1uuro+Z+Iok0IoKNd7DP0TXXu0kFEuF/PkEr8EwJFqmFIh\\n5UNLiTurHWkynWRs1jBamIhr5nzunMxZXGb77q31+kUd0TUd5oXzqVGYmn4+fYYW\\n1T1QPFNwsUhjlRa+O3PNmfpBSmc9Cy0kYHfPTzxXuQENBExqdtABCADFPsHkjTFd\\ndpJ7BQwKdwTWUOdPEO/+Qy/aiqB/jJxYqnZdGYlNLaU6FkAvn/+fgUnGbS98T74D\\nWku0AnjWdN97oA59iZe46sq6JuMkP3dobeunZrzBBExNVco7/lAtlhLKVOUZMi5Q\\nfHYwTZUtqrjwGrUGwBrxnxZQjkaFyzETlcN51hmSdRvU5GIKZbTsyzy1XwPPuyUO\\nRWf6V+IvjAdMp26j/AufMNfxvcARiqgs6GDZZtP9ZoHhFlLLFde+h29rGCBiZZXZ\\nMKT7sRnLe5m4+70fkdmUURPwcHfi1kE4W5gTHuvIq2nxdg94yXjQF+XLUQxtaWSS\\nLFU4MypNIRDPABEBAAGJATwEGAEIACYCGwwWIQTcrzX+SYX1KdvbqZ6HeyxHIENB\\n9QUCXZW0QQUJGpE+8QAKCRCHeyxHIENB9ailB/9FzGRyPI1Y1CWvn2AqfqAmAIFS\\nhYtfv3j47k87QpI7qItyFJTyq5jAhE6kFFXv2SU7Di8qqFrs5t3Iq2DtLn9y1wrh\\nnilJKldUDnz5Zl/9urXtQ7GX1oPKWFb0SucSuzTDIal/ZNvAzCmMicoy7sM6Ly1V\\n1nCZqX/U+qkJ4hDjf9+lWzsBYHQNc+xuTW77z5fSD6S2HBASx3fPdDt8LMXT2aq/\\nnN9zwEyvOF7rsz0b39wWKXlbF6YJXaMIEDrzMarHqLY1rgMEJHrFhdNWQ7MQSjj7\\nkawghOlSp6ySbnu6p6kRH+nGj7Jnk3R1ZmR4kIDYnB1/oA4+xp2+Vw9vI2/2\\n=yIXV\\n-----END PGP PUBLIC KEY BLOCK-----\\n";

// Private key connected to your GoodData SSO provider used to sign request
// Should not be exposed in production codebase, only here as an example
const privateKeyArmored = "-----BEGIN PGP PRIVATE KEY BLOCK-----\\n\\nlQWGBGDt ... \\n-----END PGP PRIVATE KEY BLOCK-----\\n";

// Passphrase for the private key
// Should not be exposed in production codebase, only here as an example
const passphrase = "passphrase";

const Home = () => {
    const [authorized, setAuthorized] = useState(false);
    const backend = useBackend();

    useEffect(() => {
        (async () => {
            const publicKey = await openpgp.readKey({ armoredKey: publicKeyArmored });

            const privateKey = await openpgp.decryptKey({
                privateKey: await openpgp.readPrivateKey({ armoredKey: privateKeyArmored }),

            // Prepare the message with the login and session validity info
            const date = new Date();
            const pgpRequest = {
                email: "",
                // set session duration to 1 day from now
                validity: date.setUTCDate(date.getUTCDate() + 1) / 1000
            const message = await openpgp.createMessage({ text: JSON.stringify(pgpRequest) });

            // Sign and encrypt the message
            const encrypted = await openpgp.encrypt({
                message: message,
                encryptionKeys: publicKey,
                signingKeys: privateKey

            // We need to replace newlines with text value '\\n'
            // in order for the request to be POSTed successfully
            const claims = encrypted.replace(/(\r\n|\n|\r)/gm, '\\n');

            // Send the SSO POST request to GoodData
            backend.sdk.user.loginSso(claims,"pgp-ramirez-internal","/").then((response) => {
            .catch(error => {
    }, [backend]);

    return (
            <div style={{ height: 400 }}>
                { authorized && <InsightView insight="insight-id" /> }

export default Home;

Set the insight-id for the InsightView component to a valid insight identifier in the workspace you are using.

Let’s look at the example a little closer. The process involves creating an encrypted claims request. The steps are as follows:

  1. Prepare a request for the PGP SSO API
  2. Sign the request using your private key
  3. Encrypt the request using the GoodData public key

We need some information to complete these steps:


This is the GoodData public PGP key which you can download here. I have replaced the newlines with text value ‘\n’ in order for it to work in the code. You do not have to change anything here.


This is the private part of the key used when SSO was configured with GoodData support. As stated previously, we include the private key here just for testing. To get the private key, you can run the following PGP command. Just replace my user ( with your user:

gpg --export-secret-key -a "" > private.key

Like the public key, we must escape newlines with the text ‘\n’. Once newlines have been escaped, you can update the code with the new value.


This is the passphrase for the private key. Similar to the private key, it is only here for testing and should not be exposed.


This is the unsigned, unencrypted request that holds the user information and the session length. Set the email parameter to the user you will be using for testing. Remember that the user will have to have the SSO Provider set in their profile. The validity or session length is set to 1 day in the example.

After setting the privateKeyArmored, passphrase, and pgpRequest variables, the code will generate the encrypted claims.


This is the final form of the request which will act as the payload for the POST request sent to the GoodData PGP SSO API. Again we have to escape the newlines with the text ‘\n’.

As a summary, the following input should have been filled in to the example code:

  • privateKeyArmored
  • passphrase
  • pgpRequest
  • Insight-id

If everything is good to go, you will see the insight rendered on the Home tab:

Insight Rendered

If you do not see the insight, there likely was an error. Open the developer console and you should see an error message:

Error Message

This example is a great starting point for implementing PGP SSO into your GD.UI application. As mentioned at the beginning of the article, use this as only an example. Exposing private keys and passphrases is never a good idea and should only be done for testing. Thanks for reading!