acoustic-apartment-12321
10/06/2026, 4:25 PMmagnificent-energy-493
acoustic-apartment-12321
10/06/2026, 4:45 PMacoustic-apartment-12321
10/06/2026, 4:45 PMacoustic-apartment-12321
10/06/2026, 4:49 PMacoustic-apartment-12321
10/06/2026, 4:49 PMmagnificent-energy-493
acoustic-apartment-12321
10/06/2026, 8:22 PMmagnificent-energy-493
magnificent-energy-493
system:serviceaccount:<namespace>:<name>. I tested it on our side with a Kubernetes-style token.
I can send some more details if you want.acoustic-apartment-12321
10/06/2026, 8:27 PMsystem:serviceaccount:<namespace>:<name> thats rightacoustic-apartment-12321
10/06/2026, 8:28 PMacoustic-apartment-12321
10/06/2026, 8:29 PMmagnificent-energy-493
volumes:
- name: ory-token
projected:
sources:
- serviceAccountToken:
path: ory-token
audience: https://<your-ory-domain>/oauth2/token
expirationSeconds: 600
2. Get the cluster's issuer from kubectl get --raw /.well-known/openid-configuration (the issuer field) and its signing keys from kubectl get --raw /openid/v1/jwks.
3. Register the cluster as a trusted issuer with POST /admin/trust/grants/jwt-bearer/issuers, one call per signing key. Replace subject with "allow_any_subject": true if every service account in the cluster should be allowed. expires_at is required, so renew the trust before that date.
{"issuer": "<cluster issuer>", "subject": "system:serviceaccount:<ns>:<sa>", "scope": ["<scopes>"], "jwk": { <key from jwks> }, "expires_at": "2027-10-01T00:00:00Z"}
4. Create an OAuth2 client with grant_types: ["urn:ietf:params:oauth:grant-type:jwt-bearer"], token_endpoint_auth_method: "none" and the same scopes. The pod needs no secret, but client_id is required.
5. Exchange from the pod:
curl -X POST https://<your-ory-domain>/oauth2/token \
-d grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer \
-d client_id=<client id> \
-d scope=<scopes> \
--data-urlencode assertion="$(cat /var/run/secrets/tokens/ory-token)"
Three things to check while you test:
⢠Each service account token can be exchanged only once. Sending the same token again returns jti_known, so the pod should cache the Ory access token and exchange again only after the kubelet rotates the token file. Please test a container restart for this case.
⢠If your Kubernetes version doesn't put a jti claim in the token, the exchange fails with "MUST contain an 'jti' (JWT ID) claim". Send me the decoded claims and we'll sort out the config.
⢠If your APIs check aud on Ory tokens, add the API's audience to the client's audience list and pass audience=value in the exchange. Otherwise aud comes back empty.
Let me know how it goes.magnificent-energy-493