@happyvertical/smrt-users
Users, sessions, memberships, hierarchical tenants, roles, fine-grained permissions, access requests, and Postgres RLS.
What it gives you
Users, sessions, memberships, hierarchical tenants, roles, fine-grained permissions, access requests, and Postgres RLS.
- 01Manifest-derived operation permissions
- 02Opt-in role inheritance through tenant trees
- 03OIDC login and terminal device-code auth on one session model
- 04Reusable mobile auth/session handlers and access-request graduation
OIDC login against your identity provider
Declare providers under packages.users.auth.oidc. Mount createOidcLoginHandler and createOidcCallbackHandler from the /sveltekit subpath. Each login creates an independent state, nonce, and PKCE verifier, and the challenge method is always S256. The callback checks state, the RFC 9207 authorization-response issuer, and provider errors. It also checks the JWKS-signed ID token and nonce before it reads claims. When the ID token omits email, the callback uses the UserInfo endpoint.
See the sign-in flows →First identity binding fails closed
A new issuer and subject can resolve to a canonical global Person that already has an owning User. Provisioning then stops with OidcProvisioningError code profile_owned. It does not create a new User, OIDC identity, or session. An application with an invitation or approval workflow can supply authorizeProfileOwner to authorize that first binding. A return value of undefined keeps the fail-closed default. A null value rejects the login.
See the sign-in flows →Terminal device-code sign-in
TerminalAuthService runs the device-code flow for command-line tools, and createRequest returns a device code, a short user code, and a verification URL. The CLI keeps the device code, and the person types the short user code. The service stores only a hash of the device code, and approval is idempotent. exchangeDeviceCode answers pending, expired, or approved. An approved answer includes a bearer token that resolves to the same session context as a browser cookie. Failed approvals have a per-user rate limit because user codes are short, and the handler reports the limit as 429.
See the sign-in flows →Sync the permission catalog after migration
syncPermissionCatalog merges three sources: manifest permissions, custom entries from smrt.config.ts, and definitions added at runtime. It reconciles them into Permission rows. The operation reports created, updated, and unchanged slugs. You can run it on every deploy. It is additive and never deletes a stale permission, grants a role, or emits row-level-security SQL.
Authorization model →Row-level security is generated, not hand-written
generatePostgresPermissionSql inspects tenant-scoped models whose scope is required and returns the statements for them; applyPostgresPermissionPolicies executes those statements. Each table gets row-level security enabled and forced, plus one policy per action guarded by the shared smrt_rls_bypass, smrt_current_tenant_id, and smrt_has_permission functions. Models it cannot target safely are reported as skipped with a reason rather than silently omitted.
Authorization model →A pinnable discovery contract
The /app-contract subpath carries the versioned discovery artifact that a CLI or MCP consumer can validate and pin. createDiscoveryConformanceArtifact emits deterministically ordered JSON with a SHA-256 integrity digest, and validateDiscoveryConformanceArtifact checks structure, ordering, and that digest in one call.
Generated interfaces →