Skip to content

Predefined providers (uitrusting/v1) + roles claim - #99

Merged
KrisSimon merged 5 commits into
mainfrom
feature/predefined-providers
Jul 7, 2026
Merged

Predefined providers (uitrusting/v1) + roles claim#99
KrisSimon merged 5 commits into
mainfrom
feature/predefined-providers

Conversation

@KrisSimon

Copy link
Copy Markdown
Contributor

Stacked on #98 (feature/provider-tenant-info) — the generated Uitrusting script sends credentials.tenant.name, which that PR adds. Review/merge #98 first; base will retarget to main afterwards.

Summary

Lets a tenant declare a predefined provider instead of hand-writing the JavaScript, and adds a roles array claim to the token.

Predefined providers

providers entries can now be either a raw script (unchanged) or an object:

providers:
  - |
    class UserLoginProvider { … }   # raw script — still supported
  - type: uitrusting/v1             # predefined provider
    url: uitrusting.acme.svc
    token: ""                      # optional; sent as X-Internal-Token

A predefined provider is expanded internally to a generated UserLoginProvider (UitrustingProviderScript) that:

  • POST {url}/verify with { tenant, username, password } (tenant name from the login),
  • sends X-Internal-Token only when token is configured (Uitrusting's INTERNAL_AUTH_TOKEN),
  • maps the response { known, valid, subject, roles, scopes, profile } onto canLogin (known && valid), the committed subject, role/roles, scopes, and userProfile.

TenantProvider is a Codable union (string | object) and ExpressibleByStringLiteral, so existing YAML/Swift keep working. TenantSpec.providerScripts expands the list; the four provider call sites use it.

Roles claim

Adds an optional roles: [String] claim alongside the existing single role (which mirrors the primary/first role). Threaded from a provider's roles getter (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

providers items now accept a string or an object (x-kubernetes-preserve-unknown-fields).

Tests & verification

  • New: TenantProviderTest (union decode/encode + expansion), UitrustingProviderScriptTest (endpoint/scheme/token header + the generated JS loads as valid script), ProviderRolesTest (getRoles() + roles claim round-trip).
  • Full suite: 860 tests pass; ./tooling.sh lint 0 violations.

Notes / decisions

  • Request contract confirmed with you: POST {url}/verify, body {tenant, username, password} (plain password; Uitrusting hashes internally), optional shared token.
  • No UserValidationProvider is 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 by ALLOW_MISSING_PROVIDERS). Easy follow-up once a lookup endpoint exists.
  • Not run: full e2e (needs a kind cluster).

KrisSimon added 5 commits July 7, 2026 15:30
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
KrisSimon force-pushed the feature/predefined-providers branch from 036f302 to 24af045 Compare July 7, 2026 15:06
Base automatically changed from feature/provider-tenant-info to main July 7, 2026 15:37
@KrisSimon
KrisSimon merged commit 6f3db0f into main Jul 7, 2026
6 checks passed
@KrisSimon
KrisSimon deleted the feature/predefined-providers branch July 7, 2026 15:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant