GoodData PGP Single Sign-On

This Single Sign-On (SSO) implementation is a custom proprietary implementation provided by GoodData. This implementation is based on the PGP keypair exchange. It allows your application to sign in an existing GoodData user. The authentication is done not by username and password but by generating a session-specific token using a pair of PGP keys.

Depending on your current situation and use case, the GoodData PGP SSO is sometimes easier to implement than SAML SSO.

Implementation Process

As a domain administrator, you can configure SSO for your workspaces and users through the SSO Providers API.

Follow these steps:

  1. Configure the ssoProvider parameter by modifying the following template according to your site parameters and submit a POST request to the endpoint https://{your-subdomain-name}.gooddata.com``/gdc/domains/{domainId}/authentication/providers:

    {
       "pgpProvider": {
          "name": "string",
          "publicKey": "string"
       }
    }
    

    Where:

    • name (String) Specifies the name of the SSO provider. This value becomes a possible value for the ssoProvider{style=“text-align: left;"} parameter. The SSO provider name must be lowercase and can contain only letters, numbers, dots, and dashes. For example: 

      example.com
      pgp-my.example.com
      
    • publicKey *(String)*Specifies the value of the public key for your SSO service. If you need to generate a new PGP key pair, see How to Generate a Public-Private Key Pair. The key will look similar to the following example: 

      -----BEGIN PGP PUBLIC KEY BLOCK-----
      
      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
      =gOqe
      -----END PGP PUBLIC KEY BLOCK-----
      

      Do not delete the PGP header and footer (BEGIN/END) from the public key that you are sending. You will need to escape and erase all of the new lines with \n to make one line. For example:

      "publicKey": "-----BEGIN PGP PUBLIC KEY BLOCK-----\n\n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\n=gOqe\n-----END PGP PUBLIC KEY BLOCK-----"
      
  2. Use the ssoProvider parameter when provisioning users through the API for managing users.

  3. You set up your SSO environment.

Example Use Case: Embedding GoodData

In the following example, you are embedding a dashboard into your application. To do so, you are doing the following:

  • Posting the PGP login request form parameters to the PGP login URL
  • Posting the result target to an iframe
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <!-- Prevent content caching --> 
    <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate"/>
    <meta http-equiv="Pragma" content="no-cache"/>
    <meta http-equiv="Expires" content="0"/>
    <title>Example GoodData PGP SSO Login</title>
</head>
<body>
<form id="helperForm" target="embeddedGoodDataIframe" method="post" action="https://{your-subdomain-name}.gooddata.com/gdc/account/customerlogin">
    <input id="targetUrl" type="hidden" name="targetUrl" value=""/>
    <input id="ssoProvider" type="hidden" name="ssoProvider" value=""/>
    <input id="encryptedClaims" type="hidden" name="encryptedClaims" value=""/>
</form>

<iframe name="embeddedGoodDataIframe"></iframe>
<script type="text/javascript">
    let targetUrl = "/dashboards/embedded";
    let ssoProvider = "sso-provider-name";
    let pgpMessage = "-----BEGIN PGP MESSAGE-----\n" +
    "\n" + "hQIOA3sav0dr/91SEAf+IzO38qQ9TBnUz2YRtHCgAX7sOwG8v/KdXNJa3TMC7Ed/\n" +
    "zoZDIzJnZHaAT+h356XX+f2lAFbJHqs1HUGoOMM1XwRShrC2tSiNdHTh14bZk3MS\n" +
    "PN52RyoNHEWa6A8d0Ptsx9vhMbPVfRVmOJHiRaKmxZMw5i4OUvj2nDgPXtzDGGDD\n" +
    ... "zW1FkFtZpE0bEHSrMEpKCKy68E4yRyFKo7CUIgmTn1EC59WMJ0a+ERt1YgRVn1fb\n" +
    "O8VPB/H6a4zyO8Q1/MnRfS++YlRHdzjT/f+cKstGrEjPAFYpW3GPkN0LI+xo/JQY\n" +
    "S/TpYhp3+Cghb0O9rusiTRnJ88dQ07aysqpB6Xs=\n" +
    "=DkmC\n" +
    "-----END PGP MESSAGE-----";
    document.getElementById('targetUrl').value = targetUrl;
    document.getElementById('ssoProvider').value = ssoProvider;
    document.getElementById('encryptedClaims').value = pgpMessage;
    document.getElementById('helperForm').submit();
</script>
</body>
</html>

Disable Caching

We recommend that you serve the page with HTTP headers which disable caching because it is more reliable than meta tags. For more information, see https://www.mnot.net/cache_docs/#META.

Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
Expires: 0

Post the PGP Login Request Form Parameters

No encoding is needed when posting the parameters.

  • API resource: https://{your-subdomain-name}.gooddata.com``/gdc/account/customerlogin
  • Method: POST
  • Parameters:  
    • targetUrl defines the URL of the dashboard, report or any other page that you want to embed. The URL is relative to the GoodData server starting with a slash ( / ).
    • ssoProvider corresponds to the ssoProvider parameter used in the API for managing users.
    • encryptedClaims must be dynamically generated based on the user who you want to authenticate (see Create Encrypted Claims).

For more information about the API call, see the API documentation for SSO PGP login.

Create Encrypted Claims

You have to dynamically generate an encryptedClaims parameter.

Steps:

  1. Create a JSON file in the following format:

    {
      "email": "end.user@domain.com",
      "validity": 123456789,
      "notBefore": 11515111,
      "notOnOrAfter": 11515211
    }
    
    • email (mandatory) corresponds to the user account set up in GoodData with SSO permissions. This email is used for logging in through the SSO login resource. The email is case-sensitive: User@domain.com and user@domain.com are not the same.

    • validity (mandatory) is a date in the UTC timezone (UNIX timestamp format and INTEGER data type) when the generated token expires. It must be set from a minimum of +10 minutes from present up to a maximum of 36 hours from present.

    • notBefore (optional) is a UNIX timestamp (in seconds) that specifies the earliest instant in time when the SSO login link begins being valid.

    • notOnOrAfter (optional) a UNIX timestamp (in seconds) that specifies the instant in time when the SSO login link expires.

  2. Sign the JSON file using the private part of your key. Do not use the --clearsign option.

    gpg --armor -u pgpOwner.user@domain.com --output signed.txt --sign json.txt
    
  3. Encrypt the result using the GoodData public PGP key.

    gpg --armor --output enc.txt --encrypt --recipient security@gooddata.com signed.txt
    
  4. Use the encrypted message as the encryptedClaims parameter in the PGP login request (see Post the PGP Login Request Form Parameters).

Authentication Process

The following picture shows the whole authentication process:

PGP-based SSO Security

Supported Encryption Algorithms

Public keyRSA, RSA-E, RSA-S, ELG-E, DSA, ECC
Cipher3DES, CAST5, BLOWFISH, AES, AES192, AES256, TWOFISH

SSO Session Duration

When an SSO session is initiated via login, the initiating application can define the length of the session token in the JSON.

This session duration is defined using the validity parameter. The validity parameter must be set as a UNIX time code that is a minimum of +10 minutes from present up to a maximum of 36 hours from present.

The header of the response to the login request contains a SuperSecured Token (SST), which defines how long the session can last regardless of whether a user is active or not. If the SSO user is still logged in to the GoodData platform at the moment when the SST expires, the GoodData application stops responding.

To renew the token, refresh the parent page. This lets you log in back to the GoodData platform, and starts a new GoodData session over SSO.

Troubleshooting

Problem:

I cannot load a dashboard.

Reason:

The SSO handshake failed.

Solution:

This may be due to an improper SSO configuration or using a wrong SSO integration method. If you are not sure what is wrong, contact GoodData Support.