Skip to content

Features

Overview

We call features of Authorino the different things one can do to enforce identity verification & authentication and authorization on requests to protected services. These can be a specific identity verification method based on a supported authentication protocol, or a method to fetch additional auth metadata in request-time, etc.

Most features of Authorino relate to the different phases of the Auth Pipeline and therefore are configured in the Authorino AuthConfig. An identity verification/authentication feature usually refers to a functionality of Authorino such as the API key-based authentication, the validation of JWTs/OIDC ID tokens, and authentication based on Kubernetes TokenReviews. Analogously, OPA, pattern-matching and Kubernetes SubjectAccessReview are examples of authorization features of Authorino.

At a deeper level, a feature can also be an additional functionality within a bigger feature, usually applicable to the whole class the bigger feature belongs to. For instance, the configuration of how auth credentials expected to be carried in the request, which is broadly available for any identity verification method. Other examples are: Identity extension and Priorities.

A full specification of all features of Authorino that can be configured in an AuthConfig can be found in the official spec of the custom resource definition.

You can also learn about Authorino features by using the kubectl explain command in a Kubernetes cluster where the Authorino CRD has been installed. E.g. kubectl explain authconfigs.spec.authentication.credentials.

Common feature: JSON paths (selector)

The first feature of Authorino to learn about is a common functionality used in the specification of many other features. JSON paths are selectors of data from the Authorization JSON used in parts of an AuthConfig for referring to dynamic values of each authorization request.

Usage examples of JSON paths are: dynamic URLs and request parameters when fetching metadata from external sources, dynamic authorization policy rules, and dynamic authorization response attributes (e.g. injected HTTP headers, Festival Wristband token claims, etc).

Syntax

The syntax to fetch data from the Authorization JSON with JSON paths is based on GJSON. Refer to GJSON Path Syntax page for more information.

String modifiers

On top of GJSON, Authorino defines a few string modifiers.

Examples below provided for the following Authorization JSON:

{
  "context": {
    "request": {
      "http": {
        "path": "/pets/123",
        "headers": {
          "authorization": "Basic amFuZTpzZWNyZXQK" // jane:secret
          "baggage": "eyJrZXkxIjoidmFsdWUxIn0=" // {"key1":"value1"}
        }
      }
    }
  },
  "auth": {
    "identity": {
      "username": "jane",
      "fullname": "Jane Smith",
      "email": "\u0006jane\u0012@petcorp.com\n"
    },
  },
}

@strip
Strips out any non-printable characters such as carriage return. E.g. auth.identity.email.@strip"jane@petcorp.com".

@case:upper|lower
Changes the case of a string. E.g. auth.identity.username.@case:upper"JANE".

@replace:{"old":string,"new":string}
Replaces a substring within a string. E.g. auth.identity.username.@replace:{"old":"Smith","new":"Doe"}"Jane Doe".

@extract:{"sep":string,"pos":int}
Splits a string at occurrences of a separator (default: " ") and selects the substring at the pos-th position (default: 0). E.g. context.request.path.@extract:{"sep":"/","pos":2}123.

@base64:encode|decode
base64-encodes or decodes a string value. E.g. auth.identity.username.decoded.@base64:encode"amFuZQo=".

In combination with @extract, @base64 can be used to extract the username in an HTTP Basic Authentication request. E.g. context.request.headers.authorization.@extract:{"pos":1}|@base64:decode|@extract:{"sep":":","pos":1}"jane".

Interpolation

JSON paths can be interpolated into strings to build template-like dynamic values. E.g. "Hello, {auth.identity.name}!".

Identity verification & authentication features (authentication)

API key (authentication.apiKey)

Authorino relies on Kubernetes Secret resources to represent API keys.

To define an API key, create a Secret in the cluster containing an api_key entry that holds the value of the API key.

API key secrets must be created in the same namespace of the AuthConfig (default) or spec.authentication.apiKey.allNamespaces must be set to true (only works with cluster-wide Authorino instances).

API key secrets must be labeled with the labels that match the selectors specified in spec.authentication.apiKey.selector in the AuthConfig.

Whenever an AuthConfig is indexed, Authorino will also index all matching API key secrets. In order for Authorino to also watch events related to API key secrets individually (e.g. new Secret created, updates, deletion/revocation), Secrets must also include a label that matches Authorino's bootstrap configuration --secret-label-selector (default: authorino.kuadrant.io/managed-by=authorino). This label may or may not be present to spec.authentication.apiKey.selector in the AuthConfig without implications for the caching of the API keys when triggered by the reconciliation of the AuthConfig; however, if not present, individual changes related to the API key secret (i.e. without touching the AuthConfig) will be ignored by the reconciler.

Example. For the following AuthConfig:

apiVersion: authorino.kuadrant.io/v1beta2
kind: AuthConfig
metadata:
  name: my-api-protection
  namespace: authorino-system
spec:
  hosts:
  - my-api.io
  authentication:
    "api-key-users":
      apiKey:
        selector:
          matchLabels: # the key-value set used to select the matching `Secret`s; resources including these labels will be accepted as valid API keys to authenticate to this service
            group: friends # some custom label
        allNamespaces: true # only works with cluster-wide Authorino instances; otherwise, create the API key secrets in the same namespace of the AuthConfig

The following Kubernetes Secret represents a valid API key:

apiVersion: v1
kind: Secret
metadata:
  name: user-1-api-key-1
  namespace: default
  labels:
    authorino.kuadrant.io/managed-by: authorino # so the Authorino controller reconciles events related to this secret
    group: friends
stringData:
  api_key: <some-randomly-generated-api-key-value>
type: Opaque

The resolved identity object, added to the authorization JSON following an API key identity source evaluation, is the Kubernetes Secret resource (as JSON).

Kubernetes TokenReview (authentication.kubernetesTokenReview)

Authorino can verify Kubernetes-valid access tokens (using Kubernetes TokenReview API).

These tokens can be either ServiceAccount tokens such as the ones issued by kubelet as part of Kubernetes Service Account Token Volume Projection, or any valid user access tokens issued to users of the Kubernetes server API.

The list of audiences of the token must include the requested host and port of the protected API (default), or all audiences specified in the Authorino AuthConfig custom resource. For example:

For the following AuthConfig CR, the Kubernetes token must include the audience my-api.io:

apiVersion: authorino.kuadrant.io/v1beta2
kind: AuthConfig
metadata:
  name: my-api-protection
spec:
  hosts:
  - my-api.io
  authentication:
    "cluster-users":
      kubernetesTokenReview: {}

Whereas for the following AuthConfig CR, the Kubernetes token audiences must include foo and bar:

apiVersion: authorino.kuadrant.io/v1beta2
kind: AuthConfig
metadata:
  name: my-api-protection
spec:
  hosts:
  - my-api.io
  authentication:
    "cluster-users":
      kubernetesTokenReview:
        audiences:
        - foo
        - bar

The resolved identity object added to the authorization JSON following a successful Kubernetes authentication identity evaluation is the status field of TokenReview response (see TokenReviewStatus for reference).

JWT verification (authentication.jwt)

In reconciliation-time, using OpenID Connect Discovery well-known endpoint, Authorino automatically discovers and caches OpenID Connect configurations and associated JSON Web Key Sets (JWKS) for all OpenID Connect issuers declared in an AuthConfig. Then, in request-time, Authorino verifies the JSON Web Signature (JWS) and check the time validity of signed JSON Web Tokens (JWT) supplied on each request.

Important! Authorino does not implement OAuth2 grants nor OIDC authentication flows. As a common recommendation of good practice, obtaining and refreshing access tokens is for clients to negotiate directly with the auth servers and token issuers. Authorino will only validate those tokens using the parameters provided by the trusted issuer authorities.

OIDC

The kid claim stated in the JWT header must match one of the keys cached by Authorino during OpenID Connect Discovery, therefore supporting JWK rotation.

The decoded payload of the validated JWT is appended to the authorization JSON as the resolved identity.

OpenID Connect configurations and linked JSON Web Key Sets can be configured to be automatically refreshed (pull again from the OpenID Connect Discovery well-known endpoints), by setting the authentication.jwt.ttl field (given in seconds, default: 0 – i.e. auto-refresh disabled).

For an excellent summary of the underlying concepts and standards that relate OpenID Connect and JSON Object Signing and Encryption (JOSE), see this article by Jan Rusnacko. For official specification and RFCs, see OpenID Connect Core, OpenID Connect Discovery, JSON Web Token (JWT) (RFC7519), and JSON Object Signing and Encryption (JOSE).

OAuth 2.0 introspection (authentication.oauth2Introspection)

For bare OAuth 2.0 implementations, Authorino can perform token introspection on the access tokens supplied in the requests to protected APIs.

Authorino does not implement any of OAuth 2.0 grants for the applications to obtain the token. However, it can verify supplied tokens with the OAuth server, including opaque tokens, as long as the server exposes the token_introspect endpoint (RFC 7662).

Developers must set the token introspection endpoint in the AuthConfig, as well as a reference to the Kubernetes secret storing the credentials of the OAuth client to be used by Authorino when requesting the introspect.

OAuth 2.0 Token Introspect

The response returned by the OAuth2 server to the token introspection request is the resolved identity appended to the authorization JSON.

X.509 client certificate authentication (authentication.x509)

Authorino can verify X.509 certificates presented by clients for authentication on the request to the protected APIs, at application level.

Trusted root Certificate Authorities (CA) are stored in Kubernetes Secrets labeled according to selectors specified in the AuthConfig, watched and indexed by Authorino. Make sure to create proper kubernetes.io/tls-typed Kubernetes Secrets, containing the public certificates of the CA stored in either a tls.crt or ca.crt entry inside the secret.

Trusted root CA secrets must be created in the same namespace of the AuthConfig (default) or spec.authentication.x509.allNamespaces must be set to true (only works with cluster-wide Authorino instances).

Client certificates must include x509 v3 extension specifying 'Client Authentication' extended key usage.

The identity object resolved out of a client x509 certificate is equal to the subject field of the certificate, and it serializes as JSON within the Authorization JSON usually as follows:

{
    "auth": {
        "identity": {
            "CommonName": "aisha",
            "Country": ["PK"],
            "ExtraNames": null,
            "Locality": ["Islamabad"],
            "Names": [
                { "Type": [2, 5, 4, 3], "Value": "aisha" },
                { "Type": [2, 5, 4, 6], "Value": "PK" },
                { "Type": [2, 5, 4, 7], "Value": "Islamabad" },
                { "Type": [2, 5, 4,10], "Value": "ACME Inc." },
                { "Type": [2, 5, 4,11], "Value": "Engineering" }
            ],
            "Organization": ["ACME Inc."],
            "OrganizationalUnit": ["Engineering"],
            "PostalCode": null,
            "Province": null,
            "SerialNumber": "",
            "StreetAddress": null
        }
  }
}

Plain (authentication.plain)

Authorino can read plain identity objects, based on authentication tokens provided and verified beforehand using other means (e.g. Envoy JWT Authentication filter, Kubernetes API server authentication), and injected into the payload to the external authorization service.

The plain identity object is retrieved from the Authorization JSON based on a JSON path specified in the AuthConfig.

This feature is particularly useful in cases where authentication/identity verification is handled before invoking the authorization service and its resolved value injected in the payload can be trusted. Examples of applications for this feature include: - Authentication handled in Envoy leveraging the Envoy JWT Authentication filter (decoded JWT injected as 'metadata_context') - Use of Authorino as Kubernetes ValidatingWebhook service (Kubernetes 'userInfo' injected in the body of the AdmissionReview request)

Example of AuthConfig to retrieve plain identity object from the Authorization JSON.

spec:
  authentication:
    "pre-validated-jwt":
      plain:
        selector: context.metadata_context.filter_metadata.envoy\.filters\.http\.jwt_authn|verified_jwt

If the specified JSON path does not exist in the Authorization JSON or the value is null, the identity verification will fail and, unless other identity config succeeds, Authorino will halt the Auth Pipeline with the usual 401 Unauthorized.

Anonymous access (authentication.anonymous)

Literally a no-op evaluator for the identity verification phase that returns a static identity object {"anonymous":true}.

It allows to implement AuthConfigs that bypasses the identity verification phase of Authorino, to such as: - enable anonymous access to protected services (always or combined with Priorities) - postpone authentication in the Auth Pipeline to be resolved as part of an OPA policy

Example of AuthConfig spec that falls back to anonymous access when OIDC authentication fails, enforcing read-only access to the protected service in such cases:

spec:
  authentication:
    "jwt":
      jwt:
        issuerUrl: "…"
    "anonymous":
      priority: 1 # expired oidc token, missing creds, etc. default to anonymous access
      anonymous: {}
  authorization:
    "read-only-access-if-authn-fails":
      when:
        - selector: auth.identity.anonymous
          operator: eq
          value: "true"
      patternMatching:
        patterns:
        - selector: context.request.http.method
          operator: eq
          value: GET

Festival Wristband authentication

Authorino-issued Festival Wristband tokens can be validated as any other signed JWT using Authorino's JWT verification.

The value of the issuer must be the same issuer specified in the custom resource for the protected API originally issuing wristband. Eventually, this can be the same custom resource where the wristband is configured as a valid source of identity, but not necessarily.

Extra: Auth credentials (authentication.credentials)

All the identity verification methods supported by Authorino can be configured regarding the location where access tokens and credentials (i.e. authentication secrets) fly within the request.

By default, authentication secrets are expected to be supplied in the Authorization HTTP header, with the default Bearer prefix and the plain authentication secret separated by space.

The full list of supported options is exemplified below:

spec:
  authentication:
    "creds-in-the-authz-header":
      credentials:
        authorizationHeader:
          prefix: JWT

    "creds-in-a-custom-header":
      credentials:
        customHeader:
          name: X-MY-CUSTOM-HEADER
          prefix: ""

    "creds-in-a-query-param":
      queryString:
        name: my_param

    "creds-in-a-cookie-entry":
      cookie:
        name: cookie-key

Extra: Identity extension (authentication.defaults and authentication.overrides)

Resolved identity objects can be extended with user-defined JSON properties. Values can be static or fetched from the Authorization JSON.

A typical use-case for this feature is token normalization. Say you have more than one identity source listed in your AuthConfig but each source issues an access token with a different JSON structure – e.g. two OIDC issuers that use different names for custom JWT claims of similar meaning; when two different identity verification/authentication methods are combined, such as API keys (whose identity objects are the corresponding Kubernetes Secrets) and Kubernetes tokens (whose identity objects are Kubernetes UserInfo data).

In such cases, identity extension can be used to normalize the token to always include the same set of JSON properties of interest, regardless of the source of identity that issued the original token verified by Authorino. This simplifies the writing of authorization policies and configuration of dynamic responses.

In case of extending an existing property of the identity object (replacing), the API allows to control whether to overwrite the value or not. This is particularly useful for normalizing tokens of a same identity source that nonetheless may occasionally differ in structure, such as in the case of JWT claims that sometimes may not be present but can be safely replaced with another (e.g. username or sub).

External auth metadata features (metadata)

HTTP GET/GET-by-POST (metadata.http)

Generic HTTP adapter that sends a request to an external service. It can be used to fetch external metadata for the authorization policies (phase ii of the Authorino Auth Pipeline), or as a web hook.

The adapter allows issuing requests either by GET or POST methods; in both cases with URL and parameters defined by the user in the spec. Dynamic values fetched from the Authorization JSON can be used.

POST request parameters as well as the encoding of the content can be controlled using the bodyParameters and contentType fields of the config, respectively. The Content-Type of POST requests can be either application/x-www-form-urlencoded (default) or application/json.

Authentication of Authorino with the external metadata server can be set either via long-lived shared secret stored in a Kubernetes Secret or via OAuth2 client credentials grant. For long-lived shared secret, set the sharedSecretRef field. For OAuth2 client credentials grant, use the oauth2 option.

In both cases, the location where the secret (long-lived or OAuth2 access token) travels in the request performed to the external HTTP service can be specified in the credentials field. By default, the authentication secret is supplied in the Authorization header with the Bearer prefix.

Custom headers can be set with the headers field. Nevertheless, headers such as Content-Type and Authorization (or eventual custom header used for carrying the authentication secret, set instead via the credentials option) will be superseded by the respective values defined for the fields contentType and sharedSecretRef.

OIDC UserInfo (metadata.userInfo)

Online fetching of OpenID Connect (OIDC) UserInfo data (phase ii of the Authorino Auth Pipeline), associated with an OIDC identity source configured and resolved in phase (i).

Apart from possibly complementing information of the JWT, fetching OpenID Connect UserInfo in request-time can be particularly useful for remote checking the state of the session, as opposed to only verifying the JWT/JWS offline.

Implementation requires a JWT verification authentication config (spec.authentication.jwt) in the same AuthConfig, so the well-known configuration of the OpenId Connect (OIDC) issuer can be reused.

The response returned by the OIDC server to the UserInfo request is appended (as JSON) to auth.metadata in the authorization JSON.

User-Managed Access (UMA) resource registry (metadata.uma)

User-Managed Access (UMA) is an OAuth-based protocol for resource owners to allow other users to access their resources. Since the UMA-compliant server is expected to know about the resources, Authorino includes a client that fetches resource data from the server and adds that as metadata of the authorization payload.

This enables the implementation of resource-level Attribute-Based Access Control (ABAC) policies. Attributes of the resource fetched in a UMA flow can be, e.g., the owner of the resource, or any business-level attributes stored in the UMA-compliant server.

A UMA-compliant server is an external authorization server (e.g., Keycloak) where the protected resources are registered. It can be as well the upstream API itself, as long as it implements the UMA protocol, with initial authentication by client_credentials grant to exchange for a Protected API Token (PAT).

UMA

It's important to notice that Authorino does NOT manage resources in the UMA-compliant server. As shown in the flow above, Authorino's UMA client is only to fetch data about the requested resources. Authorino exchanges client credentials for a Protected API Token (PAT), then queries for resources whose URI match the path of the HTTP request (as passed to Authorino by the Envoy proxy) and fetches data of each matching resource.

The resources data is added as metadata of the authorization payload and passed as input for the configured authorization policies. All resources returned by the UMA-compliant server in the query by URI are passed along. They are available in the PDPs (authorization payload) as input.auth.metadata.custom-name => Array. (See The "Auth Pipeline" for details.)

Authorization features (authorization)

Pattern-matching authorization (authorization.patternMatching)

Grant/deny access based on simple pattern-matching expressions ("patterns") compared against values selected from the Authorization JSON.

Each expression is a tuple composed of: - a selector, to fetch from the Authorization JSON – see Common feature: JSON paths for details about syntax; - an operatoreq (equals), neq (not equal); incl (includes) and excl (excludes), for arrays; and matches, for regular expressions; - a fixed comparable value

Rules can mix and combine literal expressions and references to expression sets ("named patterns") defined at the upper level of the AuthConfig spec. (See Common feature: Conditions)

spec:
  authorization:
    "my-simple-json-pattern-matching-policy":
      patternMatching:
        patterns: # All patterns must match for access to be granted
        - selector: auth.identity.email_verified
          operator: eq
          value: "true"
        - patternRef: admin

  patterns:
    admin: # a named pattern that can be reused in other sets of rules or conditions
    - selector: auth.identity.roles
      operator: incl
      value: admin

Open Policy Agent (OPA) Rego policies (authorization.opa)

You can model authorization policies in Rego language and add them as part of the protection of your APIs.

Policies can be either declared in-line in Rego language (rego) or as an HTTP endpoint where Authorino will fetch the source code of the policy in reconciliation-time (externalPolicy).

Policies pulled from external registries can be configured to be automatically refreshed (pulled again from the external registry), by setting the authorization.opa.externalPolicy.ttl field (given in seconds, default: 0 – i.e. auto-refresh disabled).

Authorino's built-in OPA module precompiles the policies during reconciliation of the AuthConfig and caches the precompiled policies for fast evaluation in runtime, where they receive the Authorization JSON as input.

OPA

An optional field allValues: boolean makes the values of all rules declared in the Rego document to be returned in the OPA output after policy evaluation. When disabled (default), only the boolean value allow is returned. Values of internal rules of the Rego document can be referenced in subsequent policies/phases of the Auth Pipeline.

Kubernetes SubjectAccessReview (authorization.kubernetesSubjectAccessReview)

Access control enforcement based on rules defined in the Kubernetes authorization system, i.e. Role, ClusterRole, RoleBinding and ClusterRoleBinding resources of Kubernetes RBAC.

Authorino issues a SubjectAccessReview (SAR) inquiry that checks with the underlying Kubernetes server whether the user can access a particular resource, resource kind or generic URL.

It supports resource attributes authorization check (parameters defined in the AuthConfig) and non-resource attributes authorization check (HTTP endpoint inferred from the original request). - Resource attributes: adequate for permissions set at namespace level, defined in terms of common attributes of operations on Kubernetes resources (namespace, API group, kind, name, subresource, verb) - Non-resource attributes: adequate for permissions set at cluster scope, defined for protected endpoints of a generic HTTP API (URL path + verb)

Example of Kubernetes role for resource attributes authorization:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: pet-reader
rules:
- apiGroups: ["pets.io"]
  resources: ["pets"]
  verbs: ["get"]

Example of Kubernetes cluster role for non-resource attributes authorization:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: pet-editor
rules:
- nonResourceURLs: ["/pets/*"]
  verbs: ["put", "delete"]

Kubernetes' authorization policy configs look like the following in an Authorino AuthConfig:

authorization:
  "kubernetes-rbac":
    kubernetesSubjectAccessReview:
      user: # values of the parameter can be fixed (`value`) or fetched from the Authorization JSON (`selector`)
        selector: auth.identity.metadata.annotations.userid

      groups: [] # user groups to test for.

      # for resource attributes permission checks; omit it to perform a non-resource attributes SubjectAccessReview with path and method/verb assumed from the original request
      # if included, use the resource attributes, where the values for each parameter can be fixed (`value`) or fetched from the Authorization JSON (`selector`)
      resourceAttributes:
        namespace:
          value: default
        group:
          value: pets.io # the api group of the protected resource to be checked for permissions for the user
        resource:
          value: pets # the resource kind
        name:
          selector: context.request.http.path.@extract:{"sep":"/","pos":2} # resource name – e.g., the {id} in `/pets/{id}`
        verb:
          selector: context.request.http.method.@case:lower # api operation – e.g., copying from the context to use the same http method of the request

user and properties of resourceAttributes can be defined from fixed values or patterns of the Authorization JSON.

An array of groups (optional) can as well be set. When defined, it will be used in the SubjectAccessReview request.

SpiceDB (authorization.spicedb)

Check permission requests via gRPC with an external Google Zanzibar-inspired SpiceDB server, by Authzed.

Subject, resource and permission parameters can be set to static values or read from the Authorization JSON.

spec:
  authorization:
    "spicedb":
      spicedb:
        endpoint: spicedb:50051
        insecure: true # disables TLS
        sharedSecretRef:
          name: spicedb
          key: token
        subject:
          kind:
            value: blog/user
          name:
            selector: auth.identity.sub
        resource:
          kind:
            value: blog/post
          name:
            selector: context.request.http.path.@extract:{"sep":"/","pos":2} # /posts/{id}
        permission:
          selector: context.request.http.method

Custom response features (response)

Custom response forms: successful authorization vs custom denial status

The response to the external authorization request can be customized in the following fashion: - Successful authorization (response.success) - Added HTTP headers (response.success.headers) - Envoy Dynamic Metadata (response.success.dynamicMetadata) - Custom denial status - Unauthenticated (response.unauthenticated) - Unauthorized (response.unauthorized)

Successful authorization custom responses can be set based on any of the supported custom authorization methods: - Plain text value - JSON injection - Festival Wristband Tokens

Added HTTP headers

Set custom responses as HTTP headers injected in the request post-successful authorization by specifying one of the supported methods under response.success.headers.

The name of the response config (default) or the value of the key option (if provided) will used as the name of the header.

Envoy Dynamic Metadata

Authorino custom response methods can also be used to propagate Envoy Dynamic Metadata. To do so, set one of the supported methods under response.success.dynamicMetadata.

The name of the response config (default) or the value of the key option (if provided) will used as the name of the root property of the dynamic metadata content.

A custom response exported as Envoy Dynamic Metadata can be set in the Envoy route or virtual host configuration as input to a consecutive filter in the filter chain.

E.g., to read metadata emitted by the authorization service with scheme { "auth-data": { "api-key-ns": string, "api-key-name": string } }, as input in a rate limit configuration placed in the filter chain after the external authorization, the Envoy config may look like the following:

# Envoy config snippet to inject `user_namespace` and `username` rate limit descriptors from metadata emitted by Authorino
rate_limits:
- actions:
  - metadata:
      metadata_key:
        key: "envoy.filters.http.ext_authz"
        path:
        - key: auth-data # root of the dynamic metadata object, as declared in a custom response config of the AuthConfig (name or key)
        - key: api-key-ns
      descriptor_key: user_namespace
  - metadata:
      metadata_key:
        key: "envoy.filters.http.ext_authz"
        path:
        - key: auth-data # root of the dynamic metadata object, as declared in a custom response config of the AuthConfig (name or key)
        - key: api-key-name
      descriptor_key: username

Custom denial status (response.unauthenticated and response.unauthorized)

By default, Authorino will inform Envoy to respond with 401 Unauthorized or 403 Forbidden respectively when the identity verification (phase i of the Auth Pipeline) or authorization (phase ii) fail. These can be customized respectively by specifying spec.response.unauthanticated and spec.response.unauthorized in the AuthConfig.

Custom response methods

Plain text (response.success.<headers|dynamicMetadata>.plain)

Simpler, yet more generalized form, for extending the authorization response for header mutation and Envoy Dynamic Metadata, based on plain text values.

The value can be static:

response:
  success:
    headers:
      "x-auth-service"
        plain:
          value: Authorino

or fetched dynamically from the Authorization JSON (which includes support for interpolation):

response:
  success:
    headers:
      "x-username":
        plain:
          selector: auth.identity.username

JSON injection (response.success.<headers|dynamicMetadata>.json)

User-defined dynamic JSON objects generated by Authorino in the response phase, from static or dynamic data of the auth pipeline, and passed back to the external authorization client within added HTTP headers or Dynamic Metadata.

The following Authorino AuthConfig custom resource is an example that defines 3 dynamic JSON response items, where two items are returned to the client, stringified, in added HTTP headers, and the third as Envoy Dynamic Metadata. Envoy proxy can be configured to propagate the dynamic metadata emitted by Authorino into another filter – e.g. the rate limit filter.

apiVersion: authorino.kuadrant.io/v1beta2
kind: AuthConfig
metadata:
  namespace: my-namespace
  name: my-api-protection
spec:
  hosts:
  - my-api.io
  authentication:
    "edge":
      apiKey:
        selector:
          matchLabels:
            authorino.kuadrant.io/managed-by: authorino
      credentials:
        authorizationHeader:
          prefix: APIKEY
  response:
    success:
      headers:
        "x-my-custom-header":
          json:
            properties:
              "prop1":
                value: value1
              "prop2":
                selector: some.path.within.auth.json
        "x-ext-auth-other-json":
          json:
            properties:
              "propX":
                value: valueX

      dynamicMetadata:
        "auth-data":
          json:
            properties:
              "api-key-ns":
                seletor: auth.identity.metadata.namespace
              "api-key-name":
                selector: auth.identity.metadata.name

Festival Wristband tokens (response.success.<headers|dynamicMetadata>.wristband)

Festival Wristbands are signed OpenID Connect JSON Web Tokens (JWTs) issued by Authorino at the end of the auth pipeline and passed back to the client, typically in added HTTP response header. It is an opt-in feature that can be used to implement Edge Authentication Architecture (EAA) and enable token normalization. Authorino wristbands include minimal standard JWT claims such as iss, iat, and exp, and optional user-defined custom claims, whose values can be static or dynamically fetched from the authorization JSON.

The Authorino AuthConfig custom resource below sets an API protection that issues a wristband after a successful authentication via API key. Apart from standard JWT claims, the wristband contains 2 custom claims: a static value aud=internal and a dynamic value born that fetches from the authorization JSON the date/time of creation of the secret that represents the API key used to authenticate.

apiVersion: authorino.kuadrant.io/v1beta2
kind: AuthConfig
metadata:
  namespace: my-namespace
  name: my-api-protection
spec:
  hosts:
  - my-api.io
  authentication:
    "edge":
      apiKey:
        selector:
          matchLabels:
            authorino.kuadrant.io/managed-by: authorino
      credentials:
        authorizationHeader:
          prefix: APIKEY
  response:
    success:
      headers:
        "x-wristband":
          wristband:
            issuer: https://authorino-oidc.default.svc:8083/my-namespace/my-api-protection/x-wristband
            customClaims:
              "aud":
                value: internal
              "born":
                selector: auth.identity.metadata.creationTimestamp
            tokenDuration: 300
            signingKeyRefs:
            - name: my-signing-key
              algorithm: ES256
            - name: my-old-signing-key
              algorithm: RS256

The signing key names listed in signingKeyRefs must match the names of Kubernetes Secret resources created in the same namespace, where each secret contains a key.pem entry that holds the value of the private key that will be used to sign the wristbands issued, formatted as PEM. The first key in this list will be used to sign the wristbands, while the others are kept to support key rotation.

For each protected API configured for the Festival Wristband issuing, Authorino exposes the following OpenID Connect Discovery well-known endpoints (available for requests within the cluster): - OpenID Connect configuration:
https://authorino-oidc.default.svc:8083/{namespace}/{api-protection-name}/{response-config-name}/.well-known/openid-configuration - JSON Web Key Set (JWKS) well-known endpoint:
https://authorino-oidc.default.svc:8083/{namespace}/{api-protection-name}/{response-config-name}/.well-known/openid-connect/certs

Callbacks (callbacks)

HTTP endpoints (callbacks.http)

Sends requests to specified HTTP endpoints at the end of the auth pipeline.

The scheme of the http field is the same as of metadata.http.

Example:

spec:
  authentication: []
  authorization: []

  callbacks:
    "log":
      http:
        url: http://logsys
        method: POST
        body:
          selector: |
              \{"requestId":context.request.http.id,"username":"{auth.identity.username}","authorizationResult":{auth.authorization}\}
    "important-forbidden":
      when:
      - selector: auth.authorization.important-policy
        operator: eq
        value: "false"
      http:
        url: "http://monitoring/important?forbidden-user={auth.identity.username}"

Common feature: Priorities

Priorities allow to set sequence of execution for blocks of concurrent evaluators within phases of the Auth Pipeline.

Evaluators of same priority execute concurrently to each other "in a block". After syncing that block (i.e. after all evaluators of the block have returned), the next block of evaluator configs of consecutive priority is triggered.

Use cases for priorities are: 1. Saving expensive tasks to be triggered when there's a high chance of returning immediately after finishing executing a less expensive one – e.g. - an identity config that calls an external IdP to verify a token that is rarely used, compared to verifying JWTs preferred by most users of the service; - an authorization policy that performs some quick checks first, such as verifying allowed paths, and only if it passes, moves to the evaluation of a more expensive policy. 2. Establishing dependencies between evaluators - e.g. - an external metadata request that needs to wait until a previous metadata responds first (in order to use data from the response)

Priorities can be set using the priority property available in all evaluator configs of all phases of the Auth Pipeline (identity, metadata, authorization and response). The lower the number, the highest the priority. By default, all evaluators have priority 0 (i.e. highest priority).

Consider the following example to understand how priorities work:

apiVersion: authorino.kuadrant.io/v1beta2
kind: AuthConfig
metadata:
  name: talker-api-protection
spec:
  hosts:
  - talker-api
  authentication:
    "tier-1":
      priority: 0
      apiKey:
        selector:
          matchLabels:
            tier: "1"
    "tier-2":
      priority: 1
      apiKey:
        selector:
          matchLabels:
            tier: "2"
    "tier-3":
      priority: 1
      apiKey:
        selector:
          matchLabels:
            tier: "3"
  metadata:
    "first":
      http:
        url: http://talker-api:3000
    "second":
      priority: 1
      http:
        url: http://talker-api:3000/first_uuid={auth.metadata.first.uuid}
  authorization:
    "allowed-endpoints":
      when:
      - selector: context.request.http.path
        operator: neq
        value: /hi
      - selector: context.request.http.path
        operator: neq
        value: /hello
      - selector: context.request.http.path
        operator: neq
        value: /aloha
      - selector: context.request.http.path
        operator: neq
        value: /ciao
      patternMatching:
        patterns:
        - selector: deny
          operator: eq
          value: "true"
    "more-expensive-policy": # no point in evaluating this one if it's not an allowed endpoint
      priority: 1
      opa:
        rego: |
          allow { true }
  response:
    success:
      headers:
        "x-auth-data":
          json:
            properties:
              "tier":
                selector: auth.identity.metadata.labels.tier
              "first-uuid":
                selector: auth.metadata.first.uuid
              "second-uuid":
                selector: auth.metadata.second.uuid
              "second-path":
                selector: auth.metadata.second.path

For the AuthConfig above,

  • Identity configs tier-2 and tier-3 (priority 1) will only trigger (concurrently) in case tier-1 (priority 0) fails to validate the authentication token first. (This behavior happens without prejudice of context canceling between concurrent evaluators – i.e. evaluators that are triggered concurrently to another, such as tier-2 and tier-3, continue to cancel the context of each other if any of them succeeds validating the token first.)

  • Metadata source second (priority 1) uses the response of the request issued by metadata source first (priority 0), so it will wait for first to finish by triggering only in the second block.

  • Authorization policy allowed-endpoints (priority 0) is considered to be a lot less expensive than more-expensive-policy (priority 1) and has a high chance of denying access to the protected service (if the path is not one of the allowed endpoints). By setting different priorities to these policies we ensure the more expensive policy if triggered in sequence of the less expensive one, instead of concurrently.

Common feature: Conditions (when)

Conditions, named when in the AuthConfig API, are logical expressions, composed of patterns and logical operator AND and OR, that can be used to condition the evaluation of a particular auth rule within an AuthConfig, as well as of the AuthConfig altogether ("top-level conditions").

The patterns are evaluated against the Authorization JSON, where each pattern is a tuple composed of: - selector: a JSON path to fetch a value from the Authorization JSON - operator: one of: eq (equals); neq (not equal); incl (includes) and excl (excludes), for when the value fetched from the Authorization JSON is expected to be an array; matches, for regular expressions - value: a static string value to compare the value selected from the Authorization JSON with.

An expression contains one or more patterns and they must either all evaluate to true ("AND" operator, declared by grouping the patterns within an all block) or at least one of the patterns must be true ("OR" operator, when grouped within an any block.) Patterns not explicitly grouped are AND'ed by default.

To avoid repetitions when listing patterns, any set of literal { pattern, operator, value } tuples can be stored at the top-level of the AuthConfig spec, indexed by name, and later referred within an expression by including a patternRef in the block of conditions.

Examples of when conditions

i) to skip an entire AuthConfig based on the context (AND operator assumed by default):

spec:
  when: # auth enforced only on requests to POST /resources/*
  - selector: context.request.http.method
    operator: eq
    value: POST
  - selector: context.request.http.path
    operator: matches
    value: ^/resources/.*

ii) equivalent to the above with explicit AND operator (i.e., all block):

spec:
  when: # auth enforced only on requests to POST /resources/*
  - all:
    - selector: context.request.http.method
      operator: eq
      value: POST
    - selector: context.request.http.path
      operator: matches
      value: ^/resources/.*

iii) OR condition (i.e., any block):

spec:
  when: # auth enforced only on requests with HTTP method equals to POST or PUT
  - any:
    - selector: context.request.http.method
      operator: eq
      value: POST
    - selector: context.request.http.method
      operator: eq
      value: PUT

iv) complex expression with nested operations:

spec:
  when: # auth enforced only on requests to POST /resources/* or PUT /resources/*
  - any:
    - all:
      - selector: context.request.http.method
        operator: eq
        value: POST
      - selector: context.request.http.path
        operator: matches
        value: ^/resources/.*
    - all:
      - selector: context.request.http.method
        operator: eq
        value: PUT
      - selector: context.request.http.path
        operator: matches
        value: ^/resources/.*

v) more concise equivalent of the above (with implicit AND operator at the top level):

spec:
  when: # auth enforced only on requests to /resources/* path with method equals to POST or PUT
  - selector: context.request.http.path
    operator: matches
    value: ^/resources/.*
  - any:
    - selector: context.request.http.method
      operator: eq
      value: POST
    - selector: context.request.http.method
      operator: eq
      value: PUT

vi) to skip part of an AuthConfig (i.e., a specific auth rule):

spec:
  metadata:
    "metadata-source":
      http:
        url: https://my-metadata-source.io
      when: # only fetch the external metadata if the context is HTTP method other than OPTIONS
      - selector: context.request.http.method
        operator: neq
        value: OPTIONS

vii) skipping part of an AuthConfig will not affect other auth rules:

spec:
  authentication:
    "authn-meth-1":
      apiKey: {} # this auth rule only triggers for POST requests to /foo[/*]
      when:
      - selector: context.request.http.method
        operator: eq
        value: POST
      - selector: context.request.http.path
        operator: matches
        value: ^/foo(/.*)?$

    "authn-meth-2": # this auth rule triggerred regardless
      jwt: {}

viii) concrete use-case: evaluating only the necessary identity checks based on the user's indication of the preferred authentication method (prefix of the value supplied in the HTTP Authorization request header):

spec:
  authentication:
    "jwt":
      when:
      - selector: context.request.http.headers.authorization
        operator: matches
        value: JWT .+
      jwt: {}

    "api-key":
      when:
      - selector: context.request.http.headers.authorization
        operator: matches
        value: APIKEY .+
      apiKey: {}

ix) to avoid repetition while defining patterns for conditions:

spec:
  patterns:
    a-pet: # a named pattern that can be reused in sets of conditions
    - selector: context.request.http.path
      operator: matches
      value: ^/pets/\d+(/.*)$

  metadata:
    "pets-info":
      when:
      - patternRef: a-pet
      http:
        url: https://pets-info.io?petId={context.request.http.path.@extract:{"sep":"/","pos":2}}

  authorization:
    "pets-owners-only":
      when:
      - patternRef: a-pet
      opa:
        rego: |
          allow { input.metadata["pets-info"].ownerid == input.auth.identity.userid }

x) combining literals and refs – concrete case: authentication required for selected operations:

spec:
  patterns:
    api-base-path:
    - selector: context.request.http.path
      operator: matches
      value: ^/api/.*

    authenticated-user:
    - selector: auth.identity.anonymous
      operator: neq
      value: "true"

  authentication:
    api-users: # tries to authenticate all requests to path /api/*
      when:
      - patternRef: api-base-path
      jwt: {}

    others: # defaults to anonymous access when authentication fails or not /api/* path
      anonymous: {}
      priority: 1

  authorization:
    api-write-access-requires-authentication: # POST/PUT/DELETE requests to /api/* path cannot be anonymous
      when:
      - all:
        - patternRef: api-base-path
        - any:
          - selector: context.request.http.method
            operator: eq
            value: POST
          - selector: context.request.http.method
            operator: eq
            value: PUT
          - selector: context.request.http.method
            operator: eq
            value: DELETE
      opa:
        patternMatching:
          rules:
          - patternRef: authenticated-user

  response: # bonus: export user data if available
    success:
      dynamicMetadata:
        "user-data":
          when:
          - patternRef: authenticated-user
          json:
            properties:
              jwt-claims:
                selector: auth.identity

Common feature: Caching (cache)

Objects resolved at runtime in an Auth Pipeline can be cached "in-memory", and avoided being evaluated again at a subsequent request, until it expires. A lookup cache key and a TTL can be set individually for any evaluator config in an AuthConfig.

Each cache config induces a completely independent cache table (or "cache namespace"). Consequently, different evaluator configs can use the same cache key and there will be no collision between entries from different evaluators.

E.g.:

spec:
  hosts:
  - my-api.io

  authentication: []

  metadata:
    "external-metadata":
      http:
        url: http://my-external-source?search={context.request.http.path}
      cache:
        key:
          selector: context.request.http.path
        ttl: 300

  authorization:
    "complex-policy":
      opa:
        externalPolicy:
          url: http://my-policy-registry
      cache:
        key:
          selector: "{auth.identity.group}-{context.request.http.method}-{context.request.http.path}"
        ttl: 60

The example above sets caching for the 'external-metadata' metadata config and for the 'complex-policy' authorization policy. In the case of 'external-metadata', the cache key is the path of the original HTTP request being authorized by Authorino (fetched dynamically from the Authorization JSON); i.e., after obtaining a metadata object from the external source for a given contextual HTTP path one first time, whenever that same HTTP path repeats in a subsequent request, Authorino will use the cached object instead of sending a request again to the external source of metadata. After 5 minutes (300 seconds), the cache entry will expire and Authorino will fetch again from the source if requested.

As for the 'complex-policy' authorization policy, the cache key is a string composed the 'group' the identity belongs to, the method of the HTTP request and the path of the HTTP request. Whenever these repeat, Authorino will use the result of the policy that was evaluated and cached priorly. Cache entries in this namespace expire after 60 seconds.

Notes on evaluator caching

Capacity - By default, each cache namespace is limited to 1 mb. Entries will be evicted following First-In-First-Out (FIFO) policy to release space. The individual capacity of cache namespaces is set at the level of the Authorino instance (via --evaluator-cache-size command-line flag or spec.evaluatorCacheSize field of the Authorino CR).

Usage - Avoid caching objects whose evaluation is considered to be relatively cheap. Examples of operations associated to Authorino auth features that are usually NOT worth caching: validation of JSON Web Tokens (JWT), Kubernetes TokenReviews and SubjectAccessReviews, API key validation, simple JSON pattern-matching authorization rules, simple OPA policies. Examples of operations where caching may be desired: OAuth2 token introspection, fetching of metadata from external sources (via HTTP request), complex OPA policies.

Common feature: Metrics (metrics)

By default, Authorino will only export metrics down to the level of the AuthConfig. Deeper metrics at the level of each evaluator within an AuthConfig can be activated by setting the common field metrics: true of the evaluator config.

E.g.:

apiVersion: authorino.kuadrant.io/v1beta2
kind: AuthConfig
metadata:
  name: my-authconfig
  namespace: my-ns
spec:
  metadata:
    "my-external-metadata":
      http:
        url: http://my-external-source?search={context.request.http.path}
      metrics: true

The above will enable the metrics auth_server_evaluator_duration_seconds (histogram) and auth_server_evaluator_total (counter) with labels namespace="my-ns", authconfig="my-authconfig", evaluator_type="METADATA_GENERIC_HTTP" and evaluator_name="my-external-metadata".

The same pattern works for other types of evaluators. Find below the list of all types and corresponding label constant used in the metric:

Evaluator type Metric's evaluator_type label
authentication.apiKey IDENTITY_APIKEY
authentication.kubernetesTokenReview IDENTITY_KUBERNETES
authentication.jwt IDENTITY_OIDC
authentication.oauth2Introspection IDENTITY_OAUTH2
authentication.x509 IDENTITY_MTLS
authentication.plain IDENTITY_PLAIN
authentication.anonymous IDENTITY_NOOP
metadata.http METADATA_GENERIC_HTTP
metadata.userInfo METADATA_USERINFO
metadata.uma METADATA_UMA
authorization.patternMatching AUTHORIZATION_JSON
authorization.opa AUTHORIZATION_OPA
authorization.kubernetesSubjectAccessReview AUTHORIZATION_KUBERNETES
authorization.spicedb AUTHORIZATION_AUTHZED
response.success..plain RESPONSE_PLAIN
response.success..json RESPONSE_JSON
response.success..wristband RESPONSE_WRISTBAND

Metrics at the level of the evaluators can also be enforced to an entire Authorino instance, by setting the --deep-metrics-enabled command-line flag. In this case, regardless of the value of the field spec.(authentication|metadata|authorization|response).metrics in the AuthConfigs, individual metrics for all evaluators of all AuthConfigs will be exported.

For more information about metrics exported by Authorino, see Observability.