Skip to main content

Mutators

A mutator transforms the credentials from incoming requests to credentials that your backend understands. For example, the Authorization: basic header might be transformed to X-User: <subject-id>. This allows you to write backends that don't care if the original request was an anonymous one, an OAuth 2.0 Access Token, or some other credential type. All your backend has to do is understand, for example, the X-User:.

The Access Control Decision API will return the mutated result as the HTTP Response.

noop

This mutator doesn't transform the HTTP request and simply forwards the headers as-is. This is useful if you don't want to replace, for example, Authorization: basic with X-User: <subject-id>.

noop configuration

# Global configuration file oathkeeper.yml
mutators:
noop:
# Set enabled to true if the authenticator should be enabled and false to disable the authenticator. Defaults to false.
enabled: true
# Some Access Rule: access-rule-1.yaml
id: access-rule-1
# match: ...
# upstream: ...
mutators:
- handler: noop

noop access rule example

cat ./rules.json
{
"id": "some-id",
"upstream": {
"url": "http://my-backend-service"
},
"match": {
"url": "http://my-app/api/users/<[0-9]+>/<[a-zA-Z]+>",
"methods": [
"GET"
]
},
"authenticators": [
{
"handler": "anonymous"
}
],
"authorizer": {
"handler": "allow"
},
"mutators": [
{
"handler": "noop"
}
]
}

curl -X GET http://my-app/some-route

HTTP/1.0 200 Status OK
The request has been allowed! The original HTTP Request hasn't been modified.

id_token

This mutator takes the authentication information (such as subject) and transforms it to a signed JSON Web Token, and more specifically to an OpenID Connect ID Token. Your backend can verify the token by fetching the (public) key from the /.well-known/jwks.json endpoint provided by the Ory Oathkeeper API.

Let's say a request is made to a resource protected by Ory Oathkeeper using Basic Authorization:

GET /api/resource HTTP/1.1
Host: www.example.com
Authorization: Basic Zm9vOmJhcg==

Assuming that Ory Oathkeeper is granting the access request, Basic Zm9vOmJhcg== will be replaced with a cryptographically signed JSON Web Token:

GET /api/resource HTTP/1.1
Host: internal-api-endpoint-dns
Authorization: Bearer <jwt-signed-id-token>

Now, the protected resource is capable of decoding and validating the JSON Web Token using the public key supplied by Ory Oathkeeper's API. The public key for decoding the ID token is available at Ory Oathkeeper's /.well-known/jwks.json endpoint:

http://oathkeeper:4456/.well-known/jwks.json

The related flow diagram looks like this:

ID Token Transformation

Let's say the oauth2_client_credentials authenticator successfully authenticated the credentials client-id:client-secret. This mutator will craft an ID Token (JWT) with the following exemplary claims:

{
"iss": "https://server.example.com",
"sub": "client-id",
"aud": "s6BhdRkqt3",
"jti": "n-0S6_WzA2Mj",
"exp": 1311281970,
"iat": 1311280970
}

The ID Token Claims are as follows:

  • iss: Issuer Identifier for the Issuer of the response. The iss value is a case sensitive URL using the https scheme that contains scheme, host, and optionally, port number and path components and no query or fragment components. Typically, this is the URL of Ory Oathkeeper, for example: https://oathkeeper.myapi.com.
  • sub: Subject Identifier. A locally unique and never reassigned identifier within the Issuer for the End-User, which is intended to be consumed by the Client, for example, 24400320 or AItOawmwtWwcT0k51BayewNvutrJUqsvl6qs7A4. It must not exceed 255 ASCII characters in length. The sub value is a case sensitive string. The End-User might also be an OAuth 2.0 Client, given that the access token was granted using the OAuth 2.0 Client Credentials flow.
  • aud: Audience(s) that this ID Token is intended for. It MUST contain the OAuth 2.0 client_id of the Relying Party as an audience value. It MAY also contain identifiers for other audiences. In the general case, the aud value is an array of case sensitive strings.
  • exp: Expiration time on or after which the ID Token MUST NOT be accepted for processing. The processing of this parameter requires that the current date/time MUST be before the expiration date/time listed in the value. Its value is a JSON number representing the number of seconds from 1970-01-01T0:0:0Z as measured in UTC until the date/time. See RFC 3339 [RFC3339] for details regarding date/times and UTC in particular.
  • iat: Time at which the JWT was issued. Its value is a JSON number representing the number of seconds from 1970-01-01T0:0:0Z as measured in UTC until the date/time.
  • jti: A cryptographically strong random identifier to ensure the ID Token's uniqueness.

id_token configuration

  • issuer_url (string, required) - Sets the "iss" value of the ID Token.
  • jwks_url (string, required) - Sets the URL where keys should be fetched from. Supports remote locations (http, https, s3, gs, azblob) as well as local filesystem paths.
  • ttl (string, optional) - Sets the time-to-live of the ID token. Defaults to one minute. Valid time units are: s (second), m (minute), h (hour).
  • claims (string, optional) - Allows you to customize the ID Token claims and support Go Templates. For more information, check section Claims
  • cache (object, optional) - Enables caching of computed tokens
    • enabled (bool, optional) - Enable the cache, will use exp time of ID token to determine when to evict from cache. Defaults to true.
    • max_cost (int) - Max cost to cache. Defaults to 33554432.
# Global configuration file oathkeeper.yml
mutators:
id_token:
# Set enabled to true if the authenticator should be enabled and false to disable the authenticator. Defaults to false.
enabled: true
config:
issuer_url: https://my-oathkeeper/
jwks_url: https://fetch-keys/from/this/location.json
# jwks_url: file:///from/this/absolute/location.json
# jwks_url: file://../from/this/relative/location.json
ttl: 60s
claims: '{"aud": ["https://my-backend-service/some/endpoint"],"def": "{{ print .Extra.some.arbitrary.data }}"}'
cache:
max_cost: 10000
# Some Access Rule: access-rule-1.yaml
id: access-rule-1
# match: ...
# upstream: ...
mutators:
- handler: id_token
config:
issuer_url: https://my-oathkeeper/
jwks_url: https://fetch-keys/from/this/location.json
# jwks_url: file:///from/this/absolute/location.json
# jwks_url: file://../from/this/relative/location.json
ttl: 60s
claims: '{"aud": ["https://my-backend-service/some/endpoint"],"def": "{{ print .Extra.some.arbitrary.data }}"}'

The first private key found in the JSON Web Key Set defined by mutators.id_token.jwks_url will be used for signing the JWT:

  • If the first key found is a symmetric key (HS256 algorithm), that key will be used. That key won't be broadcasted at /.well-known/jwks.json. You must manually configure the upstream to be able to fetch the key (for example from an environment variable).
  • If the first key found is an asymmetric private key (for example RS256, ES256, ...), that key will be used. The related public key will be broadcasted at /.well-known/jwks.json.

id_token Claims

This mutator allows you to specify custom claims, like the audience of ID tokens, via the claims field of the mutator's config field. The keys represent names of claims and the values are arbitrary data structures which will be parsed by the Go text/template package for value substitution, receiving the AuthenticationSession struct.

For more details please check Session variables

The claims configuration expects a string which is expected to be valid JSON:

{
"handler": "id_token",
"config": {
"claims": "{\"aud\": [\"https://my-backend-service/some/endpoint\"],\"def\": \"{{ print .Extra.some.arbitrary.data }}\"}"
}
}

Please keep in mind that certain keys (such as the sub) claim can't be overwritten!

id_token access rule example

cat ./rules.json
{
"id": "some-id",
"upstream": {
"url": "http://my-backend-service"
},
"match": {
"url": "http://my-app/api/users/<[0-9]+>/<[a-zA-Z]+>",
"methods": [
"GET"
]
},
"authenticators": [
{
"handler": "anonymous"
}
],
"authorizer": {
"handler": "allow"
},
"mutators": [
{
"handler": "id_token",
"config": {
"aud": [
"audience-1",
"audience-2"
],
"claims": "{\"abc\": \"{{ print .Subject }}\",\"def\": \"{{ print .Extra.some.arbitrary.data }}\"}"
}
}
]
}

This mutator will transform the request, allowing you to pass the credentials to the upstream application via the headers. This will augment, for example, Authorization: basic with X-User: <subject-id>.

header configuration

  • headers (object (string: string), required) - A keyed object (string:string) representing the headers to be added to this request, see section headers.
# Global configuration file oathkeeper.yml
mutators:
header:
# Set enabled to true if the authenticator should be enabled and false to disable the authenticator. Defaults to false.
enabled: true
config:
headers:
X-User: "{{ print .Subject }}"
X-Some-Arbitrary-Data: "{{ print .Extra.some.arbitrary.data }}"
# Some Access Rule: access-rule-1.yaml
id: access-rule-1
# match: ...
# upstream: ...
mutators:
- handler: header
config:
headers:
X-User: "{{ print .Subject }}"
X-Some-Arbitrary-Data: "{{ print .Extra.some.arbitrary.data }}"

Headers

The headers are specified via the headers field of the mutator's config field. The keys are the header name and the values are a string which will be parsed by the Go text/template package for value substitution, receiving the AuthenticationSession struct.

For more details please check Session variables

header access rule example

{
"id": "some-id",
"upstream": {
"url": "http://my-backend-service"
},
"match": {
"url": "http://my-app/api/<.*>",
"methods": ["GET"]
},
"authenticators": [
{
"handler": "anonymous"
}
],
"authorizer": {
"handler": "allow"
},
"mutators": [
{
"handler": "header",
"config": {
"headers": {
"X-User": "{{ print .Subject }}",
"X-Some-Arbitrary-Data": "{{ print .Extra.some.arbitrary.data }}"
}
}
}
]
}

This mutator will transform the request, allowing you to pass the credentials to the upstream application via the cookies.

  • cookies (object (string: string), required) - A keyed object (string:string) representing the cookies to be added to this request, see section cookies.
# Global configuration file oathkeeper.yml
mutators:
cookie:
# Set enabled to true if the authenticator should be enabled and false to disable the authenticator. Defaults to false.
enabled: true
config:
cookies:
user: "{{ print .Subject }}",
some-arbitrary-data: "{{ print .Extra.some.arbitrary.data }}"
# Some Access Rule: access-rule-1.yaml
id: access-rule-1
# match: ...
# upstream: ...
mutators:
- handler: cookie
config:
cookies:
user: "{{ print .Subject }}",
some-arbitrary-data: "{{ print .Extra.some.arbitrary.data }}"

Cookies

The cookies are specified via the cookies field of the mutators config field. The keys are the cookie name and the values are a string which will be parsed by the Go text/template package for value substitution, receiving the AuthenticationSession struct.

For more details please check Session variables

{
"id": "some-id",
"upstream": {
"url": "http://my-backend-service"
},
"match": {
"url": "http://my-app/api/<.*>",
"methods": ["GET"]
},
"authenticators": [
{
"handler": "anonymous"
}
],
"authorizer": {
"handler": "allow"
},
"mutators": [
{
"handler": "cookie",
"config": {
"cookies": {
"user": "{{ print .Subject }}",
"some-arbitrary-data": "{{ print .Extra.some.arbitrary.data }}"
}
}
}
]
}

hydrator

This mutator allows for fetching additional data from external APIs, which can be then used by other mutators. It works by making an upstream HTTP call to an API specified in the Per-Rule Configuration section below. The request is a POST request and it contains JSON representation of AuthenticationSession struct in body, which is:

{
"subject": String,
"extra": Object,
"header": Object,
"match_context": {
"regexp_capture_groups": Object,
"url": Object
}
}

As a response the mutator expects similar JSON object, but with extra or header fields modified.

Example request/response payload:

{
"subject": "anonymous",
"extra": {
"foo": "bar"
},
"header": {
"foo": ["bar1", "bar2"]
},
"match_context": {
"regexp_capture_groups": ["http", "foo"],
"url": "http://domain.com/foo"
}
}
note

Ory Oathkeeper is case-insensitive when accepting custom request headers. However, all incoming custom headers are converted to the canonical format of the MIME header key. This means that the first letter of the incoming header, as well as any letter that follows a hyphen, is converted into upper case and the rest of the letters are converted into lowercase. For example, the incoming header x-user-company is converted and returned by Oathkeeper as X-User-Company.

The AuthenticationSession from this object replaces the original one and is passed to the next mutator, where it can be used to set a particular cookie to the value received from an API.

Setting extra field doesn't transform the HTTP request, whereas headers set in the header field will be added to the final request headers.

Cache

This handler supports caching. If caching is enabled, the api.url configuration value and the full AuthenticationSession payload.

info

Because the cache key is quite complex, the caching handler has a higher chance of cache misses. This will be improved in future versions.

hydrator configuration

  • api.url (string - required) - The API URL.
  • api.auth.basic.* (optional) - Enables HTTP Basic Authorization.
  • api.auth.retry.* (optional) - Configures the retry logic.
  • cache.ttl (optional) - Configures how long to cache hydrate requests
# Global configuration file oathkeeper.yml
mutators:
hydrator:
# Set enabled to true if the authenticator should be enabled and false to disable the authenticator. Defaults to false.
enabled: true
config:
api:
url: http://my-backend-api
auth:
basic:
username: someUserName
password: somePassword
retry:
give_up_after: 2s
max_delay: 100ms
cache:
ttl: 60s
# Some Access Rule: access-rule-1.yaml
id: access-rule-1
# match: ...
# upstream: ...
mutators:
- handler: hydrator
config:
api:
url: http://my-backend-api
auth:
basic:
username: someUserName
password: somePassword
retry:
give_up_after: 2s
max_delay: 100ms
cache:
ttl: 60s

hydrator access rule example

{
"id": "some-id",
"upstream": {
"url": "http://my-backend-service"
},
"match": {
"url": "http://my-app/api/<.*>",
"methods": ["GET"]
},
"authenticators": [
{
"handler": "anonymous"
}
],
"authorizer": {
"handler": "allow"
},
"mutators": [
{
"handler": "hydrator",
"config": {
"api": {
"url": "http://my-backend-api"
}
}
},
{
"handler": "cookie",
"config": {
"cookies": {
"some-arbitrary-data": "{{ print .Extra.cookie }}"
}
}
}
]
}