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 do not 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 does not 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>.

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

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 has not been modified.

id_token​

This mutator takes the authentication information (e.g. 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, e.g., 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 in general 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.

Global Configuration​

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
# 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 }}"}'
# 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 will not be broadcasted at /.well-known/jwks.json. You must manually configure the upstream to be able to fetch the key (e.g. from an environment variable).
  • If the first key found is an asymmetric private key (e.g. RS256, ES256, ...), that key will be used. The related public key will be broadcasted at /.well-known/jwks.json.

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 not be overwritten!

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>.

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

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.

Configuration​

  • 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

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": "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 similiar 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"
}
}

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

Setting extra field does not 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 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.

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

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 }}"
}
}
}
]
}