From 9f638b1ddbb70d4b9c4202e37722c8beaf803f25 Mon Sep 17 00:00:00 2001 From: Tony Bartsits Date: Fri, 14 Aug 2026 13:00:47 +0200 Subject: [PATCH 1/8] docs(sso): document extra_settings.identifier_field and the claim picker Names the claim carrying the user identifier when the provider sends neither email nor upn. Covers the yaml form, the ordered-list form, where the setting lives in Instance settings, and why the advertised claim list is a starting point rather than a guarantee. Co-Authored-By: Claude Opus 4.8 (1M context) --- enterprise/sso/README.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/enterprise/sso/README.md b/enterprise/sso/README.md index 3747b376..340ef764 100644 --- a/enterprise/sso/README.md +++ b/enterprise/sso/README.md @@ -54,8 +54,32 @@ $ cd $(sudo supervisely where) scope: (list of additional scopes) token_endpoint_auth_method: acr_values: + identifier_field: / (claim carrying the user identifier) ``` +By default a user is identified by the `email` claim, falling back to `upn`. If your provider +authenticates by an internal user id and sends neither, name the claim that carries it with +`identifier_field` — a single claim name, or a list tried in order: + +```yaml + extra_settings: + identifier_field: Uid + # or, to prefer the internal id and fall back to email: + # identifier_field: [Uid, email] +``` + +The claim is looked for in the userinfo response first and then in the `id_token`. Leave +`identifier_field` unset to keep the default `email` → `upn` behaviour. + +The same setting is available in the UI: **Instance settings → Authorization → Open ID +authorization → EDIT → User identifier claim**, where **Fetch claims** lists the claims the +provider advertises in its discovery document. That list is instance-wide, so a provider may +advertise a claim it does not send to your client (and may send one it never advertises) — use +**Custom** to enter a claim name directly when that happens. + +Whichever claim you pick becomes both the login and the email of the created user, so it does not +have to look like an email address. + 3\. Create `docker-compose.override.yml` file ```yaml From 54db00d97f8228383f46c30a13a9457b0a604fda Mon Sep 17 00:00:00 2001 From: Tony Bartsits Date: Fri, 14 Aug 2026 13:08:18 +0200 Subject: [PATCH 2/8] docs(sso): state the claims caveat accurately claims_supported declares support; it does not guarantee a claim is implemented, released to the client, or populated. Co-Authored-By: Claude Opus 4.8 (1M context) --- enterprise/sso/README.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/enterprise/sso/README.md b/enterprise/sso/README.md index 340ef764..61bec75f 100644 --- a/enterprise/sso/README.md +++ b/enterprise/sso/README.md @@ -72,10 +72,14 @@ The claim is looked for in the userinfo response first and then in the `id_token `identifier_field` unset to keep the default `email` → `upn` behaviour. The same setting is available in the UI: **Instance settings → Authorization → Open ID -authorization → EDIT → User identifier claim**, where **Fetch claims** lists the claims the -provider advertises in its discovery document. That list is instance-wide, so a provider may -advertise a claim it does not send to your client (and may send one it never advertises) — use -**Custom** to enter a claim name directly when that happens. +authorization → EDIT → User identifier claim**. **Fetch claims** reads `claims_supported` from the +provider's discovery document and offers those names. + +Treat that list as a starting point rather than a contract. `claims_supported` states which claims +the provider declares support for, and an advertised claim is not guaranteed to be available: it may +be unimplemented, not released to your client, or empty for a particular user. The reverse also +happens — a provider may issue claims it never advertises. When the claim you need is not in the +list, switch to **Custom** and enter its name. Whichever claim you pick becomes both the login and the email of the created user, so it does not have to look like an email address. From 4f548ba261569d04e2ac513608e3383d02fd8e74 Mon Sep 17 00:00:00 2001 From: Tony Bartsits Date: Fri, 14 Aug 2026 13:20:10 +0200 Subject: [PATCH 3/8] docs(sso): warn that the identifier claim must be unique per user MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A returning user is matched by the resolved value alone, and the account is found before a new one would be created — so two people sharing the claim value share one account, with no error and no log line. Co-Authored-By: Claude Opus 4.8 (1M context) --- enterprise/sso/README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/enterprise/sso/README.md b/enterprise/sso/README.md index 61bec75f..69899733 100644 --- a/enterprise/sso/README.md +++ b/enterprise/sso/README.md @@ -71,6 +71,18 @@ authenticates by an internal user id and sends neither, name the claim that carr The claim is looked for in the userinfo response first and then in the `id_token`. Leave `identifier_field` unset to keep the default `email` → `upn` behaviour. +{% hint style="warning" %} +**The claim you choose must identify a user uniquely.** Its value becomes both the login and the +email of the Supervisely account, and a returning user is matched by that value alone. If two people +share it, the second one to sign in is logged into the first one's account — with their teams, +projects and role. Nothing fails and nothing is logged: the account is found and reused before any +new one would be created, so the uniqueness constraints on the account never come into play. + +Pick a claim the provider guarantees is unique and stable per user, such as an internal user id, +`upn` or `email`. Never pick a descriptive attribute like a department, display name, given name or +locale, even when it happens to look distinct in your directory today. +{% endhint %} + The same setting is available in the UI: **Instance settings → Authorization → Open ID authorization → EDIT → User identifier claim**. **Fetch claims** reads `claims_supported` from the provider's discovery document and offers those names. From 780f3bc435e616de762e17f51db78695394b79d0 Mon Sep 17 00:00:00 2001 From: Tony Bartsits Date: Fri, 14 Aug 2026 14:51:11 +0200 Subject: [PATCH 4/8] docs(sso): rename identifier_field to login_field, add the re-keying warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The setting names where the account login comes from. Also documents the second consequence — changing it later leaves existing users on accounts they no longer match — and stops suggesting upn, which Microsoft documents as mutable and reusable. Co-Authored-By: Claude Opus 4.8 (1M context) --- enterprise/sso/README.md | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/enterprise/sso/README.md b/enterprise/sso/README.md index 69899733..ec4dbb16 100644 --- a/enterprise/sso/README.md +++ b/enterprise/sso/README.md @@ -54,22 +54,22 @@ $ cd $(sudo supervisely where) scope: (list of additional scopes) token_endpoint_auth_method: acr_values: - identifier_field: / (claim carrying the user identifier) + login_field: / (claim used as the account login) ``` By default a user is identified by the `email` claim, falling back to `upn`. If your provider authenticates by an internal user id and sends neither, name the claim that carries it with -`identifier_field` — a single claim name, or a list tried in order: +`login_field` — a single claim name, or a list tried in order: ```yaml extra_settings: - identifier_field: Uid + login_field: Uid # or, to prefer the internal id and fall back to email: - # identifier_field: [Uid, email] + # login_field: [Uid, email] ``` The claim is looked for in the userinfo response first and then in the `id_token`. Leave -`identifier_field` unset to keep the default `email` → `upn` behaviour. +`login_field` unset to keep the default `email` → `upn` behaviour. {% hint style="warning" %} **The claim you choose must identify a user uniquely.** Its value becomes both the login and the @@ -78,13 +78,19 @@ share it, the second one to sign in is logged into the first one's account — w projects and role. Nothing fails and nothing is logged: the account is found and reused before any new one would be created, so the uniqueness constraints on the account never come into play. -Pick a claim the provider guarantees is unique and stable per user, such as an internal user id, -`upn` or `email`. Never pick a descriptive attribute like a department, display name, given name or -locale, even when it happens to look distinct in your directory today. +**Changing it later re-keys every login.** Existing users are looked up by the new claim, do not +match the accounts they have been using, and get new empty ones — the originals, with their teams, +projects and roles, are left behind. Treat this as a decision made once, when the provider is set up. + +Pick a claim the provider guarantees is unique and stable per user, such as an internal user id or +`email`. Never pick a descriptive attribute like a department, display name, given name or locale, +even when it happens to look distinct in your directory today. Note that `upn` — Microsoft's User +Principal Name, in the default fallback for historical reasons — is documented by Microsoft as +mutable and reusable, so prefer something durable when you have the choice. {% endhint %} The same setting is available in the UI: **Instance settings → Authorization → Open ID -authorization → EDIT → User identifier claim**. **Fetch claims** reads `claims_supported` from the +authorization → EDIT → Login claim**. **Fetch claims** reads `claims_supported` from the provider's discovery document and offers those names. Treat that list as a starting point rather than a contract. `claims_supported` states which claims From e4af1fd301d2b08a74c6d2706ad11492822466f8 Mon Sep 17 00:00:00 2001 From: Tony Bartsits Date: Fri, 14 Aug 2026 16:32:47 +0200 Subject: [PATCH 5/8] docs(sso): keep the name identifier_field The value is what a returning user is matched by, not only the account login. Co-Authored-By: Claude Opus 4.8 (1M context) --- enterprise/sso/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/enterprise/sso/README.md b/enterprise/sso/README.md index ec4dbb16..c1c95ec3 100644 --- a/enterprise/sso/README.md +++ b/enterprise/sso/README.md @@ -54,22 +54,22 @@ $ cd $(sudo supervisely where) scope: (list of additional scopes) token_endpoint_auth_method: acr_values: - login_field: / (claim used as the account login) + identifier_field: / (claim that identifies the user) ``` By default a user is identified by the `email` claim, falling back to `upn`. If your provider authenticates by an internal user id and sends neither, name the claim that carries it with -`login_field` — a single claim name, or a list tried in order: +`identifier_field` — a single claim name, or a list tried in order: ```yaml extra_settings: - login_field: Uid + identifier_field: Uid # or, to prefer the internal id and fall back to email: - # login_field: [Uid, email] + # identifier_field: [Uid, email] ``` The claim is looked for in the userinfo response first and then in the `id_token`. Leave -`login_field` unset to keep the default `email` → `upn` behaviour. +`identifier_field` unset to keep the default `email` → `upn` behaviour. {% hint style="warning" %} **The claim you choose must identify a user uniquely.** Its value becomes both the login and the @@ -90,7 +90,7 @@ mutable and reusable, so prefer something durable when you have the choice. {% endhint %} The same setting is available in the UI: **Instance settings → Authorization → Open ID -authorization → EDIT → Login claim**. **Fetch claims** reads `claims_supported` from the +authorization → EDIT → User identifier claim**. **Fetch claims** reads `claims_supported` from the provider's discovery document and offers those names. Treat that list as a starting point rather than a contract. `claims_supported` states which claims From 62e820dbd1bba1c1c4038aa2e88f54237328b7ca Mon Sep 17 00:00:00 2001 From: Tony Bartsits Date: Tue, 18 Aug 2026 18:34:02 +0200 Subject: [PATCH 6/8] docs(sso): note how identifier claim values are cased Co-Authored-By: Claude Opus 5 (1M context) --- enterprise/sso/README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/enterprise/sso/README.md b/enterprise/sso/README.md index c1c95ec3..e6f70aa1 100644 --- a/enterprise/sso/README.md +++ b/enterprise/sso/README.md @@ -71,6 +71,11 @@ authenticates by an internal user id and sends neither, name the claim that carr The claim is looked for in the userinfo response first and then in the `id_token`. Leave `identifier_field` unset to keep the default `email` → `upn` behaviour. +Values of `email` and `upn` are lowercased, because an address is the same address in any case. A +claim you name is stored and matched exactly as the provider sends it — an internal id is opaque, and +`sub` is defined as case-sensitive — so a provider that varies the case of the same user's value +would produce two accounts. + {% hint style="warning" %} **The claim you choose must identify a user uniquely.** Its value becomes both the login and the email of the Supervisely account, and a returning user is matched by that value alone. If two people From dc6cb640370a43328a62639e39959cdf3f98c624 Mon Sep 17 00:00:00 2001 From: Tony Bartsits Date: Tue, 18 Aug 2026 23:05:57 +0200 Subject: [PATCH 7/8] docs(sso): fold by value shape, and require a provider-set claim Co-Authored-By: Claude Opus 5 (1M context) --- enterprise/sso/README.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/enterprise/sso/README.md b/enterprise/sso/README.md index e6f70aa1..262edb15 100644 --- a/enterprise/sso/README.md +++ b/enterprise/sso/README.md @@ -71,10 +71,10 @@ authenticates by an internal user id and sends neither, name the claim that carr The claim is looked for in the userinfo response first and then in the `id_token`. Leave `identifier_field` unset to keep the default `email` → `upn` behaviour. -Values of `email` and `upn` are lowercased, because an address is the same address in any case. A -claim you name is stored and matched exactly as the provider sends it — an internal id is opaque, and -`sub` is defined as case-sensitive — so a provider that varies the case of the same user's value -would produce two accounts. +Values that look like an address are lowercased, whatever the claim is called, because an address is +the same address in any case. Anything else is stored and matched exactly as the provider sends it — +an internal id is opaque, and `sub` is defined as case-sensitive — so a provider that varies the case +of such a value would produce two accounts. {% hint style="warning" %} **The claim you choose must identify a user uniquely.** Its value becomes both the login and the @@ -87,8 +87,9 @@ new one would be created, so the uniqueness constraints on the account never com match the accounts they have been using, and get new empty ones — the originals, with their teams, projects and roles, are left behind. Treat this as a decision made once, when the provider is set up. -Pick a claim the provider guarantees is unique and stable per user, such as an internal user id or -`email`. Never pick a descriptive attribute like a department, display name, given name or locale, +Pick a claim the provider guarantees is unique and stable per user **and that the provider itself +sets**, such as an internal user id or `email`. A value users can edit in the directory lets them +choose which Supervisely account they land on, including an existing one. Never pick a descriptive attribute like a department, display name, given name or locale, even when it happens to look distinct in your directory today. Note that `upn` — Microsoft's User Principal Name, in the default fallback for historical reasons — is documented by Microsoft as mutable and reusable, so prefer something durable when you have the choice. From 689bbb3e313b3075e278fccbfa643eec3be619c7 Mon Sep 17 00:00:00 2001 From: Tony Bartsits Date: Tue, 18 Aug 2026 23:28:28 +0200 Subject: [PATCH 8/8] docs(sso): rename to login_field and document email_field Co-Authored-By: Claude Opus 5 (1M context) --- enterprise/sso/README.md | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/enterprise/sso/README.md b/enterprise/sso/README.md index 262edb15..a6ac970f 100644 --- a/enterprise/sso/README.md +++ b/enterprise/sso/README.md @@ -54,22 +54,41 @@ $ cd $(sudo supervisely where) scope: (list of additional scopes) token_endpoint_auth_method: acr_values: - identifier_field: / (claim that identifies the user) + login_field: / (claim the account login is taken from) + email_field: / (claim the account email is taken from) ``` -By default a user is identified by the `email` claim, falling back to `upn`. If your provider +By default the account login is taken from the `email` claim, falling back to `upn`. If your provider authenticates by an internal user id and sends neither, name the claim that carries it with -`identifier_field` — a single claim name, or a list tried in order: +`login_field` — a single claim name, or a list tried in order: ```yaml extra_settings: - identifier_field: Uid + login_field: Uid # or, to prefer the internal id and fall back to email: - # identifier_field: [Uid, email] + # login_field: [Uid, email] ``` The claim is looked for in the userinfo response first and then in the `id_token`. Leave -`identifier_field` unset to keep the default `email` → `upn` behaviour. +`login_field` unset to keep the default `email` → `upn` behaviour. + +`email_field` names the claim the account's **email** is taken from, separately from the login, +because the two are different facts: the login is the key your provider asserts, the email is a +mailbox. It defaults to the standard `email` claim. + +```yaml + extra_settings: + login_field: Uid + email_field: Mail +``` + +The email is read only when an account is created, and it is stored only if the value is an address — +a claim carrying something else is ignored, with a line in the log naming it. A provider that sends no +address leaves the field **empty**, which is allowed: an account without an email works, it simply +receives no mail. If the address already belongs to another account it is skipped rather than shared, +so a duplicate can never fail the login. When `email_field` is unset and the login itself is an +address — the default `email` → `upn` case — that address is used, which is what happened before this +setting existed. Values that look like an address are lowercased, whatever the claim is called, because an address is the same address in any case. Anything else is stored and matched exactly as the provider sends it — @@ -77,8 +96,8 @@ an internal id is opaque, and `sub` is defined as case-sensitive — so a provid of such a value would produce two accounts. {% hint style="warning" %} -**The claim you choose must identify a user uniquely.** Its value becomes both the login and the -email of the Supervisely account, and a returning user is matched by that value alone. If two people +**The claim you choose as `login_field` must identify a user uniquely.** Its value becomes the login of +the Supervisely account, and a returning user is matched by that value alone. If two people share it, the second one to sign in is logged into the first one's account — with their teams, projects and role. Nothing fails and nothing is logged: the account is found and reused before any new one would be created, so the uniqueness constraints on the account never come into play. @@ -95,8 +114,8 @@ Principal Name, in the default fallback for historical reasons — is documented mutable and reusable, so prefer something durable when you have the choice. {% endhint %} -The same setting is available in the UI: **Instance settings → Authorization → Open ID -authorization → EDIT → User identifier claim**. **Fetch claims** reads `claims_supported` from the +Both settings are available in the UI: **Instance settings → Authorization → Open ID authorization → +EDIT → User login claim** and **Email claim**. **Fetch claims** reads `claims_supported` from the provider's discovery document and offers those names. Treat that list as a starting point rather than a contract. `claims_supported` states which claims