JWT authentication support

This section offers an overview of how to integrate Siren Investigate with the Search Guard Classic JWT authenticator when Siren Investigate is embedded into an iframe by another application.

Prerequisites

Before you begin, ensure that you have completed the following steps:

Siren Investigate proxy

It is required that Siren Investigate and the container application are published on the same domain to enable cross frame communication. This can be achieved by implementing a proxy to Siren Investigate in the container application routes or configuring a reverse proxy on a path in the application server configuration.

JWT token issuance

The application that embeds Siren Investigate is responsible for generating JWT tokens; jwt.io provides a good overview of the technology, a browser based debugging tool and a list of libraries for several platforms.

The Search Guard Classic documentation provides an overview of all the claims supported by the add-on and a list of all the configuration options.

Note that the application must specify an expiration date claim (exp) to avoid creating tokens with unlimited duration.

Configuration

After the add-on has been installed in the cluster, you must modify sg_config.yml file and upload it to the cluster using sgadmin. If you are using the Search Guard Classic management API, ensure that you include only the sg_config.yml in the sgadmin configuration folder or you will overwrite internal users, actiongroups, roles and mappings defined through the API.

To enable JWT authentication over HTTP, you need to add a JWT authenticator stanza ( the authc.jwt_auth_domain in the example below) to searchguard.authc; an example sg_config.yml follows:

searchguard:
  dynamic:
    http:
      anonymous_auth_enabled: false
      xff:
        enabled: false
    authc:
      jwt_auth_domain:
        enabled: true
        order: 1
        http_authenticator:
          type: jwt
          challenge: false
          config:
            signing_key: "cGFzc3dvcmQ="
            jwt_header: "Authorization"
        authentication_backend:
          type: noop
      basic_internal_auth_domain:
        enabled: true
        order: 2
        http_authenticator:
          type: basic
          challenge: true
        authentication_backend:
          type: internal

With this configuration, Search Guard Classic will check if the Authorization header contains a JWT token signed with the signing key specified in http_authenticator.signing_key.

The signing key must be encoded using the base64 algorithm; in the example the decoded key is password; when using RSA public keys, it is also possible to write them on multiple lines as follows:

searchguard:
    ...
    authc:
      jwt_auth_domain:
        ...
        http_authenticator:
          ...
          config:
            signing_key: |-
              -----BEGIN PUBLIC KEY-----
              123123abcbc
              -----END PUBLIC KEY-----

If the token is decoded successfully, Search Guard Classic will validate the following claims:

  • iat: Issued At: the date when the token was issued (optional).

  • exp: Expiration Time: the date after which the token should expired; this claim is optional but you should set it, otherwise tokens will have unlimited duration.

  • nbf: Not Before: the date before which the token should be rejected (optional).

All dates are expressed as seconds since the Epoch in UTC.

If time claims are validated, Search Guard Classic will get the username from the Subject claim (sub), assign role mappings and evaluate role permissions.

If an HTTP request to the cluster contains an HTTP Basic authorization header it will be authenticated by the HTTP authenticator defined in basic_internal_auth_domain; it is necessary to leave this enabled as the Siren Investigate backend uses this method to authenticate with the cluster.

It is possible to customize the claim used to retrieve the username through the parameter subject_key, for example:

searchguard:
  dynamic:
    http:
      anonymous_auth_enabled: false
      xff:
        enabled: false
    authc:
      jwt_auth_domain:
        enabled: true
        order: 1
        http_authenticator:
          type: jwt
          challenge: false
          config:
            signing_key: |-
              -----BEGIN PUBLIC KEY-----
              123123abcbc
              -----END PUBLIC KEY-----
            subject_key: "service:username"
            jwt_header: "Authorization"
        authentication_backend:
          type: noop

User cache

When you use the JWT authentication mechanism, set the Search Guard Classic user cache TTL to a value much lower than the token expiry time. For example, if the tokens will be valid for 10 minutes, set the user cache TTL to 1 minute.

Administrators should note that when user permissions change, the system might still use the old permissions until the cache entry in Search Guard cache is revoked either automatically via searchguard.cache.ttl_minutes, or manually with the sgadmin tool. Don’t turn off the user cache by setting the value to 0, as this setting slows down each request.

To set the Search Guard Classic user cache TTL, add the following property to elasticsearch.yml:

searchguard.cache.ttl_minutes: 1

After you add the setting, you must restart each node.

Roles

It is possible to specify user roles in a token claim by setting the roles_key attribute in the authenticator configuration to the desired claim name, for example:

#...
      jwt_auth_domain:
        enabled: true
        order: 1
        http_authenticator:
          type: jwt
          challenge: false
          config:
            roles_key: "roles"
            signing_key: "cGFzc3dvcmQ="
            jwt_header: "Authorization"
#...

After the attribute is set and the configuration is updated, it is possible to assign backend roles to the user by setting the claim defined in http_authenticator.config.roles_key in the token payload, for example :

{
  "sub": "sirenuser",
  "exp": 1495711765,
  "roles": "sales,marketing"
}
To map roles set in the JWT token to Search Guard Classic roles you must define a role mapping such as the following:

<em>JWT role mapping</em>

Verification

To verify that Search Guard Classic JWT authentication is working correctly, you can generate a JWT token from your application and pass it to Elasticsearch using curl’s -H option, for example:

curl -k -H "Content-Type: application/json" -H "Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJraWJpdXNlciJ9.tqCYxJsORvro59Q01J9HUeFpQtauc81CcTlS5bVl93Y" https://localhost:9200/_searchguard/authinfo

To test if it is working correctly before the application is ready, you can use the jwt.io debugger to generate tokens using the signing key defined in sg_config.yml.

Siren Investigate JWT configuration

To enable JWT support in Siren Investigate, set the investigate_access_control.backends.searchguard.authenticator option in investigate.yml to http-jwt, for example:

investigate_access_control:
  #... existing options
  backends:
    searchguard:
      #... existing options
      authenticator: 'http-jwt'

Then restart Siren Investigate and open it in a browser; you should get a blank page and the URL should end with login.

To test JWT authentication, open your browser console (Ctrl+Shift+I on Chrome and Firefox) and call setJWTToken of the sireninvestigate object, for example:

document.getElementById('investigateframe')
.contentWindow
.sireninvestigate
.setJWTToken(yourtoken)
.then(function() {
  console.log('JWT token set.');
})
.catch(function(error) {
  console.log('An error occurred setting the token.');
});

After the token is set, Siren Investigate will store it in an encrypted cookie and send it in every request to the backend; the backend will then forward the JWT token to Search Guard Classic to authenticate the user.

After the token is set, you can switch to the desired Siren Investigate URL by changing location.href.

When the user is logged out from the main application, sessionStorage and localStorage should be cleared.