square-napkin-92357
07/17/2025, 3:26 AMbrief-egg-61061
07/17/2025, 3:26 AMbrief-egg-61061
07/17/2025, 3:26 AMjson { "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-Id": "{{ print .Subject }}" } } } ] }
In this example, the value of X-User-Id will be set to the authenticated subject (user ID) for each request. You can adjust the value using Go templates to reference other session variables if needed. For more details, see the [Oathkeeper header mutator documentation](https://www.ory.sh/docs/oathkeeper/pipeline/mutator#header).square-napkin-92357
07/17/2025, 3:30 AMlog:
# change level: info, leak_sensitive_values: false when production
level: debug
format: json
leak_sensitive_values: true
serve:
proxy:
timeout:
read: 600s
write: 600s
idle: 600s
cors:
enabled: true
allowed_origins:
- "*"
allowed_methods:
- POST
- GET
- PUT
- PATCH
- DELETE
allowed_headers:
- Authorization
- Content-Type
exposed_headers:
- Content-Type
allow_credentials: true
debug: true
errors:
handlers:
json:
enabled: true
config:
verbose: true
access_rules:
matching_strategy: regexp
repositories:
- file:///etc/config/oathkeeper/access-rules.json
- file:///etc/config/oathkeeper/access-rules-md-notification.json
- file:///etc/config/oathkeeper/access-rules-md-dashboard.json
authenticators:
anonymous:
enabled: true
config:
subject: guest
bearer_token:
enabled: true
config:
check_session_url: <http://kratos:4433/sessions/whoami>
preserve_path: true
token_from:
header: Authorization
extra_from: "@this"
subject_from: "identity.id"
noop:
enabled: true
cookie_session:
enabled: true
config:
check_session_url: <http://kratos:4433/sessions/whoami>
preserve_path: true
extra_from: "@this"
subject_from: "identity.id"
only:
- ory_kratos_session
authorizers:
allow:
enabled: true
remote_json:
enabled: true
config:
remote: <http://keto:4466/relation-tuples/check>
payload: |
{
"namespace": "admin",
"object": "/admin",
"relation": "DELETE",
"subject_id": "{{ print .Subject }}"
}
mutators:
noop:
enabled: true
id_token:
enabled: true
config:
issuer_url: <http://127.0.0.1:4455/>
jwks_url: file:///etc/config/oathkeeper/id_token.jwks.json
claims: |
{
"identity": {{ .Extra.identity | toJson }}
}
cookie:
enabled: true
config:
cookies:
identity_id: "{{ print .Subject }}"
header:
enabled: true
config:
headers:
X-User-Id: "{{ print .Subject }}"
but X-User-Id is emptybrief-egg-61061
07/17/2025, 3:30 AMbrief-egg-61061
07/17/2025, 3:30 AMX-User-Id: "{{ print .Subject }}"
, it means that the .Subject
variable is not being set by the authenticator. This typically happens if the authenticator (such as cookie_session
or bearer_token
) is not able to extract the subject from the session response, or if the subject_from
path is incorrect. Here are some things to check based on your configuration and the documentation: 1. Check the session response from Kratos: The subject_from
field in your authenticators is set to "identity.id"
. This means Oathkeeper expects the session response from Kratos at /sessions/whoami
to have a structure like: json { "identity": { "id": "user-id-value" } }
If the response does not match this structure, .Subject
will be empty. You can verify this by manually calling your Kratos endpoint and checking the JSON structure. 2. Correct use of `subject_from`: If your Kratos response uses a different structure (for example, the user ID is at identity.sub
or just id
), you need to adjust the subject_from
path accordingly. For example: yaml subject_from: "id"
or yaml subject_from: "identity.sub"
3. Ensure the correct authenticator is being used: If the request is being authenticated as anonymous
(for example, if no valid session or token is present), then .Subject
will be set to "guest"
as per your anonymous authenticator config. If you want to ensure only authenticated users get a non-empty X-User-Id, make sure the access rule does not allow the anonymous authenticator to match first. 4. Access Rule Configuration: Double-check your access rules to ensure the correct authenticator is being triggered for the endpoint in question. Summary: The most likely cause is a mismatch between the subject_from
path and the actual structure of the Kratos session response. Adjust subject_from
to match the actual path to the user ID in your Kratos response, and .Subject
should be populated correctly, resulting in a non-empty X-User-Id header. For more details, see the documentation on [cookie_session configuration](https://www.ory.sh/docs/oathkeeper/pipeline/authn#cookie_session) and [header mutator](https://www.ory.sh/docs/oathkeeper/pipeline/mutator#header) for how .Subject
is set and used in mutators.<|[cookie_session configuration](https://www.ory.sh/docs/oathkeeper/pipeline/authn#cookie_session)><|[header mutator](https://www.ory.sh/docs/oathkeeper/pipeline/mutator#header)>square-napkin-92357
07/17/2025, 3:32 AMbrief-egg-61061
07/17/2025, 3:32 AMbrief-egg-61061
07/17/2025, 3:32 AMsubject_from
field in the configuration of authenticators that support it, such as cookie_session
and bearer_token
. This field is used to specify the path in the session response where Oathkeeper should extract the subject (user ID). Here is an example for the cookie_session
authenticator in your Oathkeeper configuration file: yaml authenticators: cookie_session: enabled: true config: check_session_url: <https://session-store-host> subject_from: "identity.id"
And similarly for the bearer_token
authenticator: yaml authenticators: bearer_token: enabled: true config: check_session_url: <https://session-store-host> subject_from: "identity.id"
The subject_from
value should be a [GJSON path](https://github.com/tidwall/gjson/blob/master/SYNTAX.md) that matches the structure of your session response. If not set, it defaults to "subject"
for cookie_session
and "sub"
for bearer_token
[Docs: cookie_session](https://www.ory.sh/docs/oathkeeper/pipeline/authn#cookie_session) [Docs: bearer_token](https://www.ory.sh/docs/oathkeeper/pipeline/authn#bearer_token-configuration).square-napkin-92357
07/17/2025, 3:46 AMlog:
# change level: info, leak_sensitive_values: false when production
level: debug
format: json
leak_sensitive_values: true
serve:
proxy:
timeout:
read: 600s
write: 600s
idle: 600s
cors:
enabled: true
allowed_origins:
- "*"
allowed_methods:
- POST
- GET
- PUT
- PATCH
- DELETE
allowed_headers:
- Authorization
- Content-Type
exposed_headers:
- Content-Type
allow_credentials: true
debug: true
errors:
handlers:
json:
enabled: true
config:
verbose: true
access_rules:
matching_strategy: regexp
repositories:
- file:///etc/config/oathkeeper/access-rules.json
- file:///etc/config/oathkeeper/access-rules-md-notification.json
- file:///etc/config/oathkeeper/access-rules-md-dashboard.json
authenticators:
anonymous:
enabled: true
config:
subject: guest
bearer_token:
enabled: true
config:
check_session_url: <http://kratos:4433/sessions/whoami>
preserve_path: true
token_from:
header: Authorization
extra_from: "@this"
subject_from: "identity.id"
noop:
enabled: true
cookie_session:
enabled: true
config:
check_session_url: <http://kratos:4433/sessions/whoami>
preserve_path: true
extra_from: "@this"
subject_from: "identity.id"
only:
- ory_kratos_session
authorizers:
allow:
enabled: true
remote_json:
enabled: true
config:
remote: <http://keto:4466/relation-tuples/check>
payload: |
{
"namespace": "admin",
"object": "/admin",
"relation": "DELETE",
"subject_id": "{{ print .Subject }}"
}
mutators:
noop:
enabled: true
id_token:
enabled: true
config:
issuer_url: <http://127.0.0.1:4455/>
jwks_url: file:///etc/config/oathkeeper/id_token.jwks.json
claims: |
{
"identity": {{ .Extra.identity | toJson }}
}
cookie:
enabled: true
config:
cookies:
identity_id: "{{ print .Subject }}"
header:
enabled: true
config:
headers:
X-User-Id: "{{ print .Subject }}"
brief-egg-61061
07/17/2025, 3:46 AMbrief-egg-61061
07/17/2025, 3:47 AMsubject_from: "identity.id"
for both the cookie_session
and bearer_token
authenticators. This means Oathkeeper expects the response from Kratos at /sessions/whoami
to look like: json { "identity": { "id": "some-user-id" } }
If the Kratos response does not have this structure (for example, if the user ID is at a different path), then .Subject
will be empty and so will X-User-Id. You should verify the actual JSON returned by Kratos and ensure the path matches what you have in subject_from
[Docs: cookie_session](https://www.ory.sh/docs/oathkeeper/pipeline/authn#cookie_session). 2. Anonymous Authenticator May Be Used If the request does not contain a valid session or token, the anonymous
authenticator will be used, and .Subject
will be set to "guest" (as per your config). If you see X-User-Id as "guest", this is expected. If it is empty, it means no authenticator is setting .Subject
. 3. Access Rule May Not Use the Expected Authenticator The access rule for your endpoint must specify the correct authenticator (not just anonymous
). If the rule allows anonymous
first, it will always set .Subject
to "guest" or empty. 4. No Value in Session If the Kratos session response does not contain the expected field, .Subject
will be empty. For example, if the response is: json { "id": "some-user-id" }
Then you should set subject_from: "id"
. What to do: - Check the actual response from your Kratos /sessions/whoami
endpoint. - Make sure the subject_from
path matches the user ID location in that response. - Ensure your access rules use the correct authenticator for the endpoint in question. If you adjust subject_from
to match the actual structure of your Kratos response, .Subject
should be populated and X-User-Id will no longer be empty [Docs: cookie_session](https://www.ory.sh/docs/oathkeeper/pipeline/authn#cookie_session) [Docs: header mutator](https://www.ory.sh/docs/oathkeeper/pipeline/mutator#header). If you need further help, please provide a sample of the JSON returned by your Kratos /sessions/whoami
endpoint.