This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

External IDP

How to set up Stroom to use a 3rd party identity provider such as KeyCloak, Cognito or Google for authentication.

You may be running Stroom in an environment with an existing Identity Provider (IDP) Identity Provider (IDP) An Identity Provider is a system or service that can authenticate a user and assert their identity. Identity providers can support single sign on (SSO), which allows the user to sign in once to the Identity Provider so they are then authenticated to all systems using that IDP.Click to see more details... (KeyCloak, Cognito, Google, Active Directory, etc.) and want to use that for authenticating users. Stroom supports 3rd party IDPs that conform to the Open ID Connect specification.

This page describes what Stroom needs from any such provider. It applies whichever provider you use, so read it before following one of the provider specific pages.

What Stroom Needs from the Provider

Stroom is a confidential OAuth 2.0 client using the authorization code flow. To register it with a provider you need the following.

What Value
Client type Confidential, i.e. one that is issued a client secret. Stroom is a server side application and keeps its secret on the server.
Grant type / flow Authorization code. Stroom does not use the implicit or password flows.
Redirect URI https://STROOM_FQDN/api/auth/flow/v1/signin-oidc (see below)
Post logout redirect URI https://STROOM_FQDN/ (see below)
PKCE Supported, and may be required (see below)
Scopes openid and email by default

STROOM_FQDN is the public facing address of Stroom, which is what you have configured as appConfig.publicUri and is the address users type into their browser. If Stroom is behind a load balancer or Nginx, it is that address and not the address of an individual node.

The Redirect URI

The redirect URI, sometimes called the callback or reply URL, is where the provider sends the user’s browser once they have authenticated.

Stroom uses a single fixed redirect URI:

https://STROOM_FQDN/api/auth/flow/v1/signin-oidc

Register that exact value. It does not vary with the page the user was trying to reach, so there is no need to register a wildcard, and you should not do so. The page the user came from is remembered separately by Stroom and does not travel through the provider.

If Stroom is served under a path prefix, i.e. appConfig.publicUri.pathPrefix is set, that prefix comes before /api.

The Post Logout Redirect URI

When a user signs out, Stroom sends them to the provider’s logout endpoint and asks to be returned to Stroom’s public root:

https://STROOM_FQDN/

Stroom appends a state query parameter to that URI. Providers that match post logout redirect URIs exactly may need to be told to permit it, so if sign out leaves the user on an error page at the provider, that is the usual cause.

The name of the parameter Stroom uses to pass this URI is controlled by logoutRedirectParamName, which may be post_logout_redirect_uri, the default and current specification, or redirect_uri for older providers.

PKCE

Stroom always sends a PKCE code_challenge using the S256 method, and the matching code_verifier when it exchanges the authorization code for tokens.

There is nothing to configure in Stroom for this. Providers that require PKCE, and anything following OAuth 2.1, will be satisfied, and providers that do not support it ignore the extra parameters. Where the provider lets you insist on PKCE, as KeyCloak does, you can safely turn that on.

Claims

Stroom reads three things about a user from the token.

Setting Default Purpose
uniqueIdentityClaim sub Links the identity at the provider to a Stroom user. Must be unique at the provider and must never change for a given person.
userDisplayNameClaim preferred_username A friendlier name shown in the Stroom UI. Need not be unique and may change.
fullNameClaimTemplate ${name} Builds the user’s full name from claim values, e.g. '${given_name} ${family_name}'.

Not every provider issues preferred_username, so check the provider page before assuming the defaults will do.

Token Validation

Stroom validates every token it is given, whether that is the id_token from an interactive sign in or a bearer access token presented to the API.

The signature must verify against a key from the provider’s JWKS, and the algorithm must be one of the RSA, RSA-PSS or ECDSA families. Unsigned tokens and tokens signed with an HMAC algorithm are refused.

The issuer must match what the provider advertised, and the audience must match what Stroom expects. Audience validation is the part most likely to need attention, because providers differ in what they put in the aud claim of an access token. See Audience validation.

Users and Permissions

Authentication is handled by the provider. Authorisation, i.e. what a user may do once they are in, is always handled by Stroom.

Whenever a user successfully signs in via the provider, Stroom automatically creates an entry for them in its own user table. That user starts with no permissions and no group memberships, so an administrator must grant those. This does mean a new user has to sign in once before an administrator can do anything with them.

The very first administrator is a chicken and egg problem, since there is nobody able to grant permissions yet. That is solved with the manage_users command, described on each provider page.

1 - Stroom Configuration

A reference for every Stroom and Stroom-Proxy setting involved in authenticating against an external identity provider.

This page is the provider agnostic reference for the Stroom side of the configuration. The provider specific pages give the values to put in it for a given Identity Provider (IDP) Identity Provider (IDP) An Identity Provider is a system or service that can authenticate a user and assert their identity. Identity providers can support single sign on (SSO), which allows the user to sign in once to the Identity Provider so they are then authenticated to all systems using that IDP.Click to see more details....

All of these settings live under security.authentication.openId in the config.yml file, beneath appConfig for Stroom and proxyConfig for Stroom-Proxy. The structure is identical for both.

A Minimal Configuration

For most providers this is all that is needed:

  security:
    authentication:
      authenticationRequired: true
      openId:
        identityProviderType: EXTERNAL_IDP
        openIdConfigurationEndpoint: "https://IDP_HOST/.well-known/openid-configuration"
        clientId: "StroomClient"
        clientSecret: "THE_CLIENT_SECRET"
        logoutEndpoint: "https://IDP_HOST/logout"

Stroom fetches the provider’s configuration document from openIdConfigurationEndpoint at startup and takes the issuer, authorization endpoint, token endpoint and JWKS URI from it. The logout endpoint is not part of that document, so it is set separately.

If you also want data receipt to be authenticated:

  receive:
    # Require authentication for /datafeed requests
    authenticationRequired: true
    # Allow authentication using an Open ID token
    tokenAuthenticationEnabled: true

Choosing the Identity Provider

identityProviderType

identityProviderType: EXTERNAL_IDP
Value Meaning
INTERNAL_IDP Stroom’s own built in IDP. The default for Stroom, and not valid for Stroom-Proxy.
EXTERNAL_IDP A 3rd party IDP. Stroom’s internal IDP can be the external IDP of a Stroom-Proxy.
NO_IDP No IDP at all. Only for a Stroom-Proxy that forwards to a downstream proxy or Stroom and authenticates with an API key or certificate.

Setting this to EXTERNAL_IDP makes openIdConfigurationEndpoint mandatory; Stroom will refuse to start without it.

Endpoints

openIdConfigurationEndpoint

The provider’s OIDC discovery document, conventionally at https://IDP_HOST/.well-known/openid-configuration. Setting this is much the easiest approach, as Stroom reads the other endpoints from it.

issuer, authEndpoint, tokenEndpoint, jwksUri

Set these only if you are not using a configuration endpoint, or to override a value the provider advertises incorrectly. Anything set here takes precedence over the discovery document.

logoutEndpoint

Where Stroom sends the user to sign out at the provider. This is not part of the discovery document, so it always has to be set by hand, and some providers do not offer one at all.

If it is not set, signing out ends the Stroom session but leaves the user signed in at the provider, so their next visit signs them straight back in without being asked for credentials.

logoutRedirectParamName

logoutRedirectParamName: "post_logout_redirect_uri"

The query parameter Stroom uses to tell the provider where to send the user after signing out. The only permitted values are post_logout_redirect_uri, the default and what the current specification says, and redirect_uri for older providers.

Client Credentials

clientId and clientSecret

The client, sometimes called an application, registered at the provider.

clientSecret may be left unset when the provider authenticates Stroom by mutual TLS instead of a secret.

requestScopes

requestScopes:
  - "openid"
  - "email"

The scopes Stroom asks for during an interactive sign in. Setting this replaces the defaults rather than adding to them, so include openid in whatever you set. Add profile if you need the name, given_name or family_name claims for fullNameClaimTemplate.

clientCredentialsScopes

clientCredentialsScopes:
  - "openid"

The scopes used when Stroom or Stroom-Proxy requests a token for its own service user, rather than for a person. Again, this replaces the default. For Azure AD you will likely need openid and <your-app-id-uri>/.default.

formTokenRequest

formTokenRequest: true

Whether the token request is sent as an HTML form body. Some providers, Cognito among them, require this. It is on by default and rarely needs changing.

Audience Validation

The aud claim of a token names the application the token was minted for. Checking it is what stops a token issued to some other application at the same provider being replayed against Stroom.

Three settings control this.

validateAudience

validateAudience: true

On by default. The audience is checked against allowedAudiences, or against clientId when allowedAudiences is empty.

With identityProviderType: EXTERNAL_IDP and validateAudience left on, at least one of allowedAudiences or clientId must be set. Stroom refuses to start otherwise, rather than letting mandatory validation quietly become a no-op.

allowedAudiences

allowedAudiences: []

A set of acceptable audience values, of which a token must carry at least one. When empty, Stroom validates against clientId instead.

Set this when the provider puts something other than the client id in the aud claim of its access tokens, which is common.

audienceClaimRequired

audienceClaimRequired: true

On by default: a token with no aud claim at all is refused.

Set it to false only for a provider that omits the claim from its access tokens, Cognito being the obvious example. Doing so does not disable validation; an aud claim, where one is present, still has to match.

Token Validation

requiredAccessTokenType

requiredAccessTokenType: null

The JOSE typ header value a token must carry to be accepted as a bearer access token on the API, for example at+jwt for a provider following RFC 9068 , or Bearer for KeyCloak.

When set, a token of any other type, such as an id_token, is refused on the API even though its signature is perfectly valid. That prevents an id_token, which is meant only to tell Stroom who signed in, being replayed as an access token.

Leave it unset, the default, to accept any type. Set it once you have confirmed what your provider actually puts in that header; decoding the header of a real access token is the reliable way to find out.

This applies only to bearer tokens on the API. It has no effect on the interactive sign in flow or on an AWS load balancer data token.

validIssuers

validIssuers: []

Additional issuers to accept beyond the one the provider advertises.

Stroom checks that the issuer in the provider’s configuration response is consistent with openIdConfigurationEndpoint. Where a provider legitimately reports an issuer that is not a parent path of that endpoint, list it here so the check passes.

Signature Algorithms

Not configurable. Stroom accepts RS256/384/512, PS256/384/512 and ES256/384/512, and refuses unsigned tokens and tokens signed with an HMAC algorithm.

There is no reason to expect a mainstream provider to fall foul of this.

Claims

uniqueIdentityClaim

uniqueIdentityClaim: "sub"

The claim used to link an identity at the provider to a Stroom user. It must be unique at the provider and must never change for a given person, which is why sub is the default and normally the right answer.

userDisplayNameClaim

userDisplayNameClaim: "preferred_username"

A friendlier name for the user in the Stroom UI. Not used for identity, so it need not be unique and may change.

Change it if your provider does not issue preferred_username; email is the usual alternative.

fullNameClaimTemplate

fullNameClaimTemplate: '${name}'

Builds the user’s full name from claim values, for example '${given_name} ${family_name}'. Claim names are case sensitive.

AWS Load Balancer Authentication

These apply when an AWS Application Load Balancer in front of Stroom performs the authentication and passes the result on in an x-amzn-oidc-data header.

expectedSignerPrefixes

expectedSignerPrefixes: []

The Amazon Resource Names of the load balancer(s) fronting Stroom, used to verify the signer in the JWT header. Each value is the first N characters of an ARN and must include at least everything up to the colon after the account id, i.e. arn:aws:elasticloadbalancing:region-code:account-id:.

publicKeyUriPattern

publicKeyUriPattern: 'https://public-keys.auth.elb.${awsRegion}.amazonaws.com/${keyId}'

The pattern used to build the URI the load balancer’s public key is fetched from. Supports the ${awsRegion} and ${keyId} variables, each of which may appear more than once. Use single quotes, as with fullNameClaimTemplate.

Stroom-Proxy

Stroom-Proxy takes the same security.authentication.openId block, under proxyConfig. identityProviderType: INTERNAL_IDP is not valid for a proxy; use EXTERNAL_IDP, or NO_IDP where the proxy has no OIDC infrastructure available to it.

A proxy has no interactive users, so the settings concerned with the sign in flow, i.e. the redirect URIs, requestScopes and the claim settings, do not come into play. What it needs is the ability to obtain a token for its own service user via the client credentials grant, and to validate tokens on data it receives.

  receive:
    # Require authentication for /datafeed requests
    authenticationRequired: true
    # Allow authentication using an Open ID token
    tokenAuthenticationEnabled: true
  security:
    authentication:
      openId:
        identityProviderType: EXTERNAL_IDP
        openIdConfigurationEndpoint: "https://IDP_HOST/.well-known/openid-configuration"
        clientId: "StroomProxyClient"
        clientSecret: "THE_CLIENT_SECRET"

Where the proxy forwards data to another proxy or to Stroom, it can attach a token for its service user, provided the destination is configured against the same provider:

  forwardHttpDestinations:
      # Adds a token for the service user to the request
    - addOpenIdAccessToken: true
      enabled: true
      name: "downstream"
      forwardUrl: "http://somehost/stroom/datafeed"

The client used by the proxy needs the client credentials grant enabled at the provider, and the destination must be willing to accept the audience that grant produces. Not every provider supports issuing an OIDC token for a client credentials grant, so check the provider page.

Troubleshooting

Stroom will not start

If identityProviderType is set to ‘EXTERNAL’, property openIdConfigurationEndpoint must be set.

EXTERNAL_IDP requires a discovery endpoint. If your provider genuinely has none, you cannot use this validation route; set the individual endpoints instead and raise it as an issue.

When identityProviderType is EXTERNAL_IDP and validateAudience is true (the default), you must configure either allowedAudiences or clientId

Stroom will not start with audience validation switched on and nothing to validate against, rather than let the check quietly become a no-op. Set clientId, which you almost certainly want anyway, or allowedAudiences.

Issuer ‘X’ obtained from configuration endpoint Y does not share the same base URI.

The provider is advertising an issuer that is not a parent path of the endpoint the document was fetched from, which the OIDC discovery specification says it should be. Some providers do not follow this. Where the value is genuinely correct for your provider, add it to validIssuers.

Issuer ‘X’ obtained from configuration endpoint Y does not match those in the ‘issuer’ or ‘validIssuers’ properties.

You have set issuer or validIssuers, and what the provider advertised is not among them. Correct the configured value, or add the advertised one.

The provider refuses the sign in

An error at the provider, before the user gets back to Stroom, is almost always the redirect URI. Check that https://STROOM_FQDN/api/auth/flow/v1/signin-oidc is registered exactly, using the same scheme, host, port and path prefix as appConfig.publicUri.

This is the single most common problem when upgrading, because Stroom used to send a different redirect URI for every page.

Sign in works but API calls are refused

Interactive sign in validates the id_token, whereas the API validates an access token, and providers treat the two differently. So sign in working tells you the client id, secret and endpoints are all correct, and points at the token validation settings.

In order of likelihood:

  1. Audience. The access token’s aud claim does not match clientId or allowedAudiences, or the token has no aud claim and audienceClaimRequired is true. See Audience validation.
  2. Token type. requiredAccessTokenType is set to something the provider does not put in the token’s typ header. Unset it, or correct it to the value the provider actually uses.
  3. Token type, the other way round. The caller is presenting an id_token rather than an access token. Setting requiredAccessTokenType is what catches this.

Enable debug logging for stroom.security.common.impl.StandardJwtContextFactory to see the issuers, audiences and settings actually in use when a token is validated.

Users sign in but can see nothing

That is expected for a new user. Authentication is all the provider does; permissions are granted in Stroom, and a new user has none. See Users and permissions.

If an administrator you set up with manage_users cannot see anything either, remember that permissions are cached, so a restart may be needed if Stroom was running when the command was issued.

Signing out does not sign the user out of the provider

Either logoutEndpoint is unset, or the provider has no OIDC sign out endpoint, as is the case for Google. The Stroom session ends either way, but the provider’s session does not, so the user’s next visit signs them straight back in.

2 - KeyCloak

How to set up KeyCloak as an external identity provider for Stroom.

This is a guide to setting up a new Stroom instance or cluster with KeyCloak as the 3rd party Identity Provider (IDP) Identity Provider (IDP) An Identity Provider is a system or service that can authenticate a user and assert their identity. Identity providers can support single sign on (SSO), which allows the user to sign in once to the Identity Provider so they are then authenticated to all systems using that IDP.Click to see more details.... It assumes you have deployed a new instance or cluster of Stroom and have not yet started it.

Running KeyCloak

If you already have a KeyCloak instance running then move on to the next section.

This section is not a definitive guide to running or administering KeyCloak. It describes how to run KeyCloak using non-production settings for simplicity and to demonstrate using a 3rd party IDP. You should consult the KeyCloak documentation on how to set up a production ready instance.

The easiest way to run KeyCloak is using Docker. To create a KeyCloak container do the following:

docker create \
  --name keycloak \
  -p 9999:8080 \
  -e KEYCLOAK_ADMIN=admin \
  -e KEYCLOAK_ADMIN_PASSWORD=admin \
  quay.io/keycloak/keycloak:20.0.1 \
  start-dev

This example maps KeyCloak’s port to port 9999 to avoid any clash with Stroom that also runs on 8080. This will create a docker container called keycloak that uses an embedded H2 database to hold its state.

To start the container in the foreground, do:

docker start -a keycloak

KeyCloak should now be running on http://localhost:9999/admin . If you want to run KeyCloak on a different port then delete the container and create it with a different port for the -p argument.

Log into KeyCloak using the username admin and password admin as specified in the environment variables set in the container creation command above. You should see the admin console.

Creating a Realm

First you need to create a Realm.

  1. Click on the drop-down in the left pane that contains the word master.
  2. Click Create Realm.
  3. Set the Realm name to StroomRealm.
  4. Click Create.

Creating a Client

In the new realm click on Clients in the left pane, then Create client.

  1. Set the Client ID to StroomClient.
  2. Click Next.
  3. Set Client authentication to on, which makes this a confidential client.
  4. Ensure the following are ticked:
    • Standard flow
    • Direct access grants
  5. Click Save.

Open the new Client and on the Settings tab set:

  • Valid redirect URIs to https://STROOM_FQDN/api/auth/flow/v1/signin-oidc
  • Valid post logout redirect URIs to https://STROOM_FQDN/*

Where STROOM_FQDN is the public address of Stroom, i.e. what you have set as appConfig.publicUri.

The post logout redirect URI does use a wildcard, because Stroom appends a state parameter to it.

On the Advanced tab, under Advanced settings, set Proof Key for Code Exchange Code Challenge Method to S256. Stroom always sends a PKCE challenge, so KeyCloak can be told to insist on one.

On the Credentials tab copy the Client secret for use later in Stroom config.

Adding an Audience Mapper

This step matters, and is easy to miss.

By default KeyCloak does not put the client id in the aud claim of the access tokens it issues; typically it puts account there instead. Stroom validates the audience of every token it is given, so without this step API calls made with a KeyCloak access token are refused. Interactive sign in still works, because the id_token does carry the client id.

In the realm, click Client scopes in the left pane, then the StroomClient-dedicated scope belonging to the client.

  1. Click Add mapper => By configuration => Audience.
  2. Set Name to stroom-audience.
  3. Set Included Client Audience to StroomClient.
  4. Ensure Add to access token is on.
  5. Click Save.

The alternative, if you would rather not change KeyCloak, is to tell Stroom what KeyCloak actually issues:

        allowedAudiences:
          - "account"

That is weaker, since account is an audience every client in the realm can obtain, so a token minted for another application in the same realm would be accepted by Stroom. Prefer the mapper.

Creating Users

Click on Users in the left pane then Add user. Set the following:

  • Username - admin
  • First name - Administrator
  • Last name - Administrator

Click Create.

Select the Credentials tab and click Set password.

Set the password to admin and set Temporary to off.

Repeat this process for the following user:

  • Username - jbloggs
  • First name - Joe
  • Last name - Bloggs
  • Password - password

Configure Stroom for KeyCloak

Edit the config.yml file and set the following values:

  receive:
    # Set to true to require authentication for /datafeed requests
    authenticationRequired: true
    # Set to true to allow authentication using an Open ID token
    tokenAuthenticationEnabled: true
  security:
    authentication:
      authenticationRequired: true
      openId:
        # Tells Stroom to use an external IDP for authentication
        identityProviderType: EXTERNAL_IDP
        # The endpoint to obtain the rest of the IDP's configuration. Specific to the realm/issuer.
        openIdConfigurationEndpoint: "http://localhost:9999/realms/StroomRealm/.well-known/openid-configuration"
        # The client ID created in KeyCloak
        clientId: "StroomClient"
        # The client secret copied from KeyCloak above
        clientSecret: "XwTPPudGZkDK2hu31MZkotzRUdBWfHO6"
        # The URL on the IDP to redirect users to when logging out of Stroom
        logoutEndpoint: "http://localhost:9999/realms/StroomRealm/protocol/openid-connect/logout"
        # KeyCloak stamps its access tokens with a 'typ' header of 'Bearer'. Requiring it stops an
        # id_token being replayed against the API as though it were an access token.
        requiredAccessTokenType: "Bearer"

These values are obtained from the IDP. In the case of KeyCloak they can be found by clicking on Realm settings => Endpoints => OpenID Endpoint Configuration and extracting the various values from the JSON response. Alternatively they can typically be found at https://host/.well-known/openid-configuration on any Open ID Connect IDP. The values will reflect the host and port that the IDP is running on along with the name of the realm.

Setting the above values assumes KeyCloak is running on localhost:9999 and the realm name is StroomRealm.

The claim defaults suit KeyCloak, so there is nothing to set for them. KeyCloak issues preferred_username, which Stroom uses as the display name, and issues name where the user has a first and last name, which satisfies the default fullNameClaimTemplate of ${name}.

Setting up the Admin User in Stroom

Now that the admin user exists in the IDP we need to grant it Administrator rights in Stroom.

In the Users section of KeyCloak click on user admin. On the Details tab copy the value of the ID field. The ID is in the form of a UUID UUID A Universally Unique Identifier for uniquely identifying something. UUIDs are used as the identifier in Doc Refs. An example of a UUID is 4ffeb895-53c9-40d6-bf33-3ef025401ad3.Click to see more details.... This ID is the sub claim, which is what Stroom uses to uniquely identify the user and associate it with the identity in KeyCloak.

To set up Stroom with this admin user run the following (before Stroom has been started for the first time):

subject_id="XXX"; \
java -jar /absolute/path/to/stroom-app-all.jar \
  manage_users \
  --createUser "${subject_id}" \
  --createGroup Administrators \
  --addToGroup "${subject_id}" Administrators \
  --grantPermission Administrators "Administrator" \
  ../local.yml

Where XXX is the user ID copied from the IDP as described above. This command is repeatable as it will skip any users/groups/memberships that already exist.

This command will do the following:

  • Create the Stroom User by creating an entry in the stroom_user database table for the IDP’s admin user.
  • Ensure that an Administrators group exists (i.e. an entry in the stroom_user database table for the Administrators group).
  • Add the admin user to the group Administrators.
  • Grant the application permission Administrator to the group Administrators.

Logging into Stroom

As the Administrator

Now that the user and permissions have been set up in Stroom, the administrator can log in.

First start the Stroom instance or cluster.

Navigate to https://STROOM_FQDN and Stroom should re-direct you to the IDP (KeyCloak) to authenticate. Enter the username admin and password admin. You should be authenticated by KeyCloak and re-directed back to Stroom. Your user ID is shown in the bottom right corner of the Welcome tab.

As an administrator, the

Tools
User Permissions
menu item will be available to manage the permissions of any users that have logged on at least once.

Now select

User
Logout
to be re-directed to the IDP to log out. Once you log out of the IDP it should re-direct you back to Stroom, which will send you to the IDP login screen to log back in again.

As an Ordinary User

On the IDP login screen, log in as user jbloggs with the password password. You will be re-directed to Stroom, however the explorer tree will be empty and most of the menu items will be disabled. In order to gain permissions to do anything in Stroom, a Stroom administrator will need to grant application and document permissions and/or group memberships to the user via the

Tools
User Permissions
menu item.

Configure Stroom-Proxy for KeyCloak

Create a second client in KeyCloak for the proxy, following the steps above but with Service accounts roles enabled so that it can use the client credentials grant. A proxy has no interactive users, so it needs no redirect URIs.

Edit the proxy’s config.yml file and set the following values:

  receive:
    # Set to true to require authentication for /datafeed requests
    authenticationRequired: true
    # Set to true to allow authentication using an Open ID token
    tokenAuthenticationEnabled: true
  security:
    authentication:
      openId:
        identityProviderType: EXTERNAL_IDP
        openIdConfigurationEndpoint: "http://localhost:9999/realms/StroomRealm/.well-known/openid-configuration"
        clientId: "StroomProxyClient"
        clientSecret: "THE_PROXY_CLIENT_SECRET"
        logoutEndpoint: "http://localhost:9999/realms/StroomRealm/protocol/openid-connect/logout"

If Stroom-Proxy is configured to forward data on to another Stroom-Proxy or Stroom instance then it can use tokens when forwarding that data. This assumes the downstream Stroom or Stroom-Proxy is also configured to use the same external IDP.

  forwardHttpDestinations:

      # If true, adds a token for the service user to the request
    - addOpenIdAccessToken: true
      enabled: true
      name: "downstream"
      forwardUrl: "http://somehost/stroom/datafeed"

The token used will be for the service user account of the identity provider client used by Stroom-Proxy.

That token’s audience is validated at the destination just like any other, so the destination needs either an audience mapper on the proxy’s client, or the audience the proxy’s tokens actually carry listed in its allowedAudiences.

3 - Amazon Cognito

How to set up an Amazon Cognito user pool as an external identity provider for Stroom.

This page covers using an Amazon Cognito user pool as Stroom’s Identity Provider (IDP) Identity Provider (IDP) An Identity Provider is a system or service that can authenticate a user and assert their identity. Identity providers can support single sign on (SSO), which allows the user to sign in once to the Identity Provider so they are then authenticated to all systems using that IDP.Click to see more details....

Cognito differs from a typical OIDC provider in two ways that directly affect the Stroom configuration, so they are worth knowing before you start.

  • Its access tokens carry no aud claim, using client_id instead. Stroom requires an audience claim by default, so this has to be turned off.
  • Its authorization, token and sign out endpoints belong to the hosted UI domain, which is separate from the user pool’s issuer host.

Creating the User Pool and App Client

In the AWS console, under Cognito:

  1. Create a user pool, or use an existing one.
  2. Configure a domain for the pool, either a Cognito prefix domain giving https://YOUR_PREFIX.auth.REGION.amazoncognito.com, or your own custom domain. This provides the hosted UI and the OAuth endpoints, and is required.
  3. Create an app client of the confidential type, i.e. one with a client secret.
  4. Enable the Authorization code grant for the client. Do not enable the implicit grant.
  5. Set the OpenID Connect scopes to at least openid and email.
  6. Set the Allowed callback URL to https://STROOM_FQDN/api/auth/flow/v1/signin-oidc.
  7. Set the Allowed sign out URL to https://STROOM_FQDN/.
  8. Note the app client id and app client secret, and the user pool id.

Where STROOM_FQDN is the public address of Stroom, i.e. what you have set as appConfig.publicUri.

Cognito supports PKCE, and Stroom always sends an S256 challenge, so there is nothing to configure for it.

The Endpoints

Two different hosts are involved.

Purpose Host
Issuer, discovery document, JWKS https://cognito-idp.REGION.amazonaws.com/USER_POOL_ID
Authorization, token, sign out Your pool’s domain, e.g. https://YOUR_PREFIX.auth.REGION.amazoncognito.com

The discovery document is at:

https://cognito-idp.REGION.amazonaws.com/USER_POOL_ID/.well-known/openid-configuration

Set openIdConfigurationEndpoint to it, so that Stroom picks up the issuer and the JWKS URI.

Set authEndpoint and tokenEndpoint explicitly to your pool’s domain, since those are the endpoints your users and Stroom actually need to reach:

  • https://YOUR_PREFIX.auth.REGION.amazoncognito.com/oauth2/authorize
  • https://YOUR_PREFIX.auth.REGION.amazoncognito.com/oauth2/token

Configuring Stroom

  receive:
    # Set to true to require authentication for /datafeed requests
    authenticationRequired: true
    # Set to true to allow authentication using an Open ID token
    tokenAuthenticationEnabled: true
  security:
    authentication:
      authenticationRequired: true
      openId:
        identityProviderType: EXTERNAL_IDP
        openIdConfigurationEndpoint: "https://cognito-idp.eu-west-2.amazonaws.com/eu-west-2_ABC123456/.well-known/openid-configuration"
        # The hosted UI endpoints, which are on the pool's domain rather than the issuer host
        authEndpoint: "https://mydomain.auth.eu-west-2.amazoncognito.com/oauth2/authorize"
        tokenEndpoint: "https://mydomain.auth.eu-west-2.amazoncognito.com/oauth2/token"
        logoutEndpoint: "https://mydomain.auth.eu-west-2.amazoncognito.com/logout"
        # The app client id and secret
        clientId: "1h57kf5cpparlm9m52319hsnrf"
        clientSecret: "THE_APP_CLIENT_SECRET"
        # Cognito requires the token request to be sent as a form. This is the default.
        formTokenRequest: true
        # Cognito access tokens carry no 'aud' claim, so an absent one must not be a failure.
        # An 'aud' claim that IS present, as on an id_token, is still validated against clientId.
        audienceClaimRequired: false
        # Cognito does not issue 'preferred_username'
        userDisplayNameClaim: "cognito:username"

Audience Validation

This is the setting most likely to catch you out.

A Cognito id_token, used by interactive sign in, carries aud set to the app client id, so it validates against clientId with no further configuration.

A Cognito access token, used for API calls, carries no aud claim at all; the equivalent information is in a client_id claim, which is not something Stroom validates against. With the default of audienceClaimRequired: true those tokens are refused, so set it to false.

Leave validateAudience at its default of true. Setting it to false would switch off audience checking for the id_token as well, which Cognito populates perfectly well.

Claims

Cognito does not issue a preferred_username claim unless the user pool has been set up with that attribute, so the Stroom default for userDisplayNameClaim will usually not resolve. Use cognito:username, or email where every user has one.

uniqueIdentityClaim should be left as sub, which for Cognito is a UUID that is stable for the life of the user.

For fullNameClaimTemplate to resolve, the corresponding attributes must be populated on the user and included in the token. Add profile to requestScopes if you need name, given_name or family_name.

Signing Out

Cognito’s sign out endpoint has historically expected the return address in a logout_uri parameter rather than the post_logout_redirect_uri that Stroom sends.

Check whether sign out returns your users to Stroom. If it leaves them on an error page at Cognito, try:

        logoutRedirectParamName: "redirect_uri"

Those two values are the only ones Stroom accepts. If neither works with your pool, leave logoutEndpoint unset; signing out will then end the Stroom session without signing the user out of Cognito, which means their next visit will sign them straight back in without being asked for credentials.

Access Token Type

Leave requiredAccessTokenType unset unless you have decoded the header of a real Cognito access token and confirmed what it contains. It is a hardening measure, and setting it to a value your provider does not use will refuse every API call.

Setting up the Admin User in Stroom

The bootstrap process is the same as for any provider. Find the sub of the user who is to be the administrator, which for Cognito is the user’s UUID as shown in the console, then run the manage_users command before starting Stroom for the first time.

subject_id="XXX"; \
java -jar /absolute/path/to/stroom-app-all.jar \
  manage_users \
  --createUser "${subject_id}" \
  --createGroup Administrators \
  --addToGroup "${subject_id}" Administrators \
  --grantPermission Administrators "Administrator" \
  ../local.yml

Stroom-Proxy with Cognito

A Stroom-Proxy obtains a token for its own service user using the client credentials grant.

In Cognito that grant requires a resource server with custom scopes defined on it, and the resulting tokens carry those custom scopes rather than openid. Set clientCredentialsScopes to the custom scopes you have defined:

  security:
    authentication:
      openId:
        identityProviderType: EXTERNAL_IDP
        openIdConfigurationEndpoint: "https://cognito-idp.eu-west-2.amazonaws.com/eu-west-2_ABC123456/.well-known/openid-configuration"
        tokenEndpoint: "https://mydomain.auth.eu-west-2.amazoncognito.com/oauth2/token"
        clientId: "THE_PROXY_APP_CLIENT_ID"
        clientSecret: "THE_PROXY_APP_CLIENT_SECRET"
        formTokenRequest: true
        audienceClaimRequired: false
        clientCredentialsScopes:
          - "https://stroom.example.com/api.write"

The destination the proxy forwards to must be configured to accept the tokens this produces, which again means audienceClaimRequired: false at that end.

4 - Google

How to set up Google as an external identity provider for Stroom.

This page covers using Google Identity as Stroom’s Identity Provider (IDP) Identity Provider (IDP) An Identity Provider is a system or service that can authenticate a user and assert their identity. Identity providers can support single sign on (SSO), which allows the user to sign in once to the Identity Provider so they are then authenticated to all systems using that IDP.Click to see more details..., whether for consumer Google accounts or for a Google Workspace domain.

Creating the OAuth Client

In the Google Cloud Console :

  1. Select or create a project.
  2. Configure the OAuth consent screen. For a Workspace domain choose the Internal user type, which restricts sign in to your own domain. For consumer accounts the only option is External.
  3. Go to APIs & Services => Credentials => Create Credentials => OAuth client ID.
  4. Choose an application type of Web application.
  5. Under Authorised redirect URIs, add https://STROOM_FQDN/api/auth/flow/v1/signin-oidc.
  6. Create the client and note the Client ID and Client secret.

Where STROOM_FQDN is the public address of Stroom, i.e. what you have set as appConfig.publicUri.

Google supports PKCE, and Stroom always sends an S256 challenge, so there is nothing to configure for it.

There is no sign out URL to register, because Google has no OIDC sign out endpoint to register one with.

Configuring Stroom

  security:
    authentication:
      authenticationRequired: true
      openId:
        identityProviderType: EXTERNAL_IDP
        openIdConfigurationEndpoint: "https://accounts.google.com/.well-known/openid-configuration"
        clientId: "123456789012-abcdefghijklmnop.apps.googleusercontent.com"
        clientSecret: "THE_CLIENT_SECRET"
        # Google issues no 'preferred_username' claim
        userDisplayNameClaim: "email"
        # 'profile' is needed for the 'name' claim used by fullNameClaimTemplate
        requestScopes:
          - "openid"
          - "email"
          - "profile"

The discovery document supplies the issuer, https://accounts.google.com, along with the authorization, token and JWKS endpoints, so none of those need setting by hand.

Note that logoutEndpoint is deliberately absent; see Signing out below.

Audience Validation

Nothing to do. Google’s id_token carries aud set to your client id, so it validates against clientId with the default settings.

Claims

Google issues sub, email, email_verified, name, given_name, family_name and picture, and hd for a Workspace account. It does not issue preferred_username, which is Stroom’s default for userDisplayNameClaim, so that has to be changed. email is the natural choice.

name, given_name and family_name require the profile scope, which is why it is added to requestScopes above. Without it the default fullNameClaimTemplate of ${name} will not resolve.

Leave uniqueIdentityClaim as sub. Google’s sub is stable for a given account, unlike the email address.

Signing Out

Google offers no OIDC sign out endpoint, so leave logoutEndpoint unset.

Logging out of Stroom then ends the Stroom session but leaves the user signed in to Google. Their next visit to Stroom will sign them straight back in without being asked for credentials, which is worth being aware of on a shared machine.

Do not point logoutEndpoint at a general Google sign out URL, as that would sign the user out of every Google service on that browser, which is unlikely to be what they expect from a Stroom logout.

Access Token Type

Leave requiredAccessTokenType unset. It applies to JWT bearer tokens on the API, and Google’s access tokens are not JWTs.

Restricting Who Can Sign In

Authentication and authorisation are separate. Anyone Google will authenticate can complete a sign in and have a Stroom user created for them, but that user starts with no permissions and no group memberships, so they can see nothing.

Even so, you should restrict who can reach the sign in at all:

  • For a Workspace domain, set the OAuth consent screen to Internal, so only accounts in your domain can authenticate.
  • For consumer accounts there is no equivalent, so any Google account can reach the consent screen. Consider whether Google is the right provider in that case.

Stroom has no configuration to restrict sign in by hd or email domain, so this has to be done at Google.

Setting up the Admin User in Stroom

Find the sub of the account that is to be the administrator. Unlike KeyCloak and Cognito, Google does not show this in an admin console; the reliable way to obtain it is to decode an id_token issued for that account, or read it from Stroom’s logs after the person has signed in once.

The simplest route is therefore:

  1. Configure Stroom as above and start it.
  2. Have the intended administrator sign in once. They will land in Stroom with no permissions.
  3. Read their sub from the Stroom logs, or from the
    Tools
    Users
    screen if another administrator is available.
  4. Run the manage_users command with that value, then restart Stroom so the permission caches are rebuilt.
subject_id="XXX"; \
java -jar /absolute/path/to/stroom-app-all.jar \
  manage_users \
  --createUser "${subject_id}" \
  --createGroup Administrators \
  --addToGroup "${subject_id}" Administrators \
  --grantPermission Administrators "Administrator" \
  ../local.yml

The command is repeatable and will skip anything that already exists, so running it against a user that signed in earlier is fine.

Data Receipt and the API

Because Google’s access tokens are opaque rather than JWTs, Stroom cannot validate them, so this will not work:

  receive:
    authenticationRequired: true
    tokenAuthenticationEnabled: true

Use Stroom API Keys API Key API Keys are a form of authentication token that are created within Stroom for use by Stroom-Proxy instances or other clients that want to use Stroom’s API. It is an encrypted string that contains details of the user and the expiration date of the token. Possession of a valid API Key for a user account means that you can do anything that the user can do in the user interface via the API.Click to see more details... for API clients and for feed status checks, or client certificates for data receipt.

Stroom-Proxy with Google

Google has no OIDC client credentials grant, so a Stroom-Proxy cannot obtain a service user token from it, and addOpenIdAccessToken on a forward destination has nothing to add.

Configure the proxy with identityProviderType: NO_IDP and give it an API key created in Stroom:

  feedStatus:
    apiKey: "AN_API_KEY_CREATED_IN_STROOM"
  security:
    authentication:
      openId:
        identityProviderType: NO_IDP

5 - Microsoft Entra ID (Azure AD)

How to set up Microsoft Entra ID, formerly Azure Active Directory, as an external identity provider for Stroom.

This page covers using Microsoft Entra ID as Stroom’s Identity Provider (IDP) Identity Provider (IDP) An Identity Provider is a system or service that can authenticate a user and assert their identity. Identity providers can support single sign on (SSO), which allows the user to sign in once to the Identity Provider so they are then authenticated to all systems using that IDP.Click to see more details.... Entra ID is the current name for what was Azure Active Directory, and much of the tooling and documentation still says Azure AD.

Entra ID has two generations of endpoint, v1.0 and v2.0, which issue tokens with different issuers and different formats. Two of the three things most likely to go wrong here come from mixing them up, so it is worth being deliberate: use the v2.0 endpoints throughout.

Creating the App Registration

In the Microsoft Entra admin centre , or the Azure portal under Microsoft Entra ID:

  1. Go to App registrations => New registration.
  2. Give it a name, e.g. Stroom.
  3. For Supported account types choose Accounts in this organizational directory only, i.e. single tenant, unless you have a specific reason not to. This restricts sign in to your own tenant.
  4. Under Redirect URI select a platform of Web and enter https://STROOM_FQDN/api/auth/flow/v1/signin-oidc.
  5. Click Register, then note the Application (client) ID and the Directory (tenant) ID from the overview page.

Where STROOM_FQDN is the public address of Stroom, i.e. what you have set as appConfig.publicUri.

Then, still in the app registration:

  1. Under Authentication, add a Front-channel logout URL of https://STROOM_FQDN/, and add the same value under Redirect URIs if your tenant requires post logout redirect URIs to be registered.
  2. Under Certificates & secrets => Client secrets, create a new secret and copy its Value immediately, as it is only shown once.

Entra ID supports PKCE, and Stroom always sends an S256 challenge, so there is nothing to configure for it.

Exposing an API for Access Tokens

This step is what makes API authentication work, and is the Entra ID equivalent of KeyCloak’s audience mapper.

If Stroom only ever asks for the openid, email and profile scopes, Entra ID issues an access token for Microsoft Graph rather than for Stroom. Those tokens are intended only for Graph, are not in a format a third party can validate, and will fail validation at Stroom. Interactive sign in still works throughout, because it uses the id_token.

To get an access token that Stroom can validate, the app registration has to expose an API of its own:

  1. Go to Expose an API => Add next to Application ID URI. Accept the default of api://<client-id>, or set your own.
  2. Click Add a scope, name it something like user_impersonation, and choose who can consent.
  3. Under Manifest, set accessTokenAcceptedVersion to 2.

Callers then request that scope, e.g. api://<client-id>/user_impersonation, and the resulting access token carries an aud claim that Stroom can be configured to accept.

Configuring Stroom

  receive:
    # Set to true to require authentication for /datafeed requests
    authenticationRequired: true
    # Set to true to allow authentication using an Open ID token
    tokenAuthenticationEnabled: true
  security:
    authentication:
      authenticationRequired: true
      openId:
        identityProviderType: EXTERNAL_IDP
        # Note the '/v2.0' path part. Without it you get the v1.0 endpoints and a different issuer.
        openIdConfigurationEndpoint: "https://login.microsoftonline.com/TENANT_ID/v2.0/.well-known/openid-configuration"
        # The Application (client) ID from the app registration overview
        clientId: "11111111-2222-3333-4444-555555555555"
        clientSecret: "THE_CLIENT_SECRET_VALUE"
        logoutEndpoint: "https://login.microsoftonline.com/TENANT_ID/oauth2/v2.0/logout"
        # Accept both the id_token audience (the client id) and the access token audience
        # (the Application ID URI). Adjust to match what your tokens actually carry.
        allowedAudiences:
          - "11111111-2222-3333-4444-555555555555"
          - "api://11111111-2222-3333-4444-555555555555"

Replace TENANT_ID with the Directory (tenant) ID.

Issuers

The v2.0 discovery endpoint advertises an issuer of https://login.microsoftonline.com/TENANT_ID/v2.0, which is a parent path of the discovery endpoint itself, so Stroom’s issuer check is satisfied with no extra configuration.

The v1.0 endpoints are not so tidy. Their issuer is https://sts.windows.net/TENANT_ID/, which shares no base URI with the discovery endpoint, and Stroom will refuse to start with:

Issuer ‘X’ obtained from configuration endpoint Y does not share the same base URI.

If you must use v1.0, or you have v1.0 access tokens in circulation from an app registration you cannot change, list the issuer explicitly:

        validIssuers:
          - "https://sts.windows.net/TENANT_ID/"

Using the v2.0 endpoints and accessTokenAcceptedVersion: 2 is much the better answer.

Audience Validation

An Entra ID id_token carries aud set to the Application (client) ID, so interactive sign in validates against clientId with no further configuration.

An access token for your exposed API carries aud set to either the Application ID URI or the client id, depending on accessTokenAcceptedVersion and how the scope was requested. Listing both in allowedAudiences, as above, covers either.

Leave validateAudience at its default of true.

Claims

The Stroom defaults suit Entra ID v2.0. It issues preferred_username, normally the user principal name, which Stroom uses as the display name, and name, which satisfies the default fullNameClaimTemplate of ${name}.

For uniqueIdentityClaim you have a choice:

Claim Notes
sub The Stroom default. In Entra ID this is pairwise, i.e. a different value per application, and stable for the life of that app registration. Delete and recreate the app registration and every user’s sub changes, orphaning their Stroom user.
oid The user’s object id in the directory. Stable across applications and across app registrations, so it survives a re-registration. Unique within a tenant.

oid is the more robust choice for a single tenant deployment, and is what Microsoft’s own guidance points to as the durable identifier. sub is fine if you are confident the app registration will not be recreated.

        uniqueIdentityClaim: "oid"

Group and Role Claims

Entra ID can be configured to emit groups and roles claims. Stroom does not consume them. All authorisation is done with Stroom’s own users, groups and permissions, so directory group membership has no effect on what a user can do in Stroom.

Access Token Type

Leave requiredAccessTokenType unset until you have decoded the header of a real access token from your tenant and confirmed what it contains. Setting it to a value your tokens do not use will refuse every API call.

Setting up the Admin User in Stroom

Find the identifier of the account that is to be the administrator, matching whatever you set uniqueIdentityClaim to.

If you are using oid, it is shown as the Object ID on the user’s page under Users in the Entra admin centre. If you are using sub, it is pairwise and not shown anywhere in the portal, so you will need to decode an id_token issued for that user, or have them sign in once and read it from the Stroom logs.

Then run the following, ideally before Stroom has been started for the first time:

subject_id="XXX"; \
java -jar /absolute/path/to/stroom-app-all.jar \
  manage_users \
  --createUser "${subject_id}" \
  --createGroup Administrators \
  --addToGroup "${subject_id}" Administrators \
  --grantPermission Administrators "Administrator" \
  ../local.yml

The command is repeatable and will skip anything that already exists, so running it against a user who has already signed in is fine. Restart Stroom afterwards if it was running, as permissions are cached.

Stroom-Proxy with Entra ID

A Stroom-Proxy obtains a token for its own service user using the client credentials grant.

Create a second app registration for the proxy, then grant it access to the API exposed by the Stroom app registration:

  1. In the proxy’s app registration, go to API permissions => Add a permission => My APIs and select the Stroom app registration.
  2. Choose Application permissions, which is the client credentials case, rather than delegated permissions.
  3. Have a directory administrator grant admin consent, without which the grant will fail.

Entra ID’s client credentials flow uses the .default scope of the target API:

  security:
    authentication:
      openId:
        identityProviderType: EXTERNAL_IDP
        openIdConfigurationEndpoint: "https://login.microsoftonline.com/TENANT_ID/v2.0/.well-known/openid-configuration"
        clientId: "THE_PROXY_CLIENT_ID"
        clientSecret: "THE_PROXY_CLIENT_SECRET"
        clientCredentialsScopes:
          - "api://11111111-2222-3333-4444-555555555555/.default"

The destination the proxy forwards to must accept the audience these tokens carry, which will be the Application ID URI or client id of the Stroom app registration, so make sure it appears in that destination’s allowedAudiences.