Revisiting user type flexibility: multiple user types or a single type? #5219
ThaminduDilshan
started this conversation in
Ideas
Replies: 3 comments
|
Scoped out other related concerns to a separate discussion: #5223 |
0 replies
|
Are we using the usertype to define anonymous users that has no credential? What impact it will have on the non-credential user types? |
0 replies
Meeting Summary, 1 September 2026Participants: TL;DR:
Notes:
Action items:
|
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Problem Summary
ThunderID lets an admin define their own user schema through user types, and attach each type to an organization unit. The flexibility is useful, but it has a knock-on effect: a user type is now an input to almost every other configuration that deals with user attributes. Some of those configurations ended up per user type, some did not, and a few lifecycle rules around user types were never defined.
We are about to add more attribute driven configuration (SP claim URI mapping) and to extend the same model to agents (multiple agent types). Before that, it is worth deciding whether we keep multiple user types or restrict the system to one.
Why We Need User Types
The concept exists to separate genuinely different kinds of users in one deployment. Worth being concrete about where that separation is actually needed.
At configuration time. Different types need different attributes. An
Employeehas an employee ID and aCustomerdoes not, and required and unique constraints differ between them. This is a real requirement and nothing else in the product expresses it.At runtime. Two levels:
On this reasoning, user types are needed at configuration time only, not at runtime.
Current Design
A user type is the schema for one kind of user: its attributes, which are required, which are unique, which one holds a credential, and whether self registration is allowed. Every user has exactly one type. Agents share the same implementation but are currently restricted to a single type named
default.Three bindings matter for the rest of this:
Where user types are already considered, and where they are not:
Issues and Limitations
Current Concerns Related to Type Revisit (A)
Token and claim configuration is not per user type.
Flow inputs are not user type aware.
username, one login can match both types. But if the two types have different schemas for the same attribute (ex: email_address and email), it fails at runtime.Other Related Issues (B)
Allowed user types are not validated during authentication.
A user type is referenced by name, and the name can change.
Organization unit changes and one directional uniqueness are tracked in a separate discussion:
Impact on Planned Work
Proposed Solution
Option 1: Restrict to a Single User Type
One user type per organization, the way agent types work today. Differences between kinds of users move to groups and roles.
Pros
Cons
Option 2: Keep the Current Design and Add Per User Type Mapping Everywhere
Accept that the user type is the unit of attribute configuration. Every configuration that names an attribute becomes a map keyed by user type, following the pattern already used by the application subject attribute and connection attribute mappings.
Token, assertion, userinfo and scope to claim configs get per type entries, flow inputs get per type entries, and SP claim URI mapping is built that way from the start.
Expand Concept Example
Application OAuth config, every attribute list keyed by user type:
{ "allowedUserTypes": ["Customer", "Staff"], "token": { ... "idToken": { "userAttributes": { "Customer": ["email", "given_name"], "Staff": ["email", "employee_id"] } } }, ... "scopeClaims": { "Customer": { "profile": ["given_name", "family_name"] }, "Staff": { "profile": ["displayName", "employee_id"] } } }Flow keeps its nodes as they are, and gains a separate per user type mapping section. Node inputs stay generic, and the mapping resolves them to attributes per type.
{ "handle": "basic-login", "name": "Basic Login Flow", "flowType": "AUTHENTICATION", "nodes": [ { "id": "prompt_credentials", "type": "PROMPT", "prompts": [ { "inputs": [ { "identifier": "identifier", "type": "TEXT_INPUT", "required": true }, { "identifier": "password", "type": "PASSWORD_INPUT", "required": true } ], "action": { "ref": "action_001", "nextNode": "authenticate" } } ] }, { "id": "authenticate", "type": "TASK_EXECUTION", "executor": { "name": "CredentialsAuthExecutor" }, "onSuccess": "end" } ], "inputConfig": { "identifierMappings": { "identifier": { "Customer": "email", "Staff": "employee_id" }, "password": { "Customer": "password", "Staff": "password" } } } }Pros
Cons
Option 3: Keep Multiple User Types, with a Shared Attribute List
Define attributes once at organization level. A user type then selects from that list and adds its own constraints (required, unique, credential, regex).
Attribute identity is global and referenced by an immutable handle: a type either includes a shared attribute or it does not, and cannot rename it. Other configurations reference the shared attribute list rather than a specific type, with per user type overrides available but not required.
Expand Concept Example
New organization level attribute list:
{ "attributes": [ { "handle": "email", "displayName": "Email", "type": "string" }, { "handle": "given_name", "displayName": "First Name", "type": "string" }, { "handle": "employee_id", "displayName": "Employee ID", "type": "string" } ] }Each attribute also carries an id like any other resource, but configurations reference the immutable
handle, never the display name.A user type then contributes constraints only, and no longer declares the attribute's type or name:
{ "name": "Customer", "ouId": "01900000-0000-7000-8000-000000000001", "schema": { "email": { "required": true, "unique": true }, "given_name": { "required": false } } }Application OAuth config stays flat, exactly as it is today:
{ "allowedUserTypes": ["Customer", "Staff"], "token": { "idToken": { "userAttributes": ["email", "given_name"] } }, "scopeClaims": { "profile": ["given_name", "family_name"] } }Adding
employee_idto that list would be accepted even if neitherCustomernorStaffincluded it, because the check is against the shared list. That is the weaker check noted in the cons.Flow prompt node keeps today's shape, one definition for every type:
{ "prompts": [ { "inputs": [ { "identifier": "email", "type": "TEXT_INPUT", "required": true } ] } ] }Configs that pick which attribute plays a role still stay per user type:
{ "subjectAttribute": { "Customer": "email", "Staff": "employee_id" } }Pros
Cons
emailandemail_address). It makes divergence deliberate rather than accidental, not impossible. This may not be a problem we need to resolve in this level as well. Something like this goes to the user store level configs where email is actually stored asemail_addressin one user store andemailin another.Customeridentifies byemailandStaffbyemployee_id.Questions for Community Input
modelProviderwhile a user type carriesemail, so one combined list would put agent attributes in an user type's attribute picker.All reactions