Skip to main content
Version: Previous

SSO - SAML

SSO is a mechanism that enables a user to be authenticated against a single system, and use that authenticated id across multiple applications - including those built on the Genesis low-code platform. This has the advantage that a user is required to log in only once, rather than once per system.

SAML is an SSO protocol that can be used to authenticate users on the Genesis low-code platform. It works by connecting a Service Provider (SP) - the Genesis application in this case - and an Identity Provider (IDP), which would be an external party.

The SP and the IDP communicate using the user's web browser, and do not need to be accessible to each other.

Message flow

When SAML is enabled, a user can click on an SSO button in the GUI. This starts the SAML authentication flow:

  1. The user is directed to a Genesis endpoint, which generates the authentication (authn) request.
  2. The user is redirected to the IDP, with the authn request as a query parameter.
  3. The user identifies him or herself to the IDP.
  4. The user is redirected back to the Genesis SAML endpoint, with a response as a query parameter.
  5. The response is validated, and the user is redirected back to the Genesis login endpoint with a token.
  6. The front end starts the login process into Genesis using this token.

For more information, see wikipedia.

This workflow is described in more detail in the section on Front-to-back flow.

Prerequisites

Before starting, ensure you have access to the IDP metadata that is shared by the IDP and the SP. This metadata is generated by the IDP. Here is an example:

<?xml version="1.0"?>
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" entityID="http://localhost:8080/simplesaml/saml2/idp/metadata.php">
<md:IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<md:KeyDescriptor use="signing">
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:X509Data>
<ds:X509Certificate>MIIDXTCCAkWgAwIBAgIJALmVVuDWu4NYMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMTYxMjMxMTQzNDQ3WhcNNDgwNjI1MTQzNDQ3WjBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzUCFozgNb1h1M0jzNRSCjhOBnR+uVbVpaWfXYIR+AhWDdEe5ryY+CgavOg8bfLybyzFdehlYdDRgkedEB/GjG8aJw06l0qF4jDOAw0kEygWCu2mcH7XOxRt+YAH3TVHa/Hu1W3WjzkobqqqLQ8gkKWWM27fOgAZ6GieaJBN6VBSMMcPey3HWLBmc+TYJmv1dbaO2jHhKh8pfKw0W12VM8P1PIO8gv4Phu/uuJYieBWKixBEyy0lHjyixYFCR12xdh4CA47q958ZRGnnDUGFVE1QhgRacJCOZ9bd5t9mr8KLaVBYTCJo5ERE8jymab5dPqe5qKfJsCZiqWglbjUo9twIDAQABo1AwTjAdBgNVHQ4EFgQUxpuwcs/CYQOyui+r1G+3KxBNhxkwHwYDVR0jBBgwFoAUxpuwcs/CYQOyui+r1G+3KxBNhxkwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAAiWUKs/2x/viNCKi3Y6blEuCtAGhzOOZ9EjrvJ8+COH3Rag3tVBWrcBZ3/uhhPq5gy9lqw4OkvEws99/5jFsX1FJ6MKBgqfuy7yh5s1YfM0ANHYczMmYpZeAcQf2CGAaVfwTTfSlzNLsF2lW/ly7yapFzlYSJLGoVE+OHEu8g5SlNACUEfkXw+5Eghh+KzlIN7R6Q7r2ixWNFBC/jWf7NKUfJyX8qIG5md1YUeT6GBW9Bm2/1/RiO24JTaYlfLdKK9TYb8sG5B+OLab2DImG99CJ25RkAcSobWNF5zD0O6lgOo3cEdB/ksCq3hmtlC/DlLZ/D8CJ+7VuZnS1rR2naQ==</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</md:KeyDescriptor>
<md:KeyDescriptor use="encryption">
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:X509Data>
<ds:X509Certificate>MIIDXTCCAkWgAwIBAgIJALmVVuDWu4NYMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMTYxMjMxMTQzNDQ3WhcNNDgwNjI1MTQzNDQ3WjBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzUCFozgNb1h1M0jzNRSCjhOBnR+uVbVpaWfXYIR+AhWDdEe5ryY+CgavOg8bfLybyzFdehlYdDRgkedEB/GjG8aJw06l0qF4jDOAw0kEygWCu2mcH7XOxRt+YAH3TVHa/Hu1W3WjzkobqqqLQ8gkKWWM27fOgAZ6GieaJBN6VBSMMcPey3HWLBmc+TYJmv1dbaO2jHhKh8pfKw0W12VM8P1PIO8gv4Phu/uuJYieBWKixBEyy0lHjyixYFCR12xdh4CA47q958ZRGnnDUGFVE1QhgRacJCOZ9bd5t9mr8KLaVBYTCJo5ERE8jymab5dPqe5qKfJsCZiqWglbjUo9twIDAQABo1AwTjAdBgNVHQ4EFgQUxpuwcs/CYQOyui+r1G+3KxBNhxkwHwYDVR0jBBgwFoAUxpuwcs/CYQOyui+r1G+3KxBNhxkwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAAiWUKs/2x/viNCKi3Y6blEuCtAGhzOOZ9EjrvJ8+COH3Rag3tVBWrcBZ3/uhhPq5gy9lqw4OkvEws99/5jFsX1FJ6MKBgqfuy7yh5s1YfM0ANHYczMmYpZeAcQf2CGAaVfwTTfSlzNLsF2lW/ly7yapFzlYSJLGoVE+OHEu8g5SlNACUEfkXw+5Eghh+KzlIN7R6Q7r2ixWNFBC/jWf7NKUfJyX8qIG5md1YUeT6GBW9Bm2/1/RiO24JTaYlfLdKK9TYb8sG5B+OLab2DImG99CJ25RkAcSobWNF5zD0O6lgOo3cEdB/ksCq3hmtlC/DlLZ/D8CJ+7VuZnS1rR2naQ==</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</md:KeyDescriptor>
<md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://localhost:8080/simplesaml/saml2/idp/SingleLogoutService.php"/>
<md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</md:NameIDFormat>
<md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://localhost:8080/simplesaml/saml2/idp/SSOService.php"/>
</md:IDPSSODescriptor>
</md:EntityDescriptor>

Once you have checked this, there are two things you need to do:

  1. Enable SAML support in the Router.
  2. Configure SAML.

We shall now look at these in detail.

Configuring SAML in the Router

To enable SAML on the Genesis Router service, change the Router's config in your application-name-processes.xml file. The process name is GENESIS_ROUTER.

Specifically, you have to add:

  • global.genesis.auth.saml to the <package …/> tag
  • auth-saml-*.jar to the <classpath …/> tag

You can see these additions highlighted in the example below:

    <process name="GENESIS_ROUTER">
<start>true</start>
<groupId>GENESIS</groupId>
<options>-Xmx512m -DXSD_VALIDATE=false</options>
<module>router</module>
<package>global.genesis.router,global.genesis.console,global.genesis.auth.saml,global.genesis.auth.sso.endpoint</package>
<script>genesis-router.kts</script>
<classpath>genesis-console-5.2.*.jar,auth-saml-*.jar,auth-sso-endpoint-*.jar</classpath>
<description>Socket, Websocket and HTTP proxy which routes incoming messages to GENESIS microservices</description>
</process>

Additionally, you need an application-name-saml-config.kts file, as below:

    saml {
strictMode = false
debugMode = true
// this should be the URL of the application logon screen
loginEndpoint = "https://sso.genesislab.global/login"
tokenLifeInSeconds = 3000

serviceProvider {
// this should be the url for accessing the router
entityId = "https://sso.genesislab.global/gwf"
}

// for every identity provider we support we need one of these
identityProvider("genesis") {
// we need the IDP metadata, either a file:
metadataUrl = "genesismetadata.xml"
// or a url (IDP should be accessible from genesis box):
metadataUrl = "http://localhost:8080/simplesaml/saml2/idp/metadata.php?output=xml" // IDP metadata endpoint

// where do we get the email address from
mapNameIdToUser()
// or
mapToAttribute("email")

// optional -> add url parameter to auth request
modifyRequest { config ->
addParameter("PartnerSpId", config.settings.spEntityId)
}

// optional -> called on first user login when the user doesn't exist in the database
onFirstLogin {
// optional -> should return a User and it UserAttributes from the SamlResponse
createUser {}

// optional -> configures user's permissions
createUserPermissions {
userProfiles("emp", "trader")
}
}

// optional -> called every time after successful authentication. Has access to the database and the SamlResponse returned by the IDP
onLoginSuccess {

}
}
}
warning

The loginEndpoint is the URL to which the front end is redirected once the full SAML workflow has been completed and an SSO_TOKEN has been issued. If this URL itself is a redirect, the SSO_TOKEN query parameter could be lost.

Additionally, if the web server is routing via scripts, navigating to this URL could throw a 404 Not Found error. The remedy in this case is to add an override for 404 errors to redirect back to your application logon screen.

Here is an example of how to do this in NGINX:

error_page 404 =200 /index.html;

Finally, you need to specify an SSOToken authenticator in your application-name-auth-preferences.kts file:

    authentication {
ssoToken {}
}

If necessary, you can define advanced configuration in the file onelogin.saml.properties. You need to use this if - for example - you need to configure a key for signing the authn request.

Once this is configured, a service provider metadata endpoint will be available on: https://{url}/gwf/saml/metadata?idp={idp name}.

Other endpoints provided are:

  • the ssoLoginUrl

    • The format of this is: https://&#123;appHost}{ssoLoginRoute}?idp={id} where:
      • appHost is hostname of the app, e.g. dev-position2
      • ssoLoginRoute is /gwf/saml/login by default (this is configurable)
      • id is the ID of the selected identity provider
    • For example: https://dev-position2/gwf/saml/login?idp=provider1
  • the ssoListEndpoint

    • By default, this is gwf/saml/list (configurable)
    • This endpoint returns a list of identity providers:
    [
    {ID:'provider1', DESCRIPTION:'Description 1'},
    {ID:'provider2', DESCRIPTION:'Description 2'}
    ]

Enabling users for SAML

To enable users to be able to sign in using SAML, you must add them to the USER, USER_ATTRIBUTES and SSO_USER tables within your Genesis application.

In the SSO_USER table:

  • SSO_METHOD must be set to SAML
  • SSO_SOURCE must be set to the identity provider name defined in the saml-config.kts file

The Genesis username should be the user’s email address.

Front-to-back flow

This section provides a more detailed description of the workflow between a Genesis application SP and an external IDP. The flow assumes that the front end has been configured correctly.

The flow

  1. The front end hits ssoListEndpoint - by default, this is gwf/sso/listJWT/SSO (this is configurable).
  2. ssoListEndpoint returns a list of identity providers:
    [
    {ID:'provider1', DESCRIPTION:'Description 1'},
    {ID:'provider2', DESCRIPTION:'Description 2'}
    ]
  3. Identity providers are parsed and the dropdown is populated on the login page.
  4. The user selects an identity provider using the dropdown (or keeps the preselected default). Then the user clicks the SSO Login button.
  5. The browser redirects to the ssoLoginUrl, which might be, for example: https://dev-position2/gwf/saml/login?idp=provider1.
  6. The server sends the user to the identity provider’s login page.
  7. The user logs in using their SSO credentials.
  8. The server redirects the client back to the client-app with a new url param: SSO_TOKEN.
  9. The front end checks for the presence of an SSO_TOKEN url parameter. If found, it stores it in session storage and uses it to perform an SSO Login.
  10. The server responds with an ACK and the user is now logged in. If there is an error, a NACK is returned and the login fails.

Testing SAML

Server - setting up local SAML

In order to test the SAML flow, first, you need to run SAML locally. You can do this using a docker container, for example:

docker run -p 8080:8080 -p 8443:8443 -e SIMPLESAMLPHP_SP_ENTITY_ID=https://localhost/gwf/saml/metadata?idp=test -e SIMPLESAMLPHP_SP_ASSERTION_CONSUMER_SERVICE=https://localhost/gwf/saml/logon?idp=test -e SIMPLESAMLPHP_SP_SINGLE_LOGOUT_SERVICE=https://localhost/gwf/saml/logout?idp=test -d kristophjunge/test-saml-idp

In the above command, you need to replace:

  • IP with the address/IP of your Genesis instance
  • test with the name of the IDP

Then, make sure that auth saml has been added to the Genesis Router configuration in processes.xml, for example:

<process name="GENESIS_ROUTER">
<start>true</start>
<groupId>GENESIS</groupId>
<options>-Xmx512m -DXSD_VALIDATE=false</options>
<module>router</module>
<package>global.genesis.router,global.genesis.console,global.genesis.auth.saml</package>
<script>genesis-router.kts</script>
<language>pal</language>
<classpath>genesis-console-*.jar,auth-saml-*.jar</classpath>
<description>Socket, Websocket and HTTP proxy which routes incoming messages to GENESIS microservices</description>
</process>

Next, in your application's jvm/{application-name}-site-specific directory, create an application-name-saml-config.kts file with the following SAML details:

    saml {
strictMode = false
debugMode = true

loginEndpoint = "http://localhost:6060/login"
tokenLifeInSeconds = 3000

serviceProvider {
// this should be the url for accessing the router
entityId = "http://localhost/gwf"
}

// for every identity provider we support we need one of these
identityProvider("test") {
metadataUrl = "http://localhost:8080/simplesaml/saml2/idp/metadata.php?output=xml" // IDP metadata endpoint

// where do we get the email address from
mapToAttribute("email")
}
}
  1. Now you are ready to add some users.
  • Add user to USER table with username “user1@example.com

  • Add user to SSO_USER table:

"SSO_SOURCE","SSO_METHOD","SSO_DETAILS","USER_NAME” SSO_SOURCE = Identity Provider (as per above SAML config ’test’) SSO_METHOD = SAML SSO_DETAILS = an internal identifier (for example, TRADE_DESK_1) USER_NAME = user1@example.com

  • Add user to USER_ATTRIBUTES table: "TELEPHONE_NUMBER_DIRECT","MOBILE_NUMBER","USER_NAME","TELEPHONE_NUMBER_OFFICE","REGION","ADDRESS_LINE1","ADDRESS_LINE2","ADDRESS_LINE3","CITY","COUNTRY","ADDRESS_LINE4","POSTAL_CODE","USER_TYPE","ACCESS_TYPE","TITLE","WEBSITE” Only three are relevant! USER_NAME = user1@example.com USER_TYPE = USER ACCESS_TYPE = ALL
  1. With the users set up, you can run your server.

Running the user interface

  1. Run an NGINX proxy docker container, for example:
docker run -it --rm -d -p 80:80 -p 443:443 --name **genesis**-console-proxy --add-host localnode:$(ifconfig eth0 | grep inet | grep -v inet6 | awk '{print $2}') genesisglobal-docker-internal.jfrog.io/genesis-console-proxy
  1. In package.json, change the API_HOST property to "API_HOST": "wss://localhost/gwf/".

  2. Now you can run the front end.

Test Metadata File (testMetadata.xml)

docker run -it --rm -d -p 80:80 -p 443:443 --name genesis-console-proxy --add-host

Here is some test metadata you can use:

<?xml version="1.0"?>
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
entityID="http://localhost:8080/simplesaml/saml2/idp/metadata.php">
<md:IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<md:KeyDescriptor use="signing">
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:X509Data>
<ds:X509Certificate>
MIIDXTCCAkWgAwIBAgIJALmVVuDWu4NYMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMTYxMjMxMTQzNDQ3WhcNNDgwNjI1MTQzNDQ3WjBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzUCFozgNb1h1M0jzNRSCjhOBnR+uVbVpaWfXYIR+AhWDdEe5ryY+CgavOg8bfLybyzFdehlYdDRgkedEB/GjG8aJw06l0qF4jDOAw0kEygWCu2mcH7XOxRt+YAH3TVHa/Hu1W3WjzkobqqqLQ8gkKWWM27fOgAZ6GieaJBN6VBSMMcPey3HWLBmc+TYJmv1dbaO2jHhKh8pfKw0W12VM8P1PIO8gv4Phu/uuJYieBWKixBEyy0lHjyixYFCR12xdh4CA47q958ZRGnnDUGFVE1QhgRacJCOZ9bd5t9mr8KLaVBYTCJo5ERE8jymab5dPqe5qKfJsCZiqWglbjUo9twIDAQABo1AwTjAdBgNVHQ4EFgQUxpuwcs/CYQOyui+r1G+3KxBNhxkwHwYDVR0jBBgwFoAUxpuwcs/CYQOyui+r1G+3KxBNhxkwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAAiWUKs/2x/viNCKi3Y6blEuCtAGhzOOZ9EjrvJ8+COH3Rag3tVBWrcBZ3/uhhPq5gy9lqw4OkvEws99/5jFsX1FJ6MKBgqfuy7yh5s1YfM0ANHYczMmYpZeAcQf2CGAaVfwTTfSlzNLsF2lW/ly7yapFzlYSJLGoVE+OHEu8g5SlNACUEfkXw+5Eghh+KzlIN7R6Q7r2ixWNFBC/jWf7NKUfJyX8qIG5md1YUeT6GBW9Bm2/1/RiO24JTaYlfLdKK9TYb8sG5B+OLab2DImG99CJ25RkAcSobWNF5zD0O6lgOo3cEdB/ksCq3hmtlC/DlLZ/D8CJ+7VuZnS1rR2naQ==
</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</md:KeyDescriptor>
<md:KeyDescriptor use="encryption">
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:X509Data>
<ds:X509Certificate>
MIIDXTCCAkWgAwIBAgIJALmVVuDWu4NYMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMTYxMjMxMTQzNDQ3WhcNNDgwNjI1MTQzNDQ3WjBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzUCFozgNb1h1M0jzNRSCjhOBnR+uVbVpaWfXYIR+AhWDdEe5ryY+CgavOg8bfLybyzFdehlYdDRgkedEB/GjG8aJw06l0qF4jDOAw0kEygWCu2mcH7XOxRt+YAH3TVHa/Hu1W3WjzkobqqqLQ8gkKWWM27fOgAZ6GieaJBN6VBSMMcPey3HWLBmc+TYJmv1dbaO2jHhKh8pfKw0W12VM8P1PIO8gv4Phu/uuJYieBWKixBEyy0lHjyixYFCR12xdh4CA47q958ZRGnnDUGFVE1QhgRacJCOZ9bd5t9mr8KLaVBYTCJo5ERE8jymab5dPqe5qKfJsCZiqWglbjUo9twIDAQABo1AwTjAdBgNVHQ4EFgQUxpuwcs/CYQOyui+r1G+3KxBNhxkwHwYDVR0jBBgwFoAUxpuwcs/CYQOyui+r1G+3KxBNhxkwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAAiWUKs/2x/viNCKi3Y6blEuCtAGhzOOZ9EjrvJ8+COH3Rag3tVBWrcBZ3/uhhPq5gy9lqw4OkvEws99/5jFsX1FJ6MKBgqfuy7yh5s1YfM0ANHYczMmYpZeAcQf2CGAaVfwTTfSlzNLsF2lW/ly7yapFzlYSJLGoVE+OHEu8g5SlNACUEfkXw+5Eghh+KzlIN7R6Q7r2ixWNFBC/jWf7NKUfJyX8qIG5md1YUeT6GBW9Bm2/1/RiO24JTaYlfLdKK9TYb8sG5B+OLab2DImG99CJ25RkAcSobWNF5zD0O6lgOo3cEdB/ksCq3hmtlC/DlLZ/D8CJ+7VuZnS1rR2naQ==
</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</md:KeyDescriptor>
<md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
Location="http://localhost:8080/simplesaml/saml2/idp/SingleLogoutService.php"/>
<md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</md:NameIDFormat>
<md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
Location="http://localhost:8080/simplesaml/saml2/idp/SSOService.php"/>
</md:IDPSSODescriptor>
</md:EntityDescriptor>