Service Accounts

This section details how to configure Horizon Service Accounts authenticated via JSON Web Key Set (JWKS).

Service Accounts allow workloads (CI/CD pipelines, Kubernetes pods, third-party automations, …​) to authenticate to Horizon by presenting a JWT signed by an external Identity Provider (IdP). Horizon validates the JWT signature against the JWKS exposed by the IdP and applies the configured claim validation rules. No long-lived credential is stored on the calling workload, and tokens can be rotated by the IdP without any change on the Horizon side.

Service Accounts complement the existing username/password and X.509 client certificate authentication methods. They are best suited for unattended workloads that already obtain short-lived JWTs from a trusted IdP (GitHub Actions, GitLab CI, Kubernetes projected tokens, …​).

How to create a Service Account

1. Log in to Horizon Administration Interface.

2. Access Service Accounts from the drawer or card: Security  Access Management  Service Accounts.

3. Click on Add Service Account.

4. Fill in the mandatory fields.

General

  • Name* (string input):
    Enter a meaningful name. This name is used by clients to identify which Service Account they want to impersonate.

  • Identifier mapping (string input):
    Template string used to derive the identifier of the principal authenticated through this Service Account. Claim names must be declared between {{ and }} characters and follow the same flat-map syntax as validation rules.

    The resulting identifier is always prefixed with <service-account-name>-<jwt-kid> so that two principals cannot collide across Service Accounts or signing keys. The evaluated template is appended to that prefix, separated by a -. If no mapping is configured, the identifier stays equal to the prefix.

    For example, with a Service Account named ci-runner, a JWT signed with kid=2024-key-1 and a sub claim of repo:my-org/my-repo, setting the identifier mapping to {{sub}} produces the principal identifier ci-runner-2024-key-1-repo:my-org/my-repo.

Authorization

Authorizations that will be given to this account

  • Roles* (multiselect):
    Roles granted to the principal authenticated through this Service Account.

  • Permissions* (multiselect):
    Permissions granted to the principal authenticated through this Service Account.

Trust configuration

The trust configuration defines how Horizon retrieves the public keys used to validate the JWT signature. A Service Account can declare several trust entries; a token is accepted if any of them validates its signature.

  • Type* (select):

    • Static JWKS: the JWKS is provided inline as a JSON document. The JWKS is then used as-is to validate signatures.

    • Dynamic JWKS: the JWKS is fetched from a remote URL (typically the IdP’s /.well-known/openid-configuration or jwks_uri endpoint). Horizon refreshes it periodically and on first encounter of an unknown kid.

  • JWKS* (string input):
    For a Static JWKS, paste the JSON content. For a Dynamic JWKS, enter the URL exposing the JWKS.

  • Proxy (string select):
    HTTP/HTTPS proxy used to reach the JWKS URL, if any. Only applicable for Dynamic JWKS.

Validation rules

Validation rules ensure that an otherwise valid JWT is accepted only if its claims match the expected values. Each rule is evaluated using the standard computation rule syntax against the JWT claims.

JWT claims are exposed as a flat map and can be referenced as {\{<claim>}} template entries:

  • Scalar string claims are exposed by their name, e.g. {{sub}}, {{iss}}, {{aud}}.

  • Array-valued claims are expanded into 1-indexed entries, e.g. {{groups.1}}, {{groups.2}}.

  • Nested objects use escaped dotted paths, e.g. {{"kubernetes.io".namespace}}.

Example: validating a Kubernetes projected token

A Kubernetes-issued JWT contains claims similar to:

{
  "aud": ["https://kubernetes.default.svc.cluster.local"],
  "iss": "https://kubernetes.default.svc.cluster.local",
  "sub": "system:serviceaccount:default:my-workload",
  "kubernetes.io": {
    "namespace": "default",
    "serviceaccount": { "name": "my-workload" }
  }
}

Suitable validation rules would be:

  • {{iss}} contains "kubernetes.default.svc.cluster.local"

  • {{aud}} contains "kubernetes.default.svc.cluster.local"

  • {{"kubernetes.io".namespace}} equals "default"

  • {{"kubernetes.io".serviceaccount.name}} equals "my-workload"

JWT time validation

The following optional settings constrain the time-based claims (iat, nbf, exp) carried by the JWT. They complement signature verification and validation rules.

  • Allowed clock skew (finite duration):
    Allowed clock skew when validating JWT time-based claims.

  • iat future restriction (finite duration):
    Maximum duration in the future the JWT iat claim is allowed to be. Must be set together with iat past restriction.

  • iat past restriction (finite duration):
    Maximum duration in the past the JWT iat claim is allowed to be. Must be set together with iat future restriction.

5. Click on the save button.

You can update Edit Service Account or delete Delete Service Account a Service Account.

How to authenticate using a Service Account

Clients present the Service Account name and JWT in dedicated HTTP headers:

  • X-API-SVA: Service Account name

  • X-API-TOKEN: JWT

Service Account authentication never establishes a session. Each request must carry a valid JWT.