Authorisation for the Query Engine is based around JWTs.

There are three basic ways in which authorisation is intended to be used:

  • The Query Engine is embedded in the host application in the context of a user session and a prepared JWT is passed through by the host application.

  • The Query Engine is called directly by another system that can only provide Basic Authentication.

  • The Query Engine can be called without requiring any authorisation (some feed can be public).

Providing a JWT

There are five ways in which authorisation data can get to the Query Engine:

  • A bearer token can be provided via the standard Authentication header. This is the recommended approach when the query engine is accessed in the context of a user session in the host application. This handling can be disabled by setting the parameter enableBasicAuth to false;

  • Basic authentication can be used when the query engine is called directly. In this situation the passed in credentials will be used in making a client credentials grant to the host system. This handling can be disabled by setting the parameter enableBearerAuth to false;

  • If there is a proxy in front of the query engine that has already validated the JWT it can pass in the complete JWT payload as a base64 encoded header.

  • For the UI only, a session cookie may be generated that stores a JWT in the query engine database (and in a local cache), see OpenID Connect in the UI. The session cookie value itself is just 100 bytes generated by SecureRandom.

  • If there is no Authentication header and no OpenID Introspection header the query will be run without any authorisation data.

The security of all of these approaches is dependent on the headers reaching the query engine. It is very important that however the query engine is configured the ingress approach prevents the forging of relevant headers (i.e. X-Forwarded-Host).

Path Hijacking

The recommended approach for the ingress to the query engine is for all requests to a given path on the host to be sent directly to query engine, bypassing the main host service completely. This is easy to accomplish with most ingress proxies.

Basic Authentication

When enableBasicAuth has not been set to false credentials provided in a Basic Authorisation header will be used to make a client credentials grant to the host system. This will begin by performing OpenID Connect Discovery starting at host on which the request was made (considering the X-Forwarded-Host header if it exists, and the values supplied as jwt.issuerHostPath will be added to the host, enabling issuers that are not at the root path of the host). The query engine will then make the client credentials grant request to the authorization_endpoint found in the metadata.

Validation of JWTs

Every token will be validated before it is accepted by the query engine. This follows a fairly standard process, but there is some extra configuration of the query engine that governs it:

  1. The JWT is parsed - split into three and each component base64 decoded.

  2. The algorithm specified in the JWT header must match one of the permitted algorithms (RS256, RS384 and RS512 - this list could be extended if useful).

  3. The public key must be found. There are two ways in which the query engine can find the public key:

    • If the parameter jwt.issuerHostPath is set or the parameter jwt.jwksEndpoints is empty the JWKS url will be sought by OpenID Connect Discovery using the Host at which the request was made (considering the X-Forwarded-Host header if it exists, and the values supplied as jwt.issuerHostPath will be added to the host, enabling issuers that are not at the root path of the host).

    • Otherwise all the endpoints listed in jwt.jwksEndpoints will be queried for JWKs. However the public key is found it will be cached, obeying the cache-control(max-age) HTTP headers and used from the cache in future. If a JWT is received that references a JWK that is not found in the cache the discovery process will be run again.

  4. The issuer must either match jwt.acceptableIssuerRegexes or be found in the file specified by jwt.acceptableIssuersFile. The regex approach is aimed at design mode and test environments, production environments should prefer jwt.acceptableIssuersFile (the jwt.filePollPeriodDuration can be used to control how frequently the query engine rechecks the file).

  5. The nbf and exp claims in the JWT must indicate that the token is currently valid.

  6. The audience must match any one of the values specified by jwt.requiredAudiences (which defaults to just "query-engine").

  7. The JWT must specify a subject (it must have a non-blank "sub" claim).

OpenID Connect in the UI

The UI for the Query Engine can be configured to use OpenID Connect, but this does not supersede the requirement for validation configuration.

OpenID Connect is configured using the session configuration option, the key part of which is the oauth map.

Any number of authentication endpoints may be configured. When a user attempts to login (which will happen on first access to the UI if requireSession is set) they will be presented with a list of all the auth endpoints to choose from.

After authenticating with their chosen provider the generated JWT will be stored in the Query Engine database and a random cookie (configured at sessionCookie) will be generated to refer to it.