General Configuration Types

Assertions

This type enables configuration of required token and claim assertions. Depending on the object type (JWT or introspection response), the assertions apply to different parts of such objects.

  • scopes: Scopes Matcher (optional)

    Required scopes given to the client.

  • audience: string array (optional)

    Required entries in the aud claim. Both cases, either as whitespace separated string, or a JSON array are considered.

  • issuers: string array (mandatory)

    Issuers to trust. At least one issuer must be configured.

  • allowed_algorithms: string array (optional)

    Algorithms, which are trusted (according to RFC 7518). Defaults to the following list: ES256, ES384, ES512, PS256, PS384, PS512.

  • validity_leeway Duration (optional)

    The time leeway to consider while verifying the iat, exp and the nbf. Defaults to 10 seconds.

Example 1. Assertions configuration
issuers:
 - foo
 - bar
audience:
 - zap
scopes:
 - baz
allowed_algorithms:
 - ES512
validity_leeway: 5s

Here we say, the token must have been issued either by the issuer foo, or the issuer bar, the aud claim must contain zap, the scope claim (either scp or scope) must be present and contain the scope baz, if the token or the introspection response is signed, it must have been signed by using the ES512 algorithm (ECDSA using P-521 and SHA-512) and if the information about token validity is present, we respect a deviation of 5 seconds.

Authentication Data Source

An authentication data source is actually a list of possible strategies for subject authentication data retrieval. The entries following the first one are fallbacks and are only executed if the previous strategy could not retrieve the required authentication data from the request.

This fallback mechanism can become handy, if different clients of your application send the authentication data using different methods. RFC 6750 describes for example how to use bearer tokens in HTTP requests to access OAuth 2.0 protected resources. This RFC says, a token can either be sent in the Authorization header, or in a query parameter, or even as part of the HTTP body. So you can define the following list to let Heimdall try to extract the access token from all three places:

- header: Authorization
  schema: Bearer
- query_parameter: access_token
- body_parameter: access_token

The available strategies are described in the following sections.

This strategy can retrieve authentication data from a specific HTTP cookie. Following properties are supported:

  • cookie: string (mandatory)

    The name of the cookie to use.

Example 2. Cookie Strategy usage

Imagine you want Heimdall to verify an authentication session, which is represented by a specific cookie before the request hits your upstream service. If the client of your upstream application, which is case of a cookie would usually be a browser sends a cookie named "session", you can inform Heimdall to extract and use it by configuring this strategy as follows:

- cookie: my_session_cookie

Header Strategy

This strategy can retrieve authentication data from a specific HTTP header. Following properties are supported:

  • header: string (mandatory)

    The name of the header to use.

  • schema: string (optional)

    Schema, which should be present in the header value. If specified, but not present in the header value, the authentication data cannot be retrieved, effectively making the affected authenticator feel not responsible for the request.

Example 3. Header Strategy usage

Imagine you want Heimdall to verify an access token used to protect your upstream service. If the client of your upstream application sends the access token in the HTTP Authorization header, you can inform Heimdall to extract it from there by configuring this strategy as follows:

- header: Authorization
  schema: Bearer

Query Parameter Strategy

  • query_parameter: string (mandatory)

    The name of the query parameter to use.

Example 4. Query Parameter Strategy usage

Imagine you want Heimdall to verify an access token used to protect your upstream service. If the client of your upstream application sends the access token in the query parameter named "access_token", you can inform Heimdall to extract it from there by configuring this strategy as follows:

- query_parameter: access_token

Body Parameter Strategy

The usage of this strategy is only possible when the request payload is either JSON or application/x-www-form-urlencoded encoded. The Content-Type of the request must also either be set to application/x-www-form-urlencoded or to a MIME type, which contains json.

  • body_parameter: string (mandatory)

    The name of the body parameter to use.

Example 5. Body Parameter Strategy usage

Imagine you want Heimdall to verify an access token used to protect your upstream service. If the client of your upstream application sends the access token in the body parameter named "access_token", you can inform Heimdall to extract it from there by configuring this strategy as follows:

- body_parameter: access_token

Authentication Strategy

Authentication strategy is kind of abstract type, so you have to define which specific type to use. Each type has its own configuration options.

An AuthStrategy configuration entry must contain the following two properties:

  • type - The type of the strategy. Available types are described in the following sections.

  • config - The strategy specific configuration.

Available strategies are described in the following sections.

API Key Strategy

This strategy can be used if your endpoint expects a specific api key be send in a header or a cookie.

type must be set to api_key. config supports the following properties:

  • in: string (mandatory)

    Where to put the api key. Can be either header, or cookie.

  • name: string (mandatory)

    The name of either the header or the cookie.

  • value: string (mandatory)

    The value of the api key.

Example 6. API Key Strategy configuration

The following snippet shows how to configure this strategy to send an api key in the X-My-API-Key HTTP header.

type: api_key
config:
  in: header
  name: X-My-API-Key
  value: super-duper-secret-key

Basic Auth Strategy

This strategy can be used if your endpoint is protected by HTTP basic authentication and expects the HTTP Authorization header with required values.

type must be set to basic_auth. config supports the following properties:

  • user: string (mandatory)

    The user-id.

  • password: string (mandatory)

    The password.

Example 7. Basic Auth Strategy configuration

The following snippet shows how to configure this strategy with user set to "Alladin" and password set to "open sesame"

type: basic_auth
config:
  user: Alladin
  password: open sesame

Client Credentials Strategy

This strategy implements the OAuth2 Client Credentials Grant Flow to obtain an access token expected by the endpoint. Heimdall caches the received access token.

type must be set to client_credentials. config supports the following properties:

  • client_id: string (mandatory)

    The client identifier for Heimdall.

  • client_secret: string (mandatory)

    The client secret for Heimdall.

  • scopes: string array (optional)

    The scopes required for the access token.

  • token_url: string (mandatory)

    The token endpoint of the authorization server.

Example 8. Client Credentials Strategy configuration
type: client_credentials
config:
  token_url: https://my-auth.provider/token
  client_id: foo
  client_secret: bar
  scopes:
    - baz
    - zap

Duration

Duration is actually a string type, which adheres to the following pattern: ^[0-9]+(ns|us|ms|s|m|h)$

So with 10s you can define the duration of 10 seconds and with 2h you can say 2 hours.

Endpoint

The Endpoint type defines properties required for the communication with an endpoint.

  • url string (mandatory)

    The actual url of the endpoint.

  • method string (optional)

    The HTTP method to use while communicating with the endpoint. If not set POST is used.

  • retry Retry (optional)

    What to do if the communication fails. If not configured, no retry attempts are done.

  • auth Authentication Strategy (optional)

    Authentication strategy to apply, if the endpoint requires authentication.

  • headers map of strings (optional)

    HTTP headers to be sent to the endpoint.

    These headers are not analyzed by heimdall and are just forwarded to the endpoint. E.g. if you configure the Content-Encoding to something like gzip, the service behind the used endpoint might fail to answer, as it would expect the body to be compressed.
  • enable_http_cache bool (optional)

    Whether HTTP caching according to RFC 7234 should be used. Defaults to false if not otherwise stated in the description of the configuration type, making use of the endpoint property. If set to true heimdall will strictly follow the requirements from RFC 7234 and cache the responses if possible and reuse these if still valid.

    If the endpoint referenced by the URL does not provide any explicit expiration time, no heuristic freshness lifetime is calculated. Heimdall treats such responses as not cacheable.
Example 9. Endpoint configuration
url: http://foo.bar
method: GET
retry:
  give_up_after: 5s
  max_delay: 1s
auth:
  type: api_key
  config:
    name: foo
    value: bar
    in: cookie
headers:
  X-My-First-Header: foobar
  X-My-Second-Header: barfoo
enable_http_cache: true

Error Condition

This type supports definition of conditions, under which an error handler should execute its logic. Such conditions are required for all error handlers, but the default one. All condition elements are evaluated using boolean and. Following conditions are possible:

  • error: Error Descriptor array (mandatory)

    A list with error types to match. Configured entries are evaluated using a boolean or logic.

  • request_cidr: string array (optional)

    A list with CIDR entries to match. Configured entries are evaluated using a boolean or logic.

  • request_headers: string array map (optional)

    A map with header names and the corresponding values to match. Configured entries are evaluated using a boolean or logic. This holds also true for the header values.

Example 10. Complex Error Condition configuration

This example shows in principle all possible combinations. The actual values and the amount of them will for sure differ in your particular case. However, for showing the idea, the complexity of this example is enough.

error:
  - type: precondition_error
  # OR
  - type: authorization_error
# AND
request_cidr:
  - 192.168.0.0/16
  # OR
  - 10.0.0.0/8
# AND
request_headers:
  Accept:
    - text/html
    # OR
    - "*/*"
  # OR
  Content-Type:
    - application/json

This condition evaluates to true only if all parts of it (error, request_cidr, request_headers) evaluate to true. With * error evaluates to true, if the encountered error was either precondition_error or authorization_error. * request_cidr evaluates to true, if the request came from an IP in either 192.168.0.0/16 or 10.0.0.0/8 range. And * request_headers evaluates to true, if either the HTTP Accept header contains one of text/html, or /, or the HTTP Contet-Type header contains application/json.

Example 11. Simple Error Condition configuration

This example is a very simple one, showing just the usage of the error attribute:

error:
  - type: authentication_error

This condition evaluates to true, if the encountered error was authentication_error raised by any of the configured authenticators.

Error Descriptor

Describes an error to match in Error Conditions.

Following configuration properties are available:

  • type: Error Type (mandatory)

    An error type to match.

  • raised_by: string (optional)

    The identifier of the pipeline handler, which raised this error.

Heimdall does not verify the integrity of the pipeline handler identifier configured using raised_by on start or while loading rules. Thus, if there is no pipeline handler with the given identifier, the error will not match.
Example 12. Error Descriptor configuration
type: authentication_error
raised_by: my_oauth2_authenticator

Here the descriptor says, the expected error is of type authentication_error and should have been raise d by a pipeline handler with the id my_oauth2_authenticator.

Error Type

Heimdall defines a couple of error types, which it uses to signal errors. Some of them are available for configuring your Error Conditions via Error Descriptors.

Following types are available:

  • authentication_error - used if an authenticator failed to verify authentication data available in the request. E.g. an authenticator was configured to verify a JWT and the signature of it was invalid.

  • authorization_error - used if an authorizer failed to authorize the subject. E.g. an authorizer is configured to use a script to execute on the given subject and request context, but this script returned with an error.

  • internal_error - used if Heimdall run into an internal error condition while processing the request. E.g. something went wrong while unmarshalling a JSON object, or if there was a configuration error, which couldn’t be raised while loading a rule, etc.

  • precondition_error - used if the request does not contain required/expected data. E.g. if an authenticator could not find a cookie configured.

Retry

Implements an exponential backoff strategy for endpoint communication. It increases the backoff exponentially by multiplying the max_delay with 2^(attempt count)

  • give_up_after: Duration (optional)

    Sets an upper bound on the maximum time to wait between two requests. Default to 0, which means no upper bound.

  • max_delay: _Duration (mandatory)

    The initial backoff.

Example 13. Retry configuration

In this example the backoff will be 1, 2, 4, 8, 16, 32, 60, …​

give_up_after: 60s
max_delay: 1s

Scopes Matcher

Scopes matcher is a configuration type allowing configuration of different strategies to match required scopes. In its simplest shape it can be just an array of strings (implemented by the Exact) scope matcher. To cover many use cases, different strategies are available and described in the following sections.

Regardless of the strategy, each matcher can explicitly be configured and supports the following configuration properties:

  • matching_strategy - the type of the mathing strategy.

  • values - the list of scope patterns

Exact

This the simplest matcher and is automatically selected, if just an array of strings is configured as shown in the following snippet:

- foo
- bar

However, as written in the Scopes Matcher section, it can also explicitly be selected by setting matching_strategy to exact and defining the required scopes in the values property.

Example 14. Essentially same configurations
matching_strategy: exact
values:
  - foo
  - bar
  - foo
  - bar

Hierarchic

This matcher enables matching hierarchical scopes, which use . as separator. Imagine your system is organized that way, that it defines namespaces for different services like this:

  • my-service being the top namespace

  • my-service.booking - being the namespace of the booking service

  • my-service.orders - being the namespace of the orders service

  • my-service.orders.partners - being the namespace of the order service for partners and

  • my-service.orders.customers - being the namespace of the order service for customers

Basically you’ve established an identity for each of your services (this is comparable to how SPIFFE IDs are organized and also used for).

Now, imagine you use these namespaces as scope values to limit the usage of the issued tokens. In such situations the hierarchic scope matcher can become handy if you would like to assert any scope of the token must be in e.g. the my-service or the my-service.orders namespace.

This matcher can only be used by explicitly setting the matching_strategy to hierarchic and defining the required patterns in the values property.

Example 15. Matching of hierarchic scopes
matching_strategy: hierarchic
values:
  - my-service

This configuration will ensure all scopes withing the scope or scp claim are within the my-service namespace. So scope claim like

{
  "scope": ["my-service.orders", "my-service.orders.customers"]
}

would match, but

{
  "scope": ["not-my-service", "my-service.orders.customers"]
}

would not match.

Wildcard

This matcher enables matching scopes using wildcards. It goes beyond the Hierarchic scope matcher by enabling usage of wildcards.

This matcher can only be used by explicitly setting the matching_strategy to wildcard and defining the required patterns in the values property.

Subject

This configuration type enables extraction of subject information from responses received by Heimdall from authentication services. Following properties are available.

  • id: string (mandatory)

    A GJSON Path pointing to the id of the subject in the JSON object.

  • attributes: string (optional)

    A GJSON Path pointing to the attributes of the subject in the JSON object. Defaults to @this.

Example 16. Extracting subject id from an OAuth2 Introspection endpoint response.

This example shows how to extract the subject id from an OAuth2 Introspection endpoint response and set the subject attributes to the entire response

id: sub
attributes: @this

Setting attributes was actually not required, as @this would be set by default anyway.

Example 17. Extracting subject id from an Ory Kratos "whoami" endpoint response

This example shows how to extract the subject id from an Ory Kratos "whoami" endpoint response and set the subject attributes to the entire response. attributes is not configured, so default is used.

id: identity.id

Session Lifespan

This configuration type enables the configuration of session lifespans, used for session validation for those authenticators, which act on non-standard protocols. Following properties are available.

  • active: string (optional)

    A GJSON Path pointing to the field describing the "active" status of the session in the corresponding JSON object. The actual value in that field should be convertable to a bool type. If not provided, or not found in the session object, the session is considered to be "active". "active" means it can be used and represent a valid session between the authentication system and the subject, the session has been issued to.

  • issued_at: string (optional)

    A GJSON Path pointing to the field in the corresponding JSON object, describing the time, when the session object has been issued. If not provided or not found, the issuance time is not considered during session validation.

  • not_before: string (optional)

    A GJSON Path pointing to the field in the corresponding JSON object describing the time, until which the session object is not allowed to be used. If not provided or not found, the corresponding time is not considered during session validation.

  • not_after: string (optional)

    A GJSON Path pointing to the field in the corresponding JSON object describing the time, after which the session object is not allowed to be used. If not provided or not found, the corresponding time is not considered during session validation.

  • time_format: string (optional)

    Since different authentication system use different representations for time strings, this property allows the definition of the time format/layout used by the authentication system. Defaults to Unix Epoch time stamp.

    You can use the following Go Playground link to test your time format settings.
  • validity_leeway: Duration (optional)

    Enables definition of an allowed time drift between the authentication system and heimdall for the validation of the session validity. Defaults to 0.

Example 18. Making use of session information received from Ory’s Kratos

A typical response from Kratos' whoami endpoint looks like follows (stripped to the most interesting parts):

{
  "id": "1338410d-c473-4503-a96a-53efa06e2531",
  "active": true,
  "expires_at": "2021-10-15T15:58:57.683338Z",
  "authenticated_at": "2021-10-14T15:58:57.683338Z",
  "issued_at": "2021-10-14T15:58:57.683338Z",
  "identity": {
    "id": "9496bbd5-f426-473f-b087-c7df853f274a",
    ...
  }
}

To enable usage of these properties in Heimdall, you can configure the Session Lifespan as follows:

active: active
issued_at: issued_at
not_before: authenticated_at
not_after: expires_at
time_format: "2006-01-02T15:04:05.999999Z07"
validity_leeway: 10s
Example 19. Making use of session information received from a compliant OAuth2 authorization service

A typical response from a token introspection endpoint looks like follows:

{
  "active": true,
  "client_id": "l238j323ds-23ij4",
  "username": "jdoe",
  "scope": "read write dolphin",
  "sub": "Z5O3upPC88QrAjx00dis",
  "aud": "https://protected.example.net/resource",
  "iss": "https://server.example.com/",
  "exp": 1419356238,
  "iat": 1419350238,
  "extension_field": "twenty-seven"
 }

To enable usage of these properties in Heimdall, you can configure the Session Lifespan as follows:

active: active
issued_at: iat
not_after: exp
validity_leeway: 10s

As you see, there is no need to define the time format as the times values appearing in the responses from an introspection endpoint are Unix Epoch time stamps.

Last updated on Nov 9, 2022