SAML

ODAS supports enabling Security Assertion Markup Language (SAML) for authenticating to the HTTP-based ODAS endpoints, such as the Web UI and REST-based API service. See Wikipedia for general and the OASIS website for technical details of the protocol. This document explains how to set up the SAML support to make the Web UI login easier.

SAML Basics

For SAML-based single-sign-on (SSO) to work, the cluster needs to be configured with a secrets file that contains all of the SAML related configuration. There are separate sections defining the service provider (SP), which is the ODAS cluster, and the identity provider (IdP) that handles the authentication, which include third-party services such as Okta or Ping (see SAML service provider for a longer list).

When SAML is enabled, the WebUI is providing a login option that triggers the SAML workflow. In a nutshell, the login flow contains the following steps:

  • The ODAS WebUI is presenting a "Use SAML" button on its login screen, which contains a target URL pointing to the configured IdP and also containing an authentication request payload. This payload contains details such as the URLs provided by the ODAS cluster to handle the authentication result, referred to as assertion consumer service.

  • Upon clicking on the button, the browser is redirecting the user to the IdP, which validates the user's credentials and then returns a HTML POST form to the browser that contains a hidden parameter with the authentication response. The action of the form points to the assertion consumer service URL that was sent with the initial request. Usually the returned HTML also is containing a Javascript element that immediately submits the form, sending the response to the ODAS cluster.

  • On the ODAS cluster, the SAML response posted on behalf of the user is setting up a valid local login context. This includes the creation of a JWT token that contains the user's groups as determined by the SAML assertions returned by the IdP's response payload. The assertion consumer service call returns a redirect request to the user's browser with the landing page on the ODAS WebUI: The user sees the cluster's home page where they are now logged in as per the assertions.

Configuration Settings

The following configuration settings are used to configure SAML support:

  • SAML_SECRETS

Configuring SAML Authentication

Note

The SAML SSO implementation used by Okera is modeled after the OneLogin Python library and uses the same configuration format. Refer to the settings sections for details on the used configuration keys and values.

You will need the following pieces of information about your local service, and the SAML identity provider:

  • Service Provider (ODAS)

    • Entity ID - this is a URI that defines the SP uniquely. This is set to the ODAS FQDN with /api/saml/metadata added.
    • Assertion Consumer Service - this is the endpoint that is handling the SAML authentication response. This is set to the ODAS FQDN with /api/saml?acs added.
    • Single Logout Service - (optional) this is the endpoint used when a user logs out of all their services. Set to the ODAS FQDN with /api/saml?sls added.
    • Service Certificate and Private Key - (optional) this is needed for the IdP to be able to send a signed logout message.
  • Identity Provider (third-party)

    • Entity ID - this is a URI provided by the SAML service when creating a profile for the ODAS cluster.
    • Single Sign-on Service - this is a URI provided by the SAML service and defines the endpoint where the authentication request is sent to.
    • Single Logout Service - this is a URI provided by the SAML service and defines the endpoint where the single logout request is sent to.
    • Service Certificate - the public certificate of the IdP that is used by the ODAS services to validate the authentication response.

Creating and using the client_secrets.json file

Since the SAML configuration has many values, ODAS accepts the configuration for it as a JSON file that contains all this information.

The format of the file is:

{
  "okera_url": "<Cluster WebUI URI>",
  "strict": true,
  ...
  "sp": {
    "entity_id": "<ODAS Entity ID>",
    "assertionConsumerService": {
      "url": "https://<cluster_fqdn>:<cluster_port>/api/saml?acs",
      "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
    },
    "singleLogoutService": {
      "url": "https://<cluster_fqdn>:<cluster_port>/api/saml?sls",
      "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
    },
    "NameIDFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified",
    "x509cert": "<ODAS Certificate>",
    "privateKey": "<ODAS Private Key>",
  },
  "idp": {
    "entity_id": "<IdP Entity ID>",
    "singleSignOnService": {
      "url": "<IdP SSO URI>",
      "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
    },
    "singleLogoutService": {
      "url": "<IdP SLO URI>",
      "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
    },
    "NameIDFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified",
    "x509cert": "<IdP Public Certificate>"
  },
  "security": {
    ...
  },
  "contactPerson": {
    ...
  },
  "organization": {
    ...
  }
}

For example, if Okta SAML were to be used, after setting up a new Okta client application, we might have the following file:

{
    "okera_url": "https://odas.example.okera.com:8083",
    "lowercaseEncoding": false,
    "strict": true,
    "debug": false,
    "sp": {
        "entityId": "http://odas.example.okera.com:8083/api/saml/metadata",
        "assertionConsumerService": {
            "url": "http://odas.example.okera.com:8083/api/saml?acs",
            "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
        },
        "singleLogoutService": {
            "url": "http://odas.example.okera.com:8083/api/saml?sls",
            "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
        },
        "NameIDFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified",
        "x509cert": "MIIDWjCCAkICCQDZNVFIv5...VICNm4MEUxmL/cgbQ+1qOG8nBtbhBw==",
        "privateKey": "MIIEvgIBADANBgkqhkiG9w0B...Ke2MxTTAUYx8wFVIu4sJkozQSuxo"
    },
    "idp": {
        "entityId": "http://www.okta.com/123456789ABCDEF",
        "singleSignOnService": {
            "url": "https://123456789.okta.com/app/okera1234_okerasaml_1/123456789ABCDEF/sso/saml",
            "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
        },
        "singleLogoutService": {
            "url": "https://123456789.okta.com/app/okera1234_okerasaml_1/123456789ABCDEF/slo/saml",
            "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
        },
        "x509cert": "MIIDpjCCAo6gAwIBAgIGAXULnX...8dWNcxNuFLGHd0Qh8vUV+C6Kfeg="
    },
    "security": {
        "nameIdEncrypted": true,
        "authnRequestsSigned": true,
        "logoutRequestSigned": false,
        "logoutResponseSigned": false,
        "signMetadata": true,
        "wantMessagesSigned": false,
        "wantAssertionsSigned": false,
        "wantNameId": true,
        "wantNameIdEncrypted": false,
        "wantAssertionsEncrypted": false,
        "signatureAlgorithm": "http://www.w3.org/2000/09/xmldsig#rsa-sha1",
        "digestAlgorithm": "http://www.w3.org/2000/09/xmldsig#sha1"
    },
    "contactPerson": {
        "technical": {
            "givenName": "Jane Doe",
            "emailAddress": "janedoe@company.com"
        },
        "support": {
            "givenName": "John Doe",
            "emailAddress": "johndoe@company.com"
        }
    },
    "organization": {
        "en-US": {
            "name": "Company Inc.",
            "displayname": "Company SAML SSO",
            "url": "http://www.company.com"
        }
    }
}

Put this file in some location (e.g. /etc/okera/client_secrets.json), and then add the following to your ODAS configuration file:

SAML_SECRETS: file:///etc/okera/client_secrets.json

You can then update your cluster by running okctl update.

Overriding the assertion claims

When the IdP is returning the SAML response to the browser, which in turn is posting it to the assertion consumer services endpoint on the originating ODAS cluster, it contains a list of claims which are used to determine the user principal name (UPN) and the groups the user belongs to.

Claims consist of two pieces of information, the claim type and the claim value. The former is commonly expressed as a URI and acts as a key to find the associated value. Some claims can occur multiple times, for example, the group claim contains a single group name. If the user is in more than one group, the claim will present as many times as there are groups, each with a distinct value.

The ODAS assertion consumer service uses the following default claims:

Name Claim Type Note
UPN http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn The claim containing the user principal name.
Groups http://schemas.xmlsoap.org/claims/Group The claim containing a group of a user.

Sometimes it may be that the IdP is using a different claim type in the assertions to express the UPN and/or the group values. You can configure the claim type to use using the okera_user_assertion and okera_group_assertion configuration properties, added at the start of the JSON secrets file. For example, the following changes the default claim types to use https instead:

{
    "okera_url": "https://odas.example.okera.com:8083",
    "lowercaseEncoding": false,
    "strict": true,
    "debug": false,
    "okera_user_assertion": "https://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn",
    "okera_group_assertion": "https://schemas.xmlsoap.org/claims/Group",
    "sp": {
       ...
}

FAQ

Troubleshooting

If you encounter issues, look in the logs of the ODAS REST Server container, which will typically contain information about what went wrong. In addition, you can enable the debug mode of the SAML library by setting the following in the secrets file:

  ...
  "debug": false,
  ...

This configuration property is already in the example shown above, but defaults to false. Setting it to true increases the amount of messages emitted by the SAML library.

In general:

  • Verify the JSON validity of client_secrets.json. If it is not well-formed, the ODAS REST Server cannot read it.

  • Ensure Okera information is properly configured in the OAuth identity provider, e.g. Okera's WebUI host:port is configured as a valid redirect URI etc.

  • Confirm the claim types used by the IdP to match with what is configured in the secrets file.