Predefined providers (uitrusting/v1) + roles claim - #99
Merged
Conversation
Provider scripts (UserLoginProvider / UserValidationProvider) previously only
received the username/password. To let a provider target an external, multi-
tenant user service (e.g. Uitrusting, whose API is scoped by tenant id), expose
the tenant to the script as a nested 'tenant' object on the constructor argument:
constructor(credentials) {
const name = credentials.tenant.name;
const id = credentials.tenant.id; // Kubernetes CRD UID, null for file tenants
}
Adds JSInputTenant and threads it through all four provider call sites (login,
activate, password grant, refresh validation). The field is additive, so
existing provider scripts keep working.
Expose the tenant to provider scripts as { name, namespace } instead of
{ name, id }. CRD tenants are stored as "<namespace>/<name>"; the name is now
the bare tenant name and namespace is the Kubernetes namespace (nil for
file-based tenants).
Tenant 'providers' entries may now be either a raw JavaScript script (as
before) or a predefined-provider object:
providers:
- |
class UserLoginProvider { … } # raw script, unchanged
- type: uitrusting/v1 # predefined provider
url: uitrusting.acme.svc
token: "…" # optional X-Internal-Token
A predefined provider is expanded internally to a generated UserLoginProvider
script (UitrustingProviderScript) that POSTs {tenant, username, password} to
{url}/verify and maps the Uitrusting response (known/valid/subject/roles/
scopes/profile) onto Uitsmijter's provider interface.
Also adds an optional 'roles' array claim to the token (alongside the existing
single 'role', which mirrors the primary role), threaded from the provider's
'roles' getter through the login, activate and password/refresh flows.
- TenantProvider union type (Codable: string | object; ExpressibleByStringLiteral)
- TenantSpec.providerScripts expands predefined providers; call sites updated
- CRD: providers items accept string-or-object (x-kubernetes-preserve-unknown-fields)
- Tests for decoding, script generation (incl. JS syntax load), and the roles claim
- Login now sends password_hash (SHA256 hex) instead of the plain password.
- Decide on the response 'valid' flag, not the (always-200) status code.
- Also generate a UserValidationProvider that POSTs { username, tenant } (no
hash) to the same /verify for refresh re-validation, where 'valid' means the
user still exists and is active.
Both the login and validation /verify requests now send the tenant namespace alongside the name, so Uitrusting can disambiguate tenants by namespace.
KrisSimon
force-pushed
the
feature/predefined-providers
branch
from
July 7, 2026 15:06
036f302 to
24af045
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Lets a tenant declare a predefined provider instead of hand-writing the JavaScript, and adds a
rolesarray claim to the token.Predefined providers
providersentries can now be either a raw script (unchanged) or an object:A predefined provider is expanded internally to a generated
UserLoginProvider(UitrustingProviderScript) that:POST {url}/verifywith{ tenant, username, password }(tenant name from the login),X-Internal-Tokenonly whentokenis configured (Uitrusting'sINTERNAL_AUTH_TOKEN),{ known, valid, subject, roles, scopes, profile }ontocanLogin(known && valid), the committedsubject,role/roles,scopes, anduserProfile.TenantProvideris a Codable union (string | object) andExpressibleByStringLiteral, so existing YAML/Swift keep working.TenantSpec.providerScriptsexpands the list; the four provider call sites use it.Roles claim
Adds an optional
roles: [String]claim alongside the existing singlerole(which mirrors the primary/first role). Threaded from a provider'srolesgetter (getRoles()) through the login, activate, and password/refresh token flows. Omitted from the token when the provider only exposes a single role, so existing tokens are unchanged.CRD
providersitems now accept a string or an object (x-kubernetes-preserve-unknown-fields).Tests & verification
TenantProviderTest(union decode/encode + expansion),UitrustingProviderScriptTest(endpoint/scheme/token header + the generated JS loads as valid script),ProviderRolesTest(getRoles()+rolesclaim round-trip)../tooling.sh lint0 violations.Notes / decisions
POST {url}/verify, body{tenant, username, password}(plain password; Uitrusting hashes internally), optional shared token.UserValidationProvideris generated — token refresh re-validation would need a username-only Uitrusting lookup, which isn't defined yet. A predefined-only tenant behaves like any login-only tenant (governed byALLOW_MISSING_PROVIDERS). Easy follow-up once a lookup endpoint exists.