From dd216df4ba6899d1684b9977b1364baa1c4cfb07 Mon Sep 17 00:00:00 2001 From: Jonas Hungershausen Date: Wed, 14 Jan 2026 08:02:57 -0500 Subject: [PATCH 1/2] chore: add guidance for external_id in tokenizer --- docs/identities/session-to-jwt-cors.mdx | 28 ++++++++++++++----- docs/kratos/debug/troubleshooting.md | 11 ++++++++ .../manage-identities/60_external-id.mdx | 18 ++++++------ 3 files changed, 42 insertions(+), 15 deletions(-) diff --git a/docs/identities/session-to-jwt-cors.mdx b/docs/identities/session-to-jwt-cors.mdx index dba68b16ab..35f72d99bd 100644 --- a/docs/identities/session-to-jwt-cors.mdx +++ b/docs/identities/session-to-jwt-cors.mdx @@ -114,13 +114,13 @@ session: whoami: tokenizer: templates: - jwt_template_1: - jwks_url: base64://... # A JSON Web Key Set (required) - claims_mapper_url: base64://... # A JsonNet template for modifying the claims - ttl: 1m # 1 minute (defaults to 10 minutes) - subject_source: id # (optional, defaults to id, other option: external_id if you're using `external_id` on identities) - another_jwt_template: - jwks_url: base64://... # A JSON Web Key Set + jwt_template_1: + jwks_url: base64://... # A JSON Web Key Set (required) + claims_mapper_url: base64://... # A JsonNet template for modifying the claims + ttl: 1m # 1 minute (defaults to 10 minutes) + subject_source: id # (optional, defaults to id, other option: external_id if you're using `external_id` on identities) + another_jwt_template: + jwks_url: base64://... # A JSON Web Key Set ``` ### JSON Web Token claim mapper @@ -220,3 +220,17 @@ If the key set contains more than one key, the first key in the list will be use ], } ``` + +### Handling `external_id` in the JWT sub claim + +If your identities use `external_id`, set `subject_source` to `external_id` in the tokenizer template to populate the JWT sub +claim with that value. + +Tokenization will fail if `subject_source` is set to `external_id` but an identity is missing `external_id`. This is a security +measure that prevents issuing tokens with the same sub claim for different identities, since identity IDs and `external_id` values +are only guaranteed to be unique within their own namespaces, not across both. + +To avoid this, configure a webhook to automatically set `external_id` for new identities (for example using the same ID generation +logic as your previous system). + +After migration, you can switch to using Ory identity IDs and set `subject_source` back to `id`. diff --git a/docs/kratos/debug/troubleshooting.md b/docs/kratos/debug/troubleshooting.md index f620804b49..4873b98ce4 100644 --- a/docs/kratos/debug/troubleshooting.md +++ b/docs/kratos/debug/troubleshooting.md @@ -57,3 +57,14 @@ To resolve this issue, you can either: navigations. Read more about CNAME cloaking: https://www.cookiestatus.com/safari/#cname-cloaking + +### Failing code flows + +If an authentication flow using the one time code method was already passed, Ory Kratos will reject any further attempts to use +the same flow. + +This can happen if the user clicks the submit button multiple times, and the frontend does not properly prevent multiple +submissions. + +To fix this, ensure that your frontend disables the submit button after the first click, or otherwise prevents multiple +submissions of the same form. diff --git a/docs/kratos/manage-identities/60_external-id.mdx b/docs/kratos/manage-identities/60_external-id.mdx index a447379fd2..29a6839a11 100644 --- a/docs/kratos/manage-identities/60_external-id.mdx +++ b/docs/kratos/manage-identities/60_external-id.mdx @@ -34,7 +34,7 @@ create or update identities. Do not add `external_id` to your identity schema definition. It is handled separately by Ory Kratos internally. -### Use `external_id` in JWT `sub` claim +### Use `external_id` in tokenized session JWTs `sub` claim Set the `subject_source` to `external_id` in the tokenization config: @@ -43,15 +43,17 @@ session: whoami: tokenizer: templates: - jwt_template_1: - jwks_url: base64://... # A JSON Web Key Set (required) - claims_mapper_url: base64://... # A JsonNet template for modifying the claims - ttl: 1m # 1 minute (defaults to 10 minutes) - subject_source: external_id - another_jwt_template: - jwks_url: base64://... # A JSON Web Key Set + jwt_template_1: + jwks_url: base64://... # A JSON Web Key Set (required) + claims_mapper_url: base64://... # A JsonNet template for modifying the claims + ttl: 1m # 1 minute (defaults to 10 minutes) + subject_source: external_id + another_jwt_template: + jwks_url: base64://... # A JSON Web Key Set ``` +Read more about [session tokenization here](../../identities/session-to-jwt-cors.mdx). + This will populate the `sub` claim in JWTs with the value of `external_id`. If `external_id` is not set for a user when `subject_source` is `external_id`, tokenization will fail. From 61cf221a790a7b89d3634ec1474aeb58e029fd66 Mon Sep 17 00:00:00 2001 From: Jonas Hungershausen Date: Wed, 14 Jan 2026 08:03:19 -0500 Subject: [PATCH 2/2] chore: u --- docs/kratos/debug/troubleshooting.md | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/docs/kratos/debug/troubleshooting.md b/docs/kratos/debug/troubleshooting.md index 4873b98ce4..f620804b49 100644 --- a/docs/kratos/debug/troubleshooting.md +++ b/docs/kratos/debug/troubleshooting.md @@ -57,14 +57,3 @@ To resolve this issue, you can either: navigations. Read more about CNAME cloaking: https://www.cookiestatus.com/safari/#cname-cloaking - -### Failing code flows - -If an authentication flow using the one time code method was already passed, Ory Kratos will reject any further attempts to use -the same flow. - -This can happen if the user clicks the submit button multiple times, and the frontend does not properly prevent multiple -submissions. - -To fix this, ensure that your frontend disables the submit button after the first click, or otherwise prevents multiple -submissions of the same form.