@happyvertical/smrt-users 0.40.39 → 0.40.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunks/{TerminalAuthService-BfF-2Ymu.js → TerminalAuthService-Nxt5DA13.js} +2 -2
- package/dist/chunks/{TerminalAuthService-BfF-2Ymu.js.map → TerminalAuthService-Nxt5DA13.js.map} +1 -1
- package/dist/index.js +3 -3
- package/dist/manifest.json +2 -2
- package/dist/smrt-knowledge.json +4 -4
- package/dist/sveltekit.js +1 -1
- package/package.json +9 -9
package/dist/chunks/{TerminalAuthService-BfF-2Ymu.js.map → TerminalAuthService-Nxt5DA13.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TerminalAuthService-BfF-2Ymu.js","names":["profileId","owners","profile","user","oidcIdentity","encode","decodeBase64URL","jwk.isJWK","jwk.isSecretJWK","invalidKeyInput","jwk.isPrivateJWK","jwk.isPublicJWK","b64u","encode","#payload","#payload","#protectedHeader","#unprotectedHeader","b64u","encode","#flattened","#jwt","#protectedHeader","#jwks","#cached","#url","#timeoutDuration","#cooldownDuration","#cacheMaxAge","#headers","#customFetch","#cache","#jwksTimestamp","#local","#pendingFetch"],"sources":["../../src/__smrt-register__.ts","../../src/types/index.ts","../../src/models/User.ts","../../src/models/CliAuthRequest.ts","../../src/collections/CliAuthRequestCollection.ts","../../src/models/Group.ts","../../src/collections/GroupCollection.ts","../../src/models/GroupMember.ts","../../src/collections/GroupMemberCollection.ts","../../src/models/GroupRole.ts","../../src/collections/GroupRoleCollection.ts","../../src/models/Membership.ts","../../src/collections/MembershipCollection.ts","../../src/models/MembershipOverride.ts","../../src/collections/MembershipOverrideCollection.ts","../../src/models/Permission.ts","../../src/collections/PermissionCollection.ts","../../src/models/Role.ts","../../src/collections/RoleCollection.ts","../../src/models/RolePermission.ts","../../src/services/PermissionCatalogService.ts","../../src/collections/RolePermissionCollection.ts","../../src/models/Session.ts","../../src/collections/SessionCollection.ts","../../src/models/Tenant.ts","../../src/collections/TenantCollection.ts","../../src/models/TenantPermissionOverride.ts","../../src/collections/TenantPermissionOverrideCollection.ts","../../src/migrations/backfillUserEmailKeys.ts","../../src/collections/UserCollection.ts","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/lib/buffer_utils.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/lib/base64.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/util/base64url.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/lib/crypto_key.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/lib/invalid_key_input.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/util/errors.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/lib/is_key_like.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/lib/helpers.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/lib/type_checks.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/lib/signing.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/lib/jwk_to_key.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/lib/normalize_key.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/key/import.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/lib/validate_crit.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/lib/validate_algorithms.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/lib/check_key_type.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/jws/flattened/verify.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/jws/compact/verify.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/lib/jwt_claims_set.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/jwt/verify.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/jws/flattened/sign.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/jws/compact/sign.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/jwt/sign.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/jwks/local.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/jwks/remote.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/index.js","../../src/services/OidcLoginService.ts","../../src/services/PermissionResolver.ts","../../src/services/SessionService.ts","../../src/services/MobileAuthService.ts","../../src/services/SessionPermissionContext.ts","../../src/services/OperationPermissionService.ts","../../src/services/TerminalAuthService.ts"],"sourcesContent":["/**\n * Self-registers this package's build-time manifest before any @smrt() decorator\n * in the package fires. Fixes issue #1132: in consumer runtimes (tsx, SvelteKit\n * SSR, plain `vite dev`) the decorator's synchronous manifest lookup previously\n * missed because no step populated the global manifest cache — classes got\n * registered with zero fields and `save()` / `toJSON()` silently dropped every\n * declared property.\n *\n * Import this module as the first statement in `src/index.ts` so its top-level\n * side effect runs ahead of any class module's @smrt() decorator.\n *\n * Silent no-op in dev/test, where the vitest plugin already populates manifests\n * via a different path. Only needs to succeed in the published dist output.\n *\n * @see https://github.com/happyvertical/smrt/issues/1132\n */\nimport { ObjectRegistry } from '@happyvertical/smrt-core';\n\n// During library builds, smrtPlugin replaces this entire module with generated\n// code that embeds the scanned manifest inline (#1506/#1507) — published dists\n// never resolve this URL, so downstream bundlers cannot break registration by\n// relocating the compiled module away from dist/manifest.json. The runtime\n// lookup below is the fallback for source-mode runs without that transform.\nObjectRegistry.registerPackageManifest(\n new URL('./manifest.json', import.meta.url),\n);\n","/**\n * Type definitions for smrt-users\n * @packageDocumentation\n */\n\n// Re-export status enums from smrt-types for backwards compatibility\n// These are defined in smrt-types to allow browser-safe packages to import them\nexport {\n AccessRequestStatus,\n MembershipStatus,\n OverrideEffect,\n SessionStatus,\n TenantPermissionEffect,\n TenantStatus,\n UserStatus,\n} from '@happyvertical/smrt-types';\n\n// ============= Default System Roles =============\n\n/**\n * Default system role slugs\n */\nexport const DEFAULT_ROLE_SLUGS = {\n OWNER: 'owner',\n ADMIN: 'admin',\n MEMBER: 'member',\n VIEWER: 'viewer',\n} as const;\n\n/**\n * Default system roles configuration\n */\nexport const DEFAULT_ROLES = [\n {\n slug: DEFAULT_ROLE_SLUGS.OWNER,\n name: 'Owner',\n description: 'Full access to all resources',\n },\n {\n slug: DEFAULT_ROLE_SLUGS.ADMIN,\n name: 'Administrator',\n description: 'Manage users and settings',\n },\n {\n slug: DEFAULT_ROLE_SLUGS.MEMBER,\n name: 'Member',\n description: 'Standard access',\n },\n {\n slug: DEFAULT_ROLE_SLUGS.VIEWER,\n name: 'Viewer',\n description: 'Read-only access',\n },\n] as const;\n\nexport type DefaultRoleSlug =\n (typeof DEFAULT_ROLE_SLUGS)[keyof typeof DEFAULT_ROLE_SLUGS];\n\n// ============= Tenant Policy =============\n\n/**\n * Tenant policy mode\n *\n * - `flexible`: No tenant created on signup, user can have zero tenants\n * - `personal`: Auto-create personal tenant on first login, can delete all\n * - `required`: Auto-create personal tenant, must keep at least one\n */\nexport type TenantPolicyMode = 'flexible' | 'personal' | 'required';\n\n/**\n * Tenant policy configuration\n *\n * Set in smrt.config.js to control tenant creation behavior\n */\nexport interface TenantPolicy {\n /** Policy mode */\n mode: TenantPolicyMode;\n\n /** Maximum tenants per user (0 = unlimited) */\n maxTenants: number;\n\n /** Name for auto-created default tenant */\n defaultName: string;\n}\n\n/**\n * Default tenant policy (flexible, unlimited)\n */\nexport const DEFAULT_TENANT_POLICY: TenantPolicy = {\n mode: 'flexible',\n maxTenants: 0,\n defaultName: 'Default Workspace',\n};\n","/**\n * User model - authenticated identity linked to a Profile\n * @packageDocumentation\n */\n\nimport {\n crossPackageRef,\n field,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { normalizeIdentityEmail } from '@happyvertical/smrt-profiles';\nimport type { User as UserContract } from '@happyvertical/smrt-types';\nimport { UserStatus } from '../types/index.js';\n\n/**\n * Constructor options for {@link User}.\n */\nexport interface UserOptions extends SmrtObjectOptions {\n profileId?: string;\n email?: string;\n status?: UserStatus;\n lastLoginAt?: Date | null;\n}\n\n/**\n * Basic email validation regex.\n * Validates format: local@domain.tld\n */\nconst EMAIL_REGEX = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n\n/**\n * Validate email format.\n * @param email - Email address to validate\n * @returns true if email format is valid\n */\nexport function isValidEmail(email: string): boolean {\n if (!email || typeof email !== 'string') return false;\n return EMAIL_REGEX.test(email.trim());\n}\n\n/**\n * Normalize email address (lowercase and trim).\n * @param email - Email address to normalize\n * @returns Normalized email\n */\nexport function normalizeEmail(email: string): string {\n if (!email || typeof email !== 'string') return '';\n return email.trim() ? normalizeIdentityEmail(email) : '';\n}\n\n/**\n * User represents an authenticated identity in the system.\n *\n * Users are linked to Profiles from smrt-profiles via profileId.\n * A User can have multiple Memberships across different Tenants.\n *\n * @example\n * ```typescript\n * const user = await users.create({\n * profileId: 'profile-uuid',\n * email: 'user@example.com',\n * status: UserStatus.ACTIVE\n * });\n * await user.save();\n * ```\n */\n@smrt({\n // #1400: read-only generated surface — User is auth identity (email/status/\n // profileId), provisioned via OIDC/magic-link, not arbitrary authed POST/PUT.\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class User extends SmrtObject implements UserContract {\n /**\n * Foreign key to smrt-profiles Profile (cross-package)\n */\n @crossPackageRef('@happyvertical/smrt-profiles:Profile', { unique: true })\n profileId: string = '';\n\n /**\n * User's email address (unique, used for lookup)\n */\n email: string = '';\n\n /** Durable normalized key for cross-connection email uniqueness. */\n @field({ type: 'text', nullable: true, unique: true, readonly: true })\n emailKey: string | null = null;\n\n /**\n * User account status\n */\n @field({ type: 'text' })\n status: UserStatus = UserStatus.ACTIVE;\n\n /**\n * Last login timestamp\n */\n lastLoginAt: Date | null = null;\n\n constructor(options: UserOptions = {}) {\n super(options);\n if (options.profileId !== undefined) this.profileId = options.profileId;\n if (options.email !== undefined) this.email = normalizeEmail(options.email);\n if (options.status !== undefined) this.status = options.status;\n if (options.lastLoginAt !== undefined)\n this.lastLoginAt = options.lastLoginAt;\n }\n\n /**\n * Validate the user's email format.\n * @returns true if email is valid\n */\n hasValidEmail(): boolean {\n return isValidEmail(this.email);\n }\n\n /**\n * Check if user is active\n */\n isActive(): boolean {\n return this.status === UserStatus.ACTIVE;\n }\n\n /**\n * Check if user is suspended\n */\n isSuspended(): boolean {\n return this.status === UserStatus.SUSPENDED;\n }\n\n /**\n * Check if user is pending verification\n */\n isPending(): boolean {\n return this.status === UserStatus.PENDING;\n }\n\n /**\n * Record a login event\n */\n recordLogin(): void {\n this.lastLoginAt = new Date();\n }\n\n /** Keep the durable email key derived from the public email field. */\n override async save(): Promise<this> {\n this.email = normalizeEmail(this.email);\n this.emailKey = this.email || null;\n return super.save();\n }\n}\n","/**\n * UsersCliAuthRequest model — device-code grant for terminal/CLI logins.\n *\n * Backs the \"browser approves a terminal\" flow: a CLI starts a request, the\n * server returns a short user code, the user signs in on a browser and\n * approves the code, then the CLI polls and gets back a session token bound\n * to the approving user. Records are short-lived (TTL is enforced by\n * `expiresAt`) and store only a hash of the device code so the raw device\n * code is never recoverable from the database.\n *\n * @packageDocumentation\n */\n\nimport { field, SmrtObject, smrt } from '@happyvertical/smrt-core';\n\n/**\n * CLI auth request lifecycle states.\n */\nexport type CliAuthRequestStatus = 'pending' | 'approved' | 'expired';\n\n@smrt({\n tableName: 'users_cli_auth_requests',\n api: { include: [] },\n cli: { include: [] },\n mcp: { include: [] },\n})\nexport class UsersCliAuthRequest extends SmrtObject {\n /** Short, human-typeable code the user enters in the browser to approve the session. */\n @field({ type: 'text' })\n userCode = '';\n\n /** SHA-256 hash of the long device code the CLI keeps secret. */\n @field({ type: 'text' })\n deviceCodeHash = '';\n\n /** Lifecycle state — `pending` → `approved` | `expired`. */\n @field({ type: 'text' })\n status: CliAuthRequestStatus = 'pending';\n\n /** User id of the human who approved the request (set on approval). */\n @field({ type: 'text' })\n userId = '';\n\n /** Tenant id captured from the approving session (set on approval). */\n @field({ type: 'text' })\n tenantId = '';\n\n /** Session id minted on approval — handed to the CLI as its bearer token. */\n @field({ type: 'text' })\n sessionId = '';\n\n /** When the pending request stops accepting approvals. */\n @field({ type: 'datetime' })\n expiresAt = new Date();\n\n /** When approval happened — null while the request is still pending. */\n @field({ type: 'datetime', nullable: true })\n approvedAt: Date | null = null;\n}\n\nexport { UsersCliAuthRequest as CliAuthRequest };\n","/**\n * UsersCliAuthRequestCollection — lookups for the device-code grant flow.\n *\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { UsersCliAuthRequest } from '../models/CliAuthRequest.js';\n\nexport class UsersCliAuthRequestCollection extends SmrtCollection<UsersCliAuthRequest> {\n static readonly _itemClass = UsersCliAuthRequest;\n\n /**\n * Look up a pending or completed request by the short user code shown in the CLI.\n */\n async findByUserCode(userCode: string): Promise<UsersCliAuthRequest | null> {\n const [request] = await this.list({\n limit: 1,\n where: { userCode: userCode.trim().toUpperCase() },\n });\n return request ?? null;\n }\n\n /**\n * Look up a request by the hash of its device code (the CLI's polling key).\n */\n async findByDeviceCodeHash(\n deviceCodeHash: string,\n ): Promise<UsersCliAuthRequest | null> {\n const [request] = await this.list({\n limit: 1,\n where: { deviceCodeHash },\n });\n return request ?? null;\n }\n\n /**\n * Delete expired pending requests (cleanup job).\n */\n async deleteExpired(): Promise<number> {\n const requests = await this.list({\n where: {\n status: 'pending',\n 'expiresAt <': new Date().toISOString(),\n },\n });\n\n let count = 0;\n for (const request of requests) {\n await request.delete();\n count++;\n }\n return count;\n }\n}\n\nexport { UsersCliAuthRequestCollection as CliAuthRequestCollection };\n","/**\n * Group model - team within a tenant\n * @packageDocumentation\n */\n\nimport {\n foreignKey,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\n\n/**\n * Constructor options for {@link Group}.\n */\nexport interface GroupOptions extends SmrtObjectOptions {\n tenantId?: string;\n name?: string;\n description?: string;\n}\n\n/**\n * Group represents a team or department within a tenant.\n *\n * Groups can have roles assigned to them via GroupRole.\n * Users in a group inherit permissions from the group's roles.\n *\n * @example\n * ```typescript\n * const editorsGroup = await groups.create({\n * tenantId: tenant.id,\n * name: 'Editorial Team',\n * slug: 'editorial-team',\n * description: 'Content editors and writers'\n * });\n * await editorsGroup.save();\n * ```\n */\n@smrt({\n // #1400: read-only generated surface — RBAC/identity writes go through\n // permission-gated services, not auth-only generated CRUD.\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class Group extends SmrtObject {\n /**\n * Foreign key to Tenant\n */\n @foreignKey('Tenant', { required: true })\n tenantId?: string;\n\n /**\n * Display name for the group\n */\n name: string = '';\n\n /**\n * Description of the group\n */\n description: string = '';\n\n constructor(options: GroupOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.name !== undefined) this.name = options.name;\n if (options.description !== undefined)\n this.description = options.description;\n }\n}\n","/**\n * GroupCollection - Collection manager for Group objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { Group } from '../models/Group.js';\n\n/**\n * Collection for managing Group objects\n */\nexport class GroupCollection extends SmrtCollection<Group> {\n static readonly _itemClass = Group;\n\n /**\n * Find all groups in a tenant\n */\n async findByTenant(tenantId: string): Promise<Group[]> {\n return await this.list({\n where: { tenantId },\n orderBy: 'name ASC',\n });\n }\n\n /**\n * Find group by slug within a tenant\n */\n async findBySlug(slug: string, tenantId: string): Promise<Group | null> {\n const results = await this.list({\n where: { slug, tenantId },\n limit: 1,\n });\n return results.length > 0 ? results[0] : null;\n }\n}\n","/**\n * GroupMember model - User belongs to Group (join table)\n * @packageDocumentation\n */\n\nimport {\n foreignKey,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\n\n/**\n * Constructor options for {@link GroupMember}.\n */\nexport interface GroupMemberOptions extends SmrtObjectOptions {\n groupId?: string;\n userId?: string;\n}\n\n/**\n * GroupMember is a join table linking Users to Groups.\n *\n * A user can belong to multiple groups within a tenant.\n * Group membership grants additional permissions via GroupRole.\n *\n * @example\n * ```typescript\n * // Add user to a group\n * const groupMember = await groupMembers.create({\n * groupId: editorsGroup.id,\n * userId: user.id\n * });\n * await groupMember.save();\n * ```\n */\n@smrt({\n // #1400: read-only generated REST + MCP surface — RBAC/identity writes go\n // through permission-gated services, not auth-only generated CRUD. mcp must\n // be explicit: an omitted mcp config generates the FULL tool surface.\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class GroupMember extends SmrtObject {\n /**\n * Foreign key to Group\n */\n @foreignKey('Group', { required: true })\n groupId?: string;\n\n /**\n * Foreign key to User\n */\n @foreignKey('User', { required: true })\n userId?: string;\n\n constructor(options: GroupMemberOptions = {}) {\n super(options);\n if (options.groupId !== undefined) this.groupId = options.groupId;\n if (options.userId !== undefined) this.userId = options.userId;\n }\n}\n","/**\n * GroupMemberCollection - Collection manager for GroupMember objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { GroupMember } from '../models/GroupMember.js';\nimport { GroupCollection } from './GroupCollection.js';\n\n/**\n * Collection for managing GroupMember objects\n */\nexport class GroupMemberCollection extends SmrtCollection<GroupMember> {\n static readonly _itemClass = GroupMember;\n\n /** Memoized Group collection, used to resolve the Group table name. */\n private groupCollection?: GroupCollection;\n\n /**\n * Resolve the database table name for the `Group` model from the registry\n * (via a shared-connection GroupCollection) rather than hardcoding `groups`.\n * A `@smrt({ tableName })` override or table prefix on Group would otherwise\n * make raw joins reference a non-existent or foreign table.\n */\n private async getGroupTableName(): Promise<string> {\n if (!this.groupCollection) {\n this.groupCollection = await GroupCollection.create({\n db: this.options.db,\n });\n }\n return this.groupCollection.tableName;\n }\n\n /**\n * Find all members of a group\n */\n async findByGroup(groupId: string): Promise<GroupMember[]> {\n return await this.list({\n where: { groupId },\n });\n }\n\n /**\n * Find all groups a user belongs to\n */\n async findByUser(userId: string): Promise<GroupMember[]> {\n return await this.list({\n where: { userId },\n });\n }\n\n /**\n * Check if a user is in a group\n */\n async isMember(groupId: string, userId: string): Promise<boolean> {\n const results = await this.list({\n where: { groupId, userId },\n limit: 1,\n });\n return results.length > 0;\n }\n\n /**\n * Add user to a group\n */\n async addMember(groupId: string, userId: string): Promise<GroupMember> {\n // Check if already a member\n const existing = await this.list({\n where: { groupId, userId },\n limit: 1,\n });\n if (existing.length > 0) {\n return existing[0];\n }\n\n const member = await this.create({ groupId, userId });\n await member.save();\n return member;\n }\n\n /**\n * Remove user from a group\n */\n async removeMember(groupId: string, userId: string): Promise<boolean> {\n const existing = await this.list({\n where: { groupId, userId },\n limit: 1,\n });\n if (existing.length === 0) {\n return false;\n }\n\n await existing[0].delete();\n return true;\n }\n\n /**\n * Get group IDs for a user\n * @deprecated Use getGroupIdsForTenant to prevent cross-tenant leakage\n */\n async getGroupIds(userId: string): Promise<string[]> {\n const memberships = await this.findByUser(userId);\n return memberships.map((m) => m.groupId as string);\n }\n\n /**\n * Get group IDs for a user within a specific tenant\n * This prevents cross-tenant permission leakage by filtering groups by tenant\n */\n async getGroupIdsForTenant(\n userId: string,\n tenantId: string,\n ): Promise<string[]> {\n // Query group_members joined with the Group table to filter by tenant.\n // Use raw database query to get unhydrated results. Both table names are\n // trusted registry-resolved identifiers, not user input.\n const groupTable = await this.getGroupTableName();\n const sql = `\n SELECT gm.group_id\n FROM ${this.tableName} gm\n INNER JOIN ${groupTable} g ON g.id = gm.group_id\n WHERE gm.user_id = ? AND g.tenant_id = ?\n `;\n const result = await this.db.query(sql, userId, tenantId);\n return result.rows.map(\n (r: Record<string, unknown>) => r.group_id as string,\n );\n }\n\n /**\n * Get user IDs in a group\n */\n async getUserIds(groupId: string): Promise<string[]> {\n const members = await this.findByGroup(groupId);\n return members.map((m) => m.userId as string);\n }\n}\n","/**\n * GroupRole model - Group has Role (join table)\n * @packageDocumentation\n */\n\nimport {\n foreignKey,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\n\n/**\n * Constructor options for {@link GroupRole}.\n */\nexport interface GroupRoleOptions extends SmrtObjectOptions {\n groupId?: string;\n roleId?: string;\n}\n\n/**\n * GroupRole is a join table linking Groups to Roles.\n *\n * Groups can have multiple roles assigned, and members of the group\n * inherit the permissions from all assigned roles.\n *\n * @example\n * ```typescript\n * // Assign a role to a group\n * const groupRole = await groupRoles.create({\n * groupId: editorsGroup.id,\n * roleId: editorRole.id\n * });\n * await groupRole.save();\n * ```\n */\n@smrt({\n // #1400: read-only generated REST + MCP surface — RBAC/identity writes go\n // through permission-gated services, not auth-only generated CRUD. mcp must\n // be explicit: an omitted mcp config generates the FULL tool surface.\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class GroupRole extends SmrtObject {\n /**\n * Foreign key to Group\n */\n @foreignKey('Group', { required: true })\n groupId?: string;\n\n /**\n * Foreign key to Role\n */\n @foreignKey('Role', { required: true })\n roleId?: string;\n\n constructor(options: GroupRoleOptions = {}) {\n super(options);\n if (options.groupId !== undefined) this.groupId = options.groupId;\n if (options.roleId !== undefined) this.roleId = options.roleId;\n }\n}\n","/**\n * GroupRoleCollection - Collection manager for GroupRole objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { GroupRole } from '../models/GroupRole.js';\n\n/**\n * Collection for managing GroupRole objects\n */\nexport class GroupRoleCollection extends SmrtCollection<GroupRole> {\n static readonly _itemClass = GroupRole;\n\n /**\n * Find all roles for a group\n */\n async findByGroup(groupId: string): Promise<GroupRole[]> {\n return await this.list({\n where: { groupId },\n });\n }\n\n /**\n * Find all groups that have a role\n */\n async findByRole(roleId: string): Promise<GroupRole[]> {\n return await this.list({\n where: { roleId },\n });\n }\n\n /**\n * Check if a group has a role\n */\n async hasRole(groupId: string, roleId: string): Promise<boolean> {\n const results = await this.list({\n where: { groupId, roleId },\n limit: 1,\n });\n return results.length > 0;\n }\n\n /**\n * Add role to a group\n */\n async addRole(groupId: string, roleId: string): Promise<GroupRole> {\n // Check if already has role\n const existing = await this.list({\n where: { groupId, roleId },\n limit: 1,\n });\n if (existing.length > 0) {\n return existing[0];\n }\n\n const groupRole = await this.create({ groupId, roleId });\n await groupRole.save();\n return groupRole;\n }\n\n /**\n * Remove role from a group\n */\n async removeRole(groupId: string, roleId: string): Promise<boolean> {\n const existing = await this.list({\n where: { groupId, roleId },\n limit: 1,\n });\n if (existing.length === 0) {\n return false;\n }\n\n await existing[0].delete();\n return true;\n }\n\n /**\n * Get role IDs for a group\n */\n async getRoleIds(groupId: string): Promise<string[]> {\n const groupRoles = await this.findByGroup(groupId);\n return groupRoles.map((gr) => gr.roleId as string);\n }\n}\n","/**\n * Membership model - User belongs to Tenant with a Role\n * @packageDocumentation\n */\n\nimport {\n field,\n foreignKey,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport type { Membership as MembershipContract } from '@happyvertical/smrt-types';\nimport { MembershipStatus } from '../types/index.js';\n\n/**\n * Constructor options for {@link Membership}.\n */\nexport interface MembershipOptions extends SmrtObjectOptions {\n userId?: string;\n tenantId?: string;\n roleId?: string;\n status?: MembershipStatus;\n}\n\n/**\n * Membership represents a user's membership in a tenant with an assigned role.\n *\n * A user can have only one membership per tenant (UNIQUE userId + tenantId).\n * The role determines the base permissions for the user in that tenant.\n *\n * @example\n * ```typescript\n * const membership = await memberships.create({\n * userId: user.id,\n * tenantId: tenant.id,\n * roleId: role.id,\n * status: MembershipStatus.ACTIVE\n * });\n * await membership.save();\n * ```\n */\n@smrt({\n // #1400: read-only generated surface — RBAC/identity writes go through\n // permission-gated services, not auth-only generated CRUD (requireRouteAuth\n // is authentication-only, so a generated POST/PUT here is privilege escalation).\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class Membership extends SmrtObject implements MembershipContract {\n /**\n * Foreign key to User\n */\n @foreignKey('User', { required: true })\n userId?: string;\n\n /**\n * Foreign key to Tenant\n */\n @foreignKey('Tenant', { required: true })\n tenantId?: string;\n\n /**\n * Foreign key to Role - determines base permissions\n */\n @foreignKey('Role', { required: true })\n roleId?: string;\n\n /**\n * Membership status\n */\n @field({ type: 'text' })\n status: MembershipStatus = MembershipStatus.ACTIVE;\n\n constructor(options: MembershipOptions = {}) {\n super(options);\n if (options.userId !== undefined) this.userId = options.userId;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.roleId !== undefined) this.roleId = options.roleId;\n if (options.status !== undefined) this.status = options.status;\n }\n\n /**\n * Check if membership is active\n */\n isActive(): boolean {\n return this.status === MembershipStatus.ACTIVE;\n }\n\n /**\n * Check if membership is pending (invitation not yet accepted)\n */\n isPending(): boolean {\n return this.status === MembershipStatus.PENDING;\n }\n}\n","/**\n * MembershipCollection - Collection manager for Membership objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { Membership } from '../models/Membership.js';\nimport { MembershipStatus } from '../types/index.js';\n\n/**\n * Collection for managing Membership objects\n */\nexport class MembershipCollection extends SmrtCollection<Membership> {\n static readonly _itemClass = Membership;\n\n /**\n * Find all memberships for a user\n */\n async findByUser(userId: string): Promise<Membership[]> {\n return await this.list({\n where: { userId },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find all active memberships for a user\n */\n async findActiveByUser(userId: string): Promise<Membership[]> {\n return await this.list({\n where: { userId, status: MembershipStatus.ACTIVE },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find all memberships in a tenant\n */\n async findByTenant(tenantId: string): Promise<Membership[]> {\n return await this.list({\n where: { tenantId },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find active memberships in a tenant\n */\n async findActiveByTenant(tenantId: string): Promise<Membership[]> {\n return await this.list({\n where: { tenantId, status: MembershipStatus.ACTIVE },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find a specific user's membership in a tenant\n */\n async findByUserAndTenant(\n userId: string,\n tenantId: string,\n ): Promise<Membership | null> {\n const results = await this.list({\n where: { userId, tenantId },\n limit: 1,\n });\n return results.length > 0 ? results[0] : null;\n }\n\n /**\n * Find memberships by role\n */\n async findByRole(roleId: string): Promise<Membership[]> {\n return await this.list({\n where: { roleId },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find memberships by status\n */\n async findByStatus(status: MembershipStatus): Promise<Membership[]> {\n return await this.list({\n where: { status },\n orderBy: 'created_at DESC',\n });\n }\n}\n","/**\n * MembershipOverride model - per-user permission grant/deny\n * @packageDocumentation\n */\n\nimport {\n field,\n foreignKey,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { OverrideEffect } from '../types/index.js';\n\n/**\n * Constructor options for {@link MembershipOverride}.\n */\nexport interface MembershipOverrideOptions extends SmrtObjectOptions {\n membershipId?: string;\n permissionId?: string;\n effect?: OverrideEffect;\n}\n\n/**\n * MembershipOverride allows granting or denying specific permissions\n * to a user beyond what their role and groups provide.\n *\n * Overrides are applied after role and group permissions are resolved.\n * DENY overrides take precedence over GRANT overrides.\n *\n * @example\n * ```typescript\n * // Grant a specific permission to a user\n * const override = await membershipOverrides.create({\n * membershipId: membership.id,\n * permissionId: specialPermission.id,\n * effect: OverrideEffect.GRANT\n * });\n *\n * // Deny a permission (even if role grants it)\n * const deny = await membershipOverrides.create({\n * membershipId: membership.id,\n * permissionId: dangerousPermission.id,\n * effect: OverrideEffect.DENY\n * });\n * ```\n */\n@smrt({\n // #1400: read-only generated REST + MCP surface — RBAC/identity writes go\n // through permission-gated services, not auth-only generated CRUD. mcp must\n // be explicit: an omitted mcp config generates the FULL tool surface.\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class MembershipOverride extends SmrtObject {\n /**\n * Foreign key to Membership\n */\n @foreignKey('Membership', { required: true })\n membershipId?: string;\n\n /**\n * Foreign key to Permission\n */\n @foreignKey('Permission', { required: true })\n permissionId?: string;\n\n /**\n * Effect of the override: grant or deny\n */\n @field({ type: 'text' })\n effect: OverrideEffect = OverrideEffect.GRANT;\n\n constructor(options: MembershipOverrideOptions = {}) {\n super(options);\n if (options.membershipId !== undefined)\n this.membershipId = options.membershipId;\n if (options.permissionId !== undefined)\n this.permissionId = options.permissionId;\n if (options.effect !== undefined) this.effect = options.effect;\n }\n\n /**\n * Check if this override grants the permission\n */\n isGrant(): boolean {\n return this.effect === OverrideEffect.GRANT;\n }\n\n /**\n * Check if this override denies the permission\n */\n isDeny(): boolean {\n return this.effect === OverrideEffect.DENY;\n }\n}\n","/**\n * MembershipOverrideCollection - Collection manager for MembershipOverride objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { MembershipOverride } from '../models/MembershipOverride.js';\nimport { OverrideEffect } from '../types/index.js';\n\n/**\n * Collection for managing MembershipOverride objects\n */\nexport class MembershipOverrideCollection extends SmrtCollection<MembershipOverride> {\n static readonly _itemClass = MembershipOverride;\n\n /**\n * Find all overrides for a membership\n */\n async findByMembership(membershipId: string): Promise<MembershipOverride[]> {\n return await this.list({\n where: { membershipId },\n });\n }\n\n /**\n * Find grant overrides for a membership.\n *\n * Filters in memory because the `effect` column is JSON-typed and\n * Postgres rejects bare `json = text` comparisons. A single\n * `findByMembership` call is reused for both grant and deny lookups\n * within the same request (see `_overridesByMembership` cache).\n */\n async findGrants(membershipId: string): Promise<MembershipOverride[]> {\n const all = await this.findByMembership(membershipId);\n return all.filter((o) => o.effect === OverrideEffect.GRANT);\n }\n\n /**\n * Find deny overrides for a membership.\n *\n * See `findGrants` for rationale on in-memory filtering.\n */\n async findDenies(membershipId: string): Promise<MembershipOverride[]> {\n const all = await this.findByMembership(membershipId);\n return all.filter((o) => o.effect === OverrideEffect.DENY);\n }\n\n /**\n * Get grant permission IDs for a membership\n */\n async getGrantedPermissionIds(membershipId: string): Promise<string[]> {\n const grants = await this.findGrants(membershipId);\n return grants.map((o) => o.permissionId as string);\n }\n\n /**\n * Get denied permission IDs for a membership\n */\n async getDeniedPermissionIds(membershipId: string): Promise<string[]> {\n const denies = await this.findDenies(membershipId);\n return denies.map((o) => o.permissionId as string);\n }\n\n /**\n * Set an override for a membership\n */\n async setOverride(\n membershipId: string,\n permissionId: string,\n effect: OverrideEffect,\n ): Promise<MembershipOverride> {\n // Check if override already exists\n const existing = await this.list({\n where: { membershipId, permissionId },\n limit: 1,\n });\n\n if (existing.length > 0) {\n // Update existing override\n existing[0].effect = effect;\n await existing[0].save();\n return existing[0];\n }\n\n // Create new override\n const override = await this.create({ membershipId, permissionId, effect });\n await override.save();\n return override;\n }\n\n /**\n * Remove an override\n */\n async removeOverride(\n membershipId: string,\n permissionId: string,\n ): Promise<boolean> {\n const existing = await this.list({\n where: { membershipId, permissionId },\n limit: 1,\n });\n\n if (existing.length === 0) {\n return false;\n }\n\n await existing[0].delete();\n return true;\n }\n\n /**\n * Grant a permission to a membership\n * Convenience method for setOverride with GRANT effect\n */\n async grantPermission(\n membershipId: string,\n permissionId: string,\n ): Promise<MembershipOverride> {\n return await this.setOverride(\n membershipId,\n permissionId,\n OverrideEffect.GRANT,\n );\n }\n\n /**\n * Deny a permission for a membership\n * Convenience method for setOverride with DENY effect\n */\n async denyPermission(\n membershipId: string,\n permissionId: string,\n ): Promise<MembershipOverride> {\n return await this.setOverride(\n membershipId,\n permissionId,\n OverrideEffect.DENY,\n );\n }\n}\n","/**\n * Permission model - named capability in the system\n * @packageDocumentation\n */\n\nimport {\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\n\n/**\n * Parsed permission slug components\n */\nexport interface ParsedPermissionSlug {\n /** Resource part (e.g., 'articles' from 'articles.create') */\n resource: string;\n /** Action part (e.g., 'create' from 'articles.create') */\n action: string;\n /** Whether the slug follows the valid 'resource.action[.scope...]' pattern */\n isValid: boolean;\n}\n\n/**\n * Parse a permission slug into its resource and action components.\n * Valid format: 'resource.action[.scope...]' (e.g., 'articles.create' or\n * 'products.read.internal')\n *\n * @param slug - The permission slug to parse\n * @returns Parsed components with validation status\n */\nexport function parsePermissionSlug(slug: string): ParsedPermissionSlug {\n const parts = slug.split('.');\n if (parts.length >= 2 && parts.every(Boolean)) {\n return {\n resource: parts[0],\n action: parts.slice(1).join('.'),\n isValid: true,\n };\n }\n return {\n resource: slug,\n action: '',\n isValid: false,\n };\n}\n\n/**\n * Validate that a permission slug follows the 'resource.action[.scope...]'\n * pattern.\n *\n * @param slug - The slug to validate\n * @returns true if valid, false otherwise\n */\nexport function isValidPermissionSlug(slug: string): boolean {\n return parsePermissionSlug(slug).isValid;\n}\n\n/**\n * Constructor options for {@link Permission}.\n */\nexport interface PermissionOptions extends SmrtObjectOptions {\n name?: string;\n description?: string;\n category?: string;\n}\n\n/**\n * Permission represents a named capability in the system.\n *\n * Permissions are defined by the application and assigned to roles.\n * Permission slugs follow the pattern: resource.action[.scope...] (e.g.,\n * 'articles.create' or 'products.read.internal')\n *\n * @example\n * ```typescript\n * const permission = await permissions.create({\n * slug: 'articles.create',\n * name: 'Create Articles',\n * description: 'Allows creating new articles',\n * category: 'articles'\n * });\n * await permission.save();\n * ```\n */\n@smrt({\n // #1400: read-only generated surface — RBAC/identity writes go through\n // permission-gated services, not auth-only generated CRUD.\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class Permission extends SmrtObject {\n /**\n * Display name for the permission\n */\n name: string = '';\n\n /**\n * Description of what this permission allows\n */\n description: string = '';\n\n /**\n * Category for grouping in UI (e.g., 'articles', 'users', 'settings')\n */\n category: string = '';\n\n constructor(options: PermissionOptions = {}) {\n super(options);\n if (options.name !== undefined) this.name = options.name;\n if (options.description !== undefined)\n this.description = options.description;\n if (options.category !== undefined) this.category = options.category;\n }\n\n /**\n * Parse the permission slug into resource and action components.\n * @returns Parsed slug with resource, action, and validation status\n */\n parseSlug(): ParsedPermissionSlug {\n return parsePermissionSlug(this.slug ?? '');\n }\n\n /**\n * Get the resource part of the permission slug\n * e.g., 'articles.create' -> 'articles'\n */\n getResource(): string {\n return this.parseSlug().resource;\n }\n\n /**\n * Get the action part of the permission slug\n * e.g., 'articles.create' -> 'create'\n */\n getAction(): string {\n return this.parseSlug().action;\n }\n\n /**\n * Check if the permission slug is valid (follows 'resource.action' pattern)\n */\n isValidSlug(): boolean {\n return this.parseSlug().isValid;\n }\n}\n","/**\n * PermissionCollection - Collection manager for Permission objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { Permission } from '../models/Permission.js';\n\n/**\n * Collection for managing Permission objects\n */\nexport class PermissionCollection extends SmrtCollection<Permission> {\n static readonly _itemClass = Permission;\n\n /**\n * Find permissions by category\n */\n async findByCategory(category: string): Promise<Permission[]> {\n return await this.list({\n where: { category },\n orderBy: 'slug ASC',\n });\n }\n\n /**\n * Find permission by slug\n */\n async findBySlug(slug: string): Promise<Permission | null> {\n const results = await this.list({\n where: { slug },\n limit: 1,\n });\n return results.length > 0 ? results[0] : null;\n }\n\n /**\n * Batch fetch permissions by IDs\n * Returns a Map of id -> Permission for efficient lookup\n */\n async findByIds(ids: string[]): Promise<Map<string, Permission>> {\n if (ids.length === 0) {\n return new Map();\n }\n\n // Remove duplicates\n const uniqueIds = [...new Set(ids)];\n\n // Build placeholders for SQL IN clause\n const placeholders = uniqueIds.map(() => '?').join(', ');\n const results = await this.query(\n `SELECT * FROM ${this.tableName} WHERE id IN (${placeholders})`,\n uniqueIds,\n );\n\n const map = new Map<string, Permission>();\n for (const perm of results) {\n if (perm.id) {\n map.set(perm.id, perm);\n }\n }\n return map;\n }\n\n /**\n * Get all unique categories\n */\n async getCategories(): Promise<string[]> {\n const results = await this.query(\n `SELECT DISTINCT category FROM ${this.tableName} WHERE category != '' ORDER BY category ASC`,\n );\n return results.map((r) => r.category);\n }\n\n /**\n * Find or create a permission by slug\n */\n async findOrCreate(\n slug: string,\n defaults: Partial<{\n name: string;\n description: string;\n category: string;\n }> = {},\n ): Promise<Permission> {\n const existing = await this.findBySlug(slug);\n if (existing) {\n return existing;\n }\n\n const permission = await this.create({\n slug,\n name: defaults.name ?? slug,\n description: defaults.description ?? '',\n category: defaults.category ?? slug.split('.')[0],\n });\n await permission.save();\n return permission;\n }\n}\n","/**\n * Role model - permission template\n * @packageDocumentation\n */\n\nimport {\n foreignKey,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport type { Role as RoleContract } from '@happyvertical/smrt-types';\n\n/**\n * Constructor options for {@link Role}.\n */\nexport interface RoleOptions extends SmrtObjectOptions {\n tenantId?: string | null;\n name?: string;\n description?: string;\n isSystem?: boolean;\n inheritsToDescendants?: boolean;\n}\n\n/**\n * Role represents a permission template that can be assigned to users.\n *\n * Roles can be:\n * - System roles (tenantId = null): Available to all tenants, cannot be modified\n * - Custom roles (tenantId set): Specific to a tenant, can be customized\n *\n * @example\n * ```typescript\n * // System role (tenantId = null)\n * const adminRole = await roles.create({\n * slug: 'admin',\n * name: 'Administrator',\n * isSystem: true\n * });\n *\n * // Custom tenant role\n * const editorRole = await roles.create({\n * tenantId: tenant.id,\n * slug: 'editor',\n * name: 'Editor',\n * description: 'Can edit content'\n * });\n * ```\n */\n@smrt({\n // #1400: read-only generated surface — RBAC/identity writes go through\n // permission-gated services, not auth-only generated CRUD.\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class Role extends SmrtObject implements RoleContract {\n /**\n * Foreign key to Tenant\n * null = system role available to all tenants\n */\n @foreignKey('Tenant', { nullable: true })\n tenantId?: string | null;\n\n /**\n * Display name for the role\n */\n name: string = '';\n\n /**\n * Description of the role\n */\n description: string = '';\n\n /**\n * Whether this is a system role (cannot be deleted)\n */\n isSystem: boolean = false;\n\n /**\n * Opt-in hierarchical inheritance: when true, an ACTIVE membership holding\n * this role also resolves permissions in descendant tenants where the user\n * has no direct membership (nearest flagged ancestor membership wins).\n *\n * Default false: the role's authority is exact-tenant only, which is the\n * pre-existing resolver behavior. Flag structural roles like a network-level\n * owner/admin; leave per-tenant roles like member/viewer unflagged.\n */\n inheritsToDescendants: boolean = false;\n\n constructor(options: RoleOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.name !== undefined) this.name = options.name;\n if (options.description !== undefined)\n this.description = options.description;\n if (options.isSystem !== undefined) this.isSystem = options.isSystem;\n if (options.inheritsToDescendants !== undefined)\n this.inheritsToDescendants = options.inheritsToDescendants;\n }\n\n /**\n * Check if this is a system-wide role\n */\n isSystemRole(): boolean {\n return this.tenantId === null || this.tenantId === undefined;\n }\n\n /**\n * Check if this is a tenant-specific role\n */\n isTenantRole(): boolean {\n return this.tenantId !== null && this.tenantId !== undefined;\n }\n\n /**\n * Check if this role can be deleted.\n * System roles (isSystem = true) cannot be deleted.\n * @returns true if the role can be deleted\n */\n canDelete(): boolean {\n return !this.isSystem;\n }\n\n /**\n * Delete guard - prevents deletion of system roles.\n * Override the delete method to check isSystem flag first.\n */\n async delete(): Promise<void> {\n if (this.isSystem) {\n throw new Error(\n `Cannot delete system role '${this.slug}'. System roles are protected.`,\n );\n }\n return super.delete();\n }\n}\n","/**\n * RoleCollection - Collection manager for Role objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { Role } from '../models/Role.js';\nimport { DEFAULT_ROLES } from '../types/index.js';\nimport type {\n RolePermissionPatternMatrix,\n SeedRolePermissionsOptions,\n} from './RolePermissionCollection.js';\n\nexport interface SeedSystemRolesOptions\n extends Omit<SeedRolePermissionsOptions, 'catalog'> {\n permissionMatrix?: RolePermissionPatternMatrix;\n seedPermissions?: boolean;\n /**\n * System role slugs to flag `inheritsToDescendants: true` (opt-in\n * hierarchical membership inheritance, e.g. `['owner', 'admin']` for\n * organizational hierarchies). Additive and idempotent: listed slugs are\n * flagged on create and on already-seeded roles, but omitting a slug never\n * unsets a previously flagged role — unflag explicitly via a role update.\n * Unknown slugs throw (a typo here would silently withhold intended\n * authority). Default: no role inherits.\n */\n inheritsToDescendants?: string[];\n}\n\n/**\n * Collection for managing Role objects\n */\nexport class RoleCollection extends SmrtCollection<Role> {\n static readonly _itemClass = Role;\n\n /**\n * Find all system roles (tenantId is null)\n */\n async findSystemRoles(): Promise<Role[]> {\n // Query for roles where tenantId is null\n return await this.query(\n `SELECT * FROM ${this.tableName} WHERE tenant_id IS NULL ORDER BY name ASC`,\n );\n }\n\n /**\n * Find roles available for a tenant (system + tenant-specific)\n */\n async findByTenant(tenantId: string): Promise<Role[]> {\n return await this.query(\n `SELECT * FROM ${this.tableName}\n WHERE tenant_id IS NULL OR tenant_id = ?\n ORDER BY is_system DESC, name ASC`,\n [tenantId],\n );\n }\n\n /**\n * Find tenant-specific roles only\n */\n async findTenantRoles(tenantId: string): Promise<Role[]> {\n return await this.list({\n where: { tenantId },\n orderBy: 'name ASC',\n });\n }\n\n /**\n * Find role by slug within a tenant context\n */\n async findBySlug(slug: string, tenantId?: string): Promise<Role | null> {\n // First check tenant-specific role\n if (tenantId) {\n const tenantRoles = await this.list({\n where: { slug, tenantId },\n limit: 1,\n });\n if (tenantRoles.length > 0) {\n return tenantRoles[0];\n }\n }\n\n // Fall back to system role\n const systemRoles = await this.query(\n `SELECT * FROM ${this.tableName} WHERE slug = ? AND tenant_id IS NULL LIMIT 1`,\n [slug],\n );\n return systemRoles.length > 0 ? systemRoles[0] : null;\n }\n\n /**\n * Seed default system roles\n */\n async seedSystemRoles(options: SeedSystemRolesOptions = {}): Promise<Role[]> {\n const roles: Role[] = [];\n\n const inheritableSlugs = new Set(options.inheritsToDescendants ?? []);\n const knownSlugs = new Set<string>(DEFAULT_ROLES.map((def) => def.slug));\n for (const slug of inheritableSlugs) {\n if (!knownSlugs.has(slug)) {\n throw new Error(\n `Unknown system role slug '${slug}' in inheritsToDescendants. ` +\n `Valid slugs: ${[...knownSlugs].join(', ')}.`,\n );\n }\n }\n\n for (const roleDef of DEFAULT_ROLES) {\n const inheritable = inheritableSlugs.has(roleDef.slug);\n\n // Check if role already exists\n const existing = await this.findBySlug(roleDef.slug);\n if (existing) {\n // Additive-only convergence: flag a listed slug, never unset one.\n if (inheritable && !existing.inheritsToDescendants) {\n existing.inheritsToDescendants = true;\n await existing.save();\n }\n roles.push(existing);\n continue;\n }\n\n // Create new system role\n const role = await this.create({\n slug: roleDef.slug,\n name: roleDef.name,\n description: roleDef.description,\n tenantId: null,\n isSystem: true,\n inheritsToDescendants: inheritable,\n });\n await role.save();\n roles.push(role);\n }\n\n if (options.seedPermissions) {\n const { RolePermissionCollection } = await import(\n './RolePermissionCollection.js'\n );\n const rolePermissions = await RolePermissionCollection.create(\n this.options,\n );\n await rolePermissions.seedRolePermissions(options.permissionMatrix, {\n prune: options.prune,\n syncCatalog: options.syncCatalog,\n tenantId: options.tenantId,\n });\n }\n\n return roles;\n }\n}\n","/**\n * RolePermission model - Role has Permission (join table)\n * @packageDocumentation\n */\n\nimport {\n foreignKey,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\n\n/**\n * Constructor options for {@link RolePermission}.\n */\nexport interface RolePermissionOptions extends SmrtObjectOptions {\n roleId?: string;\n permissionId?: string;\n}\n\n/**\n * RolePermission is a join table linking Roles to Permissions.\n *\n * This enables many-to-many relationship between roles and permissions.\n * A role can have multiple permissions, and a permission can belong to multiple roles.\n *\n * @example\n * ```typescript\n * // Assign a permission to a role\n * const rolePermission = await rolePermissions.create({\n * roleId: adminRole.id,\n * permissionId: createArticlePermission.id\n * });\n * await rolePermission.save();\n * ```\n */\n@smrt({\n // #1400: read-only generated REST + MCP surface — RBAC/identity writes go\n // through permission-gated services, not auth-only generated CRUD. mcp must\n // be explicit: an omitted mcp config generates the FULL tool surface.\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class RolePermission extends SmrtObject {\n /**\n * Foreign key to Role\n */\n @foreignKey('Role', { required: true })\n roleId?: string;\n\n /**\n * Foreign key to Permission\n */\n @foreignKey('Permission', { required: true })\n permissionId?: string;\n\n constructor(options: RolePermissionOptions = {}) {\n super(options);\n if (options.roleId !== undefined) this.roleId = options.roleId;\n if (options.permissionId !== undefined)\n this.permissionId = options.permissionId;\n }\n}\n","/**\n * Manifest-derived permission cataloging and syncing\n * @packageDocumentation\n */\n\nimport { getPackageConfig } from '@happyvertical/smrt-config';\nimport {\n findManifestEntryByQualifiedName,\n ObjectRegistry,\n type SmartObjectDefinition,\n type SmrtClassOptions,\n} from '@happyvertical/smrt-core';\nimport { PermissionCollection } from '../collections/PermissionCollection.js';\nimport {\n isValidPermissionSlug,\n parsePermissionSlug,\n} from '../models/Permission.js';\n\nexport type PermissionCatalogSource = 'manifest' | 'config' | 'runtime';\n\nexport type PostgresPermissionAction =\n | 'SELECT'\n | 'INSERT'\n | 'UPDATE'\n | 'DELETE';\n\nexport interface PostgresPermissionBinding {\n action: Lowercase<PostgresPermissionAction> | PostgresPermissionAction;\n permission?: string;\n schemaName?: string;\n tableName: string;\n tenantField?: string;\n}\n\nexport interface PermissionDefinition {\n category?: string;\n className?: string;\n collection?: string;\n description?: string;\n name?: string;\n postgres?: {\n bindings?: PostgresPermissionBinding[];\n };\n qualifiedName?: string;\n slug: string;\n source?: PermissionCatalogSource;\n}\n\nexport interface PermissionCatalog {\n customPermissions: PermissionDefinition[];\n manifestPermissions: PermissionDefinition[];\n permissions: PermissionDefinition[];\n runtimePermissions: PermissionDefinition[];\n}\n\nexport interface PermissionCatalogSyncResult {\n catalog: PermissionCatalog;\n created: string[];\n unchanged: string[];\n updated: string[];\n}\n\nexport interface UsersConfig extends Record<string, unknown> {\n permissions?: {\n custom?: PermissionDefinition[];\n postgres?: {\n bindings?: PostgresPermissionBinding[];\n enabled?: boolean;\n };\n };\n}\n\nexport interface ConstructorLike {\n name?: string;\n}\n\nexport interface CollectionLike {\n getItemClass?: () => ConstructorLike;\n}\n\nexport type OperationPermissionCollectionInput =\n | string\n | ConstructorLike\n | CollectionLike\n | object;\n\ndeclare global {\n // eslint-disable-next-line no-var\n var __smrtUsersPermissionRegistrations:\n | Map<number, PermissionDefinition[]>\n | undefined;\n // eslint-disable-next-line no-var\n var __smrtUsersPermissionRegistrationCounter: number | undefined;\n}\n\nfunction getRuntimePermissionRegistrations(): Map<\n number,\n PermissionDefinition[]\n> {\n globalThis.__smrtUsersPermissionRegistrations ??= new Map<\n number,\n PermissionDefinition[]\n >();\n return globalThis.__smrtUsersPermissionRegistrations;\n}\n\nfunction toSnakeCase(value: string): string {\n return value\n .replace(/([a-z0-9])([A-Z])/g, '$1_$2')\n .replace(/[-\\s]+/g, '_')\n .toLowerCase();\n}\n\nfunction pluralize(word: string): string {\n if (word.endsWith('y') && !/[aeiou]y$/i.test(word)) {\n return `${word.slice(0, -1)}ies`;\n }\n\n if (\n word.endsWith('s') ||\n word.endsWith('x') ||\n word.endsWith('z') ||\n word.endsWith('ch') ||\n word.endsWith('sh')\n ) {\n return `${word}es`;\n }\n\n return `${word}s`;\n}\n\nfunction deriveCollectionName(className: string): string {\n return pluralize(toSnakeCase(className));\n}\n\nfunction humanizeResource(resource: string): string {\n return resource\n .replace(/[._-]+/g, ' ')\n .replace(/\\b\\w/g, (char) => char.toUpperCase());\n}\n\nfunction capitalize(value: string): string {\n return `${value[0]?.toUpperCase() ?? ''}${value.slice(1)}`;\n}\n\nfunction defaultPermissionName(slug: string): string {\n const parsed = parsePermissionSlug(slug);\n if (!parsed.isValid) {\n return humanizeResource(slug);\n }\n\n return `${humanizeResource(parsed.action)} ${humanizeResource(parsed.resource)}`;\n}\n\nfunction defaultPermissionDescription(slug: string): string {\n const parsed = parsePermissionSlug(slug);\n if (!parsed.isValid) {\n return `Allows ${slug}`;\n }\n\n return `Allows ${humanizeResource(parsed.action).toLowerCase()} access for ${humanizeResource(parsed.resource).toLowerCase()}`;\n}\n\nexport function normalizeOperationPermissionAction(action: string): string {\n const trimmed = action.trim();\n const lower = trimmed.toLowerCase();\n\n if (lower === 'list' || lower === 'get') {\n return 'read';\n }\n\n if (\n lower === 'read' ||\n lower === 'create' ||\n lower === 'update' ||\n lower === 'delete'\n ) {\n return lower;\n }\n\n return trimmed;\n}\n\nfunction isConstructorLike(value: unknown): value is ConstructorLike {\n return typeof value === 'function';\n}\n\nfunction isCollectionLike(value: unknown): value is CollectionLike {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as CollectionLike).getItemClass === 'function'\n );\n}\n\nfunction resolveConstructor(\n value: OperationPermissionCollectionInput,\n): ConstructorLike | undefined {\n if (isConstructorLike(value)) {\n return value;\n }\n\n if (isCollectionLike(value)) {\n return value.getItemClass?.();\n }\n\n if (typeof value === 'object' && value !== null) {\n const ctor = value.constructor;\n if (typeof ctor === 'function' && ctor.name && ctor.name !== 'Object') {\n return ctor;\n }\n }\n\n return undefined;\n}\n\nexport function deriveOperationPermissionCollectionName(\n input: OperationPermissionCollectionInput,\n): string {\n if (typeof input === 'string') {\n const collection = input.trim();\n if (!collection) {\n throw new Error('Operation permission collection must not be empty.');\n }\n return collection;\n }\n\n const ctor = resolveConstructor(input);\n if (!ctor?.name) {\n throw new Error(\n 'Operation permission collection must be a collection slug, model class, model instance, or collection instance.',\n );\n }\n\n const registered =\n ObjectRegistry.getClassByConstructor(\n ctor as Parameters<typeof ObjectRegistry.getClassByConstructor>[0],\n ) ?? ObjectRegistry.getClass(ctor.name);\n const configuredCollection = (\n registered?.config as { collection?: unknown } | undefined\n )?.collection;\n\n if (typeof configuredCollection === 'string' && configuredCollection) {\n return configuredCollection;\n }\n\n if (registered?.collection) {\n return registered.collection;\n }\n\n return deriveCollectionName(registered?.name ?? ctor.name);\n}\n\nexport function deriveOperationPermissionSlug(\n collection: OperationPermissionCollectionInput,\n action: string,\n): string {\n const collectionName = deriveOperationPermissionCollectionName(collection);\n const normalizedAction = normalizeOperationPermissionAction(action);\n if (!normalizedAction) {\n throw new Error('Operation permission action must not be empty.');\n }\n\n return `${collectionName}.${normalizedAction}`;\n}\n\nfunction isCollectionManifestEntry(objectDef?: SmartObjectDefinition): boolean {\n return (\n objectDef?.extends === 'SmrtCollection' ||\n objectDef?.extendsTypeArg !== undefined\n );\n}\n\ninterface ManifestMethodCandidate {\n isPublic?: boolean;\n name?: string;\n}\n\ninterface FieldReadPermissionCandidate {\n _meta?: {\n readPermission?: unknown;\n };\n readPermission?: unknown;\n}\n\ntype MethodCandidate =\n | ManifestMethodCandidate\n | readonly [string, ManifestMethodCandidate];\n\nfunction isMethodEntryTuple(\n method: MethodCandidate,\n): method is readonly [string, ManifestMethodCandidate] {\n return Array.isArray(method);\n}\n\nfunction normalizeMethodCandidate(\n method: MethodCandidate,\n): ManifestMethodCandidate {\n if (isMethodEntryTuple(method)) {\n const [name, definition] = method;\n return { ...definition, name: definition.name ?? name };\n }\n return method;\n}\n\nfunction getPublicCustomMethodNames(\n methodEntries: MethodCandidate[],\n standardActions: readonly string[],\n): string[] {\n return Array.from(\n new Set(\n methodEntries.flatMap((method) => {\n const { isPublic, name } = normalizeMethodCandidate(method);\n if (!name || isPublic !== true || standardActions.includes(name)) {\n return [];\n }\n return [name];\n }),\n ),\n );\n}\n\nfunction getCustomMethodExposureNames(\n config: unknown,\n availableCustomMethods: string[],\n): Set<string> {\n if (!config || config === false) {\n return new Set();\n }\n\n if (config === true || typeof config !== 'object') {\n return new Set(availableCustomMethods);\n }\n\n const rawInclude = (config as { include?: string[] }).include;\n const include = Array.isArray(rawInclude) ? [...rawInclude] : undefined;\n const rawExclude = (config as { exclude?: string[] }).exclude;\n const exclude: string[] = Array.isArray(rawExclude) ? [...rawExclude] : [];\n\n if (!include) {\n return new Set(\n availableCustomMethods.filter(\n (methodName) => !exclude.includes(methodName),\n ),\n );\n }\n\n const baseMethods = include.filter((methodName) =>\n availableCustomMethods.includes(methodName),\n );\n\n return new Set(\n baseMethods.filter((methodName) => !exclude.includes(methodName)),\n );\n}\n\nfunction isOperationEnabled(config: unknown, action: string): boolean {\n if (config === false) {\n return false;\n }\n\n if (config && typeof config === 'object') {\n const include = Array.isArray((config as { include?: string[] }).include)\n ? (config as { include?: string[] }).include\n : undefined;\n const rawExclude = (config as { exclude?: string[] }).exclude;\n const exclude: string[] = Array.isArray(rawExclude) ? [...rawExclude] : [];\n\n if (include && !include.includes(action)) {\n return false;\n }\n if (exclude.includes(action)) {\n return false;\n }\n }\n\n return true;\n}\n\nfunction normalizePostgresAction(\n action: PostgresPermissionBinding['action'],\n): PostgresPermissionAction {\n const normalized = action.toUpperCase();\n if (\n normalized === 'SELECT' ||\n normalized === 'INSERT' ||\n normalized === 'UPDATE' ||\n normalized === 'DELETE'\n ) {\n return normalized;\n }\n\n throw new Error(\n `Unsupported Postgres permission action '${action}'. Expected SELECT, INSERT, UPDATE, or DELETE.`,\n );\n}\n\nfunction normalizeBinding(\n binding: PostgresPermissionBinding,\n fallbackPermission: string,\n): PostgresPermissionBinding {\n return {\n action: normalizePostgresAction(binding.action),\n permission: binding.permission || fallbackPermission,\n schemaName: binding.schemaName,\n tableName: binding.tableName,\n tenantField: binding.tenantField,\n };\n}\n\nfunction mergeStringField(\n fieldName:\n | 'category'\n | 'className'\n | 'collection'\n | 'description'\n | 'name'\n | 'qualifiedName',\n existing: PermissionDefinition,\n incoming: PermissionDefinition,\n slug: string,\n): void {\n const existingValue = existing[fieldName];\n const incomingValue = incoming[fieldName];\n\n if (!incomingValue) {\n return;\n }\n\n if (!existingValue) {\n existing[fieldName] = incomingValue;\n return;\n }\n\n if (existingValue !== incomingValue) {\n throw new Error(\n `Conflicting permission metadata for '${slug}' field '${fieldName}': '${existingValue}' !== '${incomingValue}'`,\n );\n }\n}\n\nfunction mergeBindings(\n existing: PermissionDefinition,\n incoming: PermissionDefinition,\n): void {\n const existingBindings = existing.postgres?.bindings ?? [];\n const incomingBindings = incoming.postgres?.bindings ?? [];\n if (incomingBindings.length === 0) {\n return;\n }\n\n const seen = new Set(\n existingBindings.map((binding) =>\n [\n binding.permission,\n binding.action,\n binding.schemaName ?? '',\n binding.tableName,\n binding.tenantField ?? '',\n ].join('|'),\n ),\n );\n\n const mergedBindings = [...existingBindings];\n for (const binding of incomingBindings) {\n const normalized = normalizeBinding(binding, incoming.slug);\n const key = [\n normalized.permission,\n normalized.action,\n normalized.schemaName ?? '',\n normalized.tableName,\n normalized.tenantField ?? '',\n ].join('|');\n if (!seen.has(key)) {\n seen.add(key);\n mergedBindings.push(normalized);\n }\n }\n\n existing.postgres = {\n bindings: mergedBindings,\n };\n}\n\nfunction normalizeDefinition(\n definition: PermissionDefinition,\n source: PermissionCatalogSource,\n): PermissionDefinition {\n if (!definition.slug || !isValidPermissionSlug(definition.slug.trim())) {\n throw new Error(\n `Invalid permission slug '${definition.slug}'. Expected 'resource.action[.scope...]'.`,\n );\n }\n\n const slug = definition.slug.trim();\n return {\n category: definition.category ?? parsePermissionSlug(slug).resource,\n className: definition.className,\n collection: definition.collection,\n description: definition.description ?? defaultPermissionDescription(slug),\n name: definition.name ?? defaultPermissionName(slug),\n postgres: definition.postgres?.bindings\n ? {\n bindings: definition.postgres.bindings.map((binding) =>\n normalizeBinding(binding, slug),\n ),\n }\n : undefined,\n qualifiedName: definition.qualifiedName,\n slug,\n source,\n };\n}\n\nfunction mergeDefinitionSet(\n current: Map<string, PermissionDefinition>,\n incomingDefinitions: PermissionDefinition[],\n source: PermissionCatalogSource,\n): void {\n for (const rawDefinition of incomingDefinitions) {\n const definition = normalizeDefinition(rawDefinition, source);\n const existing = current.get(definition.slug);\n\n if (!existing) {\n current.set(definition.slug, definition);\n continue;\n }\n\n mergeStringField('category', existing, definition, definition.slug);\n mergeStringField('className', existing, definition, definition.slug);\n mergeStringField('collection', existing, definition, definition.slug);\n mergeStringField('description', existing, definition, definition.slug);\n mergeStringField('name', existing, definition, definition.slug);\n mergeStringField('qualifiedName', existing, definition, definition.slug);\n mergeBindings(existing, definition);\n }\n}\n\nexport function registerPermissionDefinitions(\n definitions: PermissionDefinition[],\n): () => void {\n globalThis.__smrtUsersPermissionRegistrationCounter =\n (globalThis.__smrtUsersPermissionRegistrationCounter ?? 0) + 1;\n const registrationId = globalThis.__smrtUsersPermissionRegistrationCounter;\n getRuntimePermissionRegistrations().set(registrationId, definitions);\n\n return () => {\n getRuntimePermissionRegistrations().delete(registrationId);\n };\n}\n\nexport class PermissionCatalogService {\n constructor(private readonly options: SmrtClassOptions = {}) {}\n\n getUsersConfig(): UsersConfig {\n return getPackageConfig<UsersConfig>('users', {});\n }\n\n getRuntimePermissionDefinitions(): PermissionDefinition[] {\n return Array.from(getRuntimePermissionRegistrations().values()).flat();\n }\n\n getCustomPermissionDefinitions(): PermissionDefinition[] {\n return this.getUsersConfig().permissions?.custom ?? [];\n }\n\n getCatalog(): PermissionCatalog {\n const manifestPermissions = this.getManifestPermissionDefinitions();\n const customPermissions = this.getCustomPermissionDefinitions();\n const runtimePermissions = this.getRuntimePermissionDefinitions();\n\n const merged = new Map<string, PermissionDefinition>();\n mergeDefinitionSet(merged, manifestPermissions, 'manifest');\n mergeDefinitionSet(merged, customPermissions, 'config');\n mergeDefinitionSet(merged, runtimePermissions, 'runtime');\n\n return {\n customPermissions: customPermissions.map((definition) =>\n normalizeDefinition(definition, 'config'),\n ),\n manifestPermissions: manifestPermissions.map((definition) =>\n normalizeDefinition(definition, 'manifest'),\n ),\n permissions: Array.from(merged.values()).sort((left, right) =>\n left.slug.localeCompare(right.slug),\n ),\n runtimePermissions: runtimePermissions.map((definition) =>\n normalizeDefinition(definition, 'runtime'),\n ),\n };\n }\n\n deriveOperationPermissionSlug(\n collection: OperationPermissionCollectionInput,\n action: string,\n ): string {\n return deriveOperationPermissionSlug(collection, action);\n }\n\n hasPermissionSlug(slug: string): boolean {\n return this.getCatalog().permissions.some(\n (permission) => permission.slug === slug,\n );\n }\n\n async syncPermissionCatalog(): Promise<PermissionCatalogSyncResult> {\n const catalog = this.getCatalog();\n const permissions = await PermissionCollection.create(this.options);\n\n const created: string[] = [];\n const unchanged: string[] = [];\n const updated: string[] = [];\n\n for (const definition of catalog.permissions) {\n const existing = await permissions.findBySlug(definition.slug);\n if (!existing) {\n const permission = await permissions.create({\n category:\n definition.category ??\n parsePermissionSlug(definition.slug).resource,\n description: definition.description ?? '',\n name: definition.name ?? definition.slug,\n slug: definition.slug,\n });\n await permission.save();\n created.push(definition.slug);\n continue;\n }\n\n const nextName = definition.name ?? existing.name;\n const nextDescription = definition.description ?? existing.description;\n const nextCategory = definition.category ?? existing.category;\n\n if (\n existing.name === nextName &&\n existing.description === nextDescription &&\n existing.category === nextCategory\n ) {\n unchanged.push(definition.slug);\n continue;\n }\n\n existing.name = nextName;\n existing.description = nextDescription;\n existing.category = nextCategory;\n await existing.save();\n updated.push(definition.slug);\n }\n\n return {\n catalog,\n created,\n unchanged,\n updated,\n };\n }\n\n private getManifestPermissionDefinitions(): PermissionDefinition[] {\n const standardActions = ['list', 'get', 'create', 'update', 'delete'];\n const definitions = new Map<string, PermissionDefinition>();\n\n for (const metadata of ObjectRegistry.getAllObjectMetadata()) {\n const registered =\n ObjectRegistry.getClassByConstructor(metadata.constructor) ??\n ObjectRegistry.getClass(metadata.name);\n const manifestEntry = registered?.qualifiedName\n ? findManifestEntryByQualifiedName(registered.qualifiedName)\n : undefined;\n\n if (isCollectionManifestEntry(manifestEntry)) {\n continue;\n }\n\n const className = metadata.name;\n const qualifiedName = registered?.qualifiedName;\n const objectConfig = manifestEntry?.decoratorConfig ?? metadata.config;\n const rawCollection = (\n objectConfig as { collection?: unknown } | undefined\n )?.collection;\n const configuredCollection =\n typeof rawCollection === 'string' && rawCollection.length > 0\n ? rawCollection\n : undefined;\n const collection =\n configuredCollection ??\n manifestEntry?.collection ??\n deriveCollectionName(metadata.name);\n\n const readExposed =\n isOperationEnabled(objectConfig.api, 'list') ||\n isOperationEnabled(objectConfig.api, 'get') ||\n isOperationEnabled(objectConfig.cli, 'list') ||\n isOperationEnabled(objectConfig.cli, 'get') ||\n isOperationEnabled(objectConfig.mcp, 'list') ||\n isOperationEnabled(objectConfig.mcp, 'get');\n if (readExposed) {\n definitions.set(`${collection}.read`, {\n className,\n collection,\n qualifiedName,\n slug: `${collection}.read`,\n });\n }\n\n const fieldEntries = manifestEntry?.fields\n ? Object.entries(manifestEntry.fields)\n : Array.from(\n (registered?.inheritedFields ?? metadata.fields).entries(),\n );\n for (const [fieldName, fieldDef] of fieldEntries) {\n const field = fieldDef as FieldReadPermissionCandidate;\n const readPermission =\n typeof field.readPermission === 'string'\n ? field.readPermission\n : typeof field._meta?.readPermission === 'string'\n ? field._meta.readPermission\n : undefined;\n if (!readPermission || definitions.has(readPermission)) {\n continue;\n }\n definitions.set(readPermission, {\n className,\n collection,\n description: `Allows reading ${fieldName} on ${humanizeResource(collection).toLowerCase()}`,\n name: `Read ${humanizeResource(fieldName)} on ${humanizeResource(collection)}`,\n qualifiedName,\n slug: readPermission,\n });\n }\n\n for (const action of ['create', 'update', 'delete'] as const) {\n const exposed =\n isOperationEnabled(objectConfig.api, action) ||\n isOperationEnabled(objectConfig.cli, action) ||\n isOperationEnabled(objectConfig.mcp, action);\n if (!exposed) {\n continue;\n }\n\n definitions.set(`${collection}.${action}`, {\n className,\n collection,\n qualifiedName,\n slug: `${collection}.${action}`,\n });\n }\n\n const methodEntries = manifestEntry?.methods\n ? Object.values(manifestEntry.methods)\n : Array.from(metadata.methods.entries());\n const publicCustomMethodNames = getPublicCustomMethodNames(\n methodEntries,\n standardActions,\n );\n const customApiMethods = new Set<string>();\n const customCliMethods = getCustomMethodExposureNames(\n objectConfig.cli,\n publicCustomMethodNames,\n );\n const customMcpMethods = getCustomMethodExposureNames(\n objectConfig.mcp,\n publicCustomMethodNames,\n );\n\n for (const methodName of publicCustomMethodNames) {\n if (isOperationEnabled(objectConfig.api, methodName)) {\n customApiMethods.add(methodName);\n }\n }\n\n const customMethods = new Set<string>([\n ...customApiMethods,\n ...customCliMethods,\n ...customMcpMethods,\n ]);\n\n for (const methodName of customMethods) {\n definitions.set(`${collection}.${methodName}`, {\n className,\n collection,\n description: `Allows ${methodName} on ${humanizeResource(collection).toLowerCase()}`,\n name: `${capitalize(methodName)} ${humanizeResource(collection)}`,\n qualifiedName,\n slug: `${collection}.${methodName}`,\n });\n }\n }\n\n return Array.from(definitions.values()).sort((left, right) =>\n left.slug.localeCompare(right.slug),\n );\n }\n\n static create(options: SmrtClassOptions = {}): PermissionCatalogService {\n return new PermissionCatalogService(options);\n }\n}\n\nexport async function syncPermissionCatalog(\n options: SmrtClassOptions = {},\n): Promise<PermissionCatalogSyncResult> {\n return PermissionCatalogService.create(options).syncPermissionCatalog();\n}\n","/**\n * RolePermissionCollection - Collection manager for RolePermission objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { RolePermission } from '../models/RolePermission.js';\nimport {\n normalizeOperationPermissionAction,\n type PermissionCatalog,\n PermissionCatalogService,\n} from '../services/PermissionCatalogService.js';\nimport { DEFAULT_ROLE_SLUGS, type DefaultRoleSlug } from '../types/index.js';\nimport { PermissionCollection } from './PermissionCollection.js';\nimport { RoleCollection } from './RoleCollection.js';\n\nexport type RolePermissionPatternMatrix = Partial<\n Record<string, readonly string[]>\n>;\n\nexport interface SeedRolePermissionsOptions {\n catalog?: PermissionCatalog;\n /**\n * Remove existing role mappings not selected by the role's current patterns.\n * Defaults false to keep seeding additive and low-risk.\n */\n prune?: boolean;\n /**\n * Sync catalog definitions into the Permission table before assigning role\n * permissions. Defaults true.\n */\n syncCatalog?: boolean;\n tenantId?: string;\n}\n\nexport interface SeedRolePermissionsResult {\n added: Record<string, string[]>;\n matched: Record<string, string[]>;\n missingPermissions: Record<string, string[]>;\n missingRoles: string[];\n removed: Record<string, string[]>;\n unchanged: Record<string, string[]>;\n unmatchedPatterns: Record<string, string[]>;\n}\n\nexport const DEFAULT_ROLE_PERMISSION_PATTERNS: Record<\n DefaultRoleSlug,\n readonly string[]\n> = {\n [DEFAULT_ROLE_SLUGS.OWNER]: ['*'],\n [DEFAULT_ROLE_SLUGS.ADMIN]: ['*'],\n [DEFAULT_ROLE_SLUGS.MEMBER]: ['*.read', '*.list', '*.get', '*.create'],\n [DEFAULT_ROLE_SLUGS.VIEWER]: ['*.read', '*.list', '*.get'],\n};\n\nconst DEFAULT_MEMBER_CREATE_DENIED_RESOURCES = new Set([\n 'group_members',\n 'groupmembers',\n 'group_roles',\n 'grouproles',\n 'groups',\n 'membership_overrides',\n 'membershipoverrides',\n 'memberships',\n 'permissions',\n 'role_permissions',\n 'rolepermissions',\n 'roles',\n 'sessions',\n 'tenant_permission_overrides',\n 'tenantpermissionoverrides',\n 'tenants',\n 'users',\n 'users_magic_link_tokens',\n 'usersmagiclinktokens',\n]);\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nfunction normalizePattern(pattern: string): string {\n const trimmed = pattern.trim();\n if (trimmed === '*' || !trimmed.includes('.')) {\n return trimmed;\n }\n\n const [resource, action, ...rest] = trimmed.split('.');\n if (!resource || !action || rest.length > 0) {\n return trimmed;\n }\n\n return `${resource}.${normalizeOperationPermissionAction(action)}`;\n}\n\nfunction matchesPattern(slug: string, pattern: string): boolean {\n if (pattern === '*') {\n return true;\n }\n\n const regex = new RegExp(\n `^${escapeRegExp(pattern).replaceAll('\\\\*', '.*')}$`,\n );\n return regex.test(slug);\n}\n\nfunction shouldSeedDefaultRolePermission(\n roleSlug: string,\n permissionSlug: string,\n): boolean {\n if (roleSlug !== DEFAULT_ROLE_SLUGS.MEMBER) {\n return true;\n }\n\n const [resource, action, ...rest] = permissionSlug.split('.');\n if (!resource || !action || rest.length > 0) {\n return true;\n }\n\n return (\n action !== 'create' || !DEFAULT_MEMBER_CREATE_DENIED_RESOURCES.has(resource)\n );\n}\n\nfunction emptyResult(): SeedRolePermissionsResult {\n return {\n added: {},\n matched: {},\n missingPermissions: {},\n missingRoles: [],\n removed: {},\n unchanged: {},\n unmatchedPatterns: {},\n };\n}\n\nfunction ensureResultSlot(\n result: SeedRolePermissionsResult,\n roleSlug: string,\n): void {\n result.added[roleSlug] ??= [];\n result.matched[roleSlug] ??= [];\n result.missingPermissions[roleSlug] ??= [];\n result.removed[roleSlug] ??= [];\n result.unchanged[roleSlug] ??= [];\n result.unmatchedPatterns[roleSlug] ??= [];\n}\n\n/**\n * Collection for managing RolePermission objects\n */\nexport class RolePermissionCollection extends SmrtCollection<RolePermission> {\n static readonly _itemClass = RolePermission;\n\n /**\n * Find all permissions for a role\n */\n async findByRole(roleId: string): Promise<RolePermission[]> {\n return await this.list({\n where: { roleId },\n });\n }\n\n /**\n * Find all roles that have a permission\n */\n async findByPermission(permissionId: string): Promise<RolePermission[]> {\n return await this.list({\n where: { permissionId },\n });\n }\n\n /**\n * Check if a role has a specific permission\n */\n async hasPermission(roleId: string, permissionId: string): Promise<boolean> {\n const results = await this.list({\n where: { roleId, permissionId },\n limit: 1,\n });\n return results.length > 0;\n }\n\n /**\n * Add a permission to a role\n */\n async addPermission(\n roleId: string,\n permissionId: string,\n ): Promise<RolePermission> {\n // Check if already exists\n const existing = await this.list({\n where: { roleId, permissionId },\n limit: 1,\n });\n if (existing.length > 0) {\n return existing[0];\n }\n\n const rolePermission = await this.create({ roleId, permissionId });\n await rolePermission.save();\n return rolePermission;\n }\n\n /**\n * Remove a permission from a role\n */\n async removePermission(\n roleId: string,\n permissionId: string,\n ): Promise<boolean> {\n const existing = await this.list({\n where: { roleId, permissionId },\n limit: 1,\n });\n if (existing.length === 0) {\n return false;\n }\n\n await existing[0].delete();\n return true;\n }\n\n /**\n * Get permission IDs for a role\n */\n async getPermissionIds(roleId: string): Promise<string[]> {\n const rolePermissions = await this.findByRole(roleId);\n return rolePermissions.map((rp) => rp.permissionId as string);\n }\n\n /**\n * Seed role-to-permission mappings from catalog-aware glob patterns.\n *\n * Defaults to the system owner/admin/member/viewer matrix. Re-running is\n * idempotent: existing mappings are reported as `unchanged`, new catalog\n * slugs are added, and stale mappings are only removed when `prune` is true.\n */\n async seedRolePermissions(\n matrix: RolePermissionPatternMatrix = DEFAULT_ROLE_PERMISSION_PATTERNS,\n options: SeedRolePermissionsOptions = {},\n ): Promise<SeedRolePermissionsResult> {\n const result = emptyResult();\n const catalogService = PermissionCatalogService.create(this.options);\n const syncResult =\n options.syncCatalog === false\n ? undefined\n : await catalogService.syncPermissionCatalog();\n const catalog =\n options.catalog ?? syncResult?.catalog ?? catalogService.getCatalog();\n const permissions = await PermissionCollection.create(this.options);\n const roles = await RoleCollection.create(this.options);\n const catalogSlugs = catalog.permissions.map(\n (permission) => permission.slug,\n );\n const permissionIdBySlug = new Map<string, string>();\n for (const permission of await permissions.list({})) {\n if (\n typeof permission.id === 'string' &&\n typeof permission.slug === 'string' &&\n permission.slug.length > 0\n ) {\n permissionIdBySlug.set(permission.slug, permission.id);\n }\n }\n\n for (const [roleSlug, rawPatterns] of Object.entries(matrix)) {\n ensureResultSlot(result, roleSlug);\n\n const role = await roles.findBySlug(roleSlug, options.tenantId);\n if (!role?.id) {\n result.missingRoles.push(roleSlug);\n continue;\n }\n\n const patterns = (rawPatterns ?? []).map(normalizePattern);\n const matchedSlugs = new Set<string>();\n for (const pattern of patterns) {\n const matches = catalogSlugs.filter((slug) =>\n matchesPattern(slug, pattern),\n );\n if (matches.length === 0) {\n result.unmatchedPatterns[roleSlug].push(pattern);\n }\n for (const slug of matches) {\n if (!shouldSeedDefaultRolePermission(roleSlug, slug)) {\n continue;\n }\n matchedSlugs.add(slug);\n }\n }\n\n const sortedMatchedSlugs = Array.from(matchedSlugs).sort();\n result.matched[roleSlug].push(...sortedMatchedSlugs);\n\n const existingPermissionIds = new Set(\n await this.getPermissionIds(role.id),\n );\n const targetPermissionIds = new Set<string>();\n\n for (const slug of sortedMatchedSlugs) {\n const permissionId = permissionIdBySlug.get(slug);\n if (!permissionId) {\n result.missingPermissions[roleSlug].push(slug);\n continue;\n }\n\n targetPermissionIds.add(permissionId);\n if (existingPermissionIds.has(permissionId)) {\n result.unchanged[roleSlug].push(slug);\n continue;\n }\n\n await this.addPermission(role.id, permissionId);\n result.added[roleSlug].push(slug);\n }\n\n if (!options.prune) {\n continue;\n }\n\n const existingPermissions = await permissions.findByIds(\n Array.from(existingPermissionIds),\n );\n for (const permissionId of existingPermissionIds) {\n if (targetPermissionIds.has(permissionId)) {\n continue;\n }\n\n await this.removePermission(role.id, permissionId);\n result.removed[roleSlug].push(\n existingPermissions.get(permissionId)?.slug ?? permissionId,\n );\n }\n }\n\n return result;\n }\n}\n","/**\n * Session model - Server-side session management for authenticated users\n * @packageDocumentation\n */\n\nimport {\n field,\n foreignKey,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { SessionStatus } from '../types/index.js';\n\n/**\n * Constructor options for {@link Session}.\n */\nexport interface SessionOptions extends SmrtObjectOptions {\n userId?: string;\n tenantId?: string | null;\n status?: SessionStatus;\n /** Accepts a Date or any value the Date constructor can coerce. */\n expiresAt?: Date | string | number;\n userAgent?: string;\n ipAddress?: string;\n /** Accepts a Date or any value the Date constructor can coerce. */\n lastAccessedAt?: Date | string | number;\n data?: Record<string, unknown>;\n}\n\n/**\n * Default session TTL: 7 days in seconds\n */\nexport const DEFAULT_SESSION_TTL = 7 * 24 * 60 * 60;\n\n/**\n * Generate a cryptographically secure session ID\n */\nexport function generateSessionId(): string {\n // Use crypto.randomUUID for secure, unique session IDs\n return crypto.randomUUID();\n}\n\n/**\n * Session represents an authenticated user session.\n *\n * Sessions are stored server-side and linked to users.\n * A session ID is stored in a cookie and used to look up the session.\n *\n * @example\n * ```typescript\n * const session = await sessions.create({\n * userId: user.id,\n * tenantId: tenant.id,\n * expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),\n * userAgent: request.headers.get('user-agent'),\n * ipAddress: getClientAddress(event)\n * });\n * await session.save();\n * ```\n */\n@smrt({\n // Sessions should not be exposed via public API for security\n api: { include: ['get', 'delete'] },\n mcp: { include: [] },\n cli: true,\n})\nexport class Session extends SmrtObject {\n /**\n * User who owns this session\n */\n @foreignKey('User')\n userId: string = '';\n\n /**\n * Tenant context for this session (for multi-tenant apps)\n * Null means no tenant context selected\n */\n @foreignKey('Tenant', { nullable: true })\n tenantId: string | null = null;\n\n /**\n * Session status\n */\n @field({ type: 'text' })\n status: SessionStatus = SessionStatus.ACTIVE;\n\n /**\n * Session expiration time\n */\n expiresAt: Date = new Date();\n\n /**\n * User agent string from the browser\n */\n userAgent: string = '';\n\n /**\n * IP address of the client\n */\n ipAddress: string = '';\n\n /**\n * Last activity timestamp (updated on each request)\n */\n lastAccessedAt: Date = new Date();\n\n /**\n * Custom session data (JSON serializable)\n */\n data: Record<string, unknown> = {};\n\n constructor(options: SessionOptions = {}) {\n super(options);\n if (options.userId !== undefined) this.userId = options.userId;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.status !== undefined) this.status = options.status;\n if (options.expiresAt !== undefined) {\n this.expiresAt =\n options.expiresAt instanceof Date\n ? options.expiresAt\n : new Date(options.expiresAt);\n }\n if (options.userAgent !== undefined) this.userAgent = options.userAgent;\n if (options.ipAddress !== undefined) this.ipAddress = options.ipAddress;\n if (options.lastAccessedAt !== undefined) {\n this.lastAccessedAt =\n options.lastAccessedAt instanceof Date\n ? options.lastAccessedAt\n : new Date(options.lastAccessedAt);\n }\n if (options.data !== undefined) this.data = options.data;\n }\n\n /**\n * Check if the session is currently valid (active and not expired)\n */\n isValid(): boolean {\n return this.status === SessionStatus.ACTIVE && new Date() < this.expiresAt;\n }\n\n /**\n * Check if the session has expired\n */\n isExpired(): boolean {\n return new Date() >= this.expiresAt;\n }\n\n /**\n * Check if the session was revoked\n */\n isRevoked(): boolean {\n return this.status === SessionStatus.REVOKED;\n }\n\n /**\n * Update the last accessed timestamp\n */\n touch(): void {\n this.lastAccessedAt = new Date();\n }\n\n /**\n * Extend the session expiration by the given TTL (in seconds)\n */\n extend(ttlSeconds: number = DEFAULT_SESSION_TTL): void {\n this.expiresAt = new Date(Date.now() + ttlSeconds * 1000);\n this.touch();\n }\n\n /**\n * Revoke the session\n */\n revoke(): void {\n this.status = SessionStatus.REVOKED;\n }\n\n /**\n * Set the tenant context for this session\n */\n setTenant(tenantId: string | null): void {\n this.tenantId = tenantId;\n }\n\n /**\n * Get or set custom session data\n */\n getData<T>(key: string): T | undefined {\n return this.data[key] as T | undefined;\n }\n\n /**\n * Set custom session data\n */\n setData(key: string, value: unknown): void {\n this.data[key] = value;\n }\n\n /**\n * Remove custom session data\n */\n removeData(key: string): void {\n delete this.data[key];\n }\n}\n","/**\n * SessionCollection - Collection manager for Session objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport {\n DEFAULT_SESSION_TTL,\n generateSessionId,\n Session,\n} from '../models/Session.js';\nimport { SessionStatus } from '../types/index.js';\n\n/**\n * Options for creating a new session\n */\nexport interface CreateSessionOptions {\n /** User ID for the session */\n userId: string;\n /** Optional tenant ID for multi-tenant context */\n tenantId?: string;\n /** Session TTL in seconds (default: 7 days) */\n ttl?: number;\n /** User agent string */\n userAgent?: string;\n /** Client IP address */\n ipAddress?: string;\n /** Custom session data */\n data?: Record<string, unknown>;\n}\n\n/**\n * Collection for managing Session objects\n */\nexport class SessionCollection extends SmrtCollection<Session> {\n static readonly _itemClass = Session;\n\n /**\n * Create a new session with a secure ID\n */\n async createSession(options: CreateSessionOptions): Promise<Session> {\n const ttl = options.ttl ?? DEFAULT_SESSION_TTL;\n const expiresAt = new Date(Date.now() + ttl * 1000);\n\n const session = await this.create({\n id: generateSessionId(),\n userId: options.userId,\n tenantId: options.tenantId ?? null,\n status: SessionStatus.ACTIVE,\n expiresAt,\n userAgent: options.userAgent ?? '',\n ipAddress: options.ipAddress ?? '',\n lastAccessedAt: new Date(),\n data: options.data ?? {},\n });\n\n await session.save();\n return session;\n }\n\n /**\n * Find a valid session by ID\n * Returns null if session doesn't exist, is expired, or is revoked\n */\n async findValidSession(sessionId: string): Promise<Session | null> {\n const session = await this.get(sessionId);\n if (!session) return null;\n\n // Check if session is still valid\n if (!session.isValid()) {\n // If expired but still marked active, flip the status with an atomic\n // guarded UPDATE rather than a read-then-save(). A non-atomic save()\n // rewrites the whole row and could clobber a concurrent revoke/extend on\n // a lock-less backend; the WHERE clause makes the EXPIRED transition a\n // no-op once another writer has already changed the status or expiry.\n if (session.isExpired() && session.status === SessionStatus.ACTIVE) {\n const now = new Date().toISOString();\n await this.db.query(\n `UPDATE ${this.tableName}\n SET status = ?, updated_at = ?\n WHERE id = ? AND status = ? AND expires_at < ?`,\n SessionStatus.EXPIRED,\n now,\n sessionId,\n SessionStatus.ACTIVE,\n now,\n );\n }\n return null;\n }\n\n return session;\n }\n\n /**\n * Update last accessed time and optionally extend session\n */\n async touch(\n sessionId: string,\n extendTtl: boolean = false,\n ttl: number = DEFAULT_SESSION_TTL,\n ): Promise<boolean> {\n const session = await this.findValidSession(sessionId);\n if (!session) return false;\n\n session.touch();\n if (extendTtl) {\n session.extend(ttl);\n }\n await session.save();\n return true;\n }\n\n /**\n * Find all active sessions for a user\n */\n async findByUser(userId: string): Promise<Session[]> {\n const results = await this.list({\n where: {\n userId,\n status: SessionStatus.ACTIVE,\n },\n orderBy: 'last_accessed_at DESC',\n });\n\n // Filter out expired sessions\n return results.filter((session) => session.isValid());\n }\n\n /**\n * Delete all sessions for a user (logout from all devices)\n */\n async deleteUserSessions(userId: string): Promise<number> {\n const sessions = await this.list({\n where: { userId },\n });\n\n let count = 0;\n for (const session of sessions) {\n await session.delete();\n count++;\n }\n\n return count;\n }\n\n /**\n * Revoke all sessions for a user (soft delete)\n */\n async revokeUserSessions(userId: string): Promise<number> {\n const sessions = await this.list({\n where: {\n userId,\n status: SessionStatus.ACTIVE,\n },\n });\n\n let count = 0;\n for (const session of sessions) {\n session.revoke();\n await session.save();\n count++;\n }\n\n return count;\n }\n\n /**\n * Revoke a specific session\n */\n async revokeSession(sessionId: string): Promise<boolean> {\n const session = await this.get(sessionId);\n if (!session) return false;\n\n session.revoke();\n await session.save();\n return true;\n }\n\n /**\n * Delete expired sessions (cleanup job)\n * Returns the number of deleted sessions\n */\n async deleteExpired(): Promise<number> {\n // #1400: a single atomic DELETE. The previous two-pass version listed\n // expired-by-time and revoked sessions separately, so a session that was\n // both got delete()'d twice — the second delete could throw and abort the\n // cleanup job mid-run, leaving expired sessions un-reaped. One statement\n // also avoids hydrating every row just to delete it.\n const now = new Date().toISOString();\n const { rowCount } = await this.db.query(\n `DELETE FROM ${this.tableName}\n WHERE expires_at < ? OR status = ?`,\n now,\n SessionStatus.REVOKED,\n );\n\n return rowCount ?? 0;\n }\n\n /**\n * Count active sessions for a user\n */\n async countUserSessions(userId: string): Promise<number> {\n const sessions = await this.findByUser(userId);\n return sessions.length;\n }\n\n /**\n * Update tenant context for a session (low-level primitive).\n *\n * SECURITY (#1400): this does NOT verify that the session's user is a member\n * of `tenantId` — it is the unguarded storage primitive. Application/route\n * code must go through {@link SessionService.switchTenant}, which fail-closes\n * on a missing/inactive membership before calling this. Calling it directly\n * with an untrusted `tenantId` reintroduces the cross-tenant access bug.\n */\n async setSessionTenant(\n sessionId: string,\n tenantId: string | null,\n ): Promise<boolean> {\n const session = await this.findValidSession(sessionId);\n if (!session) return false;\n\n session.setTenant(tenantId);\n await session.save();\n return true;\n }\n\n /**\n * Set custom session data\n */\n async setSessionData(\n sessionId: string,\n key: string,\n value: unknown,\n ): Promise<boolean> {\n const session = await this.findValidSession(sessionId);\n if (!session) return false;\n\n session.setData(key, value);\n await session.save();\n return true;\n }\n\n /**\n * Get custom session data\n */\n async getSessionData<T>(\n sessionId: string,\n key: string,\n ): Promise<T | undefined> {\n const session = await this.findValidSession(sessionId);\n if (!session) return undefined;\n\n return session.getData<T>(key);\n }\n}\n","/**\n * Tenant model - organizational boundary for multi-tenancy\n * @packageDocumentation\n */\n\nimport {\n field,\n foreignKey,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport type { Tenant as TenantContract } from '@happyvertical/smrt-types';\nimport { TenantStatus } from '../types/index.js';\n\n/**\n * Constructor options for {@link Tenant}.\n */\nexport interface TenantOptions extends SmrtObjectOptions {\n name?: string;\n status?: TenantStatus;\n description?: string;\n parentTenantId?: string | null;\n hierarchyLevel?: number;\n hierarchyPath?: string;\n cascadePermissions?: boolean;\n inheritPermissions?: boolean;\n}\n\n/**\n * Maximum allowed depth for tenant hierarchy.\n * Prevents excessively deep trees that could cause performance issues.\n */\nexport const MAX_TENANT_HIERARCHY_DEPTH = 10;\n\n/**\n * Tenant represents an organizational boundary in the multi-tenant system.\n *\n * Supports hierarchical organization with parent-child relationships.\n * Users can belong to multiple tenants through Memberships.\n * Each tenant can have custom roles in addition to system defaults.\n *\n * ## Hierarchical Tenants\n *\n * Tenants can be organized in a tree structure where child tenants\n * can optionally inherit permissions from their parent tenants.\n *\n * ### Cascade Control\n *\n * Two flags control permission inheritance:\n * - `cascadePermissions`: If true, this tenant pushes its permissions to children\n * - `inheritPermissions`: If true, this tenant accepts permissions from parent\n *\n * Both must be true for inheritance to flow from parent to child.\n *\n * @example\n * ```typescript\n * // Create root tenant\n * const corp = await tenants.create({\n * name: 'Acme Corporation',\n * slug: 'acme-corp',\n * cascadePermissions: true, // Push permissions to children\n * });\n * await corp.save();\n *\n * // Create child tenant that inherits\n * const division = await tenants.create({\n * name: 'Acme West Division',\n * slug: 'acme-west',\n * parentTenantId: corp.id,\n * inheritPermissions: true, // Accept parent permissions\n * });\n * await division.save();\n *\n * // Create independent child (breaks inheritance chain)\n * const independent = await tenants.create({\n * name: 'Acme Labs',\n * slug: 'acme-labs',\n * parentTenantId: corp.id,\n * inheritPermissions: false, // Does NOT inherit from parent\n * });\n * await independent.save();\n * ```\n */\n@smrt({\n tableStrategy: 'sti',\n // #1400: read-only generated surface — tenant create/update (incl. the\n // cascadePermissions/inheritPermissions flags that drive the permission\n // cascade) goes through TenantService, not auth-only generated CRUD.\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class Tenant extends SmrtObject implements TenantContract {\n /**\n * Display name for the tenant\n */\n name: string = '';\n\n /**\n * Tenant status\n */\n @field({ type: 'text' })\n status: TenantStatus = TenantStatus.ACTIVE;\n\n /**\n * Optional description\n */\n description: string = '';\n\n // ============= Hierarchy Fields =============\n\n /**\n * Parent tenant ID for hierarchical organization.\n * Null for root-level tenants.\n */\n @foreignKey('Tenant', { nullable: true })\n parentTenantId?: string | null;\n\n /**\n * Depth in the hierarchy tree (0 = root, 1 = first level child, etc.)\n * Automatically managed by TenantCollection methods.\n */\n hierarchyLevel: number = 0;\n\n /**\n * Materialized path for efficient tree traversal.\n * Format: \"ancestor-id/parent-id\" (path to parent; does not include this tenant's id)\n * Empty string for root tenants.\n * Automatically managed by TenantCollection methods.\n */\n hierarchyPath: string = '';\n\n // ============= Permission Cascade Control =============\n\n /**\n * If true, this tenant's permissions cascade DOWN to child tenants.\n * Children can still opt-out by setting inheritPermissions: false.\n * Default: true\n */\n cascadePermissions: boolean = true;\n\n /**\n * If true, this tenant ACCEPTS permissions from its parent tenant.\n * Parent must also have cascadePermissions: true for inheritance to work.\n * Default: true\n */\n inheritPermissions: boolean = true;\n\n constructor(options: TenantOptions = {}) {\n super(options);\n if (options.name !== undefined) this.name = options.name;\n if (options.status !== undefined) this.status = options.status;\n if (options.description !== undefined)\n this.description = options.description;\n if (options.parentTenantId !== undefined)\n this.parentTenantId = options.parentTenantId;\n if (options.hierarchyLevel !== undefined)\n this.hierarchyLevel = options.hierarchyLevel;\n if (options.hierarchyPath !== undefined)\n this.hierarchyPath = options.hierarchyPath;\n if (options.cascadePermissions !== undefined)\n this.cascadePermissions = options.cascadePermissions;\n if (options.inheritPermissions !== undefined)\n this.inheritPermissions = options.inheritPermissions;\n }\n\n /**\n * Check if tenant is active\n */\n isActive(): boolean {\n return this.status === TenantStatus.ACTIVE;\n }\n\n /**\n * Check if tenant is suspended\n */\n isSuspended(): boolean {\n return this.status === TenantStatus.SUSPENDED;\n }\n\n /**\n * Check if this is a root-level tenant (no parent)\n */\n isRoot(): boolean {\n return !this.parentTenantId;\n }\n\n /**\n * Check if this tenant is configured to cascade permissions to children.\n *\n * Note: This does NOT indicate whether any child tenants actually exist.\n * Use TenantCollection.findChildren() for accurate child lookup.\n */\n canCascadeToChildren(): boolean {\n return this.cascadePermissions;\n }\n\n /**\n * Check if permission inheritance is active for this tenant.\n * Inheritance is active if:\n * - This tenant has a parent AND\n * - This tenant has inheritPermissions: true\n *\n * Note: The parent must also have cascadePermissions: true\n * for actual inheritance to occur. Use PermissionResolver\n * for accurate permission calculation.\n */\n acceptsInheritance(): boolean {\n return !!this.parentTenantId && this.inheritPermissions;\n }\n\n /**\n * Get ancestor IDs from the hierarchy path.\n * Returns an array of tenant IDs from root to immediate parent.\n * Empty array for root tenants.\n */\n getAncestorIds(): string[] {\n if (!this.hierarchyPath) return [];\n return this.hierarchyPath.split('/').filter((id) => id.length > 0);\n }\n}\n","/**\n * TenantCollection - Collection manager for Tenant objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection, type SmrtCreateInput } from '@happyvertical/smrt-core';\nimport { MAX_TENANT_HIERARCHY_DEPTH, Tenant } from '../models/Tenant.js';\nimport { TenantStatus } from '../types/index.js';\n\n/**\n * Error thrown when tenant hierarchy operations fail\n */\nexport class TenantHierarchyError extends Error {\n constructor(\n message: string,\n public readonly code:\n | 'CIRCULAR_REFERENCE'\n | 'MAX_DEPTH_EXCEEDED'\n | 'PARENT_NOT_FOUND'\n | 'INVALID_OPERATION',\n ) {\n super(message);\n this.name = 'TenantHierarchyError';\n }\n}\n\n/**\n * Options for creating a child tenant\n */\nexport interface CreateChildTenantOptions {\n name: string;\n slug?: string;\n description?: string;\n status?: TenantStatus;\n /** Override parent's cascade setting for this child. Default: true */\n inheritPermissions?: boolean;\n /** Whether this child cascades permissions to its children. Default: true */\n cascadePermissions?: boolean;\n}\n\n/**\n * Collection for managing Tenant objects with hierarchical support.\n *\n * Provides methods for:\n * - Basic CRUD operations\n * - Hierarchy management (parent/child relationships)\n * - Tree traversal (ancestors, descendants, siblings)\n * - Hierarchy path maintenance\n */\nexport class TenantCollection extends SmrtCollection<Tenant> {\n static readonly _itemClass = Tenant;\n\n // ============= Basic Query Methods =============\n\n /**\n * Find tenants by status\n */\n async findByStatus(status: TenantStatus): Promise<Tenant[]> {\n return await this.list({\n where: { status },\n orderBy: 'name ASC',\n });\n }\n\n /**\n * Find all active tenants\n */\n async findActive(): Promise<Tenant[]> {\n return await this.findByStatus(TenantStatus.ACTIVE);\n }\n\n /**\n * Find tenant by slug\n */\n async findBySlug(slug: string): Promise<Tenant | null> {\n const results = await this.list({\n where: { slug },\n limit: 1,\n });\n return results.length > 0 ? results[0] : null;\n }\n\n // ============= Hierarchy Query Methods =============\n\n /**\n * Find all root tenants (tenants with no parent)\n */\n async findRoots(): Promise<Tenant[]> {\n return await this.list({\n where: { parentTenantId: null },\n orderBy: 'name ASC',\n });\n }\n\n /**\n * Find direct children of a tenant\n */\n async findChildren(parentTenantId: string): Promise<Tenant[]> {\n return await this.list({\n where: { parentTenantId },\n orderBy: 'name ASC',\n });\n }\n\n /**\n * Find the parent tenant of a given tenant\n */\n async findParent(tenantId: string): Promise<Tenant | null> {\n const tenant = await this.get({ id: tenantId });\n if (!tenant?.parentTenantId) {\n return null;\n }\n return await this.get({ id: tenant.parentTenantId });\n }\n\n /**\n * Get all ancestors of a tenant, from immediate parent to root.\n * Uses the hierarchyPath for efficient lookup.\n */\n async getAncestors(tenantId: string): Promise<Tenant[]> {\n const tenant = await this.get({ id: tenantId });\n if (!tenant) {\n return [];\n }\n\n const ancestorIds = tenant.getAncestorIds();\n if (ancestorIds.length === 0) {\n return [];\n }\n\n // Batch fetch all ancestors\n const ancestorsList = await this.listByIds(ancestorIds);\n const ancestorsMap = new Map(ancestorsList.map((a) => [a.id, a]));\n\n // Return in order from immediate parent to root\n const ancestors: Tenant[] = [];\n for (let i = ancestorIds.length - 1; i >= 0; i--) {\n const ancestor = ancestorsMap.get(ancestorIds[i]);\n if (ancestor) {\n ancestors.push(ancestor);\n }\n }\n\n return ancestors;\n }\n\n /**\n * Get all ancestors in order from root to immediate parent.\n * Reverse of getAncestors.\n */\n async getAncestorsFromRoot(tenantId: string): Promise<Tenant[]> {\n const ancestors = await this.getAncestors(tenantId);\n return ancestors.reverse();\n }\n\n /**\n * Get all descendants of a tenant (all children, grandchildren, etc.)\n * Uses hierarchyPath prefix matching for efficient lookup.\n */\n async getDescendants(tenantId: string): Promise<Tenant[]> {\n const tenant = await this.get({ id: tenantId });\n if (!tenant?.id) {\n return [];\n }\n\n // Build the path prefix to search for\n const pathPrefix = tenant.hierarchyPath\n ? `${tenant.hierarchyPath}/${tenant.id}`\n : tenant.id;\n\n // Use database-level LIKE query for initial filtering\n // This is much more efficient than loading all tenants for large hierarchies\n const candidates = await this.list({\n where: {\n 'hierarchyPath like': `${pathPrefix}%`,\n },\n });\n\n // Apply boundary check in memory to avoid false positives\n // Without the '/' check, \"ancestor/tenant1\" would incorrectly match \"ancestor/tenant123\"\n return candidates.filter(\n (t) =>\n t.hierarchyPath === pathPrefix ||\n t.hierarchyPath?.startsWith(`${pathPrefix}/`),\n );\n }\n\n /**\n * Get siblings of a tenant (other tenants with the same parent)\n */\n async getSiblings(tenantId: string): Promise<Tenant[]> {\n const tenant = await this.get({ id: tenantId });\n if (!tenant) {\n return [];\n }\n\n const siblings = await this.list({\n where: { parentTenantId: tenant.parentTenantId ?? null },\n orderBy: 'name ASC',\n });\n\n // Exclude self from siblings\n return siblings.filter((s) => s.id !== tenantId);\n }\n\n /**\n * Check if a tenant is an ancestor of another tenant\n */\n async isAncestorOf(\n potentialAncestorId: string,\n tenantId: string,\n ): Promise<boolean> {\n const tenant = await this.get({ id: tenantId });\n if (!tenant) {\n return false;\n }\n\n const ancestorIds = tenant.getAncestorIds();\n return ancestorIds.includes(potentialAncestorId);\n }\n\n /**\n * Check if a tenant is a descendant of another tenant\n */\n async isDescendantOf(\n potentialDescendantId: string,\n tenantId: string,\n ): Promise<boolean> {\n return await this.isAncestorOf(tenantId, potentialDescendantId);\n }\n\n // ============= Hierarchy Management Methods =============\n\n /**\n * Create a child tenant under a parent.\n * Automatically sets hierarchyLevel and hierarchyPath.\n */\n async createChild(\n parentTenantId: string,\n options: CreateChildTenantOptions,\n ): Promise<Tenant> {\n const parent = await this.get({ id: parentTenantId });\n if (!parent?.id) {\n throw new TenantHierarchyError(\n `Parent tenant not found: ${parentTenantId}`,\n 'PARENT_NOT_FOUND',\n );\n }\n\n // Check depth limit\n const newLevel = parent.hierarchyLevel + 1;\n if (newLevel >= MAX_TENANT_HIERARCHY_DEPTH) {\n throw new TenantHierarchyError(\n `Maximum hierarchy depth (${MAX_TENANT_HIERARCHY_DEPTH}) exceeded`,\n 'MAX_DEPTH_EXCEEDED',\n );\n }\n\n // Build hierarchy path\n const newPath = parent.hierarchyPath\n ? `${parent.hierarchyPath}/${parent.id}`\n : parent.id;\n\n const child = await this.create({\n name: options.name,\n slug: options.slug,\n description: options.description ?? '',\n status: options.status ?? TenantStatus.ACTIVE,\n parentTenantId: parentTenantId,\n hierarchyLevel: newLevel,\n hierarchyPath: newPath,\n cascadePermissions: options.cascadePermissions ?? true,\n inheritPermissions: options.inheritPermissions ?? true,\n });\n\n await child.save();\n return child;\n }\n\n /**\n * Move a tenant to a new parent.\n * Updates hierarchyLevel and hierarchyPath for the tenant and all descendants.\n */\n async moveToParent(\n tenantId: string,\n newParentId: string | null,\n ): Promise<Tenant> {\n const tenant = await this.get({ id: tenantId });\n if (!tenant?.id) {\n throw new TenantHierarchyError(\n `Tenant not found: ${tenantId}`,\n 'INVALID_OPERATION',\n );\n }\n\n // Validate not moving to self\n if (newParentId === tenantId) {\n throw new TenantHierarchyError(\n 'Cannot move tenant to itself',\n 'CIRCULAR_REFERENCE',\n );\n }\n\n // Validate not moving to a descendant\n if (newParentId) {\n const isDescendant = await this.isDescendantOf(newParentId, tenantId);\n if (isDescendant) {\n throw new TenantHierarchyError(\n 'Cannot move tenant to one of its descendants',\n 'CIRCULAR_REFERENCE',\n );\n }\n }\n\n // Fetch descendants BEFORE any changes (uses current hierarchyPath for lookup)\n const descendants = await this.getDescendants(tenantId);\n\n let newLevel: number;\n let newPath: string;\n\n if (newParentId === null) {\n // Moving to root\n newLevel = 0;\n newPath = '';\n } else {\n const newParent = await this.get({ id: newParentId });\n if (!newParent) {\n throw new TenantHierarchyError(\n `New parent tenant not found: ${newParentId}`,\n 'PARENT_NOT_FOUND',\n );\n }\n\n newLevel = newParent.hierarchyLevel + 1;\n\n // Check depth limit (considering descendants)\n const maxDescendantDepth = descendants.reduce(\n (max, d) => Math.max(max, d.hierarchyLevel - tenant.hierarchyLevel),\n 0,\n );\n\n if (newLevel + maxDescendantDepth >= MAX_TENANT_HIERARCHY_DEPTH) {\n throw new TenantHierarchyError(\n `Moving would exceed maximum hierarchy depth (${MAX_TENANT_HIERARCHY_DEPTH})`,\n 'MAX_DEPTH_EXCEEDED',\n );\n }\n\n newPath = newParent.hierarchyPath\n ? `${newParent.hierarchyPath}/${newParent.id}`\n : newParent.id!;\n }\n\n // Calculate the path prefix for descendants\n const oldPath = tenant.hierarchyPath\n ? `${tenant.hierarchyPath}/${tenant.id}`\n : tenant.id;\n const newPathForDescendants = newPath\n ? `${newPath}/${tenant.id}`\n : tenant.id;\n\n // Update the tenant\n const levelDelta = newLevel - tenant.hierarchyLevel;\n tenant.parentTenantId = newParentId;\n tenant.hierarchyLevel = newLevel;\n tenant.hierarchyPath = newPath;\n await tenant.save();\n\n // Update all descendants (using the list fetched before changes)\n for (const descendant of descendants) {\n // Validate descendant has hierarchyPath - missing path indicates data corruption\n if (!descendant.hierarchyPath) {\n throw new TenantHierarchyError(\n `Descendant tenant ${descendant.id} has no hierarchyPath while updating hierarchy from ${oldPath} to ${newPathForDescendants}`,\n 'INVALID_OPERATION',\n );\n }\n\n // Validate descendant path starts with expected prefix\n if (!descendant.hierarchyPath.startsWith(oldPath)) {\n throw new TenantHierarchyError(\n `Descendant tenant ${descendant.id} has hierarchyPath \"${descendant.hierarchyPath}\" which does not start with expected prefix \"${oldPath}\"`,\n 'INVALID_OPERATION',\n );\n }\n\n // Update path by replacing old prefix with new prefix\n descendant.hierarchyPath =\n newPathForDescendants +\n descendant.hierarchyPath.substring(oldPath.length);\n\n // Adjust level by the same delta\n descendant.hierarchyLevel += levelDelta;\n await descendant.save();\n }\n\n return tenant;\n }\n\n /**\n * Make a tenant a root tenant (remove from hierarchy)\n */\n async makeRoot(tenantId: string): Promise<Tenant> {\n return await this.moveToParent(tenantId, null);\n }\n\n /**\n * Validate that a tenant hierarchy is consistent.\n * Returns validation errors if any.\n */\n async validateHierarchy(tenantId: string): Promise<string[]> {\n const errors: string[] = [];\n const tenant = await this.get({ id: tenantId });\n\n if (!tenant) {\n return [`Tenant not found: ${tenantId}`];\n }\n\n // Check parent exists if set\n if (tenant.parentTenantId) {\n const parent = await this.get({ id: tenant.parentTenantId });\n if (!parent) {\n errors.push(`Parent tenant not found: ${tenant.parentTenantId}`);\n } else {\n // Check hierarchy level\n if (tenant.hierarchyLevel !== parent.hierarchyLevel + 1) {\n errors.push(\n `Hierarchy level mismatch: expected ${parent.hierarchyLevel + 1}, got ${tenant.hierarchyLevel}`,\n );\n }\n\n // Check hierarchy path\n const expectedPath = parent.hierarchyPath\n ? `${parent.hierarchyPath}/${parent.id}`\n : parent.id;\n if (tenant.hierarchyPath !== expectedPath) {\n errors.push(\n `Hierarchy path mismatch: expected \"${expectedPath}\", got \"${tenant.hierarchyPath}\"`,\n );\n }\n }\n } else {\n // Root tenant checks\n if (tenant.hierarchyLevel !== 0) {\n errors.push(\n `Root tenant should have hierarchyLevel 0, got ${tenant.hierarchyLevel}`,\n );\n }\n if (tenant.hierarchyPath !== '') {\n errors.push(\n `Root tenant should have empty hierarchyPath, got \"${tenant.hierarchyPath}\"`,\n );\n }\n }\n\n // Check for circular reference (shouldn't be possible with path, but double-check)\n if (tenant.parentTenantId && tenant.id) {\n const ancestorIds = tenant.getAncestorIds();\n if (ancestorIds.includes(tenant.id)) {\n errors.push('Circular reference detected in hierarchy');\n }\n }\n\n return errors;\n }\n\n /**\n * Get the full hierarchy tree starting from a tenant.\n * Returns a nested structure useful for UI rendering.\n */\n async getTree(\n rootTenantId?: string,\n ): Promise<Array<Tenant & { children: Tenant[] }>> {\n const roots = rootTenantId\n ? [await this.get({ id: rootTenantId })]\n : await this.findRoots();\n\n const buildTree = async (\n tenant: Tenant | null,\n ): Promise<(Tenant & { children: Tenant[] }) | null> => {\n if (!tenant) return null;\n\n const children = await this.findChildren(tenant.id!);\n const childTrees = await Promise.all(children.map(buildTree));\n\n // Use Object.assign to preserve Tenant instance methods while adding children\n const tenantWithChildren = Object.assign(tenant, {\n children: childTrees.filter(Boolean) as Array<\n Tenant & { children: Tenant[] }\n >,\n });\n return tenantWithChildren as Tenant & { children: Tenant[] };\n };\n\n const trees = await Promise.all(roots.map(buildTree));\n return trees.filter(Boolean) as Array<Tenant & { children: Tenant[] }>;\n }\n\n // ============= Override create to handle hierarchy =============\n\n /**\n * Override create to automatically set hierarchy fields for new tenants.\n * Only calculates them when the caller hasn't already supplied\n * `hierarchyLevel`/`hierarchyPath` in the create input — an explicitly\n * provided value is preserved as-is.\n */\n async create(options: SmrtCreateInput<Tenant>): Promise<Tenant> {\n // If parentTenantId is provided and hierarchy fields not already set\n if (options.parentTenantId) {\n // Only calculate when the caller didn't already supply them\n // (a provided hierarchyLevel/hierarchyPath is preserved as-is)\n const needsHierarchyCalc =\n options.hierarchyLevel === undefined ||\n options.hierarchyPath === undefined;\n\n if (needsHierarchyCalc) {\n const parent = await this.get({ id: options.parentTenantId });\n if (!parent?.id) {\n throw new TenantHierarchyError(\n `Parent tenant not found: ${options.parentTenantId}`,\n 'PARENT_NOT_FOUND',\n );\n }\n\n const newLevel = parent.hierarchyLevel + 1;\n if (newLevel >= MAX_TENANT_HIERARCHY_DEPTH) {\n throw new TenantHierarchyError(\n `Maximum hierarchy depth (${MAX_TENANT_HIERARCHY_DEPTH}) exceeded`,\n 'MAX_DEPTH_EXCEEDED',\n );\n }\n\n if (options.hierarchyLevel === undefined) {\n options.hierarchyLevel = newLevel;\n }\n if (options.hierarchyPath === undefined) {\n options.hierarchyPath = parent.hierarchyPath\n ? `${parent.hierarchyPath}/${parent.id}`\n : parent.id;\n }\n }\n } else {\n // Root tenant\n options.hierarchyLevel = options.hierarchyLevel ?? 0;\n options.hierarchyPath = options.hierarchyPath ?? '';\n }\n\n return super.create(options);\n }\n}\n","/**\n * TenantPermissionOverride model - tenant-level permission grant/deny/inherit\n * @packageDocumentation\n */\n\nimport {\n field,\n foreignKey,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantPermissionEffect } from '../types/index.js';\n\n/**\n * Constructor options for {@link TenantPermissionOverride}.\n */\nexport interface TenantPermissionOverrideOptions extends SmrtObjectOptions {\n tenantId?: string;\n permissionId?: string;\n effect?: TenantPermissionEffect;\n}\n\n/**\n * TenantPermissionOverride allows setting explicit permission values at the tenant level.\n *\n * This is used in hierarchical tenant structures to control permission inheritance.\n * Each override specifies whether a permission is:\n * - INHERIT: Use the parent tenant's value (default behavior)\n * - GRANT: Explicitly grant at this tenant level\n * - DENY: Explicitly deny at this tenant level (blocks inheritance)\n *\n * ## Resolution Order\n *\n * When resolving effective tenant permissions:\n * 1. Walk down the tenant hierarchy from root to target, accumulating permissions\n * 2. Apply each tenant's overrides (DENY blocks, GRANT adds)\n * 3. INHERIT means \"use whatever the parent resolved to\"\n *\n * @example\n * ```typescript\n * // Explicitly grant a permission at this tenant level\n * const override = await tenantPermissionOverrides.create({\n * tenantId: tenant.id,\n * permissionId: specialPermission.id,\n * effect: TenantPermissionEffect.GRANT\n * });\n *\n * // Block a permission from being inherited (even if parent grants it)\n * const block = await tenantPermissionOverrides.create({\n * tenantId: tenant.id,\n * permissionId: dangerousPermission.id,\n * effect: TenantPermissionEffect.DENY\n * });\n *\n * // Explicitly inherit (same as not having an override, but documents intent)\n * const inherit = await tenantPermissionOverrides.create({\n * tenantId: tenant.id,\n * permissionId: standardPermission.id,\n * effect: TenantPermissionEffect.INHERIT\n * });\n * ```\n */\n@smrt({\n // #1400: read-only generated REST + MCP surface — RBAC/identity writes go\n // through permission-gated services, not auth-only generated CRUD. mcp must\n // be explicit: an omitted mcp config generates the FULL tool surface.\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class TenantPermissionOverride extends SmrtObject {\n /**\n * Foreign key to Tenant\n */\n @foreignKey('Tenant', { required: true })\n tenantId?: string;\n\n /**\n * Foreign key to Permission\n */\n @foreignKey('Permission', { required: true })\n permissionId?: string;\n\n /**\n * Effect of the override: inherit, grant, or deny\n */\n @field({ type: 'text' })\n effect: TenantPermissionEffect = TenantPermissionEffect.INHERIT;\n\n constructor(options: TenantPermissionOverrideOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.permissionId !== undefined)\n this.permissionId = options.permissionId;\n if (options.effect !== undefined) this.effect = options.effect;\n }\n\n /**\n * Check if this override inherits from parent\n */\n isInherit(): boolean {\n return this.effect === TenantPermissionEffect.INHERIT;\n }\n\n /**\n * Check if this override grants the permission\n */\n isGrant(): boolean {\n return this.effect === TenantPermissionEffect.GRANT;\n }\n\n /**\n * Check if this override denies the permission\n */\n isDeny(): boolean {\n return this.effect === TenantPermissionEffect.DENY;\n }\n\n /**\n * Check if this override has an explicit effect (not inherit)\n */\n hasExplicitEffect(): boolean {\n return this.effect !== TenantPermissionEffect.INHERIT;\n }\n}\n","/**\n * TenantPermissionOverrideCollection - Collection manager for TenantPermissionOverride objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { TenantPermissionOverride } from '../models/TenantPermissionOverride.js';\nimport { TenantPermissionEffect } from '../types/index.js';\n\n/**\n * Result of resolving tenant permission overrides\n */\nexport interface TenantPermissionOverrideResult {\n /** Permission IDs explicitly granted at this tenant level */\n grantedPermissionIds: string[];\n /** Permission IDs explicitly denied at this tenant level */\n deniedPermissionIds: string[];\n /** Permission IDs set to inherit from parent */\n inheritedPermissionIds: string[];\n}\n\n/**\n * Collection for managing TenantPermissionOverride objects.\n *\n * Provides methods for setting and querying tenant-level permission overrides\n * used in hierarchical tenant permission inheritance.\n */\nexport class TenantPermissionOverrideCollection extends SmrtCollection<TenantPermissionOverride> {\n static readonly _itemClass = TenantPermissionOverride;\n\n /**\n * Find all overrides for a tenant\n */\n async findByTenant(tenantId: string): Promise<TenantPermissionOverride[]> {\n return await this.list({\n where: { tenantId },\n });\n }\n\n /**\n * Find grant overrides for a tenant.\n *\n * Filters in memory because the `effect` column is JSON-typed and\n * Postgres rejects bare `json = text` comparisons. A single\n * `findByTenant` call is reused for grant, deny, and inherit lookups.\n */\n async findGrants(tenantId: string): Promise<TenantPermissionOverride[]> {\n const all = await this.findByTenant(tenantId);\n return all.filter((o) => o.effect === TenantPermissionEffect.GRANT);\n }\n\n /**\n * Find deny overrides for a tenant.\n *\n * See `findGrants` for rationale on in-memory filtering.\n */\n async findDenies(tenantId: string): Promise<TenantPermissionOverride[]> {\n const all = await this.findByTenant(tenantId);\n return all.filter((o) => o.effect === TenantPermissionEffect.DENY);\n }\n\n /**\n * Find inherit overrides for a tenant.\n *\n * See `findGrants` for rationale on in-memory filtering.\n */\n async findInherits(tenantId: string): Promise<TenantPermissionOverride[]> {\n const all = await this.findByTenant(tenantId);\n return all.filter((o) => o.effect === TenantPermissionEffect.INHERIT);\n }\n\n /**\n * Get granted permission IDs for a tenant\n */\n async getGrantedPermissionIds(tenantId: string): Promise<string[]> {\n const grants = await this.findGrants(tenantId);\n return grants.map((o) => o.permissionId as string);\n }\n\n /**\n * Get denied permission IDs for a tenant\n */\n async getDeniedPermissionIds(tenantId: string): Promise<string[]> {\n const denies = await this.findDenies(tenantId);\n return denies.map((o) => o.permissionId as string);\n }\n\n /**\n * Get all overrides for a tenant, organized by effect\n */\n async getOverridesByEffect(\n tenantId: string,\n ): Promise<TenantPermissionOverrideResult> {\n const overrides = await this.findByTenant(tenantId);\n\n const result: TenantPermissionOverrideResult = {\n grantedPermissionIds: [],\n deniedPermissionIds: [],\n inheritedPermissionIds: [],\n };\n\n for (const override of overrides) {\n const permId = override.permissionId as string;\n switch (override.effect) {\n case TenantPermissionEffect.GRANT:\n result.grantedPermissionIds.push(permId);\n break;\n case TenantPermissionEffect.DENY:\n result.deniedPermissionIds.push(permId);\n break;\n case TenantPermissionEffect.INHERIT:\n result.inheritedPermissionIds.push(permId);\n break;\n }\n }\n\n return result;\n }\n\n /**\n * Batch get all overrides for multiple tenants, organized by effect.\n * Fetches all overrides in a single query to avoid N+1 query problem.\n *\n * @param tenantIds - Array of tenant IDs to fetch overrides for\n * @returns Map of tenant ID to their permission override results\n */\n async getOverridesByEffectBatch(\n tenantIds: string[],\n ): Promise<Map<string, TenantPermissionOverrideResult>> {\n const resultMap = new Map<string, TenantPermissionOverrideResult>();\n\n // Initialize empty results for all tenant IDs\n for (const tenantId of tenantIds) {\n resultMap.set(tenantId, {\n grantedPermissionIds: [],\n deniedPermissionIds: [],\n inheritedPermissionIds: [],\n });\n }\n\n if (tenantIds.length === 0) {\n return resultMap;\n }\n\n // Fetch all overrides for all tenants in a single query\n const allOverrides = await this.list({\n where: { tenantId: tenantIds },\n });\n\n // Organize overrides by tenant and effect\n for (const override of allOverrides) {\n const tenantId = override.tenantId as string;\n const permId = override.permissionId as string;\n const result = resultMap.get(tenantId);\n\n if (result) {\n switch (override.effect) {\n case TenantPermissionEffect.GRANT:\n result.grantedPermissionIds.push(permId);\n break;\n case TenantPermissionEffect.DENY:\n result.deniedPermissionIds.push(permId);\n break;\n case TenantPermissionEffect.INHERIT:\n result.inheritedPermissionIds.push(permId);\n break;\n }\n }\n }\n\n return resultMap;\n }\n\n /**\n * Set an override for a tenant permission\n */\n async setOverride(\n tenantId: string,\n permissionId: string,\n effect: TenantPermissionEffect,\n ): Promise<TenantPermissionOverride> {\n // Check if override already exists\n const existing = await this.list({\n where: { tenantId, permissionId },\n limit: 1,\n });\n\n if (existing.length > 0) {\n // Update existing override\n existing[0].effect = effect;\n await existing[0].save();\n return existing[0];\n }\n\n // Create new override\n const override = await this.create({ tenantId, permissionId, effect });\n await override.save();\n return override;\n }\n\n /**\n * Remove an override (permission will use default behavior)\n */\n async removeOverride(\n tenantId: string,\n permissionId: string,\n ): Promise<boolean> {\n const existing = await this.list({\n where: { tenantId, permissionId },\n limit: 1,\n });\n\n if (existing.length === 0) {\n return false;\n }\n\n await existing[0].delete();\n return true;\n }\n\n /**\n * Remove all overrides for a tenant\n */\n async removeAllOverrides(tenantId: string): Promise<number> {\n const overrides = await this.findByTenant(tenantId);\n await Promise.all(overrides.map((override) => override.delete()));\n return overrides.length;\n }\n\n /**\n * Grant a permission at the tenant level.\n * Convenience method for setOverride with GRANT effect.\n */\n async grantPermission(\n tenantId: string,\n permissionId: string,\n ): Promise<TenantPermissionOverride> {\n return await this.setOverride(\n tenantId,\n permissionId,\n TenantPermissionEffect.GRANT,\n );\n }\n\n /**\n * Deny a permission at the tenant level.\n * Convenience method for setOverride with DENY effect.\n * This blocks inheritance from parent tenants.\n */\n async denyPermission(\n tenantId: string,\n permissionId: string,\n ): Promise<TenantPermissionOverride> {\n return await this.setOverride(\n tenantId,\n permissionId,\n TenantPermissionEffect.DENY,\n );\n }\n\n /**\n * Set a permission to inherit from parent.\n * Convenience method for setOverride with INHERIT effect.\n * This is the default behavior, so mainly useful to document intent\n * or to reset a previous grant/deny.\n */\n async inheritPermission(\n tenantId: string,\n permissionId: string,\n ): Promise<TenantPermissionOverride> {\n return await this.setOverride(\n tenantId,\n permissionId,\n TenantPermissionEffect.INHERIT,\n );\n }\n\n /**\n * Bulk set overrides for a tenant.\n * Useful for importing or copying permission configurations.\n */\n async bulkSetOverrides(\n tenantId: string,\n overrides: Array<{ permissionId: string; effect: TenantPermissionEffect }>,\n ): Promise<TenantPermissionOverride[]> {\n const results: TenantPermissionOverride[] = [];\n for (const { permissionId, effect } of overrides) {\n const override = await this.setOverride(tenantId, permissionId, effect);\n results.push(override);\n }\n return results;\n }\n\n /**\n * Copy overrides from one tenant to another.\n * Useful when creating child tenants or templates.\n */\n async copyOverrides(\n fromTenantId: string,\n toTenantId: string,\n ): Promise<TenantPermissionOverride[]> {\n const sourceOverrides = await this.findByTenant(fromTenantId);\n const results: TenantPermissionOverride[] = [];\n\n for (const source of sourceOverrides) {\n const override = await this.setOverride(\n toTenantId,\n source.permissionId as string,\n source.effect,\n );\n results.push(override);\n }\n\n return results;\n }\n}\n","import { BackfillTracker } from '@happyvertical/smrt-core/migrations';\nimport { normalizeIdentityEmail } from '@happyvertical/smrt-profiles';\nimport type { getDatabase } from '@happyvertical/sql';\n\ntype DatabaseInterface = Awaited<ReturnType<typeof getDatabase>>;\n\nexport const USER_EMAIL_KEY_BACKFILL_NAME =\n '@happyvertical/smrt-users:user-email-keys:v1';\n\nexport interface DuplicateUserEmailKey {\n emailKey: string;\n userCount: number;\n}\n\nexport interface BackfillUserEmailKeysResult {\n updated: number;\n}\n\nexport type UserEmailKeyBackfillErrorCode =\n | 'duplicate_email'\n | 'transaction_required';\n\n/**\n * Raised before any rows are changed when legacy User emails are ambiguous.\n */\nexport class UserEmailKeyBackfillError extends Error {\n constructor(\n readonly code: UserEmailKeyBackfillErrorCode,\n message: string,\n readonly duplicates: DuplicateUserEmailKey[] = [],\n ) {\n super(message);\n this.name = 'UserEmailKeyBackfillError';\n }\n}\n\n/**\n * Populate the durable normalized-email key for Users created before the\n * `emailKey` field existed.\n *\n * Apply the schema migration first, stop or upgrade legacy writers, then call\n * this once from a single deploy process. The operation is idempotent. It\n * fails before changing any rows when two non-blank emails normalize to the\n * same key, so operators can reconcile those Users explicitly.\n */\nexport async function backfillUserEmailKeys(\n db: DatabaseInterface,\n): Promise<BackfillUserEmailKeysResult> {\n if (!db.transaction) {\n throw new UserEmailKeyBackfillError(\n 'transaction_required',\n 'User email-key backfill requires a root database with transaction().',\n );\n }\n\n const tracker = new BackfillTracker({ db });\n await tracker.initialize();\n\n return db.transaction(async (tx) => {\n BackfillTracker.inheritInitialization(tx, db);\n const result = await tx.query(\n 'SELECT id, email, email_key FROM users ORDER BY id',\n );\n const counts = new Map<string, number>();\n const updates: Array<{ emailKey: string | null; id: string }> = [];\n\n for (const row of result.rows) {\n const id = typeof row.id === 'string' ? row.id : '';\n if (!id) {\n throw new Error('User email-key backfill found a row without an id.');\n }\n const email = typeof row.email === 'string' ? row.email : '';\n const emailKey = email.trim() ? normalizeIdentityEmail(email) : null;\n if (emailKey) counts.set(emailKey, (counts.get(emailKey) ?? 0) + 1);\n const storedKey =\n typeof row.email_key === 'string' ? row.email_key : null;\n if (storedKey !== emailKey) updates.push({ emailKey, id });\n }\n\n const duplicates = [...counts.entries()]\n .filter(([, userCount]) => userCount > 1)\n .map(([emailKey, userCount]) => ({ emailKey, userCount }))\n .sort((left, right) => left.emailKey.localeCompare(right.emailKey));\n if (duplicates.length > 0) {\n throw new UserEmailKeyBackfillError(\n 'duplicate_email',\n 'Cannot backfill User.emailKey because legacy User emails contain normalized duplicates.',\n duplicates,\n );\n }\n\n // Clear every changing key first. This avoids transient unique-index\n // conflicts for valid final states such as two legacy rows whose stale\n // keys were swapped.\n for (const update of updates) {\n await tx.query(\n 'UPDATE users SET email_key = NULL WHERE id = ?',\n update.id,\n );\n }\n for (const update of updates) {\n await tx.query(\n 'UPDATE users SET email_key = ? WHERE id = ?',\n update.emailKey,\n update.id,\n );\n }\n\n await new BackfillTracker({ db: tx }).recordApplied(\n USER_EMAIL_KEY_BACKFILL_NAME,\n {\n description: 'Canonical User email keys are current.',\n packageName: '@happyvertical/smrt-users',\n },\n );\n\n return { updated: updates.length };\n });\n}\n","/**\n * UserCollection - Collection manager for User objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { BackfillTracker } from '@happyvertical/smrt-core/migrations';\nimport {\n AmbiguousOidcIdentityError,\n normalizeIdentityEmail,\n OidcIdentity,\n OidcIdentityCollection,\n type Profile,\n type ProfileCollection,\n} from '@happyvertical/smrt-profiles';\nimport {\n coordinateOidcProvisioning,\n createOidcProvisioningPerson,\n insertOidcProvisioningIdentity,\n isOidcProvisioningRaceConflict,\n saveOidcRaceArbiter,\n} from '@happyvertical/smrt-profiles/internal/oidc-provisioning';\nimport { withSystemContext } from '@happyvertical/smrt-tenancy';\nimport type { getDatabase } from '@happyvertical/sql';\nimport { USER_EMAIL_KEY_BACKFILL_NAME } from '../migrations/backfillUserEmailKeys.js';\nimport { User } from '../models/User.js';\nimport { UserStatus } from '../types/index.js';\n\ntype DatabaseInterface = Awaited<ReturnType<typeof getDatabase>>;\n\n/**\n * OIDC claims used for identity resolution\n */\nexport interface OidcClaims {\n /** Subject identifier from IdP */\n sub: string;\n /** Issuer URL */\n iss: string;\n /** User's email address */\n email?: string;\n /** Whether the IdP verified the email address */\n email_verified?: boolean;\n /** User's display name */\n name?: string;\n /** Preferred username */\n preferred_username?: string;\n}\n\n/** OIDC claims after the provisioning boundary validates and normalizes email. */\nexport type NormalizedOidcClaims = Readonly<OidcClaims & { email: string }>;\n\n/**\n * Result of OIDC identity resolution\n */\nexport interface OidcIdentityResult {\n /** The User record */\n user: User;\n /** The linked Profile */\n profile: Profile;\n /** The OidcIdentity linking profile to IdP */\n oidcIdentity: OidcIdentity;\n /** Whether the profile was newly created */\n created: boolean;\n}\n\n/** Context passed after OIDC claims are validated and before provisioning. */\nexport interface OidcProfileResolverContext {\n /** Transaction-bound database; use this for every resolver read/write. */\n db: DatabaseInterface;\n /**\n * Frozen normalized claim snapshot. OidcLoginService supplies\n * protocol-validated claims; direct collection callers must provide claims\n * from a trusted boundary.\n */\n claims: NormalizedOidcClaims;\n /** Configured provider key. */\n provider: string;\n /** Transaction-bound User collection. */\n users: UserCollection;\n}\n\n/**\n * Resolve a consumer-owned canonical Profile before User/session creation.\n *\n * Return `undefined` for the secure default, `null` to reject login, or a\n * Profile to select it explicitly. For a new issuer/subject, a supplied Profile\n * is still required to be the unique, unowned global Person for the verified\n * email. For an exact existing issuer/subject, `null` still rejects login and a\n * supplied Profile must be the already-linked Profile; the hook cannot rebind\n * identity authority. Stable-link owner and canonical-Person checks still\n * apply. The hook may be retried after a concurrent unique-key race and must\n * therefore be idempotent.\n */\nexport type OidcProfileResolver = (\n context: OidcProfileResolverContext,\n) => Profile | null | undefined | Promise<Profile | null | undefined>;\n\n/** Application authorization for binding a new OIDC identity to an owner. */\nexport interface OidcProfileOwnerAuthorization {\n /** Canonical global Person selected by the application. */\n profile: Profile;\n /** Existing User the application authorizes as that Profile's owner. */\n user: User;\n}\n\n/** Transaction-bound context supplied to the owner authorization callback. */\nexport type OidcProfileOwnerAuthorizerContext = OidcProfileResolverContext;\n\n/**\n * Explicitly authorize first OIDC binding to a pre-provisioned Profile/User.\n *\n * Return `undefined` to preserve SMRT's secure default, `null` to reject the\n * login, or both the canonical Profile and its existing owning User. SMRT\n * reloads and verifies both records inside the provisioning transaction; the\n * returned objects are never trusted as proof of ownership. The callback may\n * be retried after a concurrent unique-key race and must be idempotent.\n */\nexport type OidcProfileOwnerAuthorizer = (\n context: OidcProfileOwnerAuthorizerContext,\n) =>\n | OidcProfileOwnerAuthorization\n | null\n | undefined\n | Promise<OidcProfileOwnerAuthorization | null | undefined>;\n\nexport type OidcProvisioningErrorCode =\n | 'ambiguous_identity'\n | 'concurrency_conflict'\n | 'profile_owned'\n | 'rejected'\n | 'transaction_required'\n | 'user_email_backfill_required'\n | 'user_email_conflict';\n\n/** Fail-closed OIDC identity provisioning error. */\nexport class OidcProvisioningError extends Error {\n constructor(\n readonly code: OidcProvisioningErrorCode,\n message: string,\n options?: { cause?: unknown },\n ) {\n super(message, options);\n this.name = 'OidcProvisioningError';\n }\n}\n\n/**\n * Options for getOrCreateFromOidc\n */\nexport interface GetOrCreateFromOidcOptions {\n /** If false, skip recording login timestamp (default: true) */\n recordLogin?: boolean;\n /**\n * Provision a user even when the IdP explicitly reported the email as\n * unverified (`email_verified: false`). Default false (#1400): refuse to\n * create/resolve a user from a known-unverified address. Has no effect when\n * the claim is absent — an IdP that omits `email_verified` makes no\n * assertion, so it cannot be enforced.\n */\n allowUnverifiedEmail?: boolean;\n /**\n * Optional application resolver invoked inside the provisioning transaction\n * after token/claim validation and before OIDC identity, User, or session\n * creation. Return a canonical Profile, `null` to reject, or `undefined` to\n * use SMRT's secure default.\n */\n resolveProfile?: OidcProfileResolver;\n /**\n * Explicitly authorize a first issuer/subject binding to a pre-provisioned\n * canonical Profile and its existing owner. A successful authorization\n * requires `email_verified === true` and is revalidated atomically by SMRT.\n */\n authorizeProfileOwner?: OidcProfileOwnerAuthorizer;\n}\n\n/**\n * Collection for managing User objects\n */\nexport class UserCollection extends SmrtCollection<User> {\n static readonly _itemClass = User;\n private userEmailKeysReadyPromise: Promise<void> | null = null;\n\n /**\n * Find user by email address\n */\n async findByEmail(email: string): Promise<User | null> {\n const results = await this.list({\n where: { email },\n limit: 1,\n });\n return results.length > 0 ? results[0] : null;\n }\n\n /**\n * Find user by profile ID\n */\n async findByProfile(profileId: string): Promise<User | null> {\n const results = await this.list({\n where: { profileId },\n limit: 1,\n });\n return results.length > 0 ? results[0] : null;\n }\n\n /**\n * Find users by status\n */\n async findByStatus(status: UserStatus): Promise<User[]> {\n return await this.list({\n where: { status },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find all active users\n */\n async findActive(): Promise<User[]> {\n return await this.findByStatus(UserStatus.ACTIVE);\n }\n\n /**\n * Find all pending users\n */\n async findPending(): Promise<User[]> {\n return await this.findByStatus(UserStatus.PENDING);\n }\n\n /**\n * Get or create user for a profile\n */\n async getOrCreateForProfile(\n profileId: string,\n email: string,\n defaults: Partial<{ status: UserStatus }> = {},\n ): Promise<User> {\n const existing = await this.findByProfile(profileId);\n if (existing) {\n return existing;\n }\n\n const user = await this.create({\n profileId,\n email,\n status: defaults.status ?? UserStatus.ACTIVE,\n });\n await user.save();\n return user;\n }\n\n /**\n * Get or create user from OIDC claims\n *\n * This is the primary method for resolving identity from an OIDC login.\n * It handles the full flow:\n * 1. Find or create Profile from OIDC claims (via smrt-profiles)\n * 2. Link OidcIdentity to the Profile\n * 3. Find or create User linked to the Profile\n *\n * Direct callers must supply claims from a trusted, already validated token\n * boundary. OidcLoginService performs discovery, token exchange, issuer and\n * subject validation, and verified-email source pairing before calling this\n * collection method.\n *\n * @param claims - Trusted OIDC token claims (sub, iss, email, name)\n * @param provider - Provider name (e.g., 'kanidm', 'keycloak', 'google')\n * @param options - Login recording, unverified-email compatibility, Profile\n * resolution, and explicit owner authorization. Hooks run inside the\n * provisioning transaction, may be retried, and must be idempotent. A\n * resolver result must still be the unique, global, unowned Person. An owner\n * authorization must select both the canonical Person and its existing sole\n * approved User owner. Existing identities can only be confirmed, never\n * rebound.\n * @returns User, Profile, OidcIdentity, and whether profile was created\n *\n * @example\n * ```typescript\n * const userCollection = await UserCollection.create({ db: dbConfig });\n *\n * // In your OIDC callback handler:\n * const { user, profile } = await userCollection.getOrCreateFromOidc(\n * {\n * sub: tokenClaims.sub,\n * iss: tokenClaims.iss,\n * email: tokenClaims.email,\n * name: tokenClaims.name,\n * },\n * 'kanidm'\n * );\n *\n * // User and profile are now available\n * // Login was auto-recorded; pass { recordLogin: false } to skip\n * ```\n */\n async getOrCreateFromOidc(\n claims: OidcClaims,\n provider: string,\n options?: GetOrCreateFromOidcOptions,\n ): Promise<OidcIdentityResult> {\n // Validate required OIDC claims at runtime\n const sub = claims?.sub;\n const iss = claims?.iss;\n if (\n typeof sub !== 'string' ||\n sub.trim().length === 0 ||\n typeof iss !== 'string' ||\n iss.trim().length === 0\n ) {\n throw new Error(\n 'Invalid OIDC claims: both \"sub\" and \"iss\" must be non-empty strings.',\n );\n }\n\n // Email is required for user creation and is canonicalized before any\n // resolver or persistence operation.\n const suppliedEmail = claims.email;\n if (typeof suppliedEmail !== 'string' || !suppliedEmail.trim()) {\n throw new Error(\n 'OIDC claims missing required \"email\" for user creation.',\n );\n }\n const email = normalizeIdentityEmail(suppliedEmail);\n\n // #1400: refuse an email the IdP explicitly marked unverified. Only an\n // explicit `email_verified === false` hard-fails; an absent claim makes no\n // assertion and cannot be enforced. Opt out with allowUnverifiedEmail.\n const allowUnverified = options?.allowUnverifiedEmail === true;\n if (claims.email_verified === false && !allowUnverified) {\n throw new Error(\n 'OIDC claims report an unverified email; refusing to provision a user.',\n );\n }\n\n const normalizedClaims: NormalizedOidcClaims = Object.freeze({\n ...claims,\n sub,\n iss,\n email,\n });\n const db = this.requireProvisioningDatabase();\n return coordinateOidcProvisioning({\n db,\n lockKeys: [\n `identity:${OidcIdentity.buildIdentityKey(iss, sub)}`,\n `email:${email}`,\n ],\n isRaceConflict: isUserOidcRaceConflict,\n createTransactionError: (message, cause) =>\n new OidcProvisioningError('transaction_required', message, {\n cause,\n }),\n createConcurrencyError: (cause) =>\n new OidcProvisioningError(\n 'concurrency_conflict',\n 'Concurrent OIDC provisioning did not converge on one identity.',\n { cause },\n ),\n rebindRootResult: (result, rootDb) =>\n this.rebindOidcProvisioningResult(result, rootDb),\n provision: (tx) =>\n withSystemContext(async () => {\n const users =\n tx === db ? this : await UserCollection.create({ db: tx });\n return users.provisionOidcIdentity(\n normalizedClaims,\n provider,\n options,\n tx,\n );\n }),\n });\n }\n\n private requireProvisioningDatabase(): DatabaseInterface {\n const db = this.options.db;\n if (!db || typeof db !== 'object' || !('query' in db)) {\n throw new OidcProvisioningError(\n 'transaction_required',\n 'OIDC provisioning requires an initialized database.',\n );\n }\n return db as DatabaseInterface;\n }\n\n private async provisionOidcIdentity(\n claims: NormalizedOidcClaims,\n provider: string,\n options: GetOrCreateFromOidcOptions | undefined,\n db: DatabaseInterface,\n ): Promise<OidcIdentityResult> {\n const { ProfileCollection } = await import('@happyvertical/smrt-profiles');\n const profileCollection = await ProfileCollection.create({ db });\n const identityCollection = await OidcIdentityCollection.create({ db });\n const existingIdentity = await this.findUniqueOidcIdentity(\n identityCollection,\n claims,\n );\n const createResolverContext = (): OidcProfileResolverContext => ({\n // Never expose the internal snapshot used for retry locks and\n // persistence. A frozen copy keeps an application callback from\n // changing the authenticated identity across attempts.\n claims: Object.freeze({ ...claims }),\n db,\n provider,\n users: this,\n });\n const resolvedProfile = options?.resolveProfile\n ? await options.resolveProfile(createResolverContext())\n : undefined;\n if (resolvedProfile === null) {\n throw new OidcProvisioningError(\n 'rejected',\n 'OIDC provisioning was rejected by the application resolver.',\n );\n }\n const ownerAuthorization = options?.authorizeProfileOwner\n ? await options.authorizeProfileOwner(createResolverContext())\n : undefined;\n if (ownerAuthorization === null) {\n throw new OidcProvisioningError(\n 'rejected',\n 'OIDC provisioning was rejected by the application owner authorizer.',\n );\n }\n const validatedOwnerAuthorization = ownerAuthorization\n ? await this.validateProfileOwnerAuthorization(\n ownerAuthorization,\n claims,\n profileCollection,\n )\n : undefined;\n if (\n validatedOwnerAuthorization &&\n resolvedProfile &&\n requireId(resolvedProfile.id, 'Resolved Profile') !==\n requireId(validatedOwnerAuthorization.profile.id, 'Authorized Profile')\n ) {\n throw new OidcProvisioningError(\n 'rejected',\n 'Application Profile resolution and owner authorization selected different Profiles.',\n );\n }\n\n if (existingIdentity) {\n const profileId = requireId(\n existingIdentity.profileId,\n 'OIDC identity Profile',\n );\n if (\n resolvedProfile &&\n requireId(resolvedProfile.id, 'Resolved Profile') !== profileId\n ) {\n throw new OidcProvisioningError(\n 'rejected',\n 'An application resolver cannot rebind an existing OIDC identity.',\n );\n }\n if (\n validatedOwnerAuthorization &&\n requireId(\n validatedOwnerAuthorization.profile.id,\n 'Authorized Profile',\n ) !== profileId\n ) {\n throw new OidcProvisioningError(\n 'rejected',\n 'An application owner authorizer cannot rebind an existing OIDC identity.',\n );\n }\n const owners = validatedOwnerAuthorization\n ? [validatedOwnerAuthorization.user]\n : await this.findProfileOwners(profileId);\n if (owners.length > 1) {\n throw new OidcProvisioningError(\n 'profile_owned',\n `Profile ${profileId} belongs to multiple Users.`,\n );\n }\n const profile = validatedOwnerAuthorization\n ? validatedOwnerAuthorization.profile\n : await profileCollection.reserveCanonicalIdentityEmail(\n profileId,\n owners[0] ? undefined : claims.email,\n );\n\n const user = await this.finishUserProvisioning(\n profile,\n owners[0],\n claims.email,\n options,\n );\n existingIdentity.email = claims.email;\n existingIdentity.lastUsedAt = new Date();\n await existingIdentity.save();\n return {\n user,\n profile,\n oidcIdentity: existingIdentity,\n created: false,\n };\n }\n\n let profile: Profile | null | undefined =\n validatedOwnerAuthorization?.profile ?? resolvedProfile;\n let created = false;\n\n if (profile && !validatedOwnerAuthorization) {\n if (claims.email_verified !== true) {\n throw new OidcProvisioningError(\n 'rejected',\n 'An application resolver cannot reuse a Profile without a verified OIDC email.',\n );\n }\n profile = await profileCollection.reserveCanonicalIdentityEmail(\n requireId(profile.id, 'Resolved Profile'),\n claims.email,\n );\n } else if (claims.email_verified === true) {\n profile = await profileCollection.findUniqueGlobalPersonByEmail(\n claims.email,\n );\n if (profile) {\n profile = await profileCollection.reserveCanonicalIdentityEmail(\n requireId(profile.id, 'Matched Profile'),\n claims.email,\n );\n }\n } else {\n // Never link an unverified/unspecified email to an existing identity.\n const collision = await profileCollection.findUniqueGlobalPersonByEmail(\n claims.email,\n );\n if (collision) {\n throw new OidcProvisioningError(\n 'rejected',\n `OIDC email ${claims.email} is not verified and already belongs to a Profile.`,\n );\n }\n }\n\n if (!profile) {\n profile = await createOidcProvisioningPerson<Profile>(db, {\n email: claims.email,\n name: claims.name,\n preferredUsername: claims.preferred_username,\n subject: claims.sub,\n });\n created = true;\n }\n\n const profileId = requireId(profile.id, 'OIDC Profile');\n const owners = validatedOwnerAuthorization\n ? [validatedOwnerAuthorization.user]\n : await this.findProfileOwners(profileId);\n if (owners.length > 0) {\n if (validatedOwnerAuthorization) {\n const oidcIdentity = await insertOidcProvisioningIdentity<OidcIdentity>(\n db,\n {\n email: claims.email,\n issuer: claims.iss,\n profileId,\n provider,\n subject: claims.sub,\n },\n );\n const user = await this.finishUserProvisioning(\n profile,\n validatedOwnerAuthorization.user,\n claims.email,\n options,\n );\n return { user, profile, oidcIdentity, created: false };\n }\n // A concurrent first login can commit the exact issuer+subject mapping\n // after this transaction's initial identity lookup but before its\n // profile ownership check. Re-read the durable identity arbiter before\n // rejecting the now-owned Profile so the same identity converges while\n // every different identity still fails closed.\n const concurrentIdentity = await this.findUniqueOidcIdentity(\n identityCollection,\n claims,\n );\n if (owners.length === 1 && concurrentIdentity?.profileId === profileId) {\n const user = await this.finishUserProvisioning(\n profile,\n owners[0],\n claims.email,\n options,\n );\n concurrentIdentity.email = claims.email;\n concurrentIdentity.lastUsedAt = new Date();\n await concurrentIdentity.save();\n return {\n user,\n profile,\n oidcIdentity: concurrentIdentity,\n created: false,\n };\n }\n throw new OidcProvisioningError(\n 'profile_owned',\n `Profile ${profileId} already belongs to another User.`,\n );\n }\n\n // Initial lookup already proved this issuer+subject absent. Insert only:\n // a concurrent winner must surface the unique identity-key conflict so the\n // whole transaction rolls back and retries against the committed winner.\n const oidcIdentity = await insertOidcProvisioningIdentity<OidcIdentity>(\n db,\n {\n email: claims.email,\n issuer: claims.iss,\n profileId,\n provider,\n subject: claims.sub,\n },\n );\n const user = await this.finishUserProvisioning(\n profile,\n undefined,\n claims.email,\n options,\n );\n\n return { user, profile, oidcIdentity, created };\n }\n\n private async findProfileOwners(profileId: string): Promise<User[]> {\n const db = this.requireProvisioningDatabase();\n const lockClause = /^postgres(?:ql)?:/iu.test(db.url ?? '')\n ? ' FOR UPDATE'\n : '';\n const result = await db.query(\n `SELECT id\n FROM users\n WHERE profile_id = ?\n ORDER BY created_at ASC, id ASC\n LIMIT 2${lockClause}`,\n profileId,\n );\n const owners = await Promise.all(\n result.rows.map((row) =>\n typeof row.id === 'string' ? this.get({ id: row.id }) : null,\n ),\n );\n return owners.filter((owner): owner is User => owner !== null);\n }\n\n private async validateProfileOwnerAuthorization(\n authorization: OidcProfileOwnerAuthorization,\n claims: NormalizedOidcClaims,\n profiles: ProfileCollection,\n ): Promise<OidcProfileOwnerAuthorization> {\n if (claims.email_verified !== true) {\n throw new OidcProvisioningError(\n 'rejected',\n 'Owner-authorized OIDC binding requires email_verified to be exactly true.',\n );\n }\n if (\n !authorization ||\n typeof authorization !== 'object' ||\n !authorization.profile ||\n !authorization.user\n ) {\n throw new OidcProvisioningError(\n 'rejected',\n 'Owner-authorized OIDC binding requires both a Profile and User.',\n );\n }\n\n const profileId = requireId(authorization.profile.id, 'Authorized Profile');\n const authorizedUserId = requireId(\n authorization.user.id,\n 'Authorized User',\n );\n const profile = await profiles.reserveCanonicalIdentityEmail(\n profileId,\n claims.email,\n );\n const owners = await this.findProfileOwners(profileId);\n if (owners.length !== 1) {\n throw new OidcProvisioningError(\n 'profile_owned',\n `Owner-authorized Profile ${profileId} must belong to exactly one User.`,\n );\n }\n const owner = owners[0];\n const ownerId = requireId(owner.id, 'Profile owner');\n if (ownerId !== authorizedUserId) {\n throw new OidcProvisioningError(\n 'profile_owned',\n `Authorized User ${authorizedUserId} is not the owner of Profile ${profileId}.`,\n );\n }\n\n const emailUsers = await this.findUsersByNormalizedEmail(claims.email);\n if (\n emailUsers.length !== 1 ||\n requireId(emailUsers[0]?.id, 'Authorized email User') !== ownerId\n ) {\n throw new OidcProvisioningError(\n 'user_email_conflict',\n 'The authorized Profile owner does not have the verified OIDC email.',\n );\n }\n return { profile, user: owner };\n }\n\n private async rebindOidcProvisioningResult(\n result: OidcIdentityResult,\n rootDb: DatabaseInterface,\n ): Promise<OidcIdentityResult> {\n const profileId = requireId(result.profile.id, 'OIDC Profile');\n const identityId = requireId(result.oidcIdentity.id, 'OIDC identity');\n const userId = requireId(result.user.id, 'OIDC User');\n return withSystemContext(async () => {\n const { ProfileCollection } = await import(\n '@happyvertical/smrt-profiles'\n );\n const users = await UserCollection.create({ db: rootDb });\n const profiles = await ProfileCollection.create({ db: rootDb });\n const identities = await OidcIdentityCollection.create({ db: rootDb });\n const [user, profile, oidcIdentity] = await Promise.all([\n users.get({ id: userId }),\n profiles.get({ id: profileId }),\n identities.get({ id: identityId }),\n ]);\n if (!user || !profile || !oidcIdentity) {\n throw new OidcProvisioningError(\n 'concurrency_conflict',\n 'Committed OIDC provisioning result was not found.',\n );\n }\n return { ...result, user, profile, oidcIdentity };\n });\n }\n\n private async findUniqueOidcIdentity(\n identities: OidcIdentityCollection,\n claims: Pick<NormalizedOidcClaims, 'iss' | 'sub'>,\n ): Promise<OidcIdentity | undefined> {\n try {\n return (\n (await identities.findBySubject(claims.iss, claims.sub)) ?? undefined\n );\n } catch (error) {\n if (error instanceof AmbiguousOidcIdentityError) {\n throw new OidcProvisioningError(\n 'ambiguous_identity',\n `Multiple OIDC identities exist for ${claims.iss} subject ${claims.sub}.`,\n { cause: error },\n );\n }\n throw error;\n }\n }\n\n private async finishUserProvisioning(\n profile: Profile,\n existingOwner: User | undefined,\n email: string,\n options: GetOrCreateFromOidcOptions | undefined,\n ): Promise<User> {\n const shouldRecordLogin = options?.recordLogin !== false;\n if (existingOwner) {\n if (shouldRecordLogin) {\n existingOwner.recordLogin();\n await existingOwner.save();\n }\n return existingOwner;\n }\n\n const emailUsers = await this.findUsersByNormalizedEmail(email);\n if (emailUsers.length > 0) {\n throw new OidcProvisioningError(\n 'user_email_conflict',\n `A User already exists for ${email} without the selected Profile.`,\n );\n }\n\n return saveOidcRaceArbiter(() =>\n this.create({\n email,\n ...(shouldRecordLogin ? { lastLoginAt: new Date() } : {}),\n profileId: requireId(profile.id, 'OIDC Profile'),\n status: UserStatus.ACTIVE,\n }),\n );\n }\n\n private async findUsersByNormalizedEmail(email: string): Promise<User[]> {\n const db = this.requireProvisioningDatabase();\n await this.ensureUserEmailKeysReady(db);\n const result = await db.query(\n `SELECT id, email, email_key\n FROM users\n WHERE email_key = ?\n ORDER BY created_at ASC, id ASC\n LIMIT 2`,\n email,\n );\n for (const row of result.rows) {\n this.assertUserEmailKeyCurrent(row, email);\n }\n const users = await Promise.all(\n result.rows.map((row) =>\n typeof row.id === 'string' ? this.get({ id: row.id }) : null,\n ),\n );\n return users.filter((user): user is User => user !== null);\n }\n\n /** Require the deploy-time backfill marker before indexed identity reads. */\n private async ensureUserEmailKeysReady(db: DatabaseInterface): Promise<void> {\n if (!this.userEmailKeysReadyPromise) {\n this.userEmailKeysReadyPromise = this.checkUserEmailKeysReady(db).catch(\n (error) => {\n this.userEmailKeysReadyPromise = null;\n throw error;\n },\n );\n }\n return this.userEmailKeysReadyPromise;\n }\n\n private async checkUserEmailKeysReady(db: DatabaseInterface): Promise<void> {\n if (\n await new BackfillTracker({ db }).isApplied(USER_EMAIL_KEY_BACKFILL_NAME)\n )\n return;\n throw new OidcProvisioningError(\n 'user_email_backfill_required',\n 'User email keys are not marked ready; run backfillUserEmailKeys() before OIDC provisioning.',\n );\n }\n\n private assertUserEmailKeyCurrent(\n row: Record<string, unknown>,\n normalizedEmail?: string,\n ): void {\n const storedEmail = typeof row.email === 'string' ? row.email : '';\n const expectedKey = storedEmail.trim()\n ? normalizeIdentityEmail(storedEmail)\n : null;\n const storedKey = typeof row.email_key === 'string' ? row.email_key : null;\n if (\n storedKey !== expectedKey ||\n (normalizedEmail !== undefined && expectedKey !== normalizedEmail)\n ) {\n throw new OidcProvisioningError(\n 'user_email_backfill_required',\n `User ${String(row.id ?? '<unknown>')} has a missing or stale email key; run backfillUserEmailKeys() before OIDC provisioning.`,\n );\n }\n }\n}\n\nfunction requireId(value: unknown, label: string): string {\n if (typeof value !== 'string' || !value) {\n throw new Error(`${label} did not produce an id.`);\n }\n return value;\n}\n\nfunction isUserOidcRaceConflict(error: unknown): boolean {\n return isOidcProvisioningRaceConflict(error, {\n messagePatterns: [/users[._].*profile_id|users_profile_id/iu],\n });\n}\n","export const encoder = new TextEncoder();\nexport const decoder = new TextDecoder();\nconst MAX_INT32 = 2 ** 32;\nexport function concat(...buffers) {\n const size = buffers.reduce((acc, { length }) => acc + length, 0);\n const buf = new Uint8Array(size);\n let i = 0;\n for (const buffer of buffers) {\n buf.set(buffer, i);\n i += buffer.length;\n }\n return buf;\n}\nfunction writeUInt32BE(buf, value, offset) {\n if (value < 0 || value >= MAX_INT32) {\n throw new RangeError(`value must be >= 0 and <= ${MAX_INT32 - 1}. Received ${value}`);\n }\n buf.set([value >>> 24, value >>> 16, value >>> 8, value & 0xff], offset);\n}\nexport function uint64be(value) {\n const high = Math.floor(value / MAX_INT32);\n const low = value % MAX_INT32;\n const buf = new Uint8Array(8);\n writeUInt32BE(buf, high, 0);\n writeUInt32BE(buf, low, 4);\n return buf;\n}\nexport function uint32be(value) {\n const buf = new Uint8Array(4);\n writeUInt32BE(buf, value);\n return buf;\n}\nexport function encode(string) {\n const bytes = new Uint8Array(string.length);\n for (let i = 0; i < string.length; i++) {\n const code = string.charCodeAt(i);\n if (code > 127) {\n throw new TypeError('non-ASCII string encountered in encode()');\n }\n bytes[i] = code;\n }\n return bytes;\n}\n","export function encodeBase64(input) {\n if (Uint8Array.prototype.toBase64) {\n return input.toBase64();\n }\n const CHUNK_SIZE = 0x8000;\n const arr = [];\n for (let i = 0; i < input.length; i += CHUNK_SIZE) {\n arr.push(String.fromCharCode.apply(null, input.subarray(i, i + CHUNK_SIZE)));\n }\n return btoa(arr.join(''));\n}\nexport function decodeBase64(encoded) {\n if (Uint8Array.fromBase64) {\n return Uint8Array.fromBase64(encoded);\n }\n const binary = atob(encoded);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes;\n}\n","import { encoder, decoder } from '../lib/buffer_utils.js';\nimport { encodeBase64, decodeBase64 } from '../lib/base64.js';\nexport function decode(input) {\n if (Uint8Array.fromBase64) {\n return Uint8Array.fromBase64(typeof input === 'string' ? input : decoder.decode(input), {\n alphabet: 'base64url',\n });\n }\n let encoded = input;\n if (encoded instanceof Uint8Array) {\n encoded = decoder.decode(encoded);\n }\n encoded = encoded.replace(/-/g, '+').replace(/_/g, '/');\n try {\n return decodeBase64(encoded);\n }\n catch {\n throw new TypeError('The input to be decoded is not correctly encoded.');\n }\n}\nexport function encode(input) {\n let unencoded = input;\n if (typeof unencoded === 'string') {\n unencoded = encoder.encode(unencoded);\n }\n if (Uint8Array.prototype.toBase64) {\n return unencoded.toBase64({ alphabet: 'base64url', omitPadding: true });\n }\n return encodeBase64(unencoded).replace(/=/g, '').replace(/\\+/g, '-').replace(/\\//g, '_');\n}\n","const unusable = (name, prop = 'algorithm.name') => new TypeError(`CryptoKey does not support this operation, its ${prop} must be ${name}`);\nconst isAlgorithm = (algorithm, name) => algorithm.name === name;\nfunction getHashLength(hash) {\n return parseInt(hash.name.slice(4), 10);\n}\nfunction checkHashLength(algorithm, expected) {\n const actual = getHashLength(algorithm.hash);\n if (actual !== expected)\n throw unusable(`SHA-${expected}`, 'algorithm.hash');\n}\nfunction getNamedCurve(alg) {\n switch (alg) {\n case 'ES256':\n return 'P-256';\n case 'ES384':\n return 'P-384';\n case 'ES512':\n return 'P-521';\n default:\n throw new Error('unreachable');\n }\n}\nfunction checkUsage(key, usage) {\n if (usage && !key.usages.includes(usage)) {\n throw new TypeError(`CryptoKey does not support this operation, its usages must include ${usage}.`);\n }\n}\nexport function checkSigCryptoKey(key, alg, usage) {\n switch (alg) {\n case 'HS256':\n case 'HS384':\n case 'HS512': {\n if (!isAlgorithm(key.algorithm, 'HMAC'))\n throw unusable('HMAC');\n checkHashLength(key.algorithm, parseInt(alg.slice(2), 10));\n break;\n }\n case 'RS256':\n case 'RS384':\n case 'RS512': {\n if (!isAlgorithm(key.algorithm, 'RSASSA-PKCS1-v1_5'))\n throw unusable('RSASSA-PKCS1-v1_5');\n checkHashLength(key.algorithm, parseInt(alg.slice(2), 10));\n break;\n }\n case 'PS256':\n case 'PS384':\n case 'PS512': {\n if (!isAlgorithm(key.algorithm, 'RSA-PSS'))\n throw unusable('RSA-PSS');\n checkHashLength(key.algorithm, parseInt(alg.slice(2), 10));\n break;\n }\n case 'Ed25519':\n case 'EdDSA': {\n if (!isAlgorithm(key.algorithm, 'Ed25519'))\n throw unusable('Ed25519');\n break;\n }\n case 'ML-DSA-44':\n case 'ML-DSA-65':\n case 'ML-DSA-87': {\n if (!isAlgorithm(key.algorithm, alg))\n throw unusable(alg);\n break;\n }\n case 'ES256':\n case 'ES384':\n case 'ES512': {\n if (!isAlgorithm(key.algorithm, 'ECDSA'))\n throw unusable('ECDSA');\n const expected = getNamedCurve(alg);\n const actual = key.algorithm.namedCurve;\n if (actual !== expected)\n throw unusable(expected, 'algorithm.namedCurve');\n break;\n }\n default:\n throw new TypeError('CryptoKey does not support this operation');\n }\n checkUsage(key, usage);\n}\nexport function checkEncCryptoKey(key, alg, usage) {\n switch (alg) {\n case 'A128GCM':\n case 'A192GCM':\n case 'A256GCM': {\n if (!isAlgorithm(key.algorithm, 'AES-GCM'))\n throw unusable('AES-GCM');\n const expected = parseInt(alg.slice(1, 4), 10);\n const actual = key.algorithm.length;\n if (actual !== expected)\n throw unusable(expected, 'algorithm.length');\n break;\n }\n case 'A128KW':\n case 'A192KW':\n case 'A256KW': {\n if (!isAlgorithm(key.algorithm, 'AES-KW'))\n throw unusable('AES-KW');\n const expected = parseInt(alg.slice(1, 4), 10);\n const actual = key.algorithm.length;\n if (actual !== expected)\n throw unusable(expected, 'algorithm.length');\n break;\n }\n case 'ECDH': {\n switch (key.algorithm.name) {\n case 'ECDH':\n case 'X25519':\n break;\n default:\n throw unusable('ECDH or X25519');\n }\n break;\n }\n case 'PBES2-HS256+A128KW':\n case 'PBES2-HS384+A192KW':\n case 'PBES2-HS512+A256KW':\n if (!isAlgorithm(key.algorithm, 'PBKDF2'))\n throw unusable('PBKDF2');\n break;\n case 'RSA-OAEP':\n case 'RSA-OAEP-256':\n case 'RSA-OAEP-384':\n case 'RSA-OAEP-512': {\n if (!isAlgorithm(key.algorithm, 'RSA-OAEP'))\n throw unusable('RSA-OAEP');\n checkHashLength(key.algorithm, parseInt(alg.slice(9), 10) || 1);\n break;\n }\n default:\n throw new TypeError('CryptoKey does not support this operation');\n }\n checkUsage(key, usage);\n}\n","function message(msg, actual, ...types) {\n types = types.filter(Boolean);\n if (types.length > 2) {\n const last = types.pop();\n msg += `one of type ${types.join(', ')}, or ${last}.`;\n }\n else if (types.length === 2) {\n msg += `one of type ${types[0]} or ${types[1]}.`;\n }\n else {\n msg += `of type ${types[0]}.`;\n }\n if (actual == null) {\n msg += ` Received ${actual}`;\n }\n else if (typeof actual === 'function' && actual.name) {\n msg += ` Received function ${actual.name}`;\n }\n else if (typeof actual === 'object' && actual != null) {\n if (actual.constructor?.name) {\n msg += ` Received an instance of ${actual.constructor.name}`;\n }\n }\n return msg;\n}\nexport const invalidKeyInput = (actual, ...types) => message('Key must be ', actual, ...types);\nexport const withAlg = (alg, actual, ...types) => message(`Key for the ${alg} algorithm must be `, actual, ...types);\n","export class JOSEError extends Error {\n static code = 'ERR_JOSE_GENERIC';\n code = 'ERR_JOSE_GENERIC';\n constructor(message, options) {\n super(message, options);\n this.name = this.constructor.name;\n Error.captureStackTrace?.(this, this.constructor);\n }\n}\nexport class JWTClaimValidationFailed extends JOSEError {\n static code = 'ERR_JWT_CLAIM_VALIDATION_FAILED';\n code = 'ERR_JWT_CLAIM_VALIDATION_FAILED';\n claim;\n reason;\n payload;\n constructor(message, payload, claim = 'unspecified', reason = 'unspecified') {\n super(message, { cause: { claim, reason, payload } });\n this.claim = claim;\n this.reason = reason;\n this.payload = payload;\n }\n}\nexport class JWTExpired extends JOSEError {\n static code = 'ERR_JWT_EXPIRED';\n code = 'ERR_JWT_EXPIRED';\n claim;\n reason;\n payload;\n constructor(message, payload, claim = 'unspecified', reason = 'unspecified') {\n super(message, { cause: { claim, reason, payload } });\n this.claim = claim;\n this.reason = reason;\n this.payload = payload;\n }\n}\nexport class JOSEAlgNotAllowed extends JOSEError {\n static code = 'ERR_JOSE_ALG_NOT_ALLOWED';\n code = 'ERR_JOSE_ALG_NOT_ALLOWED';\n}\nexport class JOSENotSupported extends JOSEError {\n static code = 'ERR_JOSE_NOT_SUPPORTED';\n code = 'ERR_JOSE_NOT_SUPPORTED';\n}\nexport class JWEDecryptionFailed extends JOSEError {\n static code = 'ERR_JWE_DECRYPTION_FAILED';\n code = 'ERR_JWE_DECRYPTION_FAILED';\n constructor(message = 'decryption operation failed', options) {\n super(message, options);\n }\n}\nexport class JWEInvalid extends JOSEError {\n static code = 'ERR_JWE_INVALID';\n code = 'ERR_JWE_INVALID';\n}\nexport class JWSInvalid extends JOSEError {\n static code = 'ERR_JWS_INVALID';\n code = 'ERR_JWS_INVALID';\n}\nexport class JWTInvalid extends JOSEError {\n static code = 'ERR_JWT_INVALID';\n code = 'ERR_JWT_INVALID';\n}\nexport class JWKInvalid extends JOSEError {\n static code = 'ERR_JWK_INVALID';\n code = 'ERR_JWK_INVALID';\n}\nexport class JWKSInvalid extends JOSEError {\n static code = 'ERR_JWKS_INVALID';\n code = 'ERR_JWKS_INVALID';\n}\nexport class JWKSNoMatchingKey extends JOSEError {\n static code = 'ERR_JWKS_NO_MATCHING_KEY';\n code = 'ERR_JWKS_NO_MATCHING_KEY';\n constructor(message = 'no applicable key found in the JSON Web Key Set', options) {\n super(message, options);\n }\n}\nexport class JWKSMultipleMatchingKeys extends JOSEError {\n [Symbol.asyncIterator];\n static code = 'ERR_JWKS_MULTIPLE_MATCHING_KEYS';\n code = 'ERR_JWKS_MULTIPLE_MATCHING_KEYS';\n constructor(message = 'multiple matching keys found in the JSON Web Key Set', options) {\n super(message, options);\n }\n}\nexport class JWKSTimeout extends JOSEError {\n static code = 'ERR_JWKS_TIMEOUT';\n code = 'ERR_JWKS_TIMEOUT';\n constructor(message = 'request timed out', options) {\n super(message, options);\n }\n}\nexport class JWSSignatureVerificationFailed extends JOSEError {\n static code = 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED';\n code = 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED';\n constructor(message = 'signature verification failed', options) {\n super(message, options);\n }\n}\n","export function assertCryptoKey(key) {\n if (!isCryptoKey(key)) {\n throw new Error('CryptoKey instance expected');\n }\n}\nexport const isCryptoKey = (key) => {\n if (key?.[Symbol.toStringTag] === 'CryptoKey')\n return true;\n try {\n return key instanceof CryptoKey;\n }\n catch {\n return false;\n }\n};\nexport const isKeyObject = (key) => key?.[Symbol.toStringTag] === 'KeyObject';\nexport const isKeyLike = (key) => isCryptoKey(key) || isKeyObject(key);\n","import { decode } from '../util/base64url.js';\nexport const unprotected = Symbol();\nexport function assertNotSet(value, name) {\n if (value) {\n throw new TypeError(`${name} can only be called once`);\n }\n}\nexport function decodeBase64url(value, label, ErrorClass) {\n try {\n return decode(value);\n }\n catch {\n throw new ErrorClass(`Failed to base64url decode the ${label}`);\n }\n}\nexport async function digest(algorithm, data) {\n const subtleDigest = `SHA-${algorithm.slice(-3)}`;\n return new Uint8Array(await crypto.subtle.digest(subtleDigest, data));\n}\n","const isObjectLike = (value) => typeof value === 'object' && value !== null;\nexport function isObject(input) {\n if (!isObjectLike(input) || Object.prototype.toString.call(input) !== '[object Object]') {\n return false;\n }\n if (Object.getPrototypeOf(input) === null) {\n return true;\n }\n let proto = input;\n while (Object.getPrototypeOf(proto) !== null) {\n proto = Object.getPrototypeOf(proto);\n }\n return Object.getPrototypeOf(input) === proto;\n}\nexport function isDisjoint(...headers) {\n const sources = headers.filter(Boolean);\n if (sources.length === 0 || sources.length === 1) {\n return true;\n }\n let acc;\n for (const header of sources) {\n const parameters = Object.keys(header);\n if (!acc || acc.size === 0) {\n acc = new Set(parameters);\n continue;\n }\n for (const parameter of parameters) {\n if (acc.has(parameter)) {\n return false;\n }\n acc.add(parameter);\n }\n }\n return true;\n}\nexport const isJWK = (key) => isObject(key) && typeof key.kty === 'string';\nexport const isPrivateJWK = (key) => key.kty !== 'oct' &&\n ((key.kty === 'AKP' && typeof key.priv === 'string') || typeof key.d === 'string');\nexport const isPublicJWK = (key) => key.kty !== 'oct' && key.d === undefined && key.priv === undefined;\nexport const isSecretJWK = (key) => key.kty === 'oct' && typeof key.k === 'string';\n","import { JOSENotSupported } from '../util/errors.js';\nimport { checkSigCryptoKey } from './crypto_key.js';\nimport { invalidKeyInput } from './invalid_key_input.js';\nexport function checkKeyLength(alg, key) {\n if (alg.startsWith('RS') || alg.startsWith('PS')) {\n const { modulusLength } = key.algorithm;\n if (typeof modulusLength !== 'number' || modulusLength < 2048) {\n throw new TypeError(`${alg} requires key modulusLength to be 2048 bits or larger`);\n }\n }\n}\nfunction subtleAlgorithm(alg, algorithm) {\n const hash = `SHA-${alg.slice(-3)}`;\n switch (alg) {\n case 'HS256':\n case 'HS384':\n case 'HS512':\n return { hash, name: 'HMAC' };\n case 'PS256':\n case 'PS384':\n case 'PS512':\n return { hash, name: 'RSA-PSS', saltLength: parseInt(alg.slice(-3), 10) >> 3 };\n case 'RS256':\n case 'RS384':\n case 'RS512':\n return { hash, name: 'RSASSA-PKCS1-v1_5' };\n case 'ES256':\n case 'ES384':\n case 'ES512':\n return { hash, name: 'ECDSA', namedCurve: algorithm.namedCurve };\n case 'Ed25519':\n case 'EdDSA':\n return { name: 'Ed25519' };\n case 'ML-DSA-44':\n case 'ML-DSA-65':\n case 'ML-DSA-87':\n return { name: alg };\n default:\n throw new JOSENotSupported(`alg ${alg} is not supported either by JOSE or your javascript runtime`);\n }\n}\nasync function getSigKey(alg, key, usage) {\n if (key instanceof Uint8Array) {\n if (!alg.startsWith('HS')) {\n throw new TypeError(invalidKeyInput(key, 'CryptoKey', 'KeyObject', 'JSON Web Key'));\n }\n return crypto.subtle.importKey('raw', key, { hash: `SHA-${alg.slice(-3)}`, name: 'HMAC' }, false, [usage]);\n }\n checkSigCryptoKey(key, alg, usage);\n return key;\n}\nexport async function sign(alg, key, data) {\n const cryptoKey = await getSigKey(alg, key, 'sign');\n checkKeyLength(alg, cryptoKey);\n const signature = await crypto.subtle.sign(subtleAlgorithm(alg, cryptoKey.algorithm), cryptoKey, data);\n return new Uint8Array(signature);\n}\nexport async function verify(alg, key, signature, data) {\n const cryptoKey = await getSigKey(alg, key, 'verify');\n checkKeyLength(alg, cryptoKey);\n const algorithm = subtleAlgorithm(alg, cryptoKey.algorithm);\n try {\n return await crypto.subtle.verify(algorithm, cryptoKey, signature, data);\n }\n catch {\n return false;\n }\n}\n","import { JOSENotSupported } from '../util/errors.js';\nconst unsupportedAlg = 'Invalid or unsupported JWK \"alg\" (Algorithm) Parameter value';\nfunction subtleMapping(jwk) {\n let algorithm;\n let keyUsages;\n switch (jwk.kty) {\n case 'AKP': {\n switch (jwk.alg) {\n case 'ML-DSA-44':\n case 'ML-DSA-65':\n case 'ML-DSA-87':\n algorithm = { name: jwk.alg };\n keyUsages = jwk.priv ? ['sign'] : ['verify'];\n break;\n default:\n throw new JOSENotSupported(unsupportedAlg);\n }\n break;\n }\n case 'RSA': {\n switch (jwk.alg) {\n case 'PS256':\n case 'PS384':\n case 'PS512':\n algorithm = { name: 'RSA-PSS', hash: `SHA-${jwk.alg.slice(-3)}` };\n keyUsages = jwk.d ? ['sign'] : ['verify'];\n break;\n case 'RS256':\n case 'RS384':\n case 'RS512':\n algorithm = { name: 'RSASSA-PKCS1-v1_5', hash: `SHA-${jwk.alg.slice(-3)}` };\n keyUsages = jwk.d ? ['sign'] : ['verify'];\n break;\n case 'RSA-OAEP':\n case 'RSA-OAEP-256':\n case 'RSA-OAEP-384':\n case 'RSA-OAEP-512':\n algorithm = {\n name: 'RSA-OAEP',\n hash: `SHA-${parseInt(jwk.alg.slice(-3), 10) || 1}`,\n };\n keyUsages = jwk.d ? ['decrypt', 'unwrapKey'] : ['encrypt', 'wrapKey'];\n break;\n default:\n throw new JOSENotSupported(unsupportedAlg);\n }\n break;\n }\n case 'EC': {\n switch (jwk.alg) {\n case 'ES256':\n case 'ES384':\n case 'ES512':\n algorithm = {\n name: 'ECDSA',\n namedCurve: { ES256: 'P-256', ES384: 'P-384', ES512: 'P-521' }[jwk.alg],\n };\n keyUsages = jwk.d ? ['sign'] : ['verify'];\n break;\n case 'ECDH-ES':\n case 'ECDH-ES+A128KW':\n case 'ECDH-ES+A192KW':\n case 'ECDH-ES+A256KW':\n algorithm = { name: 'ECDH', namedCurve: jwk.crv };\n keyUsages = jwk.d ? ['deriveBits'] : [];\n break;\n default:\n throw new JOSENotSupported(unsupportedAlg);\n }\n break;\n }\n case 'OKP': {\n switch (jwk.alg) {\n case 'Ed25519':\n case 'EdDSA':\n algorithm = { name: 'Ed25519' };\n keyUsages = jwk.d ? ['sign'] : ['verify'];\n break;\n case 'ECDH-ES':\n case 'ECDH-ES+A128KW':\n case 'ECDH-ES+A192KW':\n case 'ECDH-ES+A256KW':\n algorithm = { name: jwk.crv };\n keyUsages = jwk.d ? ['deriveBits'] : [];\n break;\n default:\n throw new JOSENotSupported(unsupportedAlg);\n }\n break;\n }\n default:\n throw new JOSENotSupported('Invalid or unsupported JWK \"kty\" (Key Type) Parameter value');\n }\n return { algorithm, keyUsages };\n}\nexport async function jwkToKey(jwk) {\n if (!jwk.alg) {\n throw new TypeError('\"alg\" argument is required when \"jwk.alg\" is not present');\n }\n const { algorithm, keyUsages } = subtleMapping(jwk);\n const keyData = { ...jwk };\n if (keyData.kty !== 'AKP') {\n delete keyData.alg;\n }\n delete keyData.use;\n return crypto.subtle.importKey('jwk', keyData, algorithm, jwk.ext ?? (jwk.d || jwk.priv ? false : true), jwk.key_ops ?? keyUsages);\n}\n","import { isJWK } from './type_checks.js';\nimport { decode } from '../util/base64url.js';\nimport { jwkToKey } from './jwk_to_key.js';\nimport { isCryptoKey, isKeyObject } from './is_key_like.js';\nconst unusableForAlg = 'given KeyObject instance cannot be used for this algorithm';\nlet cache;\nconst handleJWK = async (key, jwk, alg, freeze = false) => {\n cache ||= new WeakMap();\n let cached = cache.get(key);\n if (cached?.[alg]) {\n return cached[alg];\n }\n const cryptoKey = await jwkToKey({ ...jwk, alg });\n if (freeze)\n Object.freeze(key);\n if (!cached) {\n cache.set(key, { [alg]: cryptoKey });\n }\n else {\n cached[alg] = cryptoKey;\n }\n return cryptoKey;\n};\nconst handleKeyObject = (keyObject, alg) => {\n cache ||= new WeakMap();\n let cached = cache.get(keyObject);\n if (cached?.[alg]) {\n return cached[alg];\n }\n const isPublic = keyObject.type === 'public';\n const extractable = isPublic ? true : false;\n let cryptoKey;\n if (keyObject.asymmetricKeyType === 'x25519') {\n switch (alg) {\n case 'ECDH-ES':\n case 'ECDH-ES+A128KW':\n case 'ECDH-ES+A192KW':\n case 'ECDH-ES+A256KW':\n break;\n default:\n throw new TypeError(unusableForAlg);\n }\n cryptoKey = keyObject.toCryptoKey(keyObject.asymmetricKeyType, extractable, isPublic ? [] : ['deriveBits']);\n }\n if (keyObject.asymmetricKeyType === 'ed25519') {\n if (alg !== 'EdDSA' && alg !== 'Ed25519') {\n throw new TypeError(unusableForAlg);\n }\n cryptoKey = keyObject.toCryptoKey(keyObject.asymmetricKeyType, extractable, [\n isPublic ? 'verify' : 'sign',\n ]);\n }\n switch (keyObject.asymmetricKeyType) {\n case 'ml-dsa-44':\n case 'ml-dsa-65':\n case 'ml-dsa-87': {\n if (alg !== keyObject.asymmetricKeyType.toUpperCase()) {\n throw new TypeError(unusableForAlg);\n }\n cryptoKey = keyObject.toCryptoKey(keyObject.asymmetricKeyType, extractable, [\n isPublic ? 'verify' : 'sign',\n ]);\n }\n }\n if (keyObject.asymmetricKeyType === 'rsa') {\n let hash;\n switch (alg) {\n case 'RSA-OAEP':\n hash = 'SHA-1';\n break;\n case 'RS256':\n case 'PS256':\n case 'RSA-OAEP-256':\n hash = 'SHA-256';\n break;\n case 'RS384':\n case 'PS384':\n case 'RSA-OAEP-384':\n hash = 'SHA-384';\n break;\n case 'RS512':\n case 'PS512':\n case 'RSA-OAEP-512':\n hash = 'SHA-512';\n break;\n default:\n throw new TypeError(unusableForAlg);\n }\n if (alg.startsWith('RSA-OAEP')) {\n return keyObject.toCryptoKey({\n name: 'RSA-OAEP',\n hash,\n }, extractable, isPublic ? ['encrypt'] : ['decrypt']);\n }\n cryptoKey = keyObject.toCryptoKey({\n name: alg.startsWith('PS') ? 'RSA-PSS' : 'RSASSA-PKCS1-v1_5',\n hash,\n }, extractable, [isPublic ? 'verify' : 'sign']);\n }\n if (keyObject.asymmetricKeyType === 'ec') {\n const nist = new Map([\n ['prime256v1', 'P-256'],\n ['secp384r1', 'P-384'],\n ['secp521r1', 'P-521'],\n ]);\n const namedCurve = nist.get(keyObject.asymmetricKeyDetails?.namedCurve);\n if (!namedCurve) {\n throw new TypeError(unusableForAlg);\n }\n const expectedCurve = { ES256: 'P-256', ES384: 'P-384', ES512: 'P-521' };\n if (expectedCurve[alg] && namedCurve === expectedCurve[alg]) {\n cryptoKey = keyObject.toCryptoKey({\n name: 'ECDSA',\n namedCurve,\n }, extractable, [isPublic ? 'verify' : 'sign']);\n }\n if (alg.startsWith('ECDH-ES')) {\n cryptoKey = keyObject.toCryptoKey({\n name: 'ECDH',\n namedCurve,\n }, extractable, isPublic ? [] : ['deriveBits']);\n }\n }\n if (!cryptoKey) {\n throw new TypeError(unusableForAlg);\n }\n if (!cached) {\n cache.set(keyObject, { [alg]: cryptoKey });\n }\n else {\n cached[alg] = cryptoKey;\n }\n return cryptoKey;\n};\nexport async function normalizeKey(key, alg) {\n if (key instanceof Uint8Array) {\n return key;\n }\n if (isCryptoKey(key)) {\n return key;\n }\n if (isKeyObject(key)) {\n if (key.type === 'secret') {\n return key.export();\n }\n if ('toCryptoKey' in key && typeof key.toCryptoKey === 'function') {\n try {\n return handleKeyObject(key, alg);\n }\n catch (err) {\n if (err instanceof TypeError) {\n throw err;\n }\n }\n }\n let jwk = key.export({ format: 'jwk' });\n return handleJWK(key, jwk, alg);\n }\n if (isJWK(key)) {\n if (key.k) {\n return decode(key.k);\n }\n return handleJWK(key, key, alg, true);\n }\n throw new Error('unreachable');\n}\n","import { decode as decodeBase64URL } from '../util/base64url.js';\nimport { fromSPKI, fromPKCS8, fromX509 } from '../lib/asn1.js';\nimport { jwkToKey } from '../lib/jwk_to_key.js';\nimport { JOSENotSupported } from '../util/errors.js';\nimport { isObject } from '../lib/type_checks.js';\nexport async function importSPKI(spki, alg, options) {\n if (typeof spki !== 'string' || spki.indexOf('-----BEGIN PUBLIC KEY-----') !== 0) {\n throw new TypeError('\"spki\" must be SPKI formatted string');\n }\n return fromSPKI(spki, alg, options);\n}\nexport async function importX509(x509, alg, options) {\n if (typeof x509 !== 'string' || x509.indexOf('-----BEGIN CERTIFICATE-----') !== 0) {\n throw new TypeError('\"x509\" must be X.509 formatted string');\n }\n return fromX509(x509, alg, options);\n}\nexport async function importPKCS8(pkcs8, alg, options) {\n if (typeof pkcs8 !== 'string' || pkcs8.indexOf('-----BEGIN PRIVATE KEY-----') !== 0) {\n throw new TypeError('\"pkcs8\" must be PKCS#8 formatted string');\n }\n return fromPKCS8(pkcs8, alg, options);\n}\nexport async function importJWK(jwk, alg, options) {\n if (!isObject(jwk)) {\n throw new TypeError('JWK must be an object');\n }\n let ext;\n alg ??= jwk.alg;\n ext ??= options?.extractable ?? jwk.ext;\n switch (jwk.kty) {\n case 'oct':\n if (typeof jwk.k !== 'string' || !jwk.k) {\n throw new TypeError('missing \"k\" (Key Value) Parameter value');\n }\n return decodeBase64URL(jwk.k);\n case 'RSA':\n if ('oth' in jwk && jwk.oth !== undefined) {\n throw new JOSENotSupported('RSA JWK \"oth\" (Other Primes Info) Parameter value is not supported');\n }\n return jwkToKey({ ...jwk, alg, ext });\n case 'AKP': {\n if (typeof jwk.alg !== 'string' || !jwk.alg) {\n throw new TypeError('missing \"alg\" (Algorithm) Parameter value');\n }\n if (alg !== undefined && alg !== jwk.alg) {\n throw new TypeError('JWK alg and alg option value mismatch');\n }\n return jwkToKey({ ...jwk, ext });\n }\n case 'EC':\n case 'OKP':\n return jwkToKey({ ...jwk, alg, ext });\n default:\n throw new JOSENotSupported('Unsupported \"kty\" (Key Type) Parameter value');\n }\n}\n","import { JOSENotSupported, JWEInvalid, JWSInvalid } from '../util/errors.js';\nexport function validateCrit(Err, recognizedDefault, recognizedOption, protectedHeader, joseHeader) {\n if (joseHeader.crit !== undefined && protectedHeader?.crit === undefined) {\n throw new Err('\"crit\" (Critical) Header Parameter MUST be integrity protected');\n }\n if (!protectedHeader || protectedHeader.crit === undefined) {\n return new Set();\n }\n if (!Array.isArray(protectedHeader.crit) ||\n protectedHeader.crit.length === 0 ||\n protectedHeader.crit.some((input) => typeof input !== 'string' || input.length === 0)) {\n throw new Err('\"crit\" (Critical) Header Parameter MUST be an array of non-empty strings when present');\n }\n let recognized;\n if (recognizedOption !== undefined) {\n recognized = new Map([...Object.entries(recognizedOption), ...recognizedDefault.entries()]);\n }\n else {\n recognized = recognizedDefault;\n }\n for (const parameter of protectedHeader.crit) {\n if (!recognized.has(parameter)) {\n throw new JOSENotSupported(`Extension Header Parameter \"${parameter}\" is not recognized`);\n }\n if (joseHeader[parameter] === undefined) {\n throw new Err(`Extension Header Parameter \"${parameter}\" is missing`);\n }\n if (recognized.get(parameter) && protectedHeader[parameter] === undefined) {\n throw new Err(`Extension Header Parameter \"${parameter}\" MUST be integrity protected`);\n }\n }\n return new Set(protectedHeader.crit);\n}\n","export function validateAlgorithms(option, algorithms) {\n if (algorithms !== undefined &&\n (!Array.isArray(algorithms) || algorithms.some((s) => typeof s !== 'string'))) {\n throw new TypeError(`\"${option}\" option must be an array of strings`);\n }\n if (!algorithms) {\n return undefined;\n }\n return new Set(algorithms);\n}\n","import { withAlg as invalidKeyInput } from './invalid_key_input.js';\nimport { isKeyLike } from './is_key_like.js';\nimport * as jwk from './type_checks.js';\nconst tag = (key) => key?.[Symbol.toStringTag];\nconst jwkMatchesOp = (alg, key, usage) => {\n if (key.use !== undefined) {\n let expected;\n switch (usage) {\n case 'sign':\n case 'verify':\n expected = 'sig';\n break;\n case 'encrypt':\n case 'decrypt':\n expected = 'enc';\n break;\n }\n if (key.use !== expected) {\n throw new TypeError(`Invalid key for this operation, its \"use\" must be \"${expected}\" when present`);\n }\n }\n if (key.alg !== undefined && key.alg !== alg) {\n throw new TypeError(`Invalid key for this operation, its \"alg\" must be \"${alg}\" when present`);\n }\n if (Array.isArray(key.key_ops)) {\n let expectedKeyOp;\n switch (true) {\n case usage === 'sign' || usage === 'verify':\n case alg === 'dir':\n case alg.includes('CBC-HS'):\n expectedKeyOp = usage;\n break;\n case alg.startsWith('PBES2'):\n expectedKeyOp = 'deriveBits';\n break;\n case /^A\\d{3}(?:GCM)?(?:KW)?$/.test(alg):\n if (!alg.includes('GCM') && alg.endsWith('KW')) {\n expectedKeyOp = usage === 'encrypt' ? 'wrapKey' : 'unwrapKey';\n }\n else {\n expectedKeyOp = usage;\n }\n break;\n case usage === 'encrypt' && alg.startsWith('RSA'):\n expectedKeyOp = 'wrapKey';\n break;\n case usage === 'decrypt':\n expectedKeyOp = alg.startsWith('RSA') ? 'unwrapKey' : 'deriveBits';\n break;\n }\n if (expectedKeyOp && key.key_ops?.includes?.(expectedKeyOp) === false) {\n throw new TypeError(`Invalid key for this operation, its \"key_ops\" must include \"${expectedKeyOp}\" when present`);\n }\n }\n return true;\n};\nconst symmetricTypeCheck = (alg, key, usage) => {\n if (key instanceof Uint8Array)\n return;\n if (jwk.isJWK(key)) {\n if (jwk.isSecretJWK(key) && jwkMatchesOp(alg, key, usage))\n return;\n throw new TypeError(`JSON Web Key for symmetric algorithms must have JWK \"kty\" (Key Type) equal to \"oct\" and the JWK \"k\" (Key Value) present`);\n }\n if (!isKeyLike(key)) {\n throw new TypeError(invalidKeyInput(alg, key, 'CryptoKey', 'KeyObject', 'JSON Web Key', 'Uint8Array'));\n }\n if (key.type !== 'secret') {\n throw new TypeError(`${tag(key)} instances for symmetric algorithms must be of type \"secret\"`);\n }\n};\nconst asymmetricTypeCheck = (alg, key, usage) => {\n if (jwk.isJWK(key)) {\n switch (usage) {\n case 'decrypt':\n case 'sign':\n if (jwk.isPrivateJWK(key) && jwkMatchesOp(alg, key, usage))\n return;\n throw new TypeError(`JSON Web Key for this operation must be a private JWK`);\n case 'encrypt':\n case 'verify':\n if (jwk.isPublicJWK(key) && jwkMatchesOp(alg, key, usage))\n return;\n throw new TypeError(`JSON Web Key for this operation must be a public JWK`);\n }\n }\n if (!isKeyLike(key)) {\n throw new TypeError(invalidKeyInput(alg, key, 'CryptoKey', 'KeyObject', 'JSON Web Key'));\n }\n if (key.type === 'secret') {\n throw new TypeError(`${tag(key)} instances for asymmetric algorithms must not be of type \"secret\"`);\n }\n if (key.type === 'public') {\n switch (usage) {\n case 'sign':\n throw new TypeError(`${tag(key)} instances for asymmetric algorithm signing must be of type \"private\"`);\n case 'decrypt':\n throw new TypeError(`${tag(key)} instances for asymmetric algorithm decryption must be of type \"private\"`);\n }\n }\n if (key.type === 'private') {\n switch (usage) {\n case 'verify':\n throw new TypeError(`${tag(key)} instances for asymmetric algorithm verifying must be of type \"public\"`);\n case 'encrypt':\n throw new TypeError(`${tag(key)} instances for asymmetric algorithm encryption must be of type \"public\"`);\n }\n }\n};\nexport function checkKeyType(alg, key, usage) {\n switch (alg.substring(0, 2)) {\n case 'A1':\n case 'A2':\n case 'di':\n case 'HS':\n case 'PB':\n symmetricTypeCheck(alg, key, usage);\n break;\n default:\n asymmetricTypeCheck(alg, key, usage);\n }\n}\n","import { decode as b64u } from '../../util/base64url.js';\nimport { verify } from '../../lib/signing.js';\nimport { JOSEAlgNotAllowed, JWSInvalid, JWSSignatureVerificationFailed } from '../../util/errors.js';\nimport { concat, encoder, decoder, encode } from '../../lib/buffer_utils.js';\nimport { decodeBase64url } from '../../lib/helpers.js';\nimport { isDisjoint } from '../../lib/type_checks.js';\nimport { isObject } from '../../lib/type_checks.js';\nimport { checkKeyType } from '../../lib/check_key_type.js';\nimport { validateCrit } from '../../lib/validate_crit.js';\nimport { validateAlgorithms } from '../../lib/validate_algorithms.js';\nimport { normalizeKey } from '../../lib/normalize_key.js';\nexport async function flattenedVerify(jws, key, options) {\n if (!isObject(jws)) {\n throw new JWSInvalid('Flattened JWS must be an object');\n }\n if (jws.protected === undefined && jws.header === undefined) {\n throw new JWSInvalid('Flattened JWS must have either of the \"protected\" or \"header\" members');\n }\n if (jws.protected !== undefined && typeof jws.protected !== 'string') {\n throw new JWSInvalid('JWS Protected Header incorrect type');\n }\n if (jws.payload === undefined) {\n throw new JWSInvalid('JWS Payload missing');\n }\n if (typeof jws.signature !== 'string') {\n throw new JWSInvalid('JWS Signature missing or incorrect type');\n }\n if (jws.header !== undefined && !isObject(jws.header)) {\n throw new JWSInvalid('JWS Unprotected Header incorrect type');\n }\n let parsedProt = {};\n if (jws.protected) {\n try {\n const protectedHeader = b64u(jws.protected);\n parsedProt = JSON.parse(decoder.decode(protectedHeader));\n }\n catch {\n throw new JWSInvalid('JWS Protected Header is invalid');\n }\n }\n if (!isDisjoint(parsedProt, jws.header)) {\n throw new JWSInvalid('JWS Protected and JWS Unprotected Header Parameter names must be disjoint');\n }\n const joseHeader = {\n ...parsedProt,\n ...jws.header,\n };\n const extensions = validateCrit(JWSInvalid, new Map([['b64', true]]), options?.crit, parsedProt, joseHeader);\n let b64 = true;\n if (extensions.has('b64')) {\n b64 = parsedProt.b64;\n if (typeof b64 !== 'boolean') {\n throw new JWSInvalid('The \"b64\" (base64url-encode payload) Header Parameter must be a boolean');\n }\n }\n const { alg } = joseHeader;\n if (typeof alg !== 'string' || !alg) {\n throw new JWSInvalid('JWS \"alg\" (Algorithm) Header Parameter missing or invalid');\n }\n const algorithms = options && validateAlgorithms('algorithms', options.algorithms);\n if (algorithms && !algorithms.has(alg)) {\n throw new JOSEAlgNotAllowed('\"alg\" (Algorithm) Header Parameter value not allowed');\n }\n if (b64) {\n if (typeof jws.payload !== 'string') {\n throw new JWSInvalid('JWS Payload must be a string');\n }\n }\n else if (typeof jws.payload !== 'string' && !(jws.payload instanceof Uint8Array)) {\n throw new JWSInvalid('JWS Payload must be a string or an Uint8Array instance');\n }\n let resolvedKey = false;\n if (typeof key === 'function') {\n key = await key(parsedProt, jws);\n resolvedKey = true;\n }\n checkKeyType(alg, key, 'verify');\n const data = concat(jws.protected !== undefined ? encode(jws.protected) : new Uint8Array(), encode('.'), typeof jws.payload === 'string'\n ? b64\n ? encode(jws.payload)\n : encoder.encode(jws.payload)\n : jws.payload);\n const signature = decodeBase64url(jws.signature, 'signature', JWSInvalid);\n const k = await normalizeKey(key, alg);\n const verified = await verify(alg, k, signature, data);\n if (!verified) {\n throw new JWSSignatureVerificationFailed();\n }\n let payload;\n if (b64) {\n payload = decodeBase64url(jws.payload, 'payload', JWSInvalid);\n }\n else if (typeof jws.payload === 'string') {\n payload = encoder.encode(jws.payload);\n }\n else {\n payload = jws.payload;\n }\n const result = { payload };\n if (jws.protected !== undefined) {\n result.protectedHeader = parsedProt;\n }\n if (jws.header !== undefined) {\n result.unprotectedHeader = jws.header;\n }\n if (resolvedKey) {\n return { ...result, key: k };\n }\n return result;\n}\n","import { flattenedVerify } from '../flattened/verify.js';\nimport { JWSInvalid } from '../../util/errors.js';\nimport { decoder } from '../../lib/buffer_utils.js';\nexport async function compactVerify(jws, key, options) {\n if (jws instanceof Uint8Array) {\n jws = decoder.decode(jws);\n }\n if (typeof jws !== 'string') {\n throw new JWSInvalid('Compact JWS must be a string or Uint8Array');\n }\n const { 0: protectedHeader, 1: payload, 2: signature, length } = jws.split('.');\n if (length !== 3) {\n throw new JWSInvalid('Invalid Compact JWS');\n }\n const verified = await flattenedVerify({ payload, protected: protectedHeader, signature }, key, options);\n const result = { payload: verified.payload, protectedHeader: verified.protectedHeader };\n if (typeof key === 'function') {\n return { ...result, key: verified.key };\n }\n return result;\n}\n","import { JWTClaimValidationFailed, JWTExpired, JWTInvalid } from '../util/errors.js';\nimport { encoder, decoder } from './buffer_utils.js';\nimport { isObject } from './type_checks.js';\nconst epoch = (date) => Math.floor(date.getTime() / 1000);\nconst minute = 60;\nconst hour = minute * 60;\nconst day = hour * 24;\nconst week = day * 7;\nconst year = day * 365.25;\nconst REGEX = /^(\\+|\\-)? ?(\\d+|\\d+\\.\\d+) ?(seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)(?: (ago|from now))?$/i;\nexport function secs(str) {\n const matched = REGEX.exec(str);\n if (!matched || (matched[4] && matched[1])) {\n throw new TypeError('Invalid time period format');\n }\n const value = parseFloat(matched[2]);\n const unit = matched[3].toLowerCase();\n let numericDate;\n switch (unit) {\n case 'sec':\n case 'secs':\n case 'second':\n case 'seconds':\n case 's':\n numericDate = Math.round(value);\n break;\n case 'minute':\n case 'minutes':\n case 'min':\n case 'mins':\n case 'm':\n numericDate = Math.round(value * minute);\n break;\n case 'hour':\n case 'hours':\n case 'hr':\n case 'hrs':\n case 'h':\n numericDate = Math.round(value * hour);\n break;\n case 'day':\n case 'days':\n case 'd':\n numericDate = Math.round(value * day);\n break;\n case 'week':\n case 'weeks':\n case 'w':\n numericDate = Math.round(value * week);\n break;\n default:\n numericDate = Math.round(value * year);\n break;\n }\n if (matched[1] === '-' || matched[4] === 'ago') {\n return -numericDate;\n }\n return numericDate;\n}\nfunction validateInput(label, input) {\n if (!Number.isFinite(input)) {\n throw new TypeError(`Invalid ${label} input`);\n }\n return input;\n}\nconst normalizeTyp = (value) => {\n if (value.includes('/')) {\n return value.toLowerCase();\n }\n return `application/${value.toLowerCase()}`;\n};\nconst checkAudiencePresence = (audPayload, audOption) => {\n if (typeof audPayload === 'string') {\n return audOption.includes(audPayload);\n }\n if (Array.isArray(audPayload)) {\n return audOption.some(Set.prototype.has.bind(new Set(audPayload)));\n }\n return false;\n};\nexport function validateClaimsSet(protectedHeader, encodedPayload, options = {}) {\n let payload;\n try {\n payload = JSON.parse(decoder.decode(encodedPayload));\n }\n catch {\n }\n if (!isObject(payload)) {\n throw new JWTInvalid('JWT Claims Set must be a top-level JSON object');\n }\n const { typ } = options;\n if (typ &&\n (typeof protectedHeader.typ !== 'string' ||\n normalizeTyp(protectedHeader.typ) !== normalizeTyp(typ))) {\n throw new JWTClaimValidationFailed('unexpected \"typ\" JWT header value', payload, 'typ', 'check_failed');\n }\n const { requiredClaims = [], issuer, subject, audience, maxTokenAge } = options;\n const presenceCheck = [...requiredClaims];\n if (maxTokenAge !== undefined)\n presenceCheck.push('iat');\n if (audience !== undefined)\n presenceCheck.push('aud');\n if (subject !== undefined)\n presenceCheck.push('sub');\n if (issuer !== undefined)\n presenceCheck.push('iss');\n for (const claim of new Set(presenceCheck.reverse())) {\n if (!(claim in payload)) {\n throw new JWTClaimValidationFailed(`missing required \"${claim}\" claim`, payload, claim, 'missing');\n }\n }\n if (issuer &&\n !(Array.isArray(issuer) ? issuer : [issuer]).includes(payload.iss)) {\n throw new JWTClaimValidationFailed('unexpected \"iss\" claim value', payload, 'iss', 'check_failed');\n }\n if (subject && payload.sub !== subject) {\n throw new JWTClaimValidationFailed('unexpected \"sub\" claim value', payload, 'sub', 'check_failed');\n }\n if (audience &&\n !checkAudiencePresence(payload.aud, typeof audience === 'string' ? [audience] : audience)) {\n throw new JWTClaimValidationFailed('unexpected \"aud\" claim value', payload, 'aud', 'check_failed');\n }\n let tolerance;\n switch (typeof options.clockTolerance) {\n case 'string':\n tolerance = secs(options.clockTolerance);\n break;\n case 'number':\n tolerance = options.clockTolerance;\n break;\n case 'undefined':\n tolerance = 0;\n break;\n default:\n throw new TypeError('Invalid clockTolerance option type');\n }\n const { currentDate } = options;\n const now = epoch(currentDate || new Date());\n if ((payload.iat !== undefined || maxTokenAge) && typeof payload.iat !== 'number') {\n throw new JWTClaimValidationFailed('\"iat\" claim must be a number', payload, 'iat', 'invalid');\n }\n if (payload.nbf !== undefined) {\n if (typeof payload.nbf !== 'number') {\n throw new JWTClaimValidationFailed('\"nbf\" claim must be a number', payload, 'nbf', 'invalid');\n }\n if (payload.nbf > now + tolerance) {\n throw new JWTClaimValidationFailed('\"nbf\" claim timestamp check failed', payload, 'nbf', 'check_failed');\n }\n }\n if (payload.exp !== undefined) {\n if (typeof payload.exp !== 'number') {\n throw new JWTClaimValidationFailed('\"exp\" claim must be a number', payload, 'exp', 'invalid');\n }\n if (payload.exp <= now - tolerance) {\n throw new JWTExpired('\"exp\" claim timestamp check failed', payload, 'exp', 'check_failed');\n }\n }\n if (maxTokenAge) {\n const age = now - payload.iat;\n const max = typeof maxTokenAge === 'number' ? maxTokenAge : secs(maxTokenAge);\n if (age - tolerance > max) {\n throw new JWTExpired('\"iat\" claim timestamp check failed (too far in the past)', payload, 'iat', 'check_failed');\n }\n if (age < 0 - tolerance) {\n throw new JWTClaimValidationFailed('\"iat\" claim timestamp check failed (it should be in the past)', payload, 'iat', 'check_failed');\n }\n }\n return payload;\n}\nexport class JWTClaimsBuilder {\n #payload;\n constructor(payload) {\n if (!isObject(payload)) {\n throw new TypeError('JWT Claims Set MUST be an object');\n }\n this.#payload = structuredClone(payload);\n }\n data() {\n return encoder.encode(JSON.stringify(this.#payload));\n }\n get iss() {\n return this.#payload.iss;\n }\n set iss(value) {\n this.#payload.iss = value;\n }\n get sub() {\n return this.#payload.sub;\n }\n set sub(value) {\n this.#payload.sub = value;\n }\n get aud() {\n return this.#payload.aud;\n }\n set aud(value) {\n this.#payload.aud = value;\n }\n set jti(value) {\n this.#payload.jti = value;\n }\n set nbf(value) {\n if (typeof value === 'number') {\n this.#payload.nbf = validateInput('setNotBefore', value);\n }\n else if (value instanceof Date) {\n this.#payload.nbf = validateInput('setNotBefore', epoch(value));\n }\n else {\n this.#payload.nbf = epoch(new Date()) + secs(value);\n }\n }\n set exp(value) {\n if (typeof value === 'number') {\n this.#payload.exp = validateInput('setExpirationTime', value);\n }\n else if (value instanceof Date) {\n this.#payload.exp = validateInput('setExpirationTime', epoch(value));\n }\n else {\n this.#payload.exp = epoch(new Date()) + secs(value);\n }\n }\n set iat(value) {\n if (value === undefined) {\n this.#payload.iat = epoch(new Date());\n }\n else if (value instanceof Date) {\n this.#payload.iat = validateInput('setIssuedAt', epoch(value));\n }\n else if (typeof value === 'string') {\n this.#payload.iat = validateInput('setIssuedAt', epoch(new Date()) + secs(value));\n }\n else {\n this.#payload.iat = validateInput('setIssuedAt', value);\n }\n }\n}\n","import { compactVerify } from '../jws/compact/verify.js';\nimport { validateClaimsSet } from '../lib/jwt_claims_set.js';\nimport { JWTInvalid } from '../util/errors.js';\nexport async function jwtVerify(jwt, key, options) {\n const verified = await compactVerify(jwt, key, options);\n if (verified.protectedHeader.crit?.includes('b64') && verified.protectedHeader.b64 === false) {\n throw new JWTInvalid('JWTs MUST NOT use unencoded payload');\n }\n const payload = validateClaimsSet(verified.protectedHeader, verified.payload, options);\n const result = { payload, protectedHeader: verified.protectedHeader };\n if (typeof key === 'function') {\n return { ...result, key: verified.key };\n }\n return result;\n}\n","import { encode as b64u } from '../../util/base64url.js';\nimport { sign } from '../../lib/signing.js';\nimport { isDisjoint } from '../../lib/type_checks.js';\nimport { JWSInvalid } from '../../util/errors.js';\nimport { concat, encode } from '../../lib/buffer_utils.js';\nimport { checkKeyType } from '../../lib/check_key_type.js';\nimport { validateCrit } from '../../lib/validate_crit.js';\nimport { normalizeKey } from '../../lib/normalize_key.js';\nimport { assertNotSet } from '../../lib/helpers.js';\nexport class FlattenedSign {\n #payload;\n #protectedHeader;\n #unprotectedHeader;\n constructor(payload) {\n if (!(payload instanceof Uint8Array)) {\n throw new TypeError('payload must be an instance of Uint8Array');\n }\n this.#payload = payload;\n }\n setProtectedHeader(protectedHeader) {\n assertNotSet(this.#protectedHeader, 'setProtectedHeader');\n this.#protectedHeader = protectedHeader;\n return this;\n }\n setUnprotectedHeader(unprotectedHeader) {\n assertNotSet(this.#unprotectedHeader, 'setUnprotectedHeader');\n this.#unprotectedHeader = unprotectedHeader;\n return this;\n }\n async sign(key, options) {\n if (!this.#protectedHeader && !this.#unprotectedHeader) {\n throw new JWSInvalid('either setProtectedHeader or setUnprotectedHeader must be called before #sign()');\n }\n if (!isDisjoint(this.#protectedHeader, this.#unprotectedHeader)) {\n throw new JWSInvalid('JWS Protected and JWS Unprotected Header Parameter names must be disjoint');\n }\n const joseHeader = {\n ...this.#protectedHeader,\n ...this.#unprotectedHeader,\n };\n const extensions = validateCrit(JWSInvalid, new Map([['b64', true]]), options?.crit, this.#protectedHeader, joseHeader);\n let b64 = true;\n if (extensions.has('b64')) {\n b64 = this.#protectedHeader.b64;\n if (typeof b64 !== 'boolean') {\n throw new JWSInvalid('The \"b64\" (base64url-encode payload) Header Parameter must be a boolean');\n }\n }\n const { alg } = joseHeader;\n if (typeof alg !== 'string' || !alg) {\n throw new JWSInvalid('JWS \"alg\" (Algorithm) Header Parameter missing or invalid');\n }\n checkKeyType(alg, key, 'sign');\n let payloadS;\n let payloadB;\n if (b64) {\n payloadS = b64u(this.#payload);\n payloadB = encode(payloadS);\n }\n else {\n payloadB = this.#payload;\n payloadS = '';\n }\n let protectedHeaderString;\n let protectedHeaderBytes;\n if (this.#protectedHeader) {\n protectedHeaderString = b64u(JSON.stringify(this.#protectedHeader));\n protectedHeaderBytes = encode(protectedHeaderString);\n }\n else {\n protectedHeaderString = '';\n protectedHeaderBytes = new Uint8Array();\n }\n const data = concat(protectedHeaderBytes, encode('.'), payloadB);\n const k = await normalizeKey(key, alg);\n const signature = await sign(alg, k, data);\n const jws = {\n signature: b64u(signature),\n payload: payloadS,\n };\n if (this.#unprotectedHeader) {\n jws.header = this.#unprotectedHeader;\n }\n if (this.#protectedHeader) {\n jws.protected = protectedHeaderString;\n }\n return jws;\n }\n}\n","import { FlattenedSign } from '../flattened/sign.js';\nexport class CompactSign {\n #flattened;\n constructor(payload) {\n this.#flattened = new FlattenedSign(payload);\n }\n setProtectedHeader(protectedHeader) {\n this.#flattened.setProtectedHeader(protectedHeader);\n return this;\n }\n async sign(key, options) {\n const jws = await this.#flattened.sign(key, options);\n if (jws.payload === undefined) {\n throw new TypeError('use the flattened module for creating JWS with b64: false');\n }\n return `${jws.protected}.${jws.payload}.${jws.signature}`;\n }\n}\n","import { CompactSign } from '../jws/compact/sign.js';\nimport { JWTInvalid } from '../util/errors.js';\nimport { JWTClaimsBuilder } from '../lib/jwt_claims_set.js';\nexport class SignJWT {\n #protectedHeader;\n #jwt;\n constructor(payload = {}) {\n this.#jwt = new JWTClaimsBuilder(payload);\n }\n setIssuer(issuer) {\n this.#jwt.iss = issuer;\n return this;\n }\n setSubject(subject) {\n this.#jwt.sub = subject;\n return this;\n }\n setAudience(audience) {\n this.#jwt.aud = audience;\n return this;\n }\n setJti(jwtId) {\n this.#jwt.jti = jwtId;\n return this;\n }\n setNotBefore(input) {\n this.#jwt.nbf = input;\n return this;\n }\n setExpirationTime(input) {\n this.#jwt.exp = input;\n return this;\n }\n setIssuedAt(input) {\n this.#jwt.iat = input;\n return this;\n }\n setProtectedHeader(protectedHeader) {\n this.#protectedHeader = protectedHeader;\n return this;\n }\n async sign(key, options) {\n const sig = new CompactSign(this.#jwt.data());\n sig.setProtectedHeader(this.#protectedHeader);\n if (Array.isArray(this.#protectedHeader?.crit) &&\n this.#protectedHeader.crit.includes('b64') &&\n this.#protectedHeader.b64 === false) {\n throw new JWTInvalid('JWTs MUST NOT use unencoded payload');\n }\n return sig.sign(key, options);\n }\n}\n","import { importJWK } from '../key/import.js';\nimport { JWKSInvalid, JOSENotSupported, JWKSNoMatchingKey, JWKSMultipleMatchingKeys, } from '../util/errors.js';\nimport { isObject } from '../lib/type_checks.js';\nfunction getKtyFromAlg(alg) {\n switch (typeof alg === 'string' && alg.slice(0, 2)) {\n case 'RS':\n case 'PS':\n return 'RSA';\n case 'ES':\n return 'EC';\n case 'Ed':\n return 'OKP';\n case 'ML':\n return 'AKP';\n default:\n throw new JOSENotSupported('Unsupported \"alg\" value for a JSON Web Key Set');\n }\n}\nfunction isJWKSLike(jwks) {\n return (jwks &&\n typeof jwks === 'object' &&\n Array.isArray(jwks.keys) &&\n jwks.keys.every(isJWKLike));\n}\nfunction isJWKLike(key) {\n return isObject(key);\n}\nclass LocalJWKSet {\n #jwks;\n #cached = new WeakMap();\n constructor(jwks) {\n if (!isJWKSLike(jwks)) {\n throw new JWKSInvalid('JSON Web Key Set malformed');\n }\n this.#jwks = structuredClone(jwks);\n }\n jwks() {\n return this.#jwks;\n }\n async getKey(protectedHeader, token) {\n const { alg, kid } = { ...protectedHeader, ...token?.header };\n const kty = getKtyFromAlg(alg);\n const candidates = this.#jwks.keys.filter((jwk) => {\n let candidate = kty === jwk.kty;\n if (candidate && typeof kid === 'string') {\n candidate = kid === jwk.kid;\n }\n if (candidate && (typeof jwk.alg === 'string' || kty === 'AKP')) {\n candidate = alg === jwk.alg;\n }\n if (candidate && typeof jwk.use === 'string') {\n candidate = jwk.use === 'sig';\n }\n if (candidate && Array.isArray(jwk.key_ops)) {\n candidate = jwk.key_ops.includes('verify');\n }\n if (candidate) {\n switch (alg) {\n case 'ES256':\n candidate = jwk.crv === 'P-256';\n break;\n case 'ES384':\n candidate = jwk.crv === 'P-384';\n break;\n case 'ES512':\n candidate = jwk.crv === 'P-521';\n break;\n case 'Ed25519':\n case 'EdDSA':\n candidate = jwk.crv === 'Ed25519';\n break;\n }\n }\n return candidate;\n });\n const { 0: jwk, length } = candidates;\n if (length === 0) {\n throw new JWKSNoMatchingKey();\n }\n if (length !== 1) {\n const error = new JWKSMultipleMatchingKeys();\n const _cached = this.#cached;\n error[Symbol.asyncIterator] = async function* () {\n for (const jwk of candidates) {\n try {\n yield await importWithAlgCache(_cached, jwk, alg);\n }\n catch { }\n }\n };\n throw error;\n }\n return importWithAlgCache(this.#cached, jwk, alg);\n }\n}\nasync function importWithAlgCache(cache, jwk, alg) {\n const cached = cache.get(jwk) || cache.set(jwk, {}).get(jwk);\n if (cached[alg] === undefined) {\n const key = await importJWK({ ...jwk, ext: true }, alg);\n if (key instanceof Uint8Array || key.type !== 'public') {\n throw new JWKSInvalid('JSON Web Key Set members must be public keys');\n }\n cached[alg] = key;\n }\n return cached[alg];\n}\nexport function createLocalJWKSet(jwks) {\n const set = new LocalJWKSet(jwks);\n const localJWKSet = async (protectedHeader, token) => set.getKey(protectedHeader, token);\n Object.defineProperties(localJWKSet, {\n jwks: {\n value: () => structuredClone(set.jwks()),\n enumerable: false,\n configurable: false,\n writable: false,\n },\n });\n return localJWKSet;\n}\n","import { JOSEError, JWKSNoMatchingKey, JWKSTimeout } from '../util/errors.js';\nimport { createLocalJWKSet } from './local.js';\nimport { isObject } from '../lib/type_checks.js';\nfunction isCloudflareWorkers() {\n return (typeof WebSocketPair !== 'undefined' ||\n (typeof navigator !== 'undefined' && navigator.userAgent === 'Cloudflare-Workers') ||\n (typeof EdgeRuntime !== 'undefined' && EdgeRuntime === 'vercel'));\n}\nlet USER_AGENT;\nif (typeof navigator === 'undefined' || !navigator.userAgent?.startsWith?.('Mozilla/5.0 ')) {\n const NAME = 'jose';\n const VERSION = 'v6.2.3';\n USER_AGENT = `${NAME}/${VERSION}`;\n}\nexport const customFetch = Symbol();\nasync function fetchJwks(url, headers, signal, fetchImpl = fetch) {\n const response = await fetchImpl(url, {\n method: 'GET',\n signal,\n redirect: 'manual',\n headers,\n }).catch((err) => {\n if (err.name === 'TimeoutError') {\n throw new JWKSTimeout();\n }\n throw err;\n });\n if (response.status !== 200) {\n throw new JOSEError('Expected 200 OK from the JSON Web Key Set HTTP response');\n }\n try {\n return await response.json();\n }\n catch {\n throw new JOSEError('Failed to parse the JSON Web Key Set HTTP response as JSON');\n }\n}\nexport const jwksCache = Symbol();\nfunction isFreshJwksCache(input, cacheMaxAge) {\n if (typeof input !== 'object' || input === null) {\n return false;\n }\n if (!('uat' in input) || typeof input.uat !== 'number' || Date.now() - input.uat >= cacheMaxAge) {\n return false;\n }\n if (!('jwks' in input) ||\n !isObject(input.jwks) ||\n !Array.isArray(input.jwks.keys) ||\n !Array.prototype.every.call(input.jwks.keys, isObject)) {\n return false;\n }\n return true;\n}\nclass RemoteJWKSet {\n #url;\n #timeoutDuration;\n #cooldownDuration;\n #cacheMaxAge;\n #jwksTimestamp;\n #pendingFetch;\n #headers;\n #customFetch;\n #local;\n #cache;\n constructor(url, options) {\n if (!(url instanceof URL)) {\n throw new TypeError('url must be an instance of URL');\n }\n this.#url = new URL(url.href);\n this.#timeoutDuration =\n typeof options?.timeoutDuration === 'number' ? options?.timeoutDuration : 5000;\n this.#cooldownDuration =\n typeof options?.cooldownDuration === 'number' ? options?.cooldownDuration : 30000;\n this.#cacheMaxAge = typeof options?.cacheMaxAge === 'number' ? options?.cacheMaxAge : 600000;\n this.#headers = new Headers(options?.headers);\n if (USER_AGENT && !this.#headers.has('User-Agent')) {\n this.#headers.set('User-Agent', USER_AGENT);\n }\n if (!this.#headers.has('accept')) {\n this.#headers.set('accept', 'application/json');\n this.#headers.append('accept', 'application/jwk-set+json');\n }\n this.#customFetch = options?.[customFetch];\n if (options?.[jwksCache] !== undefined) {\n this.#cache = options?.[jwksCache];\n if (isFreshJwksCache(options?.[jwksCache], this.#cacheMaxAge)) {\n this.#jwksTimestamp = this.#cache.uat;\n this.#local = createLocalJWKSet(this.#cache.jwks);\n }\n }\n }\n pendingFetch() {\n return !!this.#pendingFetch;\n }\n coolingDown() {\n return typeof this.#jwksTimestamp === 'number'\n ? Date.now() < this.#jwksTimestamp + this.#cooldownDuration\n : false;\n }\n fresh() {\n return typeof this.#jwksTimestamp === 'number'\n ? Date.now() < this.#jwksTimestamp + this.#cacheMaxAge\n : false;\n }\n jwks() {\n return this.#local?.jwks();\n }\n async getKey(protectedHeader, token) {\n if (!this.#local || !this.fresh()) {\n await this.reload();\n }\n try {\n return await this.#local(protectedHeader, token);\n }\n catch (err) {\n if (err instanceof JWKSNoMatchingKey) {\n if (this.coolingDown() === false) {\n await this.reload();\n return this.#local(protectedHeader, token);\n }\n }\n throw err;\n }\n }\n async reload() {\n if (this.#pendingFetch && isCloudflareWorkers()) {\n this.#pendingFetch = undefined;\n }\n this.#pendingFetch ||= fetchJwks(this.#url.href, this.#headers, AbortSignal.timeout(this.#timeoutDuration), this.#customFetch)\n .then((json) => {\n this.#local = createLocalJWKSet(json);\n if (this.#cache) {\n this.#cache.uat = Date.now();\n this.#cache.jwks = json;\n }\n this.#jwksTimestamp = Date.now();\n this.#pendingFetch = undefined;\n })\n .catch((err) => {\n this.#pendingFetch = undefined;\n throw err;\n });\n await this.#pendingFetch;\n }\n}\nexport function createRemoteJWKSet(url, options) {\n const set = new RemoteJWKSet(url, options);\n const remoteJWKSet = async (protectedHeader, token) => set.getKey(protectedHeader, token);\n Object.defineProperties(remoteJWKSet, {\n coolingDown: {\n get: () => set.coolingDown(),\n enumerable: true,\n configurable: false,\n },\n fresh: {\n get: () => set.fresh(),\n enumerable: true,\n configurable: false,\n },\n reload: {\n value: () => set.reload(),\n enumerable: true,\n configurable: false,\n writable: false,\n },\n reloading: {\n get: () => set.pendingFetch(),\n enumerable: true,\n configurable: false,\n },\n jwks: {\n value: () => set.jwks(),\n enumerable: true,\n configurable: false,\n writable: false,\n },\n });\n return remoteJWKSet;\n}\n","export { compactDecrypt } from './jwe/compact/decrypt.js';\nexport { flattenedDecrypt } from './jwe/flattened/decrypt.js';\nexport { generalDecrypt } from './jwe/general/decrypt.js';\nexport { GeneralEncrypt } from './jwe/general/encrypt.js';\nexport { compactVerify } from './jws/compact/verify.js';\nexport { flattenedVerify } from './jws/flattened/verify.js';\nexport { generalVerify } from './jws/general/verify.js';\nexport { jwtVerify } from './jwt/verify.js';\nexport { jwtDecrypt } from './jwt/decrypt.js';\nexport { CompactEncrypt } from './jwe/compact/encrypt.js';\nexport { FlattenedEncrypt } from './jwe/flattened/encrypt.js';\nexport { CompactSign } from './jws/compact/sign.js';\nexport { FlattenedSign } from './jws/flattened/sign.js';\nexport { GeneralSign } from './jws/general/sign.js';\nexport { SignJWT } from './jwt/sign.js';\nexport { EncryptJWT } from './jwt/encrypt.js';\nexport { calculateJwkThumbprint, calculateJwkThumbprintUri } from './jwk/thumbprint.js';\nexport { EmbeddedJWK } from './jwk/embedded.js';\nexport { createLocalJWKSet } from './jwks/local.js';\nexport { createRemoteJWKSet, jwksCache, customFetch } from './jwks/remote.js';\nexport { UnsecuredJWT } from './jwt/unsecured.js';\nexport { exportPKCS8, exportSPKI, exportJWK } from './key/export.js';\nexport { importSPKI, importPKCS8, importX509, importJWK } from './key/import.js';\nexport { decodeProtectedHeader } from './util/decode_protected_header.js';\nexport { decodeJwt } from './util/decode_jwt.js';\nimport * as errors from './util/errors.js';\nexport { errors };\nexport { generateKeyPair } from './key/generate_key_pair.js';\nexport { generateSecret } from './key/generate_secret.js';\nimport * as base64url from './util/base64url.js';\nexport { base64url };\nexport const cryptoRuntime = 'WebCryptoAPI';\n","/**\n * OidcLoginService - OAuth2/OIDC authorization-code login with PKCE.\n *\n * The service is provider-generic and works with standards-compliant IdPs such\n * as Kanidm, Dex, Keycloak, Authentik, Google, and others. It handles provider\n * discovery, authorization URL construction, token exchange, ID token\n * verification, and SMRT User/Profile creation.\n *\n * @packageDocumentation\n */\n\nimport { getPackageConfig } from '@happyvertical/smrt-config';\nimport type { SmrtClassOptions } from '@happyvertical/smrt-core';\nimport {\n createRemoteJWKSet,\n customFetch,\n type JWTPayload,\n type JWTVerifyGetKey,\n jwtVerify,\n} from 'jose';\nimport {\n type OidcClaims,\n type OidcIdentityResult,\n type OidcProfileOwnerAuthorizer,\n type OidcProfileResolver,\n UserCollection,\n} from '../collections/UserCollection.js';\n\nconst DEFAULT_SCOPES = ['openid', 'profile', 'email'] as const;\nconst DEFAULT_CLOCK_TOLERANCE_SECONDS = 15;\nconst DEFAULT_METADATA_CACHE_TTL_MS = 5 * 60 * 1000;\nconst WELL_KNOWN_PATH = '/.well-known/openid-configuration';\n\nexport type OidcProviderKind = 'kanidm' | 'dex' | 'generic';\n\nexport type OidcTokenEndpointAuthMethod =\n | 'client_secret_basic'\n | 'client_secret_post'\n | 'none';\n\nexport interface OidcProviderConfig {\n /**\n * Optional preset label for documentation and UI.\n *\n * Kanidm and Dex are standards-compliant OIDC providers, so the preset does\n * not change protocol behavior; it lets apps name their intent clearly.\n */\n kind?: OidcProviderKind;\n /** OIDC issuer URL, e.g. https://id.example.com/dex */\n issuer: string;\n /** OAuth2/OIDC client ID registered with the provider */\n clientId: string;\n /** OAuth2/OIDC client secret for confidential clients */\n clientSecret?: string;\n /**\n * Redirect URI registered with the provider.\n *\n * SvelteKit helpers can derive this from the current request when omitted.\n */\n redirectUri?: string;\n /** Scopes to request. Defaults to openid profile email. */\n scopes?: string[];\n /** Override the discovery document URL when needed. */\n discoveryUrl?: string;\n /** Additional authorization request parameters. */\n authorizationParams?: Record<string, string | number | boolean | undefined>;\n /**\n * Client authentication method for the token endpoint.\n *\n * Defaults to client_secret_basic when clientSecret exists, otherwise none.\n */\n tokenEndpointAuthMethod?: OidcTokenEndpointAuthMethod;\n}\n\nexport interface OidcProviderResolutionOptions {\n defaultProvider?: string;\n providers?: Record<string, OidcProviderConfig>;\n}\n\nexport interface UsersOidcConfig extends OidcProviderResolutionOptions {\n /** Seconds before login transaction cookies expire. */\n transactionTtl?: number;\n}\n\nexport interface OidcProviderResolution {\n provider: OidcProviderConfig;\n providerName: string;\n}\n\nexport interface ResolvedOidcProviderConfig extends OidcProviderConfig {\n redirectUri: string;\n}\n\nexport interface OidcProviderMetadata {\n authorization_endpoint: string;\n end_session_endpoint?: string;\n issuer: string;\n jwks_uri: string;\n token_endpoint: string;\n userinfo_endpoint?: string;\n [key: string]: unknown;\n}\n\nexport interface OidcTransaction {\n codeVerifier: string;\n createdAt: number;\n nonce: string;\n provider: string;\n returnTo?: string;\n state: string;\n}\n\nexport interface CreateAuthorizationUrlOptions {\n authorizationParams?: Record<string, string | number | boolean | undefined>;\n transaction?: OidcTransaction;\n}\n\nexport interface OidcTokenSet {\n accessToken?: string;\n expiresAt?: Date;\n idToken: string;\n refreshToken?: string;\n scope?: string;\n tokenType?: string;\n}\n\nexport interface OidcCallbackResult {\n claims: OidcClaims;\n tokens: OidcTokenSet;\n}\n\nexport interface OidcLoginResult extends OidcIdentityResult {\n claims: OidcClaims;\n tokens: OidcTokenSet;\n}\n\nexport interface OidcLoginServiceOptions extends SmrtClassOptions {\n /** Provider key, e.g. kanidm or dex */\n providerName: string;\n /** Fully resolved provider config. redirectUri is required here. */\n provider: ResolvedOidcProviderConfig;\n /** Optional fetch override for tests or custom runtimes. */\n fetch?: typeof fetch;\n /** JWT clock tolerance passed to jose. Defaults to 15 seconds. */\n clockTolerance?: number | string;\n /** Provider metadata cache TTL in milliseconds. Defaults to 5 minutes. */\n metadataCacheTtlMs?: number;\n /**\n * Application Profile resolver invoked after claims validation and before\n * OIDC identity, User, or session creation.\n */\n resolveProfile?: OidcProfileResolver;\n /**\n * Explicitly authorize first binding to a pre-provisioned canonical Profile\n * and its existing User owner after protocol claim validation.\n */\n authorizeProfileOwner?: OidcProfileOwnerAuthorizer;\n}\n\nexport class OidcLoginError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'OidcLoginError';\n }\n}\n\ninterface UsersAuthPackageConfig extends Record<string, unknown> {\n auth?: {\n oidc?: UsersOidcConfig;\n };\n}\n\ninterface TokenEndpointResponse {\n access_token?: unknown;\n error?: unknown;\n error_description?: unknown;\n expires_in?: unknown;\n id_token?: unknown;\n refresh_token?: unknown;\n scope?: unknown;\n token_type?: unknown;\n}\n\ntype OidcClaimSource = JWTPayload | Record<string, unknown>;\n\nexport function getUsersOidcConfig(): UsersOidcConfig {\n const config = getPackageConfig<UsersAuthPackageConfig>('users', {});\n return config.auth?.oidc ?? {};\n}\n\nfunction normalizeIssuer(issuer: string): string {\n return issuer.replace(/\\/+$/u, '');\n}\n\nfunction resolveDiscoveryUrl(provider: OidcProviderConfig): string {\n if (provider.discoveryUrl) return provider.discoveryUrl;\n return `${normalizeIssuer(provider.issuer)}${WELL_KNOWN_PATH}`;\n}\n\nfunction bytesToBase64Url(bytes: Uint8Array): string {\n let binary = '';\n for (const byte of bytes) {\n binary += String.fromCharCode(byte);\n }\n\n return btoa(binary)\n .replaceAll('+', '-')\n .replaceAll('/', '_')\n .replace(/=+$/u, '');\n}\n\nfunction base64UrlToBytes(value: string): Uint8Array {\n const normalized = value.replaceAll('-', '+').replaceAll('_', '/');\n const padded = normalized.padEnd(\n normalized.length + ((4 - (normalized.length % 4)) % 4),\n '=',\n );\n const binary = atob(padded);\n const bytes = new Uint8Array(binary.length);\n\n for (let index = 0; index < binary.length; index += 1) {\n bytes[index] = binary.charCodeAt(index);\n }\n\n return bytes;\n}\n\nfunction randomBase64Url(byteLength = 32): string {\n const bytes = new Uint8Array(byteLength);\n crypto.getRandomValues(bytes);\n return bytesToBase64Url(bytes);\n}\n\nasync function pkceChallenge(codeVerifier: string): Promise<string> {\n const bytes = new TextEncoder().encode(codeVerifier);\n const digest = await crypto.subtle.digest('SHA-256', bytes);\n return bytesToBase64Url(new Uint8Array(digest));\n}\n\nfunction bytesToBase64(bytes: Uint8Array): string {\n let binary = '';\n\n for (const byte of bytes) {\n binary += String.fromCharCode(byte);\n }\n\n return btoa(binary);\n}\n\nfunction formUrlEncode(value: string): string {\n const params = new URLSearchParams();\n params.set('value', value);\n return params.toString().slice('value='.length);\n}\n\nfunction encodeBasicAuth(clientId: string, clientSecret: string): string {\n const credentials = `${formUrlEncode(clientId)}:${formUrlEncode(clientSecret)}`;\n return bytesToBase64(new TextEncoder().encode(credentials));\n}\n\nfunction asString(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\nfunction asBoolean(value: unknown): boolean | undefined {\n if (typeof value === 'boolean') return value;\n if (value === 'true') return true;\n if (value === 'false') return false;\n return undefined;\n}\n\nfunction ensureFetch(fetchOverride?: typeof fetch): typeof fetch {\n const runtimeFetch = fetchOverride ?? globalThis.fetch;\n if (!runtimeFetch) {\n throw new OidcLoginError('OIDC login requires a fetch implementation.');\n }\n return runtimeFetch;\n}\n\nfunction requireString(value: unknown, field: string): string {\n if (typeof value !== 'string' || value.length === 0) {\n throw new OidcLoginError(`OIDC provider metadata missing \"${field}\".`);\n }\n return value;\n}\n\nasync function readJsonResponse<T>(\n response: Response,\n context: string,\n): Promise<T> {\n try {\n return (await response.json()) as T;\n } catch {\n throw new OidcLoginError(`${context} returned invalid JSON.`);\n }\n}\n\nfunction normalizeClaims(\n payload: OidcClaimSource,\n issuer: string,\n sourceName = 'OIDC claims',\n): OidcClaims {\n const sub = asString(payload.sub);\n if (!sub) {\n throw new OidcLoginError(`${sourceName} missing required \"sub\" claim.`);\n }\n\n return {\n sub,\n iss: asString(payload.iss) ?? issuer,\n email: asString(payload.email),\n email_verified: asBoolean(payload.email_verified),\n name: asString(payload.name),\n preferred_username: asString(payload.preferred_username),\n };\n}\n\nfunction mergeClaims(\n idTokenClaims: OidcClaims,\n userInfoClaims: OidcClaims,\n): OidcClaims {\n if (userInfoClaims.sub !== idTokenClaims.sub) {\n throw new OidcLoginError('OIDC userinfo subject does not match ID token.');\n }\n\n const useIdTokenEmail = idTokenClaims.email !== undefined;\n return {\n ...idTokenClaims,\n // Email verification is meaningful only for the email asserted by the\n // same source. Never borrow an ID-token verification flag for a userinfo\n // address (or vice versa).\n email: useIdTokenEmail ? idTokenClaims.email : userInfoClaims.email,\n email_verified: useIdTokenEmail\n ? idTokenClaims.email_verified\n : userInfoClaims.email_verified,\n name: idTokenClaims.name ?? userInfoClaims.name,\n preferred_username:\n idTokenClaims.preferred_username ?? userInfoClaims.preferred_username,\n };\n}\n\nfunction validateAuthorizedParty(payload: JWTPayload, clientId: string): void {\n const authorizedParty = asString(payload.azp);\n\n if (authorizedParty && authorizedParty !== clientId) {\n throw new OidcLoginError('OIDC authorized party validation failed.');\n }\n\n if (\n Array.isArray(payload.aud) &&\n payload.aud.length > 1 &&\n authorizedParty !== clientId\n ) {\n throw new OidcLoginError('OIDC authorized party validation failed.');\n }\n}\n\nfunction getTokenEndpointAuthMethod(\n provider: OidcProviderConfig,\n): OidcTokenEndpointAuthMethod {\n return (\n provider.tokenEndpointAuthMethod ??\n (provider.clientSecret ? 'client_secret_basic' : 'none')\n );\n}\n\nfunction getScopes(provider: OidcProviderConfig): string[] {\n return provider.scopes?.length ? provider.scopes : [...DEFAULT_SCOPES];\n}\n\nfunction parseTokenResponse(json: TokenEndpointResponse): OidcTokenSet {\n if (typeof json.id_token !== 'string') {\n throw new OidcLoginError('OIDC token response missing \"id_token\".');\n }\n\n return {\n accessToken: asString(json.access_token),\n expiresAt:\n typeof json.expires_in === 'number'\n ? new Date(Date.now() + json.expires_in * 1000)\n : undefined,\n idToken: json.id_token,\n refreshToken: asString(json.refresh_token),\n scope: asString(json.scope),\n tokenType: asString(json.token_type),\n };\n}\n\nfunction parseTransaction(value: unknown): OidcTransaction {\n if (!value || typeof value !== 'object') {\n throw new OidcLoginError('Invalid OIDC login transaction.');\n }\n\n const record = value as Partial<OidcTransaction>;\n if (\n !record.provider ||\n !record.state ||\n !record.nonce ||\n !record.codeVerifier ||\n typeof record.createdAt !== 'number'\n ) {\n throw new OidcLoginError('Invalid OIDC login transaction.');\n }\n\n return {\n codeVerifier: record.codeVerifier,\n createdAt: record.createdAt,\n nonce: record.nonce,\n provider: record.provider,\n returnTo: asString(record.returnTo),\n state: record.state,\n };\n}\n\nexport function encodeOidcTransaction(transaction: OidcTransaction): string {\n const json = JSON.stringify(transaction);\n return bytesToBase64Url(new TextEncoder().encode(json));\n}\n\nexport function decodeOidcTransaction(value: string): OidcTransaction {\n try {\n const json = new TextDecoder().decode(base64UrlToBytes(value));\n return parseTransaction(JSON.parse(json));\n } catch (error) {\n if (error instanceof OidcLoginError) throw error;\n throw new OidcLoginError('Invalid OIDC login transaction.');\n }\n}\n\nexport function resolveOidcProviderConfig(\n providerName?: string,\n options: OidcProviderResolutionOptions = {},\n): OidcProviderResolution {\n const oidcConfig = getUsersOidcConfig();\n const providers = {\n ...(oidcConfig.providers ?? {}),\n ...(options.providers ?? {}),\n };\n const resolvedProviderName =\n providerName ?? options.defaultProvider ?? oidcConfig.defaultProvider;\n\n if (!resolvedProviderName) {\n throw new OidcLoginError(\n 'No OIDC provider specified. Pass a provider name or configure packages.users.auth.oidc.defaultProvider.',\n );\n }\n\n const provider = providers[resolvedProviderName];\n if (!provider) {\n throw new OidcLoginError(\n `Unknown OIDC provider \"${resolvedProviderName}\". Configure it under packages.users.auth.oidc.providers.`,\n );\n }\n\n if (!provider.issuer || !provider.clientId) {\n throw new OidcLoginError(\n `OIDC provider \"${resolvedProviderName}\" requires issuer and clientId.`,\n );\n }\n\n return {\n provider,\n providerName: resolvedProviderName,\n };\n}\n\nexport class OidcLoginService {\n private readonly classOptions: SmrtClassOptions;\n private readonly clockTolerance: number | string;\n private readonly fetchImpl: typeof fetch;\n private readonly metadataCacheTtlMs: number;\n private readonly authorizeProfileOwner?: OidcProfileOwnerAuthorizer;\n private readonly resolveProfile?: OidcProfileResolver;\n private metadataFetchedAt = 0;\n private metadataPromise?: Promise<OidcProviderMetadata>;\n private remoteJwks?: JWTVerifyGetKey;\n readonly provider: ResolvedOidcProviderConfig;\n readonly providerName: string;\n\n constructor(options: OidcLoginServiceOptions) {\n const {\n clockTolerance,\n fetch: fetchOverride,\n metadataCacheTtlMs,\n provider,\n providerName,\n authorizeProfileOwner,\n resolveProfile,\n ...classOptions\n } = options;\n\n if (!provider.redirectUri) {\n throw new OidcLoginError(\n `OIDC provider \"${providerName}\" requires a redirectUri.`,\n );\n }\n\n this.classOptions = classOptions;\n this.clockTolerance = clockTolerance ?? DEFAULT_CLOCK_TOLERANCE_SECONDS;\n this.fetchImpl = ensureFetch(fetchOverride);\n this.metadataCacheTtlMs =\n metadataCacheTtlMs ?? DEFAULT_METADATA_CACHE_TTL_MS;\n this.authorizeProfileOwner = authorizeProfileOwner;\n this.resolveProfile = resolveProfile;\n this.provider = {\n ...provider,\n issuer: normalizeIssuer(provider.issuer),\n };\n this.providerName = providerName;\n }\n\n createTransaction(returnTo?: string): OidcTransaction {\n return {\n codeVerifier: randomBase64Url(32),\n createdAt: Date.now(),\n nonce: randomBase64Url(32),\n provider: this.providerName,\n returnTo,\n state: randomBase64Url(32),\n };\n }\n\n async getMetadata(): Promise<OidcProviderMetadata> {\n const cacheExpired =\n this.metadataFetchedAt > 0 &&\n Date.now() - this.metadataFetchedAt > this.metadataCacheTtlMs;\n\n if (!this.metadataPromise || cacheExpired) {\n this.metadataPromise = this.fetchMetadata()\n .then((metadata) => {\n this.metadataFetchedAt = Date.now();\n this.remoteJwks = undefined;\n return metadata;\n })\n .catch((error) => {\n this.metadataPromise = undefined;\n throw error;\n });\n }\n\n return this.metadataPromise;\n }\n\n private async fetchMetadata(): Promise<OidcProviderMetadata> {\n const discoveryUrl = resolveDiscoveryUrl(this.provider);\n const response = await this.fetchImpl(discoveryUrl, {\n headers: { accept: 'application/json' },\n });\n\n if (!response.ok) {\n throw new OidcLoginError(\n `OIDC discovery failed for \"${this.providerName}\" (${response.status}).`,\n );\n }\n\n const metadata = await readJsonResponse<Partial<OidcProviderMetadata>>(\n response,\n 'OIDC discovery',\n );\n const issuer = requireString(\n metadata.issuer ?? this.provider.issuer,\n 'issuer',\n );\n\n if (normalizeIssuer(issuer) !== this.provider.issuer) {\n throw new OidcLoginError(\n `OIDC discovery issuer mismatch for \"${this.providerName}\".`,\n );\n }\n\n return {\n ...metadata,\n authorization_endpoint: requireString(\n metadata.authorization_endpoint,\n 'authorization_endpoint',\n ),\n issuer,\n jwks_uri: requireString(metadata.jwks_uri, 'jwks_uri'),\n token_endpoint: requireString(metadata.token_endpoint, 'token_endpoint'),\n };\n }\n\n async createAuthorizationUrl(\n options: CreateAuthorizationUrlOptions = {},\n ): Promise<{ transaction: OidcTransaction; url: URL }> {\n const metadata = await this.getMetadata();\n const transaction = options.transaction ?? this.createTransaction();\n const codeChallenge = await pkceChallenge(transaction.codeVerifier);\n const url = new URL(metadata.authorization_endpoint);\n\n url.searchParams.set('response_type', 'code');\n url.searchParams.set('client_id', this.provider.clientId);\n url.searchParams.set('redirect_uri', this.provider.redirectUri);\n url.searchParams.set('scope', getScopes(this.provider).join(' '));\n url.searchParams.set('state', transaction.state);\n url.searchParams.set('nonce', transaction.nonce);\n url.searchParams.set('code_challenge', codeChallenge);\n url.searchParams.set('code_challenge_method', 'S256');\n\n for (const [key, value] of Object.entries({\n ...this.provider.authorizationParams,\n ...options.authorizationParams,\n })) {\n if (value !== undefined) {\n url.searchParams.set(key, String(value));\n }\n }\n\n return { transaction, url };\n }\n\n async exchangeCallback(\n callbackUrl: string | URL,\n transaction: OidcTransaction,\n ): Promise<OidcCallbackResult> {\n const code = this.validateCallback(callbackUrl, transaction);\n const tokens = await this.exchangeCode(code, transaction);\n const idTokenClaims = await this.verifyIdToken(\n tokens.idToken,\n transaction.nonce,\n );\n const claims = await this.enrichClaimsFromUserInfo(idTokenClaims, tokens);\n\n return { claims, tokens };\n }\n\n async completeLogin(\n callbackUrl: string | URL,\n transaction: OidcTransaction,\n ): Promise<OidcLoginResult> {\n const { claims, tokens } = await this.exchangeCallback(\n callbackUrl,\n transaction,\n );\n const users = await UserCollection.create(this.classOptions);\n const result = await users.getOrCreateFromOidc(claims, this.providerName, {\n authorizeProfileOwner: this.authorizeProfileOwner,\n resolveProfile: this.resolveProfile,\n });\n\n return {\n ...result,\n claims,\n tokens,\n };\n }\n\n private validateCallback(\n callbackUrl: string | URL,\n transaction: OidcTransaction,\n ): string {\n if (transaction.provider !== this.providerName) {\n throw new OidcLoginError('OIDC login transaction provider mismatch.');\n }\n\n const url = callbackUrl instanceof URL ? callbackUrl : new URL(callbackUrl);\n const error = url.searchParams.get('error');\n if (error) {\n const description = url.searchParams.get('error_description');\n throw new OidcLoginError(\n description\n ? `OIDC provider returned \"${error}\": ${description}`\n : `OIDC provider returned \"${error}\".`,\n );\n }\n\n const state = url.searchParams.get('state');\n if (!state || state !== transaction.state) {\n throw new OidcLoginError('OIDC state validation failed.');\n }\n\n const responseIssuer = url.searchParams.get('iss');\n if (\n responseIssuer &&\n normalizeIssuer(responseIssuer) !== this.provider.issuer\n ) {\n throw new OidcLoginError('OIDC response issuer validation failed.');\n }\n\n const code = url.searchParams.get('code');\n if (!code) {\n throw new OidcLoginError('OIDC callback missing authorization code.');\n }\n\n return code;\n }\n\n private async exchangeCode(\n code: string,\n transaction: OidcTransaction,\n ): Promise<OidcTokenSet> {\n const metadata = await this.getMetadata();\n const body = new URLSearchParams({\n code,\n code_verifier: transaction.codeVerifier,\n grant_type: 'authorization_code',\n redirect_uri: this.provider.redirectUri,\n });\n const headers = new Headers({\n accept: 'application/json',\n 'content-type': 'application/x-www-form-urlencoded',\n });\n\n const authMethod = getTokenEndpointAuthMethod(this.provider);\n if (authMethod === 'client_secret_basic') {\n if (!this.provider.clientSecret) {\n throw new OidcLoginError(\n `OIDC provider \"${this.providerName}\" is missing clientSecret.`,\n );\n }\n headers.set(\n 'authorization',\n `Basic ${encodeBasicAuth(this.provider.clientId, this.provider.clientSecret)}`,\n );\n } else {\n body.set('client_id', this.provider.clientId);\n if (authMethod === 'client_secret_post') {\n if (!this.provider.clientSecret) {\n throw new OidcLoginError(\n `OIDC provider \"${this.providerName}\" is missing clientSecret.`,\n );\n }\n body.set('client_secret', this.provider.clientSecret);\n }\n }\n\n const response = await this.fetchImpl(metadata.token_endpoint, {\n body,\n headers,\n method: 'POST',\n });\n const json = await readJsonResponse<TokenEndpointResponse>(\n response,\n 'OIDC token endpoint',\n );\n\n if (!response.ok) {\n const providerError = asString(json.error);\n const description = asString(json.error_description);\n throw new OidcLoginError(\n description\n ? `OIDC token exchange failed (${providerError ?? response.status}): ${description}`\n : `OIDC token exchange failed (${providerError ?? response.status}).`,\n );\n }\n\n return parseTokenResponse(json);\n }\n\n private async enrichClaimsFromUserInfo(\n claims: OidcClaims,\n tokens: OidcTokenSet,\n ): Promise<OidcClaims> {\n if (claims.email || !tokens.accessToken) {\n return claims;\n }\n\n const metadata = await this.getMetadata();\n if (!metadata.userinfo_endpoint) {\n return claims;\n }\n\n const tokenType = tokens.tokenType ?? 'Bearer';\n const response = await this.fetchImpl(metadata.userinfo_endpoint, {\n headers: {\n accept: 'application/json',\n authorization: `${tokenType} ${tokens.accessToken}`,\n },\n });\n\n if (!response.ok) {\n throw new OidcLoginError(\n `OIDC userinfo request failed (${response.status}).`,\n );\n }\n\n const userInfo = await readJsonResponse<Record<string, unknown>>(\n response,\n 'OIDC userinfo endpoint',\n );\n\n return mergeClaims(\n claims,\n normalizeClaims(userInfo, metadata.issuer, 'OIDC userinfo'),\n );\n }\n\n private async verifyIdToken(\n idToken: string,\n expectedNonce: string,\n ): Promise<OidcClaims> {\n const metadata = await this.getMetadata();\n this.remoteJwks ??= createRemoteJWKSet(new URL(metadata.jwks_uri), {\n [customFetch]: this.fetchImpl,\n });\n\n const { payload } = await jwtVerify(idToken, this.remoteJwks, {\n audience: this.provider.clientId,\n clockTolerance: this.clockTolerance,\n issuer: metadata.issuer,\n });\n\n validateAuthorizedParty(payload, this.provider.clientId);\n\n if (payload.nonce !== expectedNonce) {\n throw new OidcLoginError('OIDC nonce validation failed.');\n }\n\n return normalizeClaims(payload, metadata.issuer, 'OIDC ID token');\n }\n}\n","/**\n * PermissionResolver - Resolves effective permissions for a user in a tenant\n * @packageDocumentation\n */\n\nimport type { SmrtClassOptions } from '@happyvertical/smrt-core';\nimport { GroupMemberCollection } from '../collections/GroupMemberCollection.js';\nimport { GroupRoleCollection } from '../collections/GroupRoleCollection.js';\nimport { MembershipCollection } from '../collections/MembershipCollection.js';\nimport { MembershipOverrideCollection } from '../collections/MembershipOverrideCollection.js';\nimport { PermissionCollection } from '../collections/PermissionCollection.js';\nimport { RoleCollection } from '../collections/RoleCollection.js';\nimport { RolePermissionCollection } from '../collections/RolePermissionCollection.js';\nimport { TenantCollection } from '../collections/TenantCollection.js';\nimport { TenantPermissionOverrideCollection } from '../collections/TenantPermissionOverrideCollection.js';\nimport type { Membership } from '../models/Membership.js';\nimport { MAX_TENANT_HIERARCHY_DEPTH, type Tenant } from '../models/Tenant.js';\n\n/**\n * Permission resolution result\n */\nexport interface PermissionResolutionResult {\n /** Set of granted permission slugs */\n permissions: Set<string>;\n /** Membership ID used for resolution */\n membershipId: string | null;\n /** Role ID from membership */\n roleId: string | null;\n /** Group IDs that contributed permissions */\n groupIds: string[];\n /** Permission IDs explicitly denied */\n deniedPermissionIds: string[];\n /**\n * Ancestor tenant id the resolving membership belongs to, when resolution\n * used opt-in hierarchical inheritance (no direct membership in the target\n * tenant; nearest ACTIVE ancestor membership whose role has\n * `inheritsToDescendants: true`). `null` for direct-membership resolution.\n */\n inheritedFromTenantId: string | null;\n}\n\nexport interface PermissionResolutionOptions {\n /**\n * Membership row already resolved by the caller for this user/tenant.\n * Passing this lets request-scoped session loaders avoid re-querying the\n * same membership row before resolving permissions.\n *\n * Pass the RAW lookup result: an inactive row pins resolution to the empty\n * set (a suspended/pending direct membership stays effective), while an\n * explicit `null` asserts \"no direct membership row exists\" and makes the\n * resolver consider opt-in ancestor-membership inheritance. Leaving it\n * `undefined` lets the resolver perform its own lookup.\n */\n membership?: Membership | null;\n}\n\n/**\n * Tenant permission inheritance chain result\n */\nexport interface TenantPermissionInheritanceResult {\n /** Effective tenant permissions (slugs) after inheritance resolution */\n permissions: Set<string>;\n /** Tenant IDs that contributed to the inheritance chain */\n contributingTenantIds: string[];\n /** Whether inheritance was active (at least one tenant in chain had inheritPermissions: true) */\n inheritanceActive: boolean;\n /**\n * Permission slugs explicitly DENY'd by a `TenantPermissionOverride` anywhere\n * in the tenant hierarchy. These are a HARD, tenant-wide block:\n * `resolvePermissions` subtracts them AFTER role + group grants are applied,\n * so a tenant-DENY overrides a permission a role/group otherwise grants. A\n * more-specific membership-override GRANT can still re-add a slug listed here;\n * a membership-override DENY stays absolute. (This set is independent of the\n * net `permissions` above, which only reflects DENY's effect on the inherited\n * cascade.)\n */\n deniedPermissions: Set<string>;\n}\n\n/**\n * PermissionResolver resolves the effective permissions for a user in a tenant.\n *\n * Resolution algorithm:\n * 1. Resolve tenant hierarchy permissions (if hierarchical tenants are used)\n * 2. Get user's membership in the tenant\n * 3. Get base permissions from membership's role\n * 4. Get user's groups in the tenant\n * 5. Add permissions from group roles\n * 6. Apply membership overrides (grant/deny)\n *\n * DENY overrides take precedence over GRANT overrides at every level.\n *\n * ## Hierarchical Tenant Permissions\n *\n * When tenants are organized hierarchically, permissions can cascade from\n * parent tenants to child tenants. The cascade is controlled by:\n * - Parent's `cascadePermissions`: If true, parent pushes permissions down\n * - Child's `inheritPermissions`: If true, child accepts parent's permissions\n *\n * Child tenants can override inherited permissions using TenantPermissionOverride:\n * - INHERIT: Use parent's value (default)\n * - GRANT: Explicitly grant at this level\n * - DENY: Explicitly block (even if parent grants)\n *\n * ## Hierarchical Membership-Role Inheritance (opt-in)\n *\n * Independent of the tenant-level cascade above, membership-role authority\n * can follow the hierarchy DOWN when a role is explicitly flagged\n * `inheritsToDescendants: true`: a user with no direct membership in the\n * target tenant resolves through the nearest ACTIVE ancestor membership\n * holding such a role. A direct membership row in the target tenant always\n * wins (including inactive rows, which resolve empty), child tenant-DENY\n * overrides still subtract from inherited grants, and with no flagged role\n * the resolver behaves exactly as before. See `resolvePermissions`.\n *\n * @example\n * ```typescript\n * const resolver = new PermissionResolver(options);\n * await resolver.initialize();\n *\n * // Check single permission\n * const canCreate = await resolver.hasPermission(userId, tenantId, 'articles.create');\n *\n * // Get all permissions\n * const result = await resolver.resolvePermissions(userId, tenantId);\n * console.log(result.permissions); // Set<string>\n *\n * // Resolve tenant-level permissions only (without user context)\n * const tenantPerms = await resolver.resolveTenantPermissions(tenantId);\n * ```\n */\nexport class PermissionResolver {\n private options: SmrtClassOptions;\n private membershipCollection!: MembershipCollection;\n private roleCollection!: RoleCollection;\n private rolePermissionCollection!: RolePermissionCollection;\n private membershipOverrideCollection!: MembershipOverrideCollection;\n private groupMemberCollection!: GroupMemberCollection;\n private groupRoleCollection!: GroupRoleCollection;\n private permissionCollection!: PermissionCollection;\n private tenantCollection!: TenantCollection;\n private tenantPermissionOverrideCollection!: TenantPermissionOverrideCollection;\n\n constructor(options: SmrtClassOptions) {\n this.options = options;\n }\n\n /**\n * Initialize collections\n *\n * Each collection is created via the inherited static `SmrtCollection.create()`\n * factory, which is generically typed to return the concrete subclass instance.\n */\n async initialize(): Promise<void> {\n this.membershipCollection = await MembershipCollection.create(this.options);\n this.roleCollection = await RoleCollection.create(this.options);\n this.rolePermissionCollection = await RolePermissionCollection.create(\n this.options,\n );\n this.membershipOverrideCollection =\n await MembershipOverrideCollection.create(this.options);\n this.groupMemberCollection = await GroupMemberCollection.create(\n this.options,\n );\n this.groupRoleCollection = await GroupRoleCollection.create(this.options);\n this.permissionCollection = await PermissionCollection.create(this.options);\n this.tenantCollection = await TenantCollection.create(this.options);\n this.tenantPermissionOverrideCollection =\n await TenantPermissionOverrideCollection.create(this.options);\n }\n\n // ============= Tenant Hierarchy Permission Resolution =============\n\n /**\n * Resolve effective permissions for a tenant, considering hierarchy inheritance.\n *\n * Algorithm:\n * 1. Get the tenant and its ancestors (from root to immediate parent)\n * 2. Batch fetch all permission overrides for the entire chain (single query)\n * 3. Walk down the chain, building up permissions:\n * - Start with root tenant's permissions\n * - For each child: if parent.cascadePermissions && child.inheritPermissions:\n * - Merge parent's permissions\n * - Apply child's overrides (GRANT adds, DENY removes)\n * 4. Return the final effective permission set\n */\n async resolveTenantPermissions(\n tenantId: string,\n ): Promise<TenantPermissionInheritanceResult> {\n const result: TenantPermissionInheritanceResult = {\n permissions: new Set<string>(),\n contributingTenantIds: [],\n inheritanceActive: false,\n deniedPermissions: new Set<string>(),\n };\n\n const tenant = await this.tenantCollection.get({ id: tenantId });\n if (!tenant) {\n return result;\n }\n\n // Get the inheritance chain from root to this tenant\n const ancestors =\n await this.tenantCollection.getAncestorsFromRoot(tenantId);\n const chain: Tenant[] = [...ancestors, tenant];\n\n // Batch fetch all permission overrides for the entire chain (single query)\n const chainTenantIds = chain.map((t) => t.id!);\n const allOverridesMap =\n await this.tenantPermissionOverrideCollection.getOverridesByEffectBatch(\n chainTenantIds,\n );\n\n // Build a set of permission IDs for batch lookup. Also track every\n // tenant-level DENY id across the chain: tenant-DENY is a hard, tenant-wide\n // block that `resolvePermissions` subtracts from role/group grants too, so\n // it must be reported independently of the net inherited `permissions` set\n // (which only reflects DENY's effect on the cascade, not on roles).\n const allPermissionIds = new Set<string>();\n const deniedPermissionIds = new Set<string>();\n for (const overrides of allOverridesMap.values()) {\n for (const id of overrides.grantedPermissionIds) allPermissionIds.add(id);\n for (const id of overrides.deniedPermissionIds) {\n allPermissionIds.add(id);\n deniedPermissionIds.add(id);\n }\n }\n\n // Process each tenant in the chain\n let inheritedPermissions = new Set<string>();\n\n for (let i = 0; i < chain.length; i++) {\n const current = chain[i];\n const isFirst = i === 0;\n const previous = isFirst ? null : chain[i - 1];\n\n // Check if inheritance is active for this tenant\n const shouldInherit =\n !isFirst && previous?.cascadePermissions && current.inheritPermissions;\n\n if (shouldInherit) {\n result.inheritanceActive = true;\n }\n\n // Get this tenant's permission overrides from the batch result\n const overrides = allOverridesMap.get(current.id!) ?? {\n grantedPermissionIds: [],\n deniedPermissionIds: [],\n inheritedPermissionIds: [],\n };\n\n // Build this tenant's effective permissions\n const currentPermissions = new Set<string>();\n\n // Track if this tenant actually contributed to the permission set\n let contributed = false;\n\n // Start with inherited permissions if applicable\n if (shouldInherit && inheritedPermissions.size > 0) {\n for (const permId of inheritedPermissions) {\n currentPermissions.add(permId);\n }\n contributed = true;\n }\n\n // Apply grants\n for (const permId of overrides.grantedPermissionIds) {\n currentPermissions.add(permId);\n contributed = true;\n }\n\n // Apply denies (remove) - only counts as contribution if something was actually removed\n for (const permId of overrides.deniedPermissionIds) {\n if (currentPermissions.has(permId)) {\n currentPermissions.delete(permId);\n contributed = true;\n }\n }\n\n // Only add to contributingTenantIds if this tenant actually affected the final set\n if (contributed && !result.contributingTenantIds.includes(current.id!)) {\n result.contributingTenantIds.push(current.id!);\n }\n\n // This becomes the inherited set for the next iteration\n inheritedPermissions = currentPermissions;\n }\n\n // Batch fetch all permissions to get slugs\n if (allPermissionIds.size > 0) {\n const permissionsMap = await this.permissionCollection.findByIds(\n Array.from(allPermissionIds),\n );\n\n // Convert IDs to slugs in the result\n for (const permId of inheritedPermissions) {\n const perm = permissionsMap.get(permId);\n if (perm?.slug) {\n result.permissions.add(perm.slug);\n }\n }\n\n // Record the slugs DENY'd at any tenant level so callers can enforce the\n // hard tenant-wide block over role/group grants — but NET against the\n // cascade's own resolution: a slug a more-specific tenant GRANT re-added\n // (so it survives in the net-granted `inheritedPermissions`) must NOT be\n // in the hard-block set, because the cascade already decided GRANT\n // (most-specific wins). Without this guard a parent-DENY + child-GRANT\n // slug would wrongly override the child GRANT and any role/group grant.\n for (const permId of deniedPermissionIds) {\n if (inheritedPermissions.has(permId)) continue;\n const perm = permissionsMap.get(permId);\n if (perm?.slug) {\n result.deniedPermissions.add(perm.slug);\n }\n }\n }\n\n return result;\n }\n\n /**\n * Get the inheritance chain for a tenant (for debugging/display purposes)\n */\n async getTenantInheritanceChain(\n tenantId: string,\n ): Promise<Array<{ tenant: Tenant; inherits: boolean; cascades: boolean }>> {\n const tenant = await this.tenantCollection.get({ id: tenantId });\n if (!tenant) {\n return [];\n }\n\n const ancestors =\n await this.tenantCollection.getAncestorsFromRoot(tenantId);\n const chain: Array<{\n tenant: Tenant;\n inherits: boolean;\n cascades: boolean;\n }> = [];\n\n for (let i = 0; i < ancestors.length; i++) {\n const current = ancestors[i];\n const next = i + 1 < ancestors.length ? ancestors[i + 1] : tenant;\n\n chain.push({\n tenant: current,\n inherits: false, // Root or ancestor doesn't inherit in this context\n cascades: current.cascadePermissions && next.inheritPermissions,\n });\n }\n\n // Add the target tenant\n chain.push({\n tenant: tenant,\n inherits: tenant.inheritPermissions && ancestors.length > 0,\n cascades: tenant.cascadePermissions,\n });\n\n return chain;\n }\n\n /**\n * Resolve all effective permissions for a user in a tenant.\n *\n * Precedence (broad -> specific, most-specific wins):\n * tenant-inherited (cascade)\n * -> role\n * -> group roles\n * -> tenant-DENY (removes; overrides role/group grants, tenant-wide)\n * -> membership GRANT (re-adds; most specific, can win over a tenant-DENY)\n * -> membership DENY (absolute; always wins)\n *\n * ## Membership selection\n *\n * A direct membership row in the target tenant always pins resolution to\n * itself: active rows resolve normally, inactive (pending/suspended) rows\n * resolve to the empty set — an explicit direct membership is authoritative\n * even when it attenuates or suspends a user who holds broader authority on\n * an ancestor tenant.\n *\n * Only when NO direct membership row exists does the resolver consider\n * opt-in hierarchical inheritance: it walks the tenant's ancestors (nearest\n * first) and resolves through the nearest ACTIVE ancestor membership whose\n * role has `inheritsToDescendants: true`. All later layers then run\n * unchanged against the TARGET tenant — the tenant cascade and tenant-DENY\n * block come from the target tenant (so a child tenant can still carve\n * authority out of an inherited role), group roles remain exact-tenant\n * (only groups the user belongs to in the target tenant contribute), and\n * membership GRANT/DENY overrides travel with the ancestor membership used.\n * With no role flagged `inheritsToDescendants`, resolution is identical to\n * the pre-inheritance behavior.\n *\n * Algorithm:\n * 1. Get membership (direct, or nearest inheritable ancestor membership)\n * and collect all permission IDs from all sources\n * 2. Batch fetch all permissions in a single query\n * 3. Apply permissions from role, then groups\n * 4. Subtract tenant-level DENY'd slugs (hard tenant-wide block)\n * 5. Apply membership GRANT overrides (can re-add a tenant-DENY'd slug)\n * 6. Subtract membership DENY overrides (absolute precedence)\n */\n async resolvePermissions(\n userId: string,\n tenantId: string,\n options: PermissionResolutionOptions = {},\n ): Promise<PermissionResolutionResult> {\n const result: PermissionResolutionResult = {\n permissions: new Set<string>(),\n membershipId: null,\n roleId: null,\n groupIds: [],\n deniedPermissionIds: [],\n inheritedFromTenantId: null,\n };\n\n // 1. Get membership, reusing a request-scoped row when the caller already\n // resolved it for this exact user/tenant.\n let membership =\n options.membership === undefined\n ? await this.membershipCollection.findByUserAndTenant(userId, tenantId)\n : options.membership;\n\n if (membership) {\n // A direct membership row pins resolution to itself: a mismatched\n // caller-provided row is rejected, and an inactive row resolves empty\n // (suspension/pending in the target tenant is effective even for users\n // holding inheritable authority on an ancestor).\n if (membership.userId !== userId || membership.tenantId !== tenantId) {\n return result;\n }\n if (!membership.isActive()) {\n return result;\n }\n } else {\n // No direct membership row: opt-in nearest-ancestor inheritance.\n membership = await this.resolveInheritedMembership(userId, tenantId);\n if (!membership) {\n return result;\n }\n result.inheritedFromTenantId = membership.tenantId ?? null;\n }\n\n result.membershipId = membership.id ?? null;\n result.roleId = membership.roleId ?? null;\n\n const tenantPermissions = await this.resolveTenantPermissions(tenantId);\n for (const slug of tenantPermissions.permissions) {\n result.permissions.add(slug);\n }\n\n if (!membership.roleId) {\n return result;\n }\n\n // 2. Collect all permission IDs from all sources (no awaits in loops)\n const allPermissionIds: Set<string> = new Set();\n const rolePermissionIds: string[] = [];\n const groupRolePermissionIds: Map<string, string[]> = new Map();\n\n // 2a. Get base role permissions\n const baseRolePermIds =\n await this.rolePermissionCollection.getPermissionIds(membership.roleId);\n for (const id of baseRolePermIds) {\n allPermissionIds.add(id);\n rolePermissionIds.push(id);\n }\n\n // 2b. Get user's groups in the tenant (scoped to prevent cross-tenant leakage)\n const groupIds = await this.groupMemberCollection.getGroupIdsForTenant(\n userId,\n tenantId,\n );\n result.groupIds = groupIds;\n\n // 2c. Collect permissions from group roles\n for (const groupId of groupIds) {\n const groupRoleIds = await this.groupRoleCollection.getRoleIds(groupId);\n for (const roleId of groupRoleIds) {\n const permIds =\n await this.rolePermissionCollection.getPermissionIds(roleId);\n for (const id of permIds) {\n allPermissionIds.add(id);\n }\n groupRolePermissionIds.set(roleId, permIds);\n }\n }\n\n // 2d. Get membership overrides\n if (!membership.id) {\n return result;\n }\n const membershipId = membership.id;\n const grantedPermissionIds =\n await this.membershipOverrideCollection.getGrantedPermissionIds(\n membershipId,\n );\n const deniedPermissionIds =\n await this.membershipOverrideCollection.getDeniedPermissionIds(\n membershipId,\n );\n\n result.deniedPermissionIds = deniedPermissionIds;\n\n for (const id of grantedPermissionIds) {\n allPermissionIds.add(id);\n }\n for (const id of deniedPermissionIds) {\n allPermissionIds.add(id);\n }\n\n // 3. Batch fetch all permissions in a single query\n const permissionsMap = await this.permissionCollection.findByIds(\n Array.from(allPermissionIds),\n );\n\n // Build ID to slug mapping\n const permissionIdToSlug = new Map<string, string>();\n for (const [id, perm] of permissionsMap) {\n if (perm.slug) {\n permissionIdToSlug.set(id, perm.slug);\n }\n }\n\n // 4. Apply permissions from role\n for (const permId of rolePermissionIds) {\n const slug = permissionIdToSlug.get(permId);\n if (slug) {\n result.permissions.add(slug);\n }\n }\n\n // 5. Apply permissions from group roles\n for (const permIds of groupRolePermissionIds.values()) {\n for (const permId of permIds) {\n const slug = permissionIdToSlug.get(permId);\n if (slug) {\n result.permissions.add(slug);\n }\n }\n }\n\n // 6. Subtract tenant-level DENY'd slugs. A tenant-DENY is a HARD,\n // tenant-wide block that overrides role and group grants too — sitting just\n // above the most-specific membership overrides in precedence. It runs\n // BEFORE the membership GRANT pass so a more-specific membership GRANT (step\n // 7) can deliberately re-add a slug a tenant DENYs.\n for (const slug of tenantPermissions.deniedPermissions) {\n result.permissions.delete(slug);\n }\n\n // 7. Apply granted membership overrides (most specific GRANT; can re-add a\n // tenant-DENY'd slug).\n for (const permId of grantedPermissionIds) {\n const slug = permissionIdToSlug.get(permId);\n if (slug) {\n result.permissions.add(slug);\n }\n }\n\n // 8. Remove denied membership overrides (DENY takes absolute precedence over\n // tenant, role, group, and granted membership permissions).\n for (const permId of deniedPermissionIds) {\n const slug = permissionIdToSlug.get(permId);\n if (slug) {\n result.permissions.delete(slug);\n }\n }\n\n return result;\n }\n\n /**\n * Find the membership to resolve through when the user has no direct\n * membership row in the target tenant.\n *\n * Walks the target tenant's ancestor chain (from the materialized\n * `hierarchyPath`, nearest ancestor first) and returns the nearest ACTIVE\n * ancestor membership whose role is explicitly flagged\n * `inheritsToDescendants: true`. Ancestor memberships that are inactive or\n * hold an unflagged role are skipped — they neither confer nor block\n * inheritance from higher ancestors. Attenuating a user in a specific\n * tenant is expressed with a direct membership row there (which pins\n * resolution) or a tenant-level DENY, not with an intermediate unflagged\n * membership.\n *\n * Fails closed (returns null, resolving to the empty set) when the tenant\n * is missing or its `hierarchyPath` is malformed: deeper than\n * `MAX_TENANT_HIERARCHY_DEPTH`, self-referential, containing duplicate\n * ancestor ids, or inconsistent with the actual `parentTenantId` chain\n * (e.g. a stale path naming an unrelated tenant).\n */\n private async resolveInheritedMembership(\n userId: string,\n tenantId: string,\n ): Promise<Membership | null> {\n const tenant = await this.tenantCollection.get({ id: tenantId });\n if (!tenant?.id) {\n return null;\n }\n\n // Ordered root -> immediate parent, per the materialized path.\n const ancestorIds = tenant.getAncestorIds();\n if (ancestorIds.length === 0) {\n return null;\n }\n\n // Malformed hierarchy paths fail closed: a well-formed path is bounded by\n // MAX_TENANT_HIERARCHY_DEPTH (at most MAX-1 ancestors), never contains the\n // tenant itself, and never repeats an ancestor.\n if (\n ancestorIds.length >= MAX_TENANT_HIERARCHY_DEPTH ||\n ancestorIds.includes(tenant.id) ||\n new Set(ancestorIds).size !== ancestorIds.length\n ) {\n return null;\n }\n\n // The materialized path is an authorization source here, so verify it\n // against the actual parent chain before trusting it: batch-load the\n // ancestors and require an unbroken parentTenantId link\n // root -> ... -> immediate parent -> target. A stale or inconsistent path\n // (e.g. naming a tenant that is not really an ancestor) fails closed.\n const ancestors = await this.tenantCollection.listByIds(ancestorIds);\n const ancestorsById = new Map(\n ancestors.map((ancestor) => [ancestor.id, ancestor]),\n );\n let expectedParentId: string | null = null;\n for (const ancestorId of ancestorIds) {\n const ancestor = ancestorsById.get(ancestorId);\n if (!ancestor?.id) {\n return null;\n }\n if ((ancestor.parentTenantId ?? null) !== expectedParentId) {\n return null;\n }\n expectedParentId = ancestor.id;\n }\n if ((tenant.parentTenantId ?? null) !== expectedParentId) {\n return null;\n }\n\n // One query for the user's ACTIVE memberships, intersected with the\n // ancestor chain. UNIQUE(userId, tenantId) guarantees at most one row per\n // ancestor.\n const activeMemberships =\n await this.membershipCollection.findActiveByUser(userId);\n if (activeMemberships.length === 0) {\n return null;\n }\n const membershipByTenantId = new Map<string, Membership>();\n for (const row of activeMemberships) {\n if (row.tenantId && row.userId === userId) {\n membershipByTenantId.set(row.tenantId, row);\n }\n }\n\n // Candidates ordered nearest ancestor first.\n const candidates: Membership[] = [];\n for (let i = ancestorIds.length - 1; i >= 0; i--) {\n const candidate = membershipByTenantId.get(ancestorIds[i]);\n if (candidate?.roleId) {\n candidates.push(candidate);\n }\n }\n if (candidates.length === 0) {\n return null;\n }\n\n // Batch load candidate roles; only an explicit `inheritsToDescendants:\n // true` role confers inheritance, and the nearest such membership wins.\n const roleIds = [\n ...new Set(candidates.map((candidate) => candidate.roleId as string)),\n ];\n const roles = await this.roleCollection.listByIds(roleIds);\n const inheritableRoleIds = new Set<string>();\n for (const role of roles) {\n if (role.id && role.inheritsToDescendants === true) {\n inheritableRoleIds.add(role.id);\n }\n }\n\n for (const candidate of candidates) {\n if (candidate.roleId && inheritableRoleIds.has(candidate.roleId)) {\n return candidate;\n }\n }\n return null;\n }\n\n /**\n * Check if a user has a specific permission in a tenant\n */\n async hasPermission(\n userId: string,\n tenantId: string,\n permissionSlug: string,\n options: PermissionResolutionOptions = {},\n ): Promise<boolean> {\n const result = await this.resolvePermissions(userId, tenantId, options);\n return result.permissions.has(permissionSlug);\n }\n\n /**\n * Check if a user has all of the specified permissions\n */\n async hasAllPermissions(\n userId: string,\n tenantId: string,\n permissionSlugs: string[],\n options: PermissionResolutionOptions = {},\n ): Promise<boolean> {\n const result = await this.resolvePermissions(userId, tenantId, options);\n return permissionSlugs.every((slug) => result.permissions.has(slug));\n }\n\n /**\n * Check if a user has any of the specified permissions\n */\n async hasAnyPermission(\n userId: string,\n tenantId: string,\n permissionSlugs: string[],\n options: PermissionResolutionOptions = {},\n ): Promise<boolean> {\n const result = await this.resolvePermissions(userId, tenantId, options);\n return permissionSlugs.some((slug) => result.permissions.has(slug));\n }\n\n /**\n * Static factory method\n */\n static async create(options: SmrtClassOptions): Promise<PermissionResolver> {\n const resolver = new PermissionResolver(options);\n await resolver.initialize();\n return resolver;\n }\n}\n","/**\n * SessionService - Higher-level session management combining sessions with user/permission loading\n * @packageDocumentation\n */\n\nimport type { SmrtClassOptions } from '@happyvertical/smrt-core';\nimport { MembershipCollection } from '../collections/MembershipCollection.js';\nimport {\n type CreateSessionOptions,\n SessionCollection,\n} from '../collections/SessionCollection.js';\nimport { UserCollection } from '../collections/UserCollection.js';\nimport type { Membership } from '../models/Membership.js';\nimport { DEFAULT_SESSION_TTL, type Session } from '../models/Session.js';\nimport type { User } from '../models/User.js';\nimport { PermissionResolver } from './PermissionResolver.js';\n\n/**\n * Session context with user and permissions\n */\nexport interface SessionContext {\n /** The User record */\n user: User;\n /** Active membership for the current tenant, if one was resolved */\n membership?: Membership | null;\n /** Resolved permission slugs */\n permissions: string[];\n /** Tenant ID from session (if any) */\n tenantId: string | null;\n /** Session ID */\n sessionId: string;\n}\n\n/**\n * Result of {@link SessionService.switchTenant}.\n *\n * A successful switch into a non-null tenant ROTATES the session id (#1354\n * follow-up): a brand-new {@link Session} is minted and the old one is revoked,\n * so a captured pre-switch id stops validating. Callers MUST persist `sessionId`\n * (e.g. re-set the session cookie) after a rotation.\n */\nexport interface SwitchTenantResult {\n /** Whether the switch succeeded (true also for a `null` clear). */\n switched: boolean;\n /**\n * The session id to use going forward: the NEW id after a rotation, the\n * unchanged id after a `null` clear, or `null` when the switch failed\n * (unknown session or non-member — fail-closed).\n */\n sessionId: string | null;\n /** The resulting session (new on rotation; existing on clear; null on failure). */\n session: Session | null;\n /** True only when a fresh session id was minted (non-null tenant switch). */\n rotated: boolean;\n}\n\n/**\n * Options for SessionService\n */\nexport interface SessionServiceOptions extends SmrtClassOptions {\n /** Default session TTL in seconds (default: 7 days) */\n defaultTTL?: number;\n /** Cookie name (default: 'sid') */\n cookieName?: string;\n /** Whether to auto-extend sessions on access (default: false) */\n autoExtend?: boolean;\n}\n\n/**\n * SessionService provides high-level session management that combines\n * session storage with user and permission loading.\n *\n * This is the main service to use for session-based authentication.\n *\n * @example\n * ```typescript\n * const sessionService = await SessionService.create({\n * db: { type: 'sqlite', url: 'app.db' },\n * defaultTTL: 7 * 24 * 60 * 60, // 7 days\n * });\n *\n * // Create session after login\n * const sessionId = await sessionService.createSession(userId, tenantId);\n *\n * // Load session context on each request\n * const context = await sessionService.loadSessionContext(sessionId);\n * if (context) {\n * console.log('User:', context.user.email);\n * console.log('Permissions:', context.permissions);\n * }\n *\n * // Destroy session on logout\n * await sessionService.destroySession(sessionId);\n * ```\n */\nexport class SessionService {\n private options: SessionServiceOptions;\n private sessionCollection!: SessionCollection;\n private userCollection!: UserCollection;\n private membershipCollection!: MembershipCollection;\n private permissionResolver!: PermissionResolver;\n private defaultTTL: number;\n private autoExtend: boolean;\n\n constructor(options: SessionServiceOptions) {\n this.options = options;\n this.defaultTTL = options.defaultTTL ?? DEFAULT_SESSION_TTL;\n this.autoExtend = options.autoExtend ?? false;\n }\n\n /**\n * Initialize collections\n */\n async initialize(): Promise<void> {\n this.sessionCollection = await SessionCollection.create(this.options);\n this.userCollection = await UserCollection.create(this.options);\n this.membershipCollection = await MembershipCollection.create(this.options);\n this.permissionResolver = await PermissionResolver.create(this.options);\n }\n\n /**\n * Create a new session for a user\n *\n * @param userId - The user ID\n * @param tenantId - Optional tenant context\n * @param options - Additional session options\n * @returns The session ID\n */\n async createSession(\n userId: string,\n tenantId?: string,\n options?: Partial<CreateSessionOptions>,\n ): Promise<string> {\n const session = await this.sessionCollection.createSession({\n userId,\n tenantId,\n ttl: options?.ttl ?? this.defaultTTL,\n userAgent: options?.userAgent,\n ipAddress: options?.ipAddress,\n data: options?.data,\n });\n\n return session.id as string;\n }\n\n /**\n * Load full session context (user + permissions)\n *\n * Returns null if session is invalid or user doesn't exist\n */\n async loadSessionContext(sessionId: string): Promise<SessionContext | null> {\n // Find valid session\n const session = await this.sessionCollection.findValidSession(sessionId);\n if (!session) return null;\n\n // Load user\n const user = await this.userCollection.get(session.userId);\n if (!user?.isActive()) return null;\n\n // Resolve permissions (only if tenant context exists)\n let permissions: string[] = [];\n let membership: Membership | null = null;\n if (session.tenantId) {\n const resolvedMembership =\n await this.membershipCollection.findByUserAndTenant(\n session.userId,\n session.tenantId,\n );\n membership = resolvedMembership?.isActive() ? resolvedMembership : null;\n\n // Pass the RAW row: an inactive direct membership pins resolution to\n // the empty set, while a missing row (null) lets the resolver apply\n // opt-in ancestor-membership inheritance (`Role.inheritsToDescendants`).\n const result = await this.permissionResolver.resolvePermissions(\n session.userId,\n session.tenantId,\n { membership: resolvedMembership },\n );\n permissions = Array.from(result.permissions);\n }\n\n // Touch session (and optionally extend)\n if (this.autoExtend) {\n session.extend(this.defaultTTL);\n } else {\n session.touch();\n }\n await session.save();\n\n return {\n user,\n membership,\n permissions,\n tenantId: session.tenantId,\n sessionId: session.id as string,\n };\n }\n\n /**\n * Get the initialized database connection backing this session service.\n */\n getDatabase() {\n return this.sessionCollection.db;\n }\n\n /**\n * Refresh session (extend expiry, update lastAccessed)\n */\n async refreshSession(sessionId: string): Promise<boolean> {\n return this.sessionCollection.touch(sessionId, true, this.defaultTTL);\n }\n\n /**\n * Destroy a session (revoke it)\n */\n async destroySession(sessionId: string): Promise<boolean> {\n return this.sessionCollection.revokeSession(sessionId);\n }\n\n /**\n * Destroy all sessions for a user (logout from all devices)\n */\n async destroyAllUserSessions(userId: string): Promise<number> {\n return this.sessionCollection.revokeUserSessions(userId);\n }\n\n /**\n * Switch tenant context for a session.\n *\n * A session's `tenantId` is the tenant-isolation key for every `@TenantScoped`\n * query, so it must never be set to a tenant the session's user is not an\n * active member of — otherwise a caller could read/write another tenant's data\n * by feeding an arbitrary id here (e.g. straight from untrusted form data).\n *\n * Fail-closed (#1400): the user's ACTIVE membership in the target tenant is\n * verified BEFORE any write. A non-member switch returns\n * `{ switched: false, ... }` and mutates nothing.\n *\n * Session-id ROTATION (#1354 follow-up): a successful switch into a non-null\n * tenant mints a BRAND-NEW session (fresh secure id, fresh TTL) for the same\n * user with the new tenant, then REVOKES the old session — so any captured\n * pre-switch session id immediately stops validating, shrinking the blast\n * radius of a leaked id across a privilege/tenant boundary. The device context\n * (user agent, IP, custom data) carries over to the new session. Callers MUST\n * persist the returned `sessionId` (e.g. re-set the cookie).\n *\n * Passing `null` clears the tenant context, is always allowed, and stays\n * in-place (no rotation — there is no privilege boundary being crossed).\n *\n * @returns A {@link SwitchTenantResult}; check `switched` for success.\n */\n async switchTenant(\n sessionId: string,\n tenantId: string | null,\n ): Promise<SwitchTenantResult> {\n const failClosed: SwitchTenantResult = {\n switched: false,\n sessionId: null,\n session: null,\n rotated: false,\n };\n\n const session = await this.sessionCollection.findValidSession(sessionId);\n if (!session) return failClosed;\n\n // Clearing the tenant context crosses no privilege boundary, so keep the\n // existing session in place (no rotation needed).\n if (tenantId === null) {\n const ok = await this.sessionCollection.setSessionTenant(sessionId, null);\n if (!ok) return failClosed;\n const updated = await this.sessionCollection.findValidSession(sessionId);\n return {\n switched: true,\n sessionId,\n session: updated,\n rotated: false,\n };\n }\n\n // Fail-closed membership check BEFORE any write.\n const membership = await this.membershipCollection.findByUserAndTenant(\n session.userId,\n tenantId,\n );\n if (!membership?.isActive()) {\n return failClosed;\n }\n\n // Rotate FAIL-CLOSED: revoke the old session FIRST, then mint the fresh one.\n // If the second write fails the caller ends up needing to re-auth (the old\n // id is already invalid) rather than retaining a still-valid stale-tenant\n // session — so a captured pre-switch id provably stops validating before we\n // ever report success.\n await this.sessionCollection.revokeSession(sessionId);\n const rotated = await this.sessionCollection.createSession({\n userId: session.userId,\n tenantId,\n ttl: this.defaultTTL,\n userAgent: session.userAgent,\n ipAddress: session.ipAddress,\n data: session.data,\n });\n\n return {\n switched: true,\n sessionId: rotated.id as string,\n session: rotated,\n rotated: true,\n };\n }\n\n /**\n * Get all active sessions for a user (for \"manage sessions\" UI)\n */\n async getUserSessions(userId: string) {\n return this.sessionCollection.findByUser(userId);\n }\n\n /**\n * Clean up expired sessions (run periodically)\n */\n async cleanupExpiredSessions(): Promise<number> {\n return this.sessionCollection.deleteExpired();\n }\n\n /**\n * Check if a permission is granted for the session\n */\n async hasPermission(sessionId: string, permission: string): Promise<boolean> {\n const context = await this.loadSessionContext(sessionId);\n if (!context) return false;\n return context.permissions.includes(permission);\n }\n\n /**\n * Get session data\n */\n async getSessionData<T>(\n sessionId: string,\n key: string,\n ): Promise<T | undefined> {\n return this.sessionCollection.getSessionData<T>(sessionId, key);\n }\n\n /**\n * Set session data\n */\n async setSessionData(\n sessionId: string,\n key: string,\n value: unknown,\n ): Promise<boolean> {\n return this.sessionCollection.setSessionData(sessionId, key, value);\n }\n\n /**\n * Static factory method\n */\n static async create(options: SessionServiceOptions): Promise<SessionService> {\n const service = new SessionService(options);\n await service.initialize();\n return service;\n }\n}\n","/**\n * MobileAuthService — server side of the SMRT mobile `/api/mobile` contract\n * (ADR 0001 Phase 3.5, issue #1748).\n *\n * Implements server-brokered OIDC/PKCE for native mobile clients on top of\n * the existing building blocks: {@link OidcLoginService} for the protocol\n * work, {@link SessionService} for the opaque bearer session (the session id\n * IS the bearer token — the same convention `TerminalAuthService` ships), and\n * the membership/role/tenant collections for the session bootstrap.\n *\n * Wire DTOs come from `@happyvertical/smrt-mobile-contract` — the owning\n * package for both the Kotlin client contract and these TypeScript types.\n *\n * ## Stateless completion: the state token\n *\n * The mobile flow has no cookie jar, so the web flow's HMAC-signed\n * transaction cookie cannot carry `state`/`nonce`/`codeVerifier` across the\n * redirect. Instead (per the frozen contract) the CLIENT persists and echoes\n * `state` + `codeVerifier`, and the server stays stateless by making the\n * OAuth `state` value itself a signed token: base64url JSON carrying the\n * nonce, provider, and creation time, plus an HMAC-SHA256 signature when a\n * secret is available (`stateSecret` option, defaulting to the provider's\n * `clientSecret` — the same fallback the web transaction cookie uses). The\n * client treats `state` as opaque; `MobileSessionManager` just compares the\n * redirect's `state` to the persisted one and echoes it back.\n *\n * The `codeVerifier` deliberately NEVER rides inside the state token: state\n * appears in the authorization URL (browser history, proxy logs), and a\n * verifier there would defeat PKCE. It stays client-held — returned once in\n * the `auth/start` response body and echoed once in the `auth/complete`\n * request body, both over TLS POSTs. The nonce, by contrast, is already a\n * query parameter of the authorization URL, so embedding it in `state`\n * leaks nothing while letting the server enforce full ID-token nonce\n * verification without server-side pending state.\n *\n * @packageDocumentation\n */\n\nimport type { SmrtClassOptions } from '@happyvertical/smrt-core';\nimport type {\n MobileAuthCompleteRequest,\n MobileAuthSession,\n MobileAuthStartRequest,\n MobileAuthStartResponse,\n MobileSessionBootstrap,\n MobileTenantOption,\n MobileUserSummary,\n} from '@happyvertical/smrt-mobile-contract';\nimport { MembershipCollection } from '../collections/MembershipCollection.js';\nimport { RoleCollection } from '../collections/RoleCollection.js';\nimport { TenantCollection } from '../collections/TenantCollection.js';\nimport {\n type OidcClaims,\n UserCollection,\n} from '../collections/UserCollection.js';\nimport type { Membership } from '../models/Membership.js';\nimport type { Role } from '../models/Role.js';\nimport type { Tenant } from '../models/Tenant.js';\nimport {\n OidcLoginError,\n OidcLoginService,\n type OidcProviderResolutionOptions,\n type OidcTokenSet,\n type OidcTransaction,\n resolveOidcProviderConfig,\n} from './OidcLoginService.js';\nimport type { SessionContext } from './SessionService.js';\nimport { SessionService } from './SessionService.js';\n\n/** 30 days — mobile sessions outlive browser sessions by design. */\nconst DEFAULT_MOBILE_SESSION_TTL_SECONDS = 30 * 24 * 60 * 60;\n/** 10 minutes — matches the web flow's transaction-cookie TTL. */\nconst DEFAULT_MOBILE_TRANSACTION_TTL_SECONDS = 10 * 60;\n/** Tolerated forward clock skew when checking state-token freshness. */\nconst STATE_TOKEN_CLOCK_SKEW_MS = 60 * 1000;\n/** Upper bound for a client-supplied correlation `state` value. */\nconst MAX_CLIENT_STATE_LENGTH = 512;\n/** Cap for the per-(provider, redirectUri) OIDC service cache. */\nconst MAX_OIDC_SERVICE_CACHE = 16;\n/** Cap for IdP-sourced strings persisted in the session data blob. */\nconst MAX_STORED_CLAIM_LENGTH = 1024;\n\n/**\n * Machine-readable error codes carried on {@link MobileAuthError} and in the\n * JSON error body (`{ error, code }`) the SvelteKit handlers emit.\n */\nexport type MobileAuthErrorCode =\n | 'invalid_request'\n | 'unknown_provider'\n | 'invalid_redirect_uri'\n | 'missing_code_verifier'\n | 'invalid_state'\n | 'expired_transaction'\n | 'exchange_failed'\n | 'signin_not_permitted'\n | 'missing_bearer_token'\n | 'invalid_bearer_token'\n | 'provider_unavailable'\n | 'server_misconfigured';\n\n/**\n * HTTP-mapped mobile-auth failure. `status` drives the response code; `code`\n * gives clients a stable discriminator (messages may change).\n */\nexport class MobileAuthError extends Error {\n readonly status: number;\n readonly code: MobileAuthErrorCode;\n\n constructor(status: number, code: MobileAuthErrorCode, message: string) {\n super(message);\n this.name = 'MobileAuthError';\n this.status = status;\n this.code = code;\n }\n}\n\n/** Context handed to the {@link MobileAuthServiceOptions.resolveUser} hook. */\nexport interface MobileLoginContext {\n claims: OidcClaims;\n tokens: OidcTokenSet;\n providerName: string;\n}\n\n/** Minimal user identity a {@link MobileAuthServiceOptions.resolveUser} hook returns. */\nexport interface MobileResolvedUser {\n id: string;\n email?: string | null;\n}\n\n/** Context handed to the {@link MobileAuthServiceOptions.resolveTenantId} hook. */\nexport interface MobileTenantContext {\n userId: string;\n claims: OidcClaims;\n providerName: string;\n}\n\n/** Context handed to the {@link MobileAuthServiceOptions.buildExtras} hook. */\nexport interface MobileBootstrapContext {\n /** Full session context (user, membership, tenantId, sessionId). */\n session: SessionContext;\n /**\n * The session's resolved permission slugs. Any MODEL JSON placed into\n * `extras` must be projected with `toPublicJSON({ permissions })` using\n * THIS set, or the response leaks fields the generated routes redact\n * (`@field({ readPermission })`, #1822). Fail closed.\n */\n permissions: string[];\n tenants: MobileTenantOption[];\n activeTenant: MobileTenantOption | null;\n}\n\n/** Request metadata recorded onto the minted session. */\nexport interface MobileRequestMeta {\n userAgent?: string;\n ipAddress?: string;\n}\n\nexport interface MobileAuthServiceOptions\n extends SmrtClassOptions,\n OidcProviderResolutionOptions {\n /** Optional fetch override for tests or custom runtimes. */\n fetch?: typeof fetch;\n /** JWT clock tolerance passed to jose. */\n clockTolerance?: number | string;\n /**\n * Allowed mobile redirect URIs. Entries are exact matches, except entries\n * ending in `/` which allow any sub-path of that prefix. When omitted or\n * empty, any structurally valid mobile redirect URI is accepted (https,\n * loopback http, or a private app scheme — RFC 8252); configure this in\n * production so authorization responses cannot be pointed at an\n * attacker-controlled URI.\n */\n redirectUris?: string[];\n /** Mobile bearer session TTL in seconds. Default: 30 days. */\n sessionTtl?: number;\n /**\n * Auth handshake (start → complete) TTL in seconds. Default: 10 minutes,\n * matching the web flow's transaction cookie.\n */\n transactionTtl?: number;\n /**\n * HMAC secret for state-token integrity. Defaults to the resolved\n * provider's `clientSecret`.\n *\n * State signing binds the `nonce`/provider/createdAt in the OAuth `state`\n * so they cannot be forged, so it is REQUIRED by default: if neither\n * `stateSecret` nor the provider's `clientSecret` is available, sign-in\n * fails closed (500 `server_misconfigured`). A public OIDC client (no\n * client secret — the PKCE case) just supplies a `stateSecret`; it is a\n * server-side HMAC key unrelated to OAuth client authentication, so any\n * deployment can set one. Set {@link allowUnsignedState} to opt into\n * unsigned tokens (NOT recommended — then `redirectUris` is the only\n * defense against state forgery).\n */\n stateSecret?: string;\n /**\n * Permit unsigned state tokens when no `stateSecret`/`clientSecret` is\n * configured. Default false (fail closed). Only enable for local\n * development or a deployment that accepts the state-forgery risk;\n * production should configure a `stateSecret` instead.\n */\n allowUnsignedState?: boolean;\n /**\n * Include descendant tenants reachable through ACTIVE memberships whose\n * role has `inheritsToDescendants: true` (#1867) in the bootstrap tenant\n * list. Default true.\n */\n includeInheritedTenants?: boolean;\n /**\n * Provision a user even when the IdP explicitly reported the email as\n * unverified. Passed through to `UserCollection.getOrCreateFromOidc`.\n */\n allowUnverifiedEmail?: boolean;\n /**\n * Map verified IdP claims to a SMRT user. The default provisions (or\n * resolves) the user via `UserCollection.getOrCreateFromOidc`. Return\n * `null`/`undefined` to REFUSE sign-in (403 `signin_not_permitted`) —\n * invite-gated apps resolve against their own membership rules here\n * without any user row being created. THROWING (vs returning null) is\n * treated as an unexpected server error and surfaces as a generic 500;\n * translate expected denials into a `null` return or a thrown\n * {@link MobileAuthError}.\n */\n resolveUser?: (\n context: MobileLoginContext,\n ) => Promise<MobileResolvedUser | null | undefined>;\n /**\n * Choose the tenant the minted session is bound to. Return a tenant id,\n * `null` for an explicitly tenant-less session, or `undefined` to fall\n * back to the default (the first direct ACTIVE membership's tenant,\n * sorted by tenant name). The session's tenant is the isolation key for\n * every `@TenantScoped` query, so only return tenants the user can\n * actually resolve permissions in.\n */\n resolveTenantId?: (\n context: MobileTenantContext,\n ) => Promise<string | null | undefined>;\n /**\n * App-defined `extras` object for the session bootstrap. Must be a plain\n * JSON object (the Kotlin client decodes it as `JsonObject`). See\n * {@link MobileBootstrapContext.permissions} for the read-permission\n * redaction requirement on model JSON.\n */\n buildExtras?: (\n context: MobileBootstrapContext,\n ) => Promise<Record<string, unknown> | null | undefined>;\n}\n\n/** Result of {@link MobileAuthService.logout}. */\nexport interface MobileLogoutResult {\n ok: true;\n /** Whether a live session was actually revoked. */\n destroyed: boolean;\n}\n\n/**\n * Payload encoded into the OAuth `state` value. Short keys keep the\n * authorization URL compact.\n */\ninterface MobileStatePayload {\n /** Version discriminator. */\n v: 1;\n /** Random entropy (CSRF binding for the client's opaque comparison). */\n s: string;\n /** OIDC nonce echoed for ID-token verification at complete time. */\n n: string;\n /** Provider name the handshake was started for. */\n p: string;\n /** Creation time (epoch ms) for TTL enforcement. */\n t: number;\n /** Optional client-supplied correlation value (opaque passthrough). */\n c?: string;\n}\n\nfunction bytesToBase64Url(bytes: Uint8Array): string {\n let binary = '';\n for (const byte of bytes) {\n binary += String.fromCharCode(byte);\n }\n return btoa(binary)\n .replaceAll('+', '-')\n .replaceAll('/', '_')\n .replace(/=+$/u, '');\n}\n\nfunction base64UrlToBytes(value: string): Uint8Array {\n const normalized = value.replaceAll('-', '+').replaceAll('_', '/');\n const padded = normalized.padEnd(\n normalized.length + ((4 - (normalized.length % 4)) % 4),\n '=',\n );\n const binary = atob(padded);\n const bytes = new Uint8Array(binary.length);\n for (let index = 0; index < binary.length; index += 1) {\n bytes[index] = binary.charCodeAt(index);\n }\n return bytes;\n}\n\nfunction randomBase64Url(byteLength = 16): string {\n const bytes = new Uint8Array(byteLength);\n crypto.getRandomValues(bytes);\n return bytesToBase64Url(bytes);\n}\n\nasync function hmacBase64Url(payload: string, secret: string): Promise<string> {\n const key = await crypto.subtle.importKey(\n 'raw',\n new TextEncoder().encode(secret),\n { hash: 'SHA-256', name: 'HMAC' },\n false,\n ['sign'],\n );\n const signature = await crypto.subtle.sign(\n 'HMAC',\n key,\n new TextEncoder().encode(payload),\n );\n return bytesToBase64Url(new Uint8Array(signature));\n}\n\nfunction timingSafeEqual(left: string, right: string): boolean {\n const leftBytes = new TextEncoder().encode(left);\n const rightBytes = new TextEncoder().encode(right);\n let diff = leftBytes.length ^ rightBytes.length;\n const length = Math.max(leftBytes.length, rightBytes.length);\n for (let index = 0; index < length; index += 1) {\n diff |= (leftBytes[index] ?? 0) ^ (rightBytes[index] ?? 0);\n }\n return diff === 0;\n}\n\nasync function encodeMobileState(\n payload: MobileStatePayload,\n secret: string | undefined,\n): Promise<string> {\n const encoded = bytesToBase64Url(\n new TextEncoder().encode(JSON.stringify(payload)),\n );\n if (!secret) return encoded;\n return `${encoded}.${await hmacBase64Url(encoded, secret)}`;\n}\n\nasync function decodeMobileState(\n raw: string,\n secret: string | undefined,\n): Promise<MobileStatePayload> {\n const invalid = () =>\n new MobileAuthError(401, 'invalid_state', 'Invalid mobile auth state.');\n\n let encoded = raw;\n if (secret) {\n const separator = raw.lastIndexOf('.');\n if (separator < 0) throw invalid();\n encoded = raw.slice(0, separator);\n const signature = raw.slice(separator + 1);\n const expected = await hmacBase64Url(encoded, secret);\n if (!timingSafeEqual(signature, expected)) throw invalid();\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(new TextDecoder().decode(base64UrlToBytes(encoded)));\n } catch {\n throw invalid();\n }\n\n if (!parsed || typeof parsed !== 'object') throw invalid();\n const record = parsed as Partial<MobileStatePayload>;\n if (\n record.v !== 1 ||\n typeof record.s !== 'string' ||\n record.s.length === 0 ||\n typeof record.n !== 'string' ||\n record.n.length === 0 ||\n typeof record.p !== 'string' ||\n record.p.length === 0 ||\n typeof record.t !== 'number' ||\n !Number.isFinite(record.t) ||\n (record.c !== undefined && typeof record.c !== 'string')\n ) {\n throw invalid();\n }\n\n return {\n v: 1,\n s: record.s,\n n: record.n,\n p: record.p,\n t: record.t,\n c: record.c,\n };\n}\n\n/**\n * Pull the token out of an `Authorization: Bearer <token>` header. Returns\n * `null` when the header is missing or malformed.\n */\nexport function readMobileBearerToken(\n authorizationHeader: string | null | undefined,\n): string | null {\n const match = authorizationHeader?.match(/^Bearer\\s+(.+)$/iu);\n const token = match?.[1]?.trim();\n return token && token.length > 0 ? token : null;\n}\n\n// Schemes that could execute script or read local resources if a redirect\n// value ever reached a browser context. Rejected regardless of allow list.\nconst DANGEROUS_REDIRECT_SCHEMES = new Set([\n 'javascript:',\n 'data:',\n 'vbscript:',\n 'file:',\n 'blob:',\n 'about:',\n]);\n\n// Well-known IANA schemes that are neither `https`, loopback `http`, nor a\n// private-use app scheme (RFC 8252 §7.1). A mobile OAuth redirect would never\n// legitimately use one of these, so reject them even when no `redirectUris`\n// allow list is configured — an app-owned custom scheme (e.g.\n// `com.example.app://`) still passes.\nconst NON_APP_REDIRECT_SCHEMES = new Set([\n 'ftp:',\n 'ftps:',\n 'ws:',\n 'wss:',\n 'mailto:',\n 'tel:',\n 'sms:',\n 'gopher:',\n 'ldap:',\n 'ldaps:',\n]);\n\nfunction isLoopbackHost(hostname: string): boolean {\n const host = hostname.toLowerCase();\n return (\n host === 'localhost' ||\n host === '127.0.0.1' ||\n host === '::1' ||\n host === '[::1]'\n );\n}\n\nfunction normalizeOptionalString(value: unknown): string | undefined {\n return typeof value === 'string' && value.trim().length > 0\n ? value.trim()\n : undefined;\n}\n\n/**\n * Validate a mobile redirect URI: absolute, a safe scheme (https, loopback\n * http, or a private app scheme per RFC 8252), and — when an allow list is\n * configured — present on it. Returns the normalized URI.\n */\nexport function validateMobileRedirectUri(\n value: unknown,\n allowList: string[] = [],\n): string {\n const redirectUri = normalizeOptionalString(value);\n if (!redirectUri) {\n throw new MobileAuthError(\n 400,\n 'invalid_redirect_uri',\n 'Missing mobile redirect URI.',\n );\n }\n\n let parsed: URL;\n try {\n parsed = new URL(redirectUri);\n } catch {\n throw new MobileAuthError(\n 400,\n 'invalid_redirect_uri',\n 'Mobile redirect URI must be an absolute URL.',\n );\n }\n\n const scheme = parsed.protocol.toLowerCase();\n if (\n DANGEROUS_REDIRECT_SCHEMES.has(scheme) ||\n NON_APP_REDIRECT_SCHEMES.has(scheme)\n ) {\n throw new MobileAuthError(\n 400,\n 'invalid_redirect_uri',\n 'Mobile redirect URI uses an unsupported scheme.',\n );\n }\n // Plain http is only permitted for native loopback redirects (RFC 8252).\n // https and private-use app schemes (e.g. com.example.app://) are allowed.\n if (scheme === 'http:' && !isLoopbackHost(parsed.hostname)) {\n throw new MobileAuthError(\n 400,\n 'invalid_redirect_uri',\n 'Mobile redirect URI must use https, a loopback address, or an app scheme.',\n );\n }\n\n const entries = allowList.map((entry) => entry.trim()).filter(Boolean);\n if (entries.length > 0) {\n const allowed = entries.some((entry) =>\n redirectUriMatchesAllowEntry(parsed, redirectUri, entry),\n );\n if (!allowed) {\n throw new MobileAuthError(\n 400,\n 'invalid_redirect_uri',\n 'Mobile redirect URI is not allowed.',\n );\n }\n }\n\n return redirectUri;\n}\n\n/**\n * Does a validated redirect URI match one allow-list entry?\n *\n * - A NON-prefix entry (no trailing `/`) is an exact string match.\n * - A prefix entry (trailing `/`) matches on the PARSED URL — same protocol,\n * same host (incl. port), and the candidate's NORMALIZED pathname starting\n * with the entry's pathname. Comparing normalized pathnames is what defeats\n * path traversal: `http://127.0.0.1/cb/../evil/` parses to pathname\n * `/evil/`, which does not start with `/cb/`, so a raw-string\n * `startsWith('http://127.0.0.1/cb/')` false-positive can't slip a code to\n * an unregistered path.\n */\nfunction redirectUriMatchesAllowEntry(\n parsedCandidate: URL,\n candidate: string,\n entry: string,\n): boolean {\n if (!entry.endsWith('/')) {\n return candidate === entry;\n }\n let parsedEntry: URL;\n try {\n parsedEntry = new URL(entry);\n } catch {\n return false;\n }\n return (\n parsedCandidate.protocol === parsedEntry.protocol &&\n parsedCandidate.host === parsedEntry.host &&\n parsedCandidate.pathname.startsWith(parsedEntry.pathname)\n );\n}\n\nfunction isModuleNotFoundError(error: unknown): boolean {\n return (\n error instanceof Error &&\n ((error as NodeJS.ErrnoException).code === 'ERR_MODULE_NOT_FOUND' ||\n /Cannot find package '@happyvertical\\/smrt-tenancy'/.test(error.message))\n );\n}\n\n/**\n * Run `fn` inside smrt-tenancy's system context when the package is\n * available (mirrors `SessionPermissionContext`'s tolerance). Mobile session\n * resolution and tenant enumeration are inherently CROSS-tenant reads —\n * under strict tenancy interceptors they must not be pinned to any single\n * tenant context.\n */\nasync function runWithSystemContext<T>(fn: () => Promise<T>): Promise<T> {\n try {\n const tenancy = await import('@happyvertical/smrt-tenancy');\n return await tenancy.withSystemContext(fn);\n } catch (error) {\n if (isModuleNotFoundError(error)) {\n return await fn();\n }\n throw error;\n }\n}\n\ninterface TenantOptionSources {\n memberships: Membership[];\n tenantById: Map<string, Tenant>;\n roleById: Map<string, Role>;\n}\n\n/**\n * Server implementation of the `/api/mobile` auth + session contract.\n *\n * Framework-agnostic: methods take plain wire DTOs and header strings and\n * return wire DTOs or throw {@link MobileAuthError}. The SvelteKit adapters\n * live in `@happyvertical/smrt-users/sveltekit`\n * (`createMobileAuthHandlers`).\n */\nexport class MobileAuthService {\n private readonly options: MobileAuthServiceOptions;\n private readonly classOptions: SmrtClassOptions;\n private readonly sessionTtl: number;\n private readonly transactionTtl: number;\n private sessionService!: SessionService;\n private userCollection!: UserCollection;\n private membershipCollection!: MembershipCollection;\n private roleCollection!: RoleCollection;\n private tenantCollection!: TenantCollection;\n /** Per-(provider, redirectUri) service cache preserving metadata caches. */\n private readonly oidcServices = new Map<string, OidcLoginService>();\n\n constructor(options: MobileAuthServiceOptions) {\n const {\n fetch: _fetch,\n clockTolerance: _clockTolerance,\n redirectUris: _redirectUris,\n sessionTtl,\n transactionTtl,\n stateSecret: _stateSecret,\n allowUnsignedState: _allowUnsignedState,\n includeInheritedTenants: _includeInheritedTenants,\n allowUnverifiedEmail: _allowUnverifiedEmail,\n resolveUser: _resolveUser,\n resolveTenantId: _resolveTenantId,\n buildExtras: _buildExtras,\n defaultProvider: _defaultProvider,\n providers: _providers,\n ...classOptions\n } = options;\n this.options = options;\n this.classOptions = classOptions;\n this.sessionTtl =\n sessionTtl && sessionTtl > 0\n ? Math.floor(sessionTtl)\n : DEFAULT_MOBILE_SESSION_TTL_SECONDS;\n this.transactionTtl =\n transactionTtl && transactionTtl > 0\n ? Math.floor(transactionTtl)\n : DEFAULT_MOBILE_TRANSACTION_TTL_SECONDS;\n }\n\n private async initialize(): Promise<void> {\n this.sessionService = await SessionService.create({\n ...this.classOptions,\n defaultTTL: this.sessionTtl,\n });\n this.userCollection = await UserCollection.create(this.classOptions);\n this.membershipCollection = await MembershipCollection.create(\n this.classOptions,\n );\n this.roleCollection = await RoleCollection.create(this.classOptions);\n this.tenantCollection = await TenantCollection.create(this.classOptions);\n }\n\n static async create(\n options: MobileAuthServiceOptions,\n ): Promise<MobileAuthService> {\n const service = new MobileAuthService(options);\n await service.initialize();\n return service;\n }\n\n /**\n * The underlying {@link SessionService}. Exposed so route guards can share\n * it with `withSessionPermissionContext` instead of minting a second one.\n */\n getSessionService(): SessionService {\n return this.sessionService;\n }\n\n private resolveProvider(requested: string | null | undefined) {\n try {\n return resolveOidcProviderConfig(\n normalizeOptionalString(requested),\n this.options,\n );\n } catch (error) {\n const message =\n error instanceof OidcLoginError\n ? error.message\n : 'Unknown mobile auth provider.';\n throw new MobileAuthError(400, 'unknown_provider', message);\n }\n }\n\n private getOidcService(\n providerName: string,\n redirectUri: string,\n ): OidcLoginService {\n const key = `${providerName}\\n${redirectUri}`;\n const cached = this.oidcServices.get(key);\n if (cached) {\n // LRU: re-insert so the hot entry moves to the newest slot and eviction\n // drops a genuinely-cold provider rather than the one in active use.\n this.oidcServices.delete(key);\n this.oidcServices.set(key, cached);\n return cached;\n }\n\n const { provider } = this.resolveProvider(providerName);\n const service = new OidcLoginService({\n ...this.classOptions,\n fetch: this.options.fetch,\n clockTolerance: this.options.clockTolerance,\n provider: { ...provider, redirectUri },\n providerName,\n });\n\n // Bounded cache: redirect URIs are allow-list validated before we get\n // here, but a no-allow-list dev setup must not grow this unboundedly.\n // Map iteration is insertion-order, so with re-insert-on-hit above the\n // first key is the least-recently-used entry.\n if (this.oidcServices.size >= MAX_OIDC_SERVICE_CACHE) {\n const lru = this.oidcServices.keys().next().value;\n if (lru !== undefined) this.oidcServices.delete(lru);\n }\n this.oidcServices.set(key, service);\n return service;\n }\n\n /**\n * The HMAC secret for state signing, or `undefined` when unsigned tokens\n * are explicitly permitted via {@link MobileAuthServiceOptions.allowUnsignedState}.\n * Fails closed (throws → 500 `server_misconfigured`) when no secret is\n * available and unsigned tokens are not opted in, so a deployment can never\n * silently fall back to forgeable state tokens.\n */\n private stateSecretFor(provider: {\n clientSecret?: string;\n }): string | undefined {\n const secret = this.options.stateSecret ?? provider.clientSecret;\n if (secret && secret.length > 0) {\n return secret;\n }\n if (this.options.allowUnsignedState === true) {\n return undefined;\n }\n throw new MobileAuthError(\n 500,\n 'server_misconfigured',\n 'Mobile auth is not configured for signed state. Set a stateSecret (or provider clientSecret), or opt into allowUnsignedState.',\n );\n }\n\n /**\n * `POST /api/mobile/auth/start` — begin the server-brokered PKCE\n * handshake. Returns the authorization URL plus the `state` and\n * `codeVerifier` the client must persist and echo back on complete.\n */\n async start(input: MobileAuthStartRequest): Promise<MobileAuthStartResponse> {\n const resolution = this.resolveProvider(input.providerId);\n const redirectUri = validateMobileRedirectUri(\n input.redirectUri,\n this.options.redirectUris,\n );\n\n const clientState = normalizeOptionalString(input.state);\n if (clientState && clientState.length > MAX_CLIENT_STATE_LENGTH) {\n throw new MobileAuthError(\n 400,\n 'invalid_request',\n `Client state exceeds ${MAX_CLIENT_STATE_LENGTH} characters.`,\n );\n }\n\n const service = this.getOidcService(resolution.providerName, redirectUri);\n const base = service.createTransaction();\n const state = await encodeMobileState(\n {\n v: 1,\n s: randomBase64Url(16),\n n: base.nonce,\n p: resolution.providerName,\n t: Date.now(),\n c: clientState,\n },\n this.stateSecretFor(resolution.provider),\n );\n const transaction: OidcTransaction = { ...base, state };\n\n const scopes = Array.isArray(input.scopes)\n ? input.scopes\n .filter((scope): scope is string => typeof scope === 'string')\n .map((scope) => scope.trim())\n .filter(Boolean)\n : [];\n const loginHint = normalizeOptionalString(input.loginHint);\n\n let url: URL;\n try {\n ({ url } = await service.createAuthorizationUrl({\n transaction,\n authorizationParams: {\n ...(scopes.length > 0 ? { scope: scopes.join(' ') } : {}),\n ...(loginHint ? { login_hint: loginHint } : {}),\n },\n }));\n } catch (error) {\n const message =\n error instanceof OidcLoginError\n ? error.message\n : 'Mobile auth provider is unavailable.';\n throw new MobileAuthError(502, 'provider_unavailable', message);\n }\n\n return {\n providerId: resolution.providerName,\n authorizationUrl: url.toString(),\n state,\n codeVerifier: transaction.codeVerifier,\n nonce: transaction.nonce,\n redirectUri,\n };\n }\n\n /**\n * `POST /api/mobile/auth/complete` — exchange the authorization code (with\n * the echoed `state` + `codeVerifier`) for a mobile bearer session.\n */\n async complete(\n input: MobileAuthCompleteRequest,\n meta: MobileRequestMeta = {},\n ): Promise<MobileAuthSession> {\n const code = normalizeOptionalString(input.code);\n if (!code) {\n throw new MobileAuthError(\n 400,\n 'invalid_request',\n 'Missing authorization code.',\n );\n }\n const rawState = normalizeOptionalString(input.state);\n if (!rawState) {\n throw new MobileAuthError(\n 401,\n 'invalid_state',\n 'Missing mobile auth state.',\n );\n }\n const codeVerifier = normalizeOptionalString(input.codeVerifier);\n if (!codeVerifier) {\n throw new MobileAuthError(\n 400,\n 'missing_code_verifier',\n 'Missing PKCE code verifier.',\n );\n }\n const redirectUri = validateMobileRedirectUri(\n input.redirectUri,\n this.options.redirectUris,\n );\n\n // The state token names the provider the handshake was started for; a\n // client-supplied providerId must agree with it.\n const requestedProvider = normalizeOptionalString(input.providerId);\n const probe = this.resolveProvider(requestedProvider);\n const payload = await decodeMobileState(\n rawState,\n this.stateSecretFor(probe.provider),\n );\n if (payload.p !== probe.providerName) {\n throw new MobileAuthError(\n 401,\n 'invalid_state',\n 'Mobile auth state does not match the requested provider.',\n );\n }\n const age = Date.now() - payload.t;\n if (age > this.transactionTtl * 1000 || age < -STATE_TOKEN_CLOCK_SKEW_MS) {\n throw new MobileAuthError(\n 401,\n 'expired_transaction',\n 'Mobile auth handshake has expired. Start a new sign-in.',\n );\n }\n\n const transaction: OidcTransaction = {\n codeVerifier,\n createdAt: payload.t,\n nonce: payload.n,\n provider: payload.p,\n state: rawState,\n };\n\n const service = this.getOidcService(probe.providerName, redirectUri);\n const callbackUrl = new URL(redirectUri);\n callbackUrl.searchParams.set('code', code);\n callbackUrl.searchParams.set('state', rawState);\n\n let claims: OidcClaims;\n let tokens: OidcTokenSet;\n try {\n ({ claims, tokens } = await service.exchangeCallback(\n callbackUrl,\n transaction,\n ));\n } catch (error) {\n const message =\n error instanceof OidcLoginError\n ? error.message\n : 'Mobile auth code exchange failed.';\n throw new MobileAuthError(401, 'exchange_failed', message);\n }\n\n const resolved = await this.resolveUserFromLogin({\n claims,\n tokens,\n providerName: probe.providerName,\n });\n if (!resolved?.id) {\n throw new MobileAuthError(\n 403,\n 'signin_not_permitted',\n 'This account is not permitted to sign in.',\n );\n }\n\n const tenants = await this.listTenantOptions(resolved.id);\n const tenantId = await this.resolveSessionTenantId(\n { userId: resolved.id, claims, providerName: probe.providerName },\n tenants,\n );\n\n const sessionId = await this.sessionService.createSession(\n resolved.id,\n tenantId ?? undefined,\n {\n ttl: this.sessionTtl,\n userAgent: meta.userAgent,\n ipAddress: meta.ipAddress,\n data: {\n source: 'mobile',\n oidcProvider: probe.providerName,\n // Cap the stored IdP-sourced strings so a hostile/misbehaving IdP\n // cannot bloat every session row (iss is already constrained to the\n // configured issuer; sub is bounded defensively).\n oidcIssuer: claims.iss.slice(0, MAX_STORED_CLAIM_LENGTH),\n oidcSubject: claims.sub.slice(0, MAX_STORED_CLAIM_LENGTH),\n },\n },\n );\n\n const email = resolved.email ?? claims.email ?? '';\n return {\n accessToken: sessionId,\n tokenType: 'Bearer',\n expiresAt: new Date(Date.now() + this.sessionTtl * 1000).toISOString(),\n user: this.toUserSummary(resolved.id, email),\n activeTenant: tenants.find((tenant) => tenant.id === tenantId) ?? null,\n tenants,\n };\n }\n\n private async resolveUserFromLogin(\n context: MobileLoginContext,\n ): Promise<MobileResolvedUser | null | undefined> {\n if (this.options.resolveUser) {\n return this.options.resolveUser(context);\n }\n const result = await this.userCollection.getOrCreateFromOidc(\n context.claims,\n context.providerName,\n { allowUnverifiedEmail: this.options.allowUnverifiedEmail },\n );\n const id = result.user.id as string | undefined;\n if (!id) return null;\n return { id, email: result.user.email };\n }\n\n private async resolveSessionTenantId(\n context: MobileTenantContext,\n tenants: MobileTenantOption[],\n ): Promise<string | null> {\n if (this.options.resolveTenantId) {\n const chosen = await this.options.resolveTenantId(context);\n if (chosen !== undefined) return chosen;\n }\n // Default: the first DIRECT membership tenant (options are sorted by\n // name; inherited descendants are excluded because `switchTenant` — and\n // any flow that verifies direct membership — must accept the default).\n const direct = await runWithSystemContext(() =>\n this.membershipCollection.findActiveByUser(context.userId),\n );\n const directIds = new Set(\n direct\n .map((membership) => membership.tenantId)\n .filter((id): id is string => Boolean(id)),\n );\n return tenants.find((tenant) => directIds.has(tenant.id))?.id ?? null;\n }\n\n /**\n * `GET /api/mobile/session` — bootstrap the app from a bearer token.\n * Throws 401 when the token is missing, unknown, expired, or revoked.\n */\n async bootstrap(\n authorizationHeader: string | null | undefined,\n ): Promise<MobileSessionBootstrap> {\n const context = await this.resolveSessionContext(authorizationHeader);\n const userId = context.user.id as string;\n const tenants = await this.listTenantOptions(userId);\n const activeTenant =\n tenants.find((tenant) => tenant.id === context.tenantId) ?? null;\n\n const extras = this.options.buildExtras\n ? await this.options.buildExtras({\n session: context,\n permissions: context.permissions,\n tenants,\n activeTenant,\n })\n : undefined;\n\n return {\n user: this.toUserSummary(userId, context.user.email ?? ''),\n activeTenant,\n tenants,\n ...(extras ? { extras } : {}),\n };\n }\n\n /**\n * Resolve a bearer header into a full {@link SessionContext} (user,\n * membership, resolved permissions, tenant). Throws {@link MobileAuthError}\n * with the 401 semantics the mobile client's re-auth flow expects.\n */\n async resolveSessionContext(\n authorizationHeader: string | null | undefined,\n ): Promise<SessionContext> {\n const token = readMobileBearerToken(authorizationHeader);\n if (!token) {\n throw new MobileAuthError(\n 401,\n 'missing_bearer_token',\n 'Missing mobile bearer token.',\n );\n }\n // System context: session loading resolves permissions across the\n // user's memberships, which strict tenancy interceptors reject outside\n // an explicit context. Request-scoped tenant entry belongs to the route\n // guard (`withSessionPermissionContext`), not here.\n const context = await runWithSystemContext(() =>\n this.sessionService.loadSessionContext(token),\n );\n if (!context?.user?.id) {\n throw new MobileAuthError(\n 401,\n 'invalid_bearer_token',\n 'Invalid or expired mobile bearer token.',\n );\n }\n return context;\n }\n\n /**\n * `DELETE /api/mobile/session` — revoke the bearer session. Idempotent:\n * a missing or unknown token reports `destroyed: false` with 200.\n */\n async logout(\n authorizationHeader: string | null | undefined,\n ): Promise<MobileLogoutResult> {\n const token = readMobileBearerToken(authorizationHeader);\n if (!token) return { ok: true, destroyed: false };\n const destroyed = await this.sessionService.destroySession(token);\n return { ok: true, destroyed };\n }\n\n /**\n * The user's selectable tenants: direct ACTIVE memberships plus — when\n * `includeInheritedTenants` is on (default) — descendant tenants reachable\n * through an ACTIVE membership whose role has `inheritsToDescendants:\n * true` (#1867). Selection mirrors the permission resolver: the NEAREST\n * flagged ancestor membership labels an inherited option, unflagged or\n * inactive ancestors neither confer nor block, and ANY direct membership\n * row on a tenant pins it (active → its own role; inactive → excluded,\n * since a pinned inactive row resolves to the empty permission set).\n *\n * This list is informational — per-request authorization always re-runs\n * through `PermissionResolver`, which additionally fail-closes on\n * malformed hierarchy paths.\n */\n async listTenantOptions(userId: string): Promise<MobileTenantOption[]> {\n // One system-context entry for the whole cross-tenant computation (session\n // resolution and tenant enumeration span tenants, so a single-tenant\n // context would be wrong); the nested reads below inherit it.\n return runWithSystemContext(() => this.buildTenantOptions(userId));\n }\n\n private async buildTenantOptions(\n userId: string,\n ): Promise<MobileTenantOption[]> {\n const { memberships, tenantById, roleById } =\n await this.loadTenantOptionSources(userId);\n\n const active = memberships.filter(\n (membership) => membership.isActive() && membership.tenantId,\n );\n // Pin from ALL rows (any status), NOT just active ones: a direct\n // membership row always pins its tenant in the resolver, and an INACTIVE\n // direct row pins to the empty set. So an inactive direct row on a\n // descendant must EXCLUDE that descendant from the inherited list (below)\n // — narrowing this to active-only would wrongly surface it as inherited.\n const pinnedTenantIds = new Set(\n memberships\n .map((membership) => membership.tenantId)\n .filter((id): id is string => Boolean(id)),\n );\n\n const options: MobileTenantOption[] = [];\n for (const membership of active) {\n const tenant = tenantById.get(membership.tenantId as string);\n if (!tenant?.id) continue;\n const role = membership.roleId\n ? roleById.get(membership.roleId)\n : undefined;\n options.push(this.toTenantOption(tenant, role));\n }\n\n if (this.options.includeInheritedTenants !== false) {\n const activeByTenant = new Map<string, Membership>();\n for (const membership of active) {\n activeByTenant.set(membership.tenantId as string, membership);\n }\n const isFlagged = (membership: Membership | undefined): boolean => {\n const role = membership?.roleId\n ? roleById.get(membership.roleId)\n : undefined;\n return role?.inheritsToDescendants === true;\n };\n\n // Batch the descendant reads across all flagged memberships instead of\n // awaiting one per membership in series.\n const flaggedTenantIds = active\n .filter(isFlagged)\n .map((membership) => membership.tenantId as string);\n const descendantLists = await Promise.all(\n flaggedTenantIds.map((tenantId) =>\n this.tenantCollection.getDescendants(tenantId),\n ),\n );\n\n const candidates = new Map<string, Tenant>();\n for (const descendants of descendantLists) {\n for (const descendant of descendants) {\n if (!descendant.id) continue;\n if (pinnedTenantIds.has(descendant.id)) continue;\n if (!candidates.has(descendant.id)) {\n candidates.set(descendant.id, descendant);\n }\n }\n }\n\n for (const descendant of candidates.values()) {\n // Nearest ancestor first (getAncestorIds is root → parent order).\n const ancestorIds = descendant.getAncestorIds();\n for (let i = ancestorIds.length - 1; i >= 0; i -= 1) {\n const ancestorMembership = activeByTenant.get(ancestorIds[i]);\n if (!isFlagged(ancestorMembership)) continue;\n const role = roleById.get(ancestorMembership?.roleId as string);\n options.push(this.toTenantOption(descendant, role));\n break;\n }\n }\n }\n\n return options.sort(\n (a, b) =>\n (a.name ?? '').localeCompare(b.name ?? '', undefined, {\n sensitivity: 'base',\n }) || a.id.localeCompare(b.id),\n );\n }\n\n private async loadTenantOptionSources(\n userId: string,\n ): Promise<TenantOptionSources> {\n const memberships = await this.membershipCollection.findByUser(userId);\n const active = memberships.filter(\n (membership) => membership.isActive() && membership.tenantId,\n );\n\n const tenantIds = [\n ...new Set(active.map((membership) => membership.tenantId as string)),\n ];\n const roleIds = [\n ...new Set(\n active\n .map((membership) => membership.roleId)\n .filter((id): id is string => Boolean(id)),\n ),\n ];\n\n const [tenants, roles] = await Promise.all([\n tenantIds.length > 0\n ? this.tenantCollection.listByIds(tenantIds)\n : Promise.resolve([]),\n roleIds.length > 0\n ? this.roleCollection.listByIds(roleIds)\n : Promise.resolve([]),\n ]);\n\n return {\n memberships,\n tenantById: new Map(\n tenants\n .filter((tenant): tenant is Tenant => Boolean(tenant?.id))\n .map((tenant) => [tenant.id as string, tenant]),\n ),\n roleById: new Map(\n roles\n .filter((role): role is Role => Boolean(role?.id))\n .map((role) => [role.id as string, role]),\n ),\n };\n }\n\n private toTenantOption(tenant: Tenant, role: Role | undefined) {\n return {\n id: tenant.id as string,\n name: tenant.name ?? '',\n slug: tenant.slug ?? '',\n roleSlug: role?.slug ?? '',\n roleLabel: role?.name || role?.slug || '',\n } satisfies MobileTenantOption;\n }\n\n private toUserSummary(id: string, email: string): MobileUserSummary {\n return { id, email, label: email || id };\n }\n}\n","/**\n * Request-scoped session permission context helpers\n * @packageDocumentation\n */\n\nimport { AsyncLocalStorage } from 'node:async_hooks';\nimport { getPackageConfig } from '@happyvertical/smrt-config';\nimport type { SmrtClassOptions } from '@happyvertical/smrt-core';\nimport type { DatabaseInterface } from '@happyvertical/sql';\nimport type { Membership } from '../models/Membership.js';\nimport { PermissionResolver } from './PermissionResolver.js';\nimport {\n type SessionContext,\n SessionService,\n type SessionServiceOptions,\n} from './SessionService.js';\n\ninterface QueryableDatabase extends DatabaseInterface {\n beginTransaction?: () => Promise<TransactionDatabase>;\n url: string;\n}\n\ninterface TransactionDatabase extends QueryableDatabase {\n commit: () => Promise<void>;\n isActive: () => boolean;\n rollback: () => Promise<void>;\n}\n\nexport interface SessionPermissionRuntimeContext {\n database?: QueryableDatabase;\n permissions: string[];\n permissionSet: Set<string>;\n membership: SessionContext['membership'];\n postgresRls: boolean;\n session: SessionContext | null;\n sessionId: string | null;\n superAdminBypass: boolean;\n systemContext: boolean;\n tenantId: string | null;\n user: SessionContext['user'] | null;\n userId: string | null;\n}\n\nexport interface SessionPermissionRuntimeOptions extends SmrtClassOptions {\n enterTenantContext?: boolean;\n postgresRls?: boolean;\n sessionId?: string | null;\n sessionService?: SessionService;\n superAdminBypass?: boolean;\n systemContext?: boolean;\n}\n\n/**\n * Options for {@link withPrincipalPermissionContext}.\n *\n * Where {@link SessionPermissionRuntimeOptions} sources the principal from a\n * loaded session, this sources it directly from a `(userId, tenantId)` pair —\n * used to run work AS a bound principal (e.g. an agent persona's `runAsUserId`)\n * with that principal's *live* resolved permissions.\n */\nexport interface PrincipalPermissionRuntimeOptions extends SmrtClassOptions {\n /** Enter tenant context so tenant auto-filtering applies on every adapter. */\n enterTenantContext?: boolean;\n /**\n * Raw membership row for the `(userId, tenantId)` pair, forwarded to\n * {@link PermissionResolver.resolvePermissions}. Only consulted when\n * `permissions` is not supplied. Pass `null` to assert \"no direct membership\"\n * (enabling opt-in ancestor inheritance); leave `undefined` to let the\n * resolver look it up.\n */\n membership?: Membership | null;\n /**\n * Pre-resolved permission slugs. When omitted, the principal's permissions\n * are resolved LIVE via {@link PermissionResolver.resolvePermissions} so role\n * changes reflect on the next call.\n */\n permissions?: string[];\n /** Opt into Postgres RLS transaction wrapping (defaults to package config). */\n postgresRls?: boolean;\n /** Reuse an initialized resolver instead of creating one per call. */\n resolver?: PermissionResolver;\n /** Tenant the principal acts within. `null` resolves to no permissions. */\n tenantId: string | null;\n /** The user whose live permissions bound this execution. */\n userId: string;\n}\n\n/**\n * The controls shared by both permission-context entry points that influence\n * transaction wrapping and tenant-context entry.\n */\ninterface PermissionRuntimeControls {\n enterTenantContext?: boolean;\n postgresRls?: boolean;\n systemContext?: boolean;\n}\n\ntype PermissionRuntimeDatabaseConfig =\n | SmrtClassOptions['db']\n | SmrtClassOptions['persistence'];\n\ndeclare global {\n // eslint-disable-next-line no-var\n var __smrtGetRequestPermissionContext:\n | (() => SessionPermissionRuntimeContext | undefined)\n | undefined;\n // eslint-disable-next-line no-var\n var __smrtGetRequestScopedDatabase:\n | (() => QueryableDatabase | undefined)\n | undefined;\n}\n\n// globalThis key for the shared AsyncLocalStorage. Bundler pipelines can\n// evaluate this module more than once from a single copy on disk; a\n// module-local storage would strand context entered through one instance\n// (the `globalThis.__smrtGetRequestPermissionContext` getter below only helps\n// readers reach the FIRST instance's storage — a second instance running\n// `.run()` would still be invisible). Symbol.for() resolves identically in\n// every instance, so all of them share the one storage.\nconst PERMISSION_STORAGE_KEY = Symbol.for(\n 'smrt:session-permission-context-storage',\n);\n\nfunction resolvePermissionContextStorage(): AsyncLocalStorage<SessionPermissionRuntimeContext> {\n const root = globalThis as typeof globalThis & {\n [PERMISSION_STORAGE_KEY]?: AsyncLocalStorage<SessionPermissionRuntimeContext>;\n };\n root[PERMISSION_STORAGE_KEY] ??=\n new AsyncLocalStorage<SessionPermissionRuntimeContext>();\n return root[PERMISSION_STORAGE_KEY];\n}\n\nconst requestPermissionContextStorage = resolvePermissionContextStorage();\n\nexport function getCurrentSessionPermissionContext():\n | SessionPermissionRuntimeContext\n | undefined {\n return requestPermissionContextStorage.getStore();\n}\n\nexport function getRequestScopedDatabase(): QueryableDatabase | undefined {\n return getCurrentSessionPermissionContext()?.database;\n}\n\nglobalThis.__smrtGetRequestPermissionContext ??=\n getCurrentSessionPermissionContext;\nglobalThis.__smrtGetRequestScopedDatabase ??= getRequestScopedDatabase;\n\nfunction isProbablyPostgres(\n configDb: SmrtClassOptions['db'],\n database: QueryableDatabase,\n): boolean {\n if (\n configDb &&\n typeof configDb === 'object' &&\n !('query' in configDb) &&\n 'type' in configDb &&\n configDb.type === 'postgres'\n ) {\n return true;\n }\n\n if (typeof database.url === 'string' && database.url.startsWith('postgres')) {\n return true;\n }\n\n return (database.constructor?.name || '').toLowerCase().includes('postgres');\n}\n\nasync function setPostgresSessionVariables(\n database: QueryableDatabase,\n context: SessionPermissionRuntimeContext,\n): Promise<void> {\n await database.query(\n \"SELECT set_config('smrt.tenant_id', $1, true)\",\n context.tenantId ?? '',\n );\n await database.query(\n \"SELECT set_config('smrt.user_id', $1, true)\",\n context.userId ?? '',\n );\n await database.query(\n \"SELECT set_config('smrt.session_id', $1, true)\",\n context.sessionId ?? '',\n );\n await database.query(\n \"SELECT set_config('smrt.permissions', $1, true)\",\n JSON.stringify(context.permissions),\n );\n await database.query(\n \"SELECT set_config('smrt.super_admin_bypass', $1, true)\",\n context.superAdminBypass ? 'true' : 'false',\n );\n await database.query(\n \"SELECT set_config('smrt.system_context', $1, true)\",\n context.systemContext ? 'true' : 'false',\n );\n}\n\nfunction isModuleNotFoundError(error: unknown): boolean {\n return (\n error instanceof Error &&\n ((error as NodeJS.ErrnoException).code === 'ERR_MODULE_NOT_FOUND' ||\n /Cannot find package '@happyvertical\\/smrt-tenancy'/.test(error.message))\n );\n}\n\nasync function runWithOptionalTenantContext<T>(\n controls: PermissionRuntimeControls,\n context: SessionPermissionRuntimeContext,\n fn: () => Promise<T>,\n): Promise<T> {\n if (controls.systemContext) {\n try {\n const tenancy = await import('@happyvertical/smrt-tenancy');\n return await tenancy.withSystemContext(fn);\n } catch (error) {\n if (isModuleNotFoundError(error)) {\n return await fn();\n }\n throw error;\n }\n }\n\n if (!controls.enterTenantContext || !context.tenantId) {\n return await fn();\n }\n\n try {\n const tenancy = await import('@happyvertical/smrt-tenancy');\n return await tenancy.withTenant(\n {\n database: context.database,\n permissions: context.permissionSet,\n superAdminBypass: context.superAdminBypass,\n tenantId: context.tenantId,\n user: context.user ?? undefined,\n userId: context.userId ?? undefined,\n },\n fn,\n );\n } catch (error) {\n if (isModuleNotFoundError(error)) {\n return await fn();\n }\n throw error;\n }\n}\n\n/**\n * Shared runtime: given an already-resolved principal `spec` and the base\n * database, optionally open a Postgres RLS transaction, publish the principal\n * onto the DB session (`set_config(..., is_local=true)`), and run `fn` inside\n * the request-scoped ALS store (and optional tenant context).\n *\n * Both {@link withSessionPermissionContext} (principal from a loaded session)\n * and {@link withPrincipalPermissionContext} (principal from a `userId` +\n * live-resolved permissions) funnel through here, so the transaction lifecycle\n * and the exact DB-session publication stay identical no matter which door the\n * principal entered through.\n */\nasync function runWithinPermissionRuntime<T>(\n controls: PermissionRuntimeControls,\n configuredDb: PermissionRuntimeDatabaseConfig,\n baseDatabase: QueryableDatabase,\n spec: {\n permissions: string[];\n membership: SessionContext['membership'];\n session: SessionContext | null;\n sessionId: string | null;\n superAdminBypass: boolean;\n systemContext: boolean;\n tenantId: string | null;\n user: SessionContext['user'] | null;\n userId: string | null;\n },\n fn: (context: SessionPermissionRuntimeContext) => Promise<T>,\n): Promise<T> {\n const config = getPackageConfig<{\n permissions?: { postgres?: { enabled?: boolean } };\n }>('users', {});\n const usePostgresRls =\n (controls.postgresRls ?? config.permissions?.postgres?.enabled ?? false) &&\n isProbablyPostgres(configuredDb, baseDatabase);\n\n let transaction: TransactionDatabase | undefined;\n let rolledBack = false;\n if (usePostgresRls) {\n if (!baseDatabase.beginTransaction) {\n throw new Error(\n 'Postgres RLS requires a database adapter that supports beginTransaction().',\n );\n }\n transaction = await baseDatabase.beginTransaction();\n }\n\n const runtimeContext: SessionPermissionRuntimeContext = {\n database: transaction ?? baseDatabase,\n permissions: spec.permissions,\n permissionSet: new Set(spec.permissions),\n membership: spec.membership,\n postgresRls: usePostgresRls,\n session: spec.session,\n sessionId: spec.sessionId,\n superAdminBypass: spec.superAdminBypass,\n systemContext: spec.systemContext,\n tenantId: spec.tenantId,\n user: spec.user,\n userId: spec.userId,\n };\n\n try {\n if (transaction) {\n await setPostgresSessionVariables(transaction, runtimeContext);\n }\n\n return await requestPermissionContextStorage.run(runtimeContext, async () =>\n runWithOptionalTenantContext(controls, runtimeContext, () =>\n fn(runtimeContext),\n ),\n );\n } catch (error) {\n if (transaction) {\n const active = transaction.isActive ? transaction.isActive() : true;\n if (active) {\n rolledBack = true;\n await transaction.rollback();\n }\n }\n throw error;\n } finally {\n if (transaction && !rolledBack) {\n const active = transaction.isActive ? transaction.isActive() : true;\n if (active) {\n await transaction.commit();\n }\n }\n }\n}\n\nexport async function withSessionPermissionContext<T>(\n options: SessionPermissionRuntimeOptions,\n fn: (context: SessionPermissionRuntimeContext) => Promise<T>,\n): Promise<T> {\n const {\n enterTenantContext: _enterTenantContext,\n postgresRls: _postgresRls,\n sessionId: _sessionId,\n sessionService: _sessionService,\n superAdminBypass: _superAdminBypass,\n systemContext: _systemContext,\n ...sessionServiceOptions\n } = options;\n const sessionService =\n options.sessionService ??\n (await SessionService.create({\n ...sessionServiceOptions,\n } as SessionServiceOptions));\n const configuredDb =\n (sessionServiceOptions as SessionServiceOptions).db ??\n (sessionServiceOptions as SessionServiceOptions).persistence;\n const session =\n options.sessionId === undefined || options.sessionId === null\n ? null\n : await sessionService.loadSessionContext(options.sessionId);\n const baseDatabase = sessionService.getDatabase() as QueryableDatabase;\n\n return runWithinPermissionRuntime(\n {\n enterTenantContext: options.enterTenantContext,\n postgresRls: options.postgresRls,\n systemContext: options.systemContext,\n },\n configuredDb,\n baseDatabase,\n {\n permissions: session?.permissions ?? [],\n membership: session?.membership ?? null,\n session,\n sessionId: session?.sessionId ?? null,\n superAdminBypass: options.superAdminBypass ?? false,\n systemContext: options.systemContext ?? false,\n tenantId: session?.tenantId ?? null,\n user: session?.user ?? null,\n userId: session?.user.id ?? null,\n },\n fn,\n );\n}\n\n/**\n * Run `fn` AS a bound principal — a `(userId, tenantId)` pair rather than a\n * session — inside a session-permission context carrying that principal's\n * **live** resolved permissions.\n *\n * This is the door-agnostic mechanism behind \"execute as principal\": it does\n * NOT introduce a new authorization layer. It resolves the principal's\n * permissions through the standard {@link PermissionResolver} cascade (unless\n * the caller passes a pre-resolved set) and publishes `(smrt.user_id,\n * smrt.tenant_id, smrt.permissions[])` onto the DB session exactly like\n * {@link withSessionPermissionContext}, so the manifest-derived Postgres RLS\n * policies bound every query on that session per-`(table, action)`.\n *\n * A principal run never bypasses: `super_admin_bypass` and `system_context`\n * are always published as `false`. Resolution runs on the base connection\n * BEFORE any RLS transaction opens (identical ordering to session loading), so\n * the trusted cascade is never bounded by the not-yet-published principal.\n *\n * With RLS off (SQLite/dev) the same permission set is still resolved and\n * carried, but the DB has no per-operation teeth — callers relying on this on a\n * non-Postgres adapter must additionally assert the catalog permission for the\n * `(collection, action)` at their tool/exec seam (see\n * `assertOperationPermission`).\n */\nexport async function withPrincipalPermissionContext<T>(\n options: PrincipalPermissionRuntimeOptions,\n fn: (context: SessionPermissionRuntimeContext) => Promise<T>,\n): Promise<T> {\n const {\n enterTenantContext: _enterTenantContext,\n membership,\n permissions: providedPermissions,\n postgresRls: _postgresRls,\n resolver: providedResolver,\n tenantId,\n userId,\n ...serviceOptions\n } = options;\n\n // Reuse SessionService purely as the blessed database accessor — no session\n // is loaded on this path; the principal comes straight from (userId,\n // tenantId).\n const sessionService = await SessionService.create({\n ...serviceOptions,\n } as SessionServiceOptions);\n const configuredDb =\n (serviceOptions as SessionServiceOptions).db ??\n (serviceOptions as SessionServiceOptions).persistence;\n const baseDatabase = sessionService.getDatabase() as QueryableDatabase;\n\n // Resolve the bound principal's LIVE permission set unless the caller passed\n // one explicitly. Distinguish \"not provided\" (`undefined` → resolve live)\n // from \"explicitly empty\" (`[]` → run with ZERO permissions): a truthy test\n // would treat an intentional empty set as \"not provided\" and fail open. A\n // missing tenant likewise resolves to no permissions (fail closed).\n let permissions: string[];\n if (providedPermissions !== undefined) {\n permissions = providedPermissions;\n } else if (tenantId) {\n const resolver =\n providedResolver ??\n (await PermissionResolver.create({\n ...serviceOptions,\n } as SmrtClassOptions));\n const resolved = await resolver.resolvePermissions(\n userId,\n tenantId,\n membership === undefined ? {} : { membership },\n );\n permissions = Array.from(resolved.permissions);\n } else {\n permissions = [];\n }\n\n return runWithinPermissionRuntime(\n {\n enterTenantContext: options.enterTenantContext,\n postgresRls: options.postgresRls,\n systemContext: false,\n },\n configuredDb,\n baseDatabase,\n {\n permissions,\n membership: null,\n session: null,\n sessionId: null,\n superAdminBypass: false,\n systemContext: false,\n tenantId: tenantId ?? null,\n user: null,\n userId,\n },\n fn,\n );\n}\n","/**\n * Reusable operation-level permission guard for hand-written server surfaces.\n * @packageDocumentation\n */\n\nimport type { SmrtClassOptions } from '@happyvertical/smrt-core';\nimport {\n deriveOperationPermissionSlug,\n type OperationPermissionCollectionInput,\n type PermissionCatalog,\n PermissionCatalogService,\n} from './PermissionCatalogService.js';\nimport {\n type PermissionResolutionOptions,\n PermissionResolver,\n} from './PermissionResolver.js';\nimport { getCurrentSessionPermissionContext } from './SessionPermissionContext.js';\n\nexport type OperationPermissionDenyReason =\n | 'invalid_operation'\n | 'missing_principal'\n | 'resolution_error'\n | 'unknown_permission'\n | 'permission_denied';\n\nexport type OperationPermissionAllowReason =\n | 'permission_granted'\n | 'super_admin_bypass'\n | 'system_context_bypass';\n\nexport interface OperationPermissionDecision {\n allowed: boolean;\n permission: string | null;\n reason: OperationPermissionAllowReason | OperationPermissionDenyReason;\n error?: unknown;\n}\n\nexport interface OperationPermissionOptions\n extends SmrtClassOptions,\n PermissionResolutionOptions {\n /**\n * Collection slug, model class, model instance, or collection instance.\n */\n collection: OperationPermissionCollectionInput;\n /**\n * Operation action. `list` and `get` normalize to the catalog's `.read`.\n */\n action: string;\n /**\n * Default true. When false, a super-admin tenant context must still hold the\n * resolved permission.\n */\n allowSuperAdminBypass?: boolean;\n /**\n * Default true. System context represents trusted server-side execution.\n */\n allowSystemContextBypass?: boolean;\n catalog?: PermissionCatalog;\n onDeny?: 'return' | 'throw';\n resolver?: PermissionResolver;\n tenantId?: string | null;\n userId?: string | null;\n /**\n * When provided, authorize against THIS exact permission set instead of\n * re-resolving live RBAC. Pass the published principal set\n * (`context.permissionSet`) so an RLS-off catalog gate enforces the same\n * snapshot authority the RLS-on session published — keeping the authority\n * bound adapter-independent. Without it a mid-run role grant, or a reduced\n * pre-resolved `permissions` set, could let SQLite/dev allow an operation the\n * Postgres RLS path for the same principal context would deny.\n */\n permissionSet?: ReadonlySet<string> | readonly string[];\n}\n\nexport class OperationPermissionError extends Error {\n readonly decision: OperationPermissionDecision;\n readonly permission: string | null;\n readonly status = 403;\n\n constructor(decision: OperationPermissionDecision) {\n super(\n decision.permission\n ? `Permission denied for '${decision.permission}'.`\n : 'Permission denied.',\n );\n this.name = 'OperationPermissionError';\n this.decision = decision;\n this.permission = decision.permission;\n }\n}\n\ninterface RuntimeBypassContext {\n superAdminBypass: boolean;\n systemContext: boolean;\n}\n\nfunction isModuleNotFoundError(error: unknown): boolean {\n return (\n error instanceof Error &&\n ((error as NodeJS.ErrnoException).code === 'ERR_MODULE_NOT_FOUND' ||\n /Cannot find package '@happyvertical\\/smrt-tenancy'/.test(error.message))\n );\n}\n\nasync function readTenancyBypassContext(): Promise<RuntimeBypassContext> {\n try {\n const tenancy = await import('@happyvertical/smrt-tenancy');\n return {\n superAdminBypass: tenancy.isSuperAdminBypass(),\n systemContext: tenancy.isSystemContext(),\n };\n } catch (error) {\n if (!isModuleNotFoundError(error)) {\n throw error;\n }\n\n return {\n superAdminBypass: false,\n systemContext: false,\n };\n }\n}\n\nfunction deny(\n permission: string | null,\n reason: OperationPermissionDenyReason,\n error?: unknown,\n): OperationPermissionDecision {\n return {\n allowed: false,\n error,\n permission,\n reason,\n };\n}\n\nfunction allow(\n permission: string,\n reason: OperationPermissionAllowReason,\n): OperationPermissionDecision {\n return {\n allowed: true,\n permission,\n reason,\n };\n}\n\nfunction hasCatalogSlug(\n catalog: PermissionCatalog,\n permission: string,\n): boolean {\n return catalog.permissions.some(\n (definition) => definition.slug === permission,\n );\n}\n\nexport async function checkOperationPermission(\n options: OperationPermissionOptions,\n): Promise<OperationPermissionDecision> {\n let permission: string;\n try {\n permission = deriveOperationPermissionSlug(\n options.collection,\n options.action,\n );\n } catch (error) {\n return deny(null, 'invalid_operation', error);\n }\n\n const catalog =\n options.catalog ?? PermissionCatalogService.create(options).getCatalog();\n if (!hasCatalogSlug(catalog, permission)) {\n return deny(permission, 'unknown_permission');\n }\n\n const sessionContext = getCurrentSessionPermissionContext();\n let tenancyContext: RuntimeBypassContext;\n try {\n tenancyContext = await readTenancyBypassContext();\n } catch (error) {\n return deny(permission, 'resolution_error', error);\n }\n const systemContext =\n sessionContext?.systemContext === true || tenancyContext.systemContext;\n const superAdminBypass =\n sessionContext?.superAdminBypass === true ||\n tenancyContext.superAdminBypass;\n\n if (systemContext && options.allowSystemContextBypass !== false) {\n return allow(permission, 'system_context_bypass');\n }\n\n if (superAdminBypass && options.allowSuperAdminBypass !== false) {\n return allow(permission, 'super_admin_bypass');\n }\n\n const userId = options.userId ?? sessionContext?.userId ?? null;\n const tenantId = options.tenantId ?? sessionContext?.tenantId ?? null;\n if (!userId || !tenantId) {\n return deny(permission, 'missing_principal');\n }\n\n // Explicit set wins over a live re-resolve: authorize against the exact\n // published principal set so the gate matches what an RLS session enforces\n // for the same context (adapter-independent, snapshot-consistent).\n if (options.permissionSet !== undefined) {\n const granted = Array.isArray(options.permissionSet)\n ? options.permissionSet.includes(permission)\n : (options.permissionSet as ReadonlySet<string>).has(permission);\n return granted\n ? allow(permission, 'permission_granted')\n : deny(permission, 'permission_denied');\n }\n\n try {\n const resolver =\n options.resolver ?? (await PermissionResolver.create(options));\n const hasPermission = await resolver.hasPermission(\n userId,\n tenantId,\n permission,\n { membership: options.membership },\n );\n return hasPermission\n ? allow(permission, 'permission_granted')\n : deny(permission, 'permission_denied');\n } catch (error) {\n return deny(permission, 'resolution_error', error);\n }\n}\n\nexport async function hasOperationPermission(\n options: OperationPermissionOptions,\n): Promise<boolean> {\n return (await checkOperationPermission(options)).allowed;\n}\n\nexport async function assertOperationPermission(\n options: OperationPermissionOptions,\n): Promise<OperationPermissionDecision> {\n const decision = await checkOperationPermission(options);\n if (decision.allowed || options.onDeny === 'return') {\n return decision;\n }\n\n throw new OperationPermissionError(decision);\n}\n","/**\n * TerminalAuthService — device-code grant for terminal/CLI logins.\n *\n * Flow:\n * 1. CLI calls `createRequest(origin)` → user code (short, displayed) + device\n * code (long, kept secret) + verification URL.\n * 2. User opens the verification URL in a browser, signs in normally, and\n * POSTs the user code via `approveRequest({ user, tenantId, userCode })`.\n * 3. CLI polls `exchangeDeviceCode(deviceCode)`; once approved, gets back the\n * bearer session id (an opaque token usable as `Authorization: Bearer …`).\n * 4. Server-side request handlers resolve incoming bearer tokens via\n * `loadBearerSession(token)`, and revoke them via `destroyBearerSession`.\n *\n * The service is framework-agnostic: it does not set cookies, parse Request\n * objects, or render pages. SvelteKit glue lives in the package's\n * `./sveltekit` subpath.\n *\n * @packageDocumentation\n */\n\nimport { createHash, randomBytes } from 'node:crypto';\nimport type { SmrtClassOptions } from '@happyvertical/smrt-core';\nimport { UsersCliAuthRequestCollection } from '../collections/CliAuthRequestCollection.js';\nimport type {\n CliAuthRequestStatus,\n UsersCliAuthRequest,\n} from '../models/CliAuthRequest.js';\nimport type { User } from '../models/User.js';\nimport { type SessionContext, SessionService } from './SessionService.js';\n\n/** Default polling interval the CLI should honour while waiting for approval. */\nexport const DEFAULT_CLI_AUTH_POLL_INTERVAL_SECONDS = 2;\n/** Default lifetime of a pending request (10 minutes). */\nexport const DEFAULT_CLI_AUTH_REQUEST_TTL_SECONDS = 10 * 60;\n/** Default lifetime of the session minted on approval (30 days). */\nexport const DEFAULT_CLI_SESSION_TTL_SECONDS = 30 * 24 * 60 * 60;\n/** Default cap on consecutive failed approve attempts per user before lockout. */\nexport const DEFAULT_CLI_AUTH_MAX_APPROVE_ATTEMPTS = 5;\n/** Default sliding window for counting failed approve attempts (5 minutes). */\nexport const DEFAULT_CLI_AUTH_APPROVE_ATTEMPT_WINDOW_SECONDS = 5 * 60;\n\n/**\n * Options for {@link TerminalAuthService}.\n */\nexport interface TerminalAuthServiceOptions extends SmrtClassOptions {\n /**\n * Cookie name passed through to {@link SessionService}. Should match the\n * site's normal session cookie name so the same SessionService can\n * resolve both browser cookies and CLI bearer tokens.\n */\n sessionCookieName?: string;\n /**\n * Prefix prepended to randomly generated user codes — purely for human\n * recognition (\"WG-1A2B3C4D\"). Defaults to no prefix.\n */\n userCodePrefix?: string;\n /** TTL of a pending request, in seconds. */\n requestTtlSeconds?: number;\n /** TTL of the session minted on approval, in seconds. */\n sessionTtlSeconds?: number;\n /** Polling interval hint returned to clients. */\n pollIntervalSeconds?: number;\n /**\n * Path on the site's origin where the approval page is mounted. Used to\n * build the verification URL returned to the CLI. Defaults to\n * `/terminal-login`.\n */\n verificationPath?: string;\n /** Whether the underlying SessionService should auto-extend on access. */\n sessionAutoExtend?: boolean;\n /**\n * Maximum consecutive failed approve attempts allowed per user inside the\n * sliding window before the user is locked out from approving any further\n * codes. Defaults to {@link DEFAULT_CLI_AUTH_MAX_APPROVE_ATTEMPTS}.\n *\n * User codes are only 32 bits of entropy; without this throttle, an\n * authenticated attacker could brute-force pending codes and hijack\n * another user's CLI session by approving it themselves.\n */\n maxApproveAttempts?: number;\n /**\n * Sliding window (seconds) over which {@link maxApproveAttempts} counts.\n * Defaults to {@link DEFAULT_CLI_AUTH_APPROVE_ATTEMPT_WINDOW_SECONDS}.\n */\n approveAttemptWindowSeconds?: number;\n}\n\n/**\n * What `createRequest` returns to the CLI.\n */\nexport interface CliAuthStartResult {\n deviceCode: string;\n expiresAt: string;\n interval: number;\n userCode: string;\n verificationUrl: string;\n}\n\n/**\n * What `exchangeDeviceCode` returns to the polling CLI.\n */\nexport type CliAuthTokenResult =\n | { status: 'pending'; expiresAt: string; interval: number }\n | { status: 'expired' }\n | {\n status: 'approved';\n accessToken: string;\n expiresIn: number;\n tokenType: 'Bearer';\n };\n\n/**\n * Inputs for `approveRequest`. The caller is responsible for authenticating\n * the user via its existing browser session before calling this.\n */\nexport interface ApproveCliAuthRequestInput {\n userCode: string;\n user: Pick<User, 'id' | 'email'>;\n tenantId: string | null | undefined;\n ipAddress?: string;\n userAgent?: string;\n}\n\nfunction base64url(bytes: Buffer): string {\n return bytes.toString('base64url');\n}\n\nfunction hashDeviceCode(deviceCode: string): string {\n return createHash('sha256').update(deviceCode).digest('hex');\n}\n\nfunction isExpired(request: UsersCliAuthRequest): boolean {\n return new Date(request.expiresAt).getTime() <= Date.now();\n}\n\n/**\n * High-level orchestration of the terminal device-code flow.\n */\nexport class TerminalAuthService {\n private readonly options: TerminalAuthServiceOptions;\n private readonly userCodePrefix: string;\n private readonly requestTtlSeconds: number;\n private readonly sessionTtlSeconds: number;\n private readonly pollIntervalSeconds: number;\n private readonly verificationPath: string;\n private readonly maxApproveAttempts: number;\n private readonly approveAttemptWindowMs: number;\n private readonly failedApprovesByUser = new Map<\n string,\n { count: number; firstAttemptAt: number }\n >();\n private requestCollection!: UsersCliAuthRequestCollection;\n private sessionService!: SessionService;\n\n constructor(options: TerminalAuthServiceOptions) {\n this.options = options;\n // Lookups uppercase the submitted code before querying, so the stored\n // form must also be uppercase — normalize once here so consumers can\n // pass any case.\n this.userCodePrefix = (options.userCodePrefix ?? '').trim().toUpperCase();\n this.requestTtlSeconds =\n options.requestTtlSeconds ?? DEFAULT_CLI_AUTH_REQUEST_TTL_SECONDS;\n this.sessionTtlSeconds =\n options.sessionTtlSeconds ?? DEFAULT_CLI_SESSION_TTL_SECONDS;\n this.pollIntervalSeconds =\n options.pollIntervalSeconds ?? DEFAULT_CLI_AUTH_POLL_INTERVAL_SECONDS;\n this.maxApproveAttempts =\n options.maxApproveAttempts ?? DEFAULT_CLI_AUTH_MAX_APPROVE_ATTEMPTS;\n this.approveAttemptWindowMs =\n (options.approveAttemptWindowSeconds ??\n DEFAULT_CLI_AUTH_APPROVE_ATTEMPT_WINDOW_SECONDS) * 1000;\n const verificationPath = options.verificationPath ?? '/terminal-login';\n this.verificationPath = verificationPath.startsWith('/')\n ? verificationPath\n : `/${verificationPath}`;\n }\n\n async initialize(): Promise<void> {\n this.requestCollection = await UsersCliAuthRequestCollection.create(\n this.options,\n );\n this.sessionService = await SessionService.create({\n ...this.options,\n autoExtend: this.options.sessionAutoExtend ?? true,\n cookieName: this.options.sessionCookieName,\n defaultTTL: this.sessionTtlSeconds,\n });\n }\n\n static async create(\n options: TerminalAuthServiceOptions,\n ): Promise<TerminalAuthService> {\n const service = new TerminalAuthService(options);\n await service.initialize();\n return service;\n }\n\n private makeUserCode(): string {\n const raw = randomBytes(6).toString('hex').toUpperCase().slice(0, 8);\n return this.userCodePrefix ? `${this.userCodePrefix}-${raw}` : raw;\n }\n\n /**\n * Generate a user code that is not currently in use by another *active*\n * (pending or approved-but-unclaimed) request. Collisions are vanishingly\n * rare (1 in 2^32 per attempt) but happen at scale; retrying keeps the\n * affected CLI from being locked out.\n */\n private async makeUniqueUserCode(maxAttempts = 5): Promise<string> {\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n const candidate = this.makeUserCode();\n const existing = await this.requestCollection.findByUserCode(candidate);\n if (!existing || existing.status === 'expired') {\n return candidate;\n }\n }\n throw new TerminalAuthError(\n 'Unable to generate a unique terminal user code; try again.',\n );\n }\n\n /**\n * Start a new request. Returns the device code the CLI keeps secret, the\n * user code the human types into the browser, and the verification URL to\n * open. The device code is stored only as a hash.\n */\n async createRequest(origin: string): Promise<CliAuthStartResult> {\n const trimmedOrigin = origin.replace(/\\/+$/u, '');\n const deviceCode = base64url(randomBytes(32));\n const userCode = await this.makeUniqueUserCode();\n const expiresAt = new Date(Date.now() + this.requestTtlSeconds * 1000);\n\n const request = await this.requestCollection.create({\n deviceCodeHash: hashDeviceCode(deviceCode),\n expiresAt,\n status: 'pending',\n userCode,\n });\n await request.save();\n\n return {\n deviceCode,\n expiresAt: expiresAt.toISOString(),\n interval: this.pollIntervalSeconds,\n userCode,\n verificationUrl: `${trimmedOrigin}${this.verificationPath}?code=${encodeURIComponent(userCode)}`,\n };\n }\n\n /**\n * Look up a request by user code. Performs lazy expiry: pending requests\n * past their TTL are flipped to `expired` and persisted.\n */\n async getRequestForUserCode(\n userCode: string,\n ): Promise<UsersCliAuthRequest | null> {\n const request = await this.requestCollection.findByUserCode(userCode);\n if (!request) return null;\n\n if (request.status === 'pending' && isExpired(request)) {\n request.status = 'expired';\n await request.save();\n }\n return request;\n }\n\n /**\n * Mark a pending request as approved and mint a bearer session bound to the\n * approving user. Idempotent: re-approving an already-approved request is a\n * no-op. Throws if the user/tenant are missing, the request is unknown, or\n * the request has expired.\n */\n async approveRequest(\n input: ApproveCliAuthRequestInput,\n ): Promise<UsersCliAuthRequest> {\n if (!input.user.id || !input.tenantId) {\n throw new TerminalAuthError(\n 'An authenticated tenant session is required.',\n );\n }\n\n this.assertApproveRateLimit(input.user.id);\n\n const request = await this.getRequestForUserCode(input.userCode);\n if (!request) {\n this.recordFailedApprove(input.user.id);\n throw new TerminalAuthError('Terminal login request not found.');\n }\n if (request.status === 'approved') {\n // Idempotent success — don't penalize a re-approval.\n return request;\n }\n if (request.status !== 'pending' || isExpired(request)) {\n request.status = 'expired';\n await request.save();\n this.recordFailedApprove(input.user.id);\n throw new TerminalAuthError('Terminal login request has expired.');\n }\n\n const sessionId = await this.sessionService.createSession(\n input.user.id,\n input.tenantId,\n {\n data: {\n approvedBy: input.user.email ?? input.user.id,\n kind: 'terminal',\n },\n ipAddress: input.ipAddress,\n ttl: this.sessionTtlSeconds,\n userAgent: input.userAgent,\n },\n );\n\n request.approvedAt = new Date();\n request.sessionId = sessionId;\n request.status = 'approved';\n request.tenantId = input.tenantId;\n request.userId = input.user.id;\n await request.save();\n this.failedApprovesByUser.delete(input.user.id);\n return request;\n }\n\n /**\n * Throw {@link TerminalAuthRateLimitError} if the user has exceeded the\n * configured failed-approve budget inside the current sliding window.\n */\n private assertApproveRateLimit(userId: string): void {\n const tracking = this.failedApprovesByUser.get(userId);\n if (!tracking) return;\n\n const elapsedMs = Date.now() - tracking.firstAttemptAt;\n if (elapsedMs >= this.approveAttemptWindowMs) {\n this.failedApprovesByUser.delete(userId);\n return;\n }\n\n if (tracking.count >= this.maxApproveAttempts) {\n const retryAfterSeconds = Math.max(\n 1,\n Math.ceil((this.approveAttemptWindowMs - elapsedMs) / 1000),\n );\n throw new TerminalAuthRateLimitError(\n 'Too many failed terminal-login attempts. Try again later.',\n retryAfterSeconds,\n );\n }\n }\n\n /** Increment the failed-approve counter for `userId`, starting the window if needed. */\n private recordFailedApprove(userId: string): void {\n const tracking = this.failedApprovesByUser.get(userId);\n if (!tracking) {\n this.failedApprovesByUser.set(userId, {\n count: 1,\n firstAttemptAt: Date.now(),\n });\n return;\n }\n tracking.count += 1;\n }\n\n /**\n * Exchange a device code (the secret the CLI keeps) for an access token\n * once the request has been approved. Returns `{ status: 'pending' }` while\n * the CLI should keep polling, and `{ status: 'expired' }` if the request\n * was never approved within its TTL.\n */\n async exchangeDeviceCode(deviceCode: string): Promise<CliAuthTokenResult> {\n const request = await this.requestCollection.findByDeviceCodeHash(\n hashDeviceCode(deviceCode),\n );\n if (!request) {\n return { status: 'expired' };\n }\n\n // Approved requests stay claimable even after the original request TTL —\n // the session has its own (longer) lifetime, and the CLI may poll just\n // after approval but after `expiresAt`. Check this before TTL expiry so\n // freshly minted sessions don't get orphaned.\n if (request.status === 'approved' && request.sessionId) {\n return {\n accessToken: request.sessionId,\n expiresIn: this.sessionTtlSeconds,\n status: 'approved',\n tokenType: 'Bearer',\n };\n }\n\n if (isExpired(request)) {\n if (request.status === 'pending') {\n request.status = 'expired';\n await request.save();\n }\n return { status: 'expired' };\n }\n\n if (request.status === 'pending') {\n return {\n expiresAt: new Date(request.expiresAt).toISOString(),\n interval: this.pollIntervalSeconds,\n status: 'pending',\n };\n }\n\n return { status: 'expired' };\n }\n\n /**\n * Resolve a bearer token issued by this service into a full session\n * context — load the user, permissions, and tenant just like the cookie\n * path does. Returns `null` if the token is unknown, expired, or revoked.\n */\n async loadBearerSession(token: string): Promise<SessionContext | null> {\n return this.sessionService.loadSessionContext(token);\n }\n\n /**\n * Revoke a bearer token (CLI logout). Returns true if a session was\n * actually revoked.\n */\n async destroyBearerSession(token: string): Promise<boolean> {\n return this.sessionService.destroySession(token);\n }\n\n /**\n * Delete expired pending requests (cleanup job).\n */\n async cleanupExpiredRequests(): Promise<number> {\n return this.requestCollection.deleteExpired();\n }\n\n /** The default polling interval clients should use. Exposed for tests. */\n get pollInterval(): number {\n return this.pollIntervalSeconds;\n }\n}\n\n/**\n * Error class for terminal auth failures the caller is expected to surface to\n * the user (vs. unexpected internal errors).\n */\nexport class TerminalAuthError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'TerminalAuthError';\n }\n}\n\n/**\n * Thrown when a user has exceeded the configured failed-approve budget. The\n * caller (SvelteKit handler, REST adapter, etc.) is expected to surface this\n * as HTTP 429 with `Retry-After: retryAfterSeconds`.\n */\nexport class TerminalAuthRateLimitError extends TerminalAuthError {\n readonly retryAfterSeconds: number;\n\n constructor(message: string, retryAfterSeconds: number) {\n super(message);\n this.name = 'TerminalAuthRateLimitError';\n this.retryAfterSeconds = retryAfterSeconds;\n }\n}\n\nexport type { CliAuthRequestStatus };\n"],"x_google_ignoreList":[30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TerminalAuthService-Nxt5DA13.js","names":["profileId","owners","profile","user","oidcIdentity","encode","decodeBase64URL","jwk.isJWK","jwk.isSecretJWK","invalidKeyInput","jwk.isPrivateJWK","jwk.isPublicJWK","b64u","encode","#payload","#payload","#protectedHeader","#unprotectedHeader","b64u","encode","#flattened","#jwt","#protectedHeader","#jwks","#cached","#url","#timeoutDuration","#cooldownDuration","#cacheMaxAge","#headers","#customFetch","#cache","#jwksTimestamp","#local","#pendingFetch"],"sources":["../../src/__smrt-register__.ts","../../src/types/index.ts","../../src/models/User.ts","../../src/models/CliAuthRequest.ts","../../src/collections/CliAuthRequestCollection.ts","../../src/models/Group.ts","../../src/collections/GroupCollection.ts","../../src/models/GroupMember.ts","../../src/collections/GroupMemberCollection.ts","../../src/models/GroupRole.ts","../../src/collections/GroupRoleCollection.ts","../../src/models/Membership.ts","../../src/collections/MembershipCollection.ts","../../src/models/MembershipOverride.ts","../../src/collections/MembershipOverrideCollection.ts","../../src/models/Permission.ts","../../src/collections/PermissionCollection.ts","../../src/models/Role.ts","../../src/collections/RoleCollection.ts","../../src/models/RolePermission.ts","../../src/services/PermissionCatalogService.ts","../../src/collections/RolePermissionCollection.ts","../../src/models/Session.ts","../../src/collections/SessionCollection.ts","../../src/models/Tenant.ts","../../src/collections/TenantCollection.ts","../../src/models/TenantPermissionOverride.ts","../../src/collections/TenantPermissionOverrideCollection.ts","../../src/migrations/backfillUserEmailKeys.ts","../../src/collections/UserCollection.ts","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/lib/buffer_utils.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/lib/base64.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/util/base64url.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/lib/crypto_key.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/lib/invalid_key_input.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/util/errors.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/lib/is_key_like.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/lib/helpers.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/lib/type_checks.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/lib/signing.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/lib/jwk_to_key.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/lib/normalize_key.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/key/import.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/lib/validate_crit.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/lib/validate_algorithms.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/lib/check_key_type.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/jws/flattened/verify.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/jws/compact/verify.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/lib/jwt_claims_set.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/jwt/verify.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/jws/flattened/sign.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/jws/compact/sign.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/jwt/sign.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/jwks/local.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/jwks/remote.js","../../../../node_modules/.pnpm/jose@6.2.3/node_modules/jose/dist/webapi/index.js","../../src/services/OidcLoginService.ts","../../src/services/PermissionResolver.ts","../../src/services/SessionService.ts","../../src/services/MobileAuthService.ts","../../src/services/SessionPermissionContext.ts","../../src/services/OperationPermissionService.ts","../../src/services/TerminalAuthService.ts"],"sourcesContent":["/**\n * Self-registers this package's build-time manifest before any @smrt() decorator\n * in the package fires. Fixes issue #1132: in consumer runtimes (tsx, SvelteKit\n * SSR, plain `vite dev`) the decorator's synchronous manifest lookup previously\n * missed because no step populated the global manifest cache — classes got\n * registered with zero fields and `save()` / `toJSON()` silently dropped every\n * declared property.\n *\n * Import this module as the first statement in `src/index.ts` so its top-level\n * side effect runs ahead of any class module's @smrt() decorator.\n *\n * Silent no-op in dev/test, where the vitest plugin already populates manifests\n * via a different path. Only needs to succeed in the published dist output.\n *\n * @see https://github.com/happyvertical/smrt/issues/1132\n */\nimport { ObjectRegistry } from '@happyvertical/smrt-core';\n\n// During library builds, smrtPlugin replaces this entire module with generated\n// code that embeds the scanned manifest inline (#1506/#1507) — published dists\n// never resolve this URL, so downstream bundlers cannot break registration by\n// relocating the compiled module away from dist/manifest.json. The runtime\n// lookup below is the fallback for source-mode runs without that transform.\nObjectRegistry.registerPackageManifest(\n new URL('./manifest.json', import.meta.url),\n);\n","/**\n * Type definitions for smrt-users\n * @packageDocumentation\n */\n\n// Re-export status enums from smrt-types for backwards compatibility\n// These are defined in smrt-types to allow browser-safe packages to import them\nexport {\n AccessRequestStatus,\n MembershipStatus,\n OverrideEffect,\n SessionStatus,\n TenantPermissionEffect,\n TenantStatus,\n UserStatus,\n} from '@happyvertical/smrt-types';\n\n// ============= Default System Roles =============\n\n/**\n * Default system role slugs\n */\nexport const DEFAULT_ROLE_SLUGS = {\n OWNER: 'owner',\n ADMIN: 'admin',\n MEMBER: 'member',\n VIEWER: 'viewer',\n} as const;\n\n/**\n * Default system roles configuration\n */\nexport const DEFAULT_ROLES = [\n {\n slug: DEFAULT_ROLE_SLUGS.OWNER,\n name: 'Owner',\n description: 'Full access to all resources',\n },\n {\n slug: DEFAULT_ROLE_SLUGS.ADMIN,\n name: 'Administrator',\n description: 'Manage users and settings',\n },\n {\n slug: DEFAULT_ROLE_SLUGS.MEMBER,\n name: 'Member',\n description: 'Standard access',\n },\n {\n slug: DEFAULT_ROLE_SLUGS.VIEWER,\n name: 'Viewer',\n description: 'Read-only access',\n },\n] as const;\n\nexport type DefaultRoleSlug =\n (typeof DEFAULT_ROLE_SLUGS)[keyof typeof DEFAULT_ROLE_SLUGS];\n\n// ============= Tenant Policy =============\n\n/**\n * Tenant policy mode\n *\n * - `flexible`: No tenant created on signup, user can have zero tenants\n * - `personal`: Auto-create personal tenant on first login, can delete all\n * - `required`: Auto-create personal tenant, must keep at least one\n */\nexport type TenantPolicyMode = 'flexible' | 'personal' | 'required';\n\n/**\n * Tenant policy configuration\n *\n * Set in smrt.config.js to control tenant creation behavior\n */\nexport interface TenantPolicy {\n /** Policy mode */\n mode: TenantPolicyMode;\n\n /** Maximum tenants per user (0 = unlimited) */\n maxTenants: number;\n\n /** Name for auto-created default tenant */\n defaultName: string;\n}\n\n/**\n * Default tenant policy (flexible, unlimited)\n */\nexport const DEFAULT_TENANT_POLICY: TenantPolicy = {\n mode: 'flexible',\n maxTenants: 0,\n defaultName: 'Default Workspace',\n};\n","/**\n * User model - authenticated identity linked to a Profile\n * @packageDocumentation\n */\n\nimport {\n crossPackageRef,\n field,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { normalizeIdentityEmail } from '@happyvertical/smrt-profiles';\nimport type { User as UserContract } from '@happyvertical/smrt-types';\nimport { UserStatus } from '../types/index.js';\n\n/**\n * Constructor options for {@link User}.\n */\nexport interface UserOptions extends SmrtObjectOptions {\n profileId?: string;\n email?: string;\n status?: UserStatus;\n lastLoginAt?: Date | null;\n}\n\n/**\n * Basic email validation regex.\n * Validates format: local@domain.tld\n */\nconst EMAIL_REGEX = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n\n/**\n * Validate email format.\n * @param email - Email address to validate\n * @returns true if email format is valid\n */\nexport function isValidEmail(email: string): boolean {\n if (!email || typeof email !== 'string') return false;\n return EMAIL_REGEX.test(email.trim());\n}\n\n/**\n * Normalize email address (lowercase and trim).\n * @param email - Email address to normalize\n * @returns Normalized email\n */\nexport function normalizeEmail(email: string): string {\n if (!email || typeof email !== 'string') return '';\n return email.trim() ? normalizeIdentityEmail(email) : '';\n}\n\n/**\n * User represents an authenticated identity in the system.\n *\n * Users are linked to Profiles from smrt-profiles via profileId.\n * A User can have multiple Memberships across different Tenants.\n *\n * @example\n * ```typescript\n * const user = await users.create({\n * profileId: 'profile-uuid',\n * email: 'user@example.com',\n * status: UserStatus.ACTIVE\n * });\n * await user.save();\n * ```\n */\n@smrt({\n // #1400: read-only generated surface — User is auth identity (email/status/\n // profileId), provisioned via OIDC/magic-link, not arbitrary authed POST/PUT.\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class User extends SmrtObject implements UserContract {\n /**\n * Foreign key to smrt-profiles Profile (cross-package)\n */\n @crossPackageRef('@happyvertical/smrt-profiles:Profile', { unique: true })\n profileId: string = '';\n\n /**\n * User's email address (unique, used for lookup)\n */\n email: string = '';\n\n /** Durable normalized key for cross-connection email uniqueness. */\n @field({ type: 'text', nullable: true, unique: true, readonly: true })\n emailKey: string | null = null;\n\n /**\n * User account status\n */\n @field({ type: 'text' })\n status: UserStatus = UserStatus.ACTIVE;\n\n /**\n * Last login timestamp\n */\n lastLoginAt: Date | null = null;\n\n constructor(options: UserOptions = {}) {\n super(options);\n if (options.profileId !== undefined) this.profileId = options.profileId;\n if (options.email !== undefined) this.email = normalizeEmail(options.email);\n if (options.status !== undefined) this.status = options.status;\n if (options.lastLoginAt !== undefined)\n this.lastLoginAt = options.lastLoginAt;\n }\n\n /**\n * Validate the user's email format.\n * @returns true if email is valid\n */\n hasValidEmail(): boolean {\n return isValidEmail(this.email);\n }\n\n /**\n * Check if user is active\n */\n isActive(): boolean {\n return this.status === UserStatus.ACTIVE;\n }\n\n /**\n * Check if user is suspended\n */\n isSuspended(): boolean {\n return this.status === UserStatus.SUSPENDED;\n }\n\n /**\n * Check if user is pending verification\n */\n isPending(): boolean {\n return this.status === UserStatus.PENDING;\n }\n\n /**\n * Record a login event\n */\n recordLogin(): void {\n this.lastLoginAt = new Date();\n }\n\n /** Keep the durable email key derived from the public email field. */\n override async save(): Promise<this> {\n this.email = normalizeEmail(this.email);\n this.emailKey = this.email || null;\n return super.save();\n }\n}\n","/**\n * UsersCliAuthRequest model — device-code grant for terminal/CLI logins.\n *\n * Backs the \"browser approves a terminal\" flow: a CLI starts a request, the\n * server returns a short user code, the user signs in on a browser and\n * approves the code, then the CLI polls and gets back a session token bound\n * to the approving user. Records are short-lived (TTL is enforced by\n * `expiresAt`) and store only a hash of the device code so the raw device\n * code is never recoverable from the database.\n *\n * @packageDocumentation\n */\n\nimport { field, SmrtObject, smrt } from '@happyvertical/smrt-core';\n\n/**\n * CLI auth request lifecycle states.\n */\nexport type CliAuthRequestStatus = 'pending' | 'approved' | 'expired';\n\n@smrt({\n tableName: 'users_cli_auth_requests',\n api: { include: [] },\n cli: { include: [] },\n mcp: { include: [] },\n})\nexport class UsersCliAuthRequest extends SmrtObject {\n /** Short, human-typeable code the user enters in the browser to approve the session. */\n @field({ type: 'text' })\n userCode = '';\n\n /** SHA-256 hash of the long device code the CLI keeps secret. */\n @field({ type: 'text' })\n deviceCodeHash = '';\n\n /** Lifecycle state — `pending` → `approved` | `expired`. */\n @field({ type: 'text' })\n status: CliAuthRequestStatus = 'pending';\n\n /** User id of the human who approved the request (set on approval). */\n @field({ type: 'text' })\n userId = '';\n\n /** Tenant id captured from the approving session (set on approval). */\n @field({ type: 'text' })\n tenantId = '';\n\n /** Session id minted on approval — handed to the CLI as its bearer token. */\n @field({ type: 'text' })\n sessionId = '';\n\n /** When the pending request stops accepting approvals. */\n @field({ type: 'datetime' })\n expiresAt = new Date();\n\n /** When approval happened — null while the request is still pending. */\n @field({ type: 'datetime', nullable: true })\n approvedAt: Date | null = null;\n}\n\nexport { UsersCliAuthRequest as CliAuthRequest };\n","/**\n * UsersCliAuthRequestCollection — lookups for the device-code grant flow.\n *\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { UsersCliAuthRequest } from '../models/CliAuthRequest.js';\n\nexport class UsersCliAuthRequestCollection extends SmrtCollection<UsersCliAuthRequest> {\n static readonly _itemClass = UsersCliAuthRequest;\n\n /**\n * Look up a pending or completed request by the short user code shown in the CLI.\n */\n async findByUserCode(userCode: string): Promise<UsersCliAuthRequest | null> {\n const [request] = await this.list({\n limit: 1,\n where: { userCode: userCode.trim().toUpperCase() },\n });\n return request ?? null;\n }\n\n /**\n * Look up a request by the hash of its device code (the CLI's polling key).\n */\n async findByDeviceCodeHash(\n deviceCodeHash: string,\n ): Promise<UsersCliAuthRequest | null> {\n const [request] = await this.list({\n limit: 1,\n where: { deviceCodeHash },\n });\n return request ?? null;\n }\n\n /**\n * Delete expired pending requests (cleanup job).\n */\n async deleteExpired(): Promise<number> {\n const requests = await this.list({\n where: {\n status: 'pending',\n 'expiresAt <': new Date().toISOString(),\n },\n });\n\n let count = 0;\n for (const request of requests) {\n await request.delete();\n count++;\n }\n return count;\n }\n}\n\nexport { UsersCliAuthRequestCollection as CliAuthRequestCollection };\n","/**\n * Group model - team within a tenant\n * @packageDocumentation\n */\n\nimport {\n foreignKey,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\n\n/**\n * Constructor options for {@link Group}.\n */\nexport interface GroupOptions extends SmrtObjectOptions {\n tenantId?: string;\n name?: string;\n description?: string;\n}\n\n/**\n * Group represents a team or department within a tenant.\n *\n * Groups can have roles assigned to them via GroupRole.\n * Users in a group inherit permissions from the group's roles.\n *\n * @example\n * ```typescript\n * const editorsGroup = await groups.create({\n * tenantId: tenant.id,\n * name: 'Editorial Team',\n * slug: 'editorial-team',\n * description: 'Content editors and writers'\n * });\n * await editorsGroup.save();\n * ```\n */\n@smrt({\n // #1400: read-only generated surface — RBAC/identity writes go through\n // permission-gated services, not auth-only generated CRUD.\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class Group extends SmrtObject {\n /**\n * Foreign key to Tenant\n */\n @foreignKey('Tenant', { required: true })\n tenantId?: string;\n\n /**\n * Display name for the group\n */\n name: string = '';\n\n /**\n * Description of the group\n */\n description: string = '';\n\n constructor(options: GroupOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.name !== undefined) this.name = options.name;\n if (options.description !== undefined)\n this.description = options.description;\n }\n}\n","/**\n * GroupCollection - Collection manager for Group objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { Group } from '../models/Group.js';\n\n/**\n * Collection for managing Group objects\n */\nexport class GroupCollection extends SmrtCollection<Group> {\n static readonly _itemClass = Group;\n\n /**\n * Find all groups in a tenant\n */\n async findByTenant(tenantId: string): Promise<Group[]> {\n return await this.list({\n where: { tenantId },\n orderBy: 'name ASC',\n });\n }\n\n /**\n * Find group by slug within a tenant\n */\n async findBySlug(slug: string, tenantId: string): Promise<Group | null> {\n const results = await this.list({\n where: { slug, tenantId },\n limit: 1,\n });\n return results.length > 0 ? results[0] : null;\n }\n}\n","/**\n * GroupMember model - User belongs to Group (join table)\n * @packageDocumentation\n */\n\nimport {\n foreignKey,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\n\n/**\n * Constructor options for {@link GroupMember}.\n */\nexport interface GroupMemberOptions extends SmrtObjectOptions {\n groupId?: string;\n userId?: string;\n}\n\n/**\n * GroupMember is a join table linking Users to Groups.\n *\n * A user can belong to multiple groups within a tenant.\n * Group membership grants additional permissions via GroupRole.\n *\n * @example\n * ```typescript\n * // Add user to a group\n * const groupMember = await groupMembers.create({\n * groupId: editorsGroup.id,\n * userId: user.id\n * });\n * await groupMember.save();\n * ```\n */\n@smrt({\n // #1400: read-only generated REST + MCP surface — RBAC/identity writes go\n // through permission-gated services, not auth-only generated CRUD. mcp must\n // be explicit: an omitted mcp config generates the FULL tool surface.\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class GroupMember extends SmrtObject {\n /**\n * Foreign key to Group\n */\n @foreignKey('Group', { required: true })\n groupId?: string;\n\n /**\n * Foreign key to User\n */\n @foreignKey('User', { required: true })\n userId?: string;\n\n constructor(options: GroupMemberOptions = {}) {\n super(options);\n if (options.groupId !== undefined) this.groupId = options.groupId;\n if (options.userId !== undefined) this.userId = options.userId;\n }\n}\n","/**\n * GroupMemberCollection - Collection manager for GroupMember objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { GroupMember } from '../models/GroupMember.js';\nimport { GroupCollection } from './GroupCollection.js';\n\n/**\n * Collection for managing GroupMember objects\n */\nexport class GroupMemberCollection extends SmrtCollection<GroupMember> {\n static readonly _itemClass = GroupMember;\n\n /** Memoized Group collection, used to resolve the Group table name. */\n private groupCollection?: GroupCollection;\n\n /**\n * Resolve the database table name for the `Group` model from the registry\n * (via a shared-connection GroupCollection) rather than hardcoding `groups`.\n * A `@smrt({ tableName })` override or table prefix on Group would otherwise\n * make raw joins reference a non-existent or foreign table.\n */\n private async getGroupTableName(): Promise<string> {\n if (!this.groupCollection) {\n this.groupCollection = await GroupCollection.create({\n db: this.options.db,\n });\n }\n return this.groupCollection.tableName;\n }\n\n /**\n * Find all members of a group\n */\n async findByGroup(groupId: string): Promise<GroupMember[]> {\n return await this.list({\n where: { groupId },\n });\n }\n\n /**\n * Find all groups a user belongs to\n */\n async findByUser(userId: string): Promise<GroupMember[]> {\n return await this.list({\n where: { userId },\n });\n }\n\n /**\n * Check if a user is in a group\n */\n async isMember(groupId: string, userId: string): Promise<boolean> {\n const results = await this.list({\n where: { groupId, userId },\n limit: 1,\n });\n return results.length > 0;\n }\n\n /**\n * Add user to a group\n */\n async addMember(groupId: string, userId: string): Promise<GroupMember> {\n // Check if already a member\n const existing = await this.list({\n where: { groupId, userId },\n limit: 1,\n });\n if (existing.length > 0) {\n return existing[0];\n }\n\n const member = await this.create({ groupId, userId });\n await member.save();\n return member;\n }\n\n /**\n * Remove user from a group\n */\n async removeMember(groupId: string, userId: string): Promise<boolean> {\n const existing = await this.list({\n where: { groupId, userId },\n limit: 1,\n });\n if (existing.length === 0) {\n return false;\n }\n\n await existing[0].delete();\n return true;\n }\n\n /**\n * Get group IDs for a user\n * @deprecated Use getGroupIdsForTenant to prevent cross-tenant leakage\n */\n async getGroupIds(userId: string): Promise<string[]> {\n const memberships = await this.findByUser(userId);\n return memberships.map((m) => m.groupId as string);\n }\n\n /**\n * Get group IDs for a user within a specific tenant\n * This prevents cross-tenant permission leakage by filtering groups by tenant\n */\n async getGroupIdsForTenant(\n userId: string,\n tenantId: string,\n ): Promise<string[]> {\n // Query group_members joined with the Group table to filter by tenant.\n // Use raw database query to get unhydrated results. Both table names are\n // trusted registry-resolved identifiers, not user input.\n const groupTable = await this.getGroupTableName();\n const sql = `\n SELECT gm.group_id\n FROM ${this.tableName} gm\n INNER JOIN ${groupTable} g ON g.id = gm.group_id\n WHERE gm.user_id = ? AND g.tenant_id = ?\n `;\n const result = await this.db.query(sql, userId, tenantId);\n return result.rows.map(\n (r: Record<string, unknown>) => r.group_id as string,\n );\n }\n\n /**\n * Get user IDs in a group\n */\n async getUserIds(groupId: string): Promise<string[]> {\n const members = await this.findByGroup(groupId);\n return members.map((m) => m.userId as string);\n }\n}\n","/**\n * GroupRole model - Group has Role (join table)\n * @packageDocumentation\n */\n\nimport {\n foreignKey,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\n\n/**\n * Constructor options for {@link GroupRole}.\n */\nexport interface GroupRoleOptions extends SmrtObjectOptions {\n groupId?: string;\n roleId?: string;\n}\n\n/**\n * GroupRole is a join table linking Groups to Roles.\n *\n * Groups can have multiple roles assigned, and members of the group\n * inherit the permissions from all assigned roles.\n *\n * @example\n * ```typescript\n * // Assign a role to a group\n * const groupRole = await groupRoles.create({\n * groupId: editorsGroup.id,\n * roleId: editorRole.id\n * });\n * await groupRole.save();\n * ```\n */\n@smrt({\n // #1400: read-only generated REST + MCP surface — RBAC/identity writes go\n // through permission-gated services, not auth-only generated CRUD. mcp must\n // be explicit: an omitted mcp config generates the FULL tool surface.\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class GroupRole extends SmrtObject {\n /**\n * Foreign key to Group\n */\n @foreignKey('Group', { required: true })\n groupId?: string;\n\n /**\n * Foreign key to Role\n */\n @foreignKey('Role', { required: true })\n roleId?: string;\n\n constructor(options: GroupRoleOptions = {}) {\n super(options);\n if (options.groupId !== undefined) this.groupId = options.groupId;\n if (options.roleId !== undefined) this.roleId = options.roleId;\n }\n}\n","/**\n * GroupRoleCollection - Collection manager for GroupRole objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { GroupRole } from '../models/GroupRole.js';\n\n/**\n * Collection for managing GroupRole objects\n */\nexport class GroupRoleCollection extends SmrtCollection<GroupRole> {\n static readonly _itemClass = GroupRole;\n\n /**\n * Find all roles for a group\n */\n async findByGroup(groupId: string): Promise<GroupRole[]> {\n return await this.list({\n where: { groupId },\n });\n }\n\n /**\n * Find all groups that have a role\n */\n async findByRole(roleId: string): Promise<GroupRole[]> {\n return await this.list({\n where: { roleId },\n });\n }\n\n /**\n * Check if a group has a role\n */\n async hasRole(groupId: string, roleId: string): Promise<boolean> {\n const results = await this.list({\n where: { groupId, roleId },\n limit: 1,\n });\n return results.length > 0;\n }\n\n /**\n * Add role to a group\n */\n async addRole(groupId: string, roleId: string): Promise<GroupRole> {\n // Check if already has role\n const existing = await this.list({\n where: { groupId, roleId },\n limit: 1,\n });\n if (existing.length > 0) {\n return existing[0];\n }\n\n const groupRole = await this.create({ groupId, roleId });\n await groupRole.save();\n return groupRole;\n }\n\n /**\n * Remove role from a group\n */\n async removeRole(groupId: string, roleId: string): Promise<boolean> {\n const existing = await this.list({\n where: { groupId, roleId },\n limit: 1,\n });\n if (existing.length === 0) {\n return false;\n }\n\n await existing[0].delete();\n return true;\n }\n\n /**\n * Get role IDs for a group\n */\n async getRoleIds(groupId: string): Promise<string[]> {\n const groupRoles = await this.findByGroup(groupId);\n return groupRoles.map((gr) => gr.roleId as string);\n }\n}\n","/**\n * Membership model - User belongs to Tenant with a Role\n * @packageDocumentation\n */\n\nimport {\n field,\n foreignKey,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport type { Membership as MembershipContract } from '@happyvertical/smrt-types';\nimport { MembershipStatus } from '../types/index.js';\n\n/**\n * Constructor options for {@link Membership}.\n */\nexport interface MembershipOptions extends SmrtObjectOptions {\n userId?: string;\n tenantId?: string;\n roleId?: string;\n status?: MembershipStatus;\n}\n\n/**\n * Membership represents a user's membership in a tenant with an assigned role.\n *\n * A user can have only one membership per tenant (UNIQUE userId + tenantId).\n * The role determines the base permissions for the user in that tenant.\n *\n * @example\n * ```typescript\n * const membership = await memberships.create({\n * userId: user.id,\n * tenantId: tenant.id,\n * roleId: role.id,\n * status: MembershipStatus.ACTIVE\n * });\n * await membership.save();\n * ```\n */\n@smrt({\n // #1400: read-only generated surface — RBAC/identity writes go through\n // permission-gated services, not auth-only generated CRUD (requireRouteAuth\n // is authentication-only, so a generated POST/PUT here is privilege escalation).\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class Membership extends SmrtObject implements MembershipContract {\n /**\n * Foreign key to User\n */\n @foreignKey('User', { required: true })\n userId?: string;\n\n /**\n * Foreign key to Tenant\n */\n @foreignKey('Tenant', { required: true })\n tenantId?: string;\n\n /**\n * Foreign key to Role - determines base permissions\n */\n @foreignKey('Role', { required: true })\n roleId?: string;\n\n /**\n * Membership status\n */\n @field({ type: 'text' })\n status: MembershipStatus = MembershipStatus.ACTIVE;\n\n constructor(options: MembershipOptions = {}) {\n super(options);\n if (options.userId !== undefined) this.userId = options.userId;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.roleId !== undefined) this.roleId = options.roleId;\n if (options.status !== undefined) this.status = options.status;\n }\n\n /**\n * Check if membership is active\n */\n isActive(): boolean {\n return this.status === MembershipStatus.ACTIVE;\n }\n\n /**\n * Check if membership is pending (invitation not yet accepted)\n */\n isPending(): boolean {\n return this.status === MembershipStatus.PENDING;\n }\n}\n","/**\n * MembershipCollection - Collection manager for Membership objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { Membership } from '../models/Membership.js';\nimport { MembershipStatus } from '../types/index.js';\n\n/**\n * Collection for managing Membership objects\n */\nexport class MembershipCollection extends SmrtCollection<Membership> {\n static readonly _itemClass = Membership;\n\n /**\n * Find all memberships for a user\n */\n async findByUser(userId: string): Promise<Membership[]> {\n return await this.list({\n where: { userId },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find all active memberships for a user\n */\n async findActiveByUser(userId: string): Promise<Membership[]> {\n return await this.list({\n where: { userId, status: MembershipStatus.ACTIVE },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find all memberships in a tenant\n */\n async findByTenant(tenantId: string): Promise<Membership[]> {\n return await this.list({\n where: { tenantId },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find active memberships in a tenant\n */\n async findActiveByTenant(tenantId: string): Promise<Membership[]> {\n return await this.list({\n where: { tenantId, status: MembershipStatus.ACTIVE },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find a specific user's membership in a tenant\n */\n async findByUserAndTenant(\n userId: string,\n tenantId: string,\n ): Promise<Membership | null> {\n const results = await this.list({\n where: { userId, tenantId },\n limit: 1,\n });\n return results.length > 0 ? results[0] : null;\n }\n\n /**\n * Find memberships by role\n */\n async findByRole(roleId: string): Promise<Membership[]> {\n return await this.list({\n where: { roleId },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find memberships by status\n */\n async findByStatus(status: MembershipStatus): Promise<Membership[]> {\n return await this.list({\n where: { status },\n orderBy: 'created_at DESC',\n });\n }\n}\n","/**\n * MembershipOverride model - per-user permission grant/deny\n * @packageDocumentation\n */\n\nimport {\n field,\n foreignKey,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { OverrideEffect } from '../types/index.js';\n\n/**\n * Constructor options for {@link MembershipOverride}.\n */\nexport interface MembershipOverrideOptions extends SmrtObjectOptions {\n membershipId?: string;\n permissionId?: string;\n effect?: OverrideEffect;\n}\n\n/**\n * MembershipOverride allows granting or denying specific permissions\n * to a user beyond what their role and groups provide.\n *\n * Overrides are applied after role and group permissions are resolved.\n * DENY overrides take precedence over GRANT overrides.\n *\n * @example\n * ```typescript\n * // Grant a specific permission to a user\n * const override = await membershipOverrides.create({\n * membershipId: membership.id,\n * permissionId: specialPermission.id,\n * effect: OverrideEffect.GRANT\n * });\n *\n * // Deny a permission (even if role grants it)\n * const deny = await membershipOverrides.create({\n * membershipId: membership.id,\n * permissionId: dangerousPermission.id,\n * effect: OverrideEffect.DENY\n * });\n * ```\n */\n@smrt({\n // #1400: read-only generated REST + MCP surface — RBAC/identity writes go\n // through permission-gated services, not auth-only generated CRUD. mcp must\n // be explicit: an omitted mcp config generates the FULL tool surface.\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class MembershipOverride extends SmrtObject {\n /**\n * Foreign key to Membership\n */\n @foreignKey('Membership', { required: true })\n membershipId?: string;\n\n /**\n * Foreign key to Permission\n */\n @foreignKey('Permission', { required: true })\n permissionId?: string;\n\n /**\n * Effect of the override: grant or deny\n */\n @field({ type: 'text' })\n effect: OverrideEffect = OverrideEffect.GRANT;\n\n constructor(options: MembershipOverrideOptions = {}) {\n super(options);\n if (options.membershipId !== undefined)\n this.membershipId = options.membershipId;\n if (options.permissionId !== undefined)\n this.permissionId = options.permissionId;\n if (options.effect !== undefined) this.effect = options.effect;\n }\n\n /**\n * Check if this override grants the permission\n */\n isGrant(): boolean {\n return this.effect === OverrideEffect.GRANT;\n }\n\n /**\n * Check if this override denies the permission\n */\n isDeny(): boolean {\n return this.effect === OverrideEffect.DENY;\n }\n}\n","/**\n * MembershipOverrideCollection - Collection manager for MembershipOverride objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { MembershipOverride } from '../models/MembershipOverride.js';\nimport { OverrideEffect } from '../types/index.js';\n\n/**\n * Collection for managing MembershipOverride objects\n */\nexport class MembershipOverrideCollection extends SmrtCollection<MembershipOverride> {\n static readonly _itemClass = MembershipOverride;\n\n /**\n * Find all overrides for a membership\n */\n async findByMembership(membershipId: string): Promise<MembershipOverride[]> {\n return await this.list({\n where: { membershipId },\n });\n }\n\n /**\n * Find grant overrides for a membership.\n *\n * Filters in memory because the `effect` column is JSON-typed and\n * Postgres rejects bare `json = text` comparisons. A single\n * `findByMembership` call is reused for both grant and deny lookups\n * within the same request (see `_overridesByMembership` cache).\n */\n async findGrants(membershipId: string): Promise<MembershipOverride[]> {\n const all = await this.findByMembership(membershipId);\n return all.filter((o) => o.effect === OverrideEffect.GRANT);\n }\n\n /**\n * Find deny overrides for a membership.\n *\n * See `findGrants` for rationale on in-memory filtering.\n */\n async findDenies(membershipId: string): Promise<MembershipOverride[]> {\n const all = await this.findByMembership(membershipId);\n return all.filter((o) => o.effect === OverrideEffect.DENY);\n }\n\n /**\n * Get grant permission IDs for a membership\n */\n async getGrantedPermissionIds(membershipId: string): Promise<string[]> {\n const grants = await this.findGrants(membershipId);\n return grants.map((o) => o.permissionId as string);\n }\n\n /**\n * Get denied permission IDs for a membership\n */\n async getDeniedPermissionIds(membershipId: string): Promise<string[]> {\n const denies = await this.findDenies(membershipId);\n return denies.map((o) => o.permissionId as string);\n }\n\n /**\n * Set an override for a membership\n */\n async setOverride(\n membershipId: string,\n permissionId: string,\n effect: OverrideEffect,\n ): Promise<MembershipOverride> {\n // Check if override already exists\n const existing = await this.list({\n where: { membershipId, permissionId },\n limit: 1,\n });\n\n if (existing.length > 0) {\n // Update existing override\n existing[0].effect = effect;\n await existing[0].save();\n return existing[0];\n }\n\n // Create new override\n const override = await this.create({ membershipId, permissionId, effect });\n await override.save();\n return override;\n }\n\n /**\n * Remove an override\n */\n async removeOverride(\n membershipId: string,\n permissionId: string,\n ): Promise<boolean> {\n const existing = await this.list({\n where: { membershipId, permissionId },\n limit: 1,\n });\n\n if (existing.length === 0) {\n return false;\n }\n\n await existing[0].delete();\n return true;\n }\n\n /**\n * Grant a permission to a membership\n * Convenience method for setOverride with GRANT effect\n */\n async grantPermission(\n membershipId: string,\n permissionId: string,\n ): Promise<MembershipOverride> {\n return await this.setOverride(\n membershipId,\n permissionId,\n OverrideEffect.GRANT,\n );\n }\n\n /**\n * Deny a permission for a membership\n * Convenience method for setOverride with DENY effect\n */\n async denyPermission(\n membershipId: string,\n permissionId: string,\n ): Promise<MembershipOverride> {\n return await this.setOverride(\n membershipId,\n permissionId,\n OverrideEffect.DENY,\n );\n }\n}\n","/**\n * Permission model - named capability in the system\n * @packageDocumentation\n */\n\nimport {\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\n\n/**\n * Parsed permission slug components\n */\nexport interface ParsedPermissionSlug {\n /** Resource part (e.g., 'articles' from 'articles.create') */\n resource: string;\n /** Action part (e.g., 'create' from 'articles.create') */\n action: string;\n /** Whether the slug follows the valid 'resource.action[.scope...]' pattern */\n isValid: boolean;\n}\n\n/**\n * Parse a permission slug into its resource and action components.\n * Valid format: 'resource.action[.scope...]' (e.g., 'articles.create' or\n * 'products.read.internal')\n *\n * @param slug - The permission slug to parse\n * @returns Parsed components with validation status\n */\nexport function parsePermissionSlug(slug: string): ParsedPermissionSlug {\n const parts = slug.split('.');\n if (parts.length >= 2 && parts.every(Boolean)) {\n return {\n resource: parts[0],\n action: parts.slice(1).join('.'),\n isValid: true,\n };\n }\n return {\n resource: slug,\n action: '',\n isValid: false,\n };\n}\n\n/**\n * Validate that a permission slug follows the 'resource.action[.scope...]'\n * pattern.\n *\n * @param slug - The slug to validate\n * @returns true if valid, false otherwise\n */\nexport function isValidPermissionSlug(slug: string): boolean {\n return parsePermissionSlug(slug).isValid;\n}\n\n/**\n * Constructor options for {@link Permission}.\n */\nexport interface PermissionOptions extends SmrtObjectOptions {\n name?: string;\n description?: string;\n category?: string;\n}\n\n/**\n * Permission represents a named capability in the system.\n *\n * Permissions are defined by the application and assigned to roles.\n * Permission slugs follow the pattern: resource.action[.scope...] (e.g.,\n * 'articles.create' or 'products.read.internal')\n *\n * @example\n * ```typescript\n * const permission = await permissions.create({\n * slug: 'articles.create',\n * name: 'Create Articles',\n * description: 'Allows creating new articles',\n * category: 'articles'\n * });\n * await permission.save();\n * ```\n */\n@smrt({\n // #1400: read-only generated surface — RBAC/identity writes go through\n // permission-gated services, not auth-only generated CRUD.\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class Permission extends SmrtObject {\n /**\n * Display name for the permission\n */\n name: string = '';\n\n /**\n * Description of what this permission allows\n */\n description: string = '';\n\n /**\n * Category for grouping in UI (e.g., 'articles', 'users', 'settings')\n */\n category: string = '';\n\n constructor(options: PermissionOptions = {}) {\n super(options);\n if (options.name !== undefined) this.name = options.name;\n if (options.description !== undefined)\n this.description = options.description;\n if (options.category !== undefined) this.category = options.category;\n }\n\n /**\n * Parse the permission slug into resource and action components.\n * @returns Parsed slug with resource, action, and validation status\n */\n parseSlug(): ParsedPermissionSlug {\n return parsePermissionSlug(this.slug ?? '');\n }\n\n /**\n * Get the resource part of the permission slug\n * e.g., 'articles.create' -> 'articles'\n */\n getResource(): string {\n return this.parseSlug().resource;\n }\n\n /**\n * Get the action part of the permission slug\n * e.g., 'articles.create' -> 'create'\n */\n getAction(): string {\n return this.parseSlug().action;\n }\n\n /**\n * Check if the permission slug is valid (follows 'resource.action' pattern)\n */\n isValidSlug(): boolean {\n return this.parseSlug().isValid;\n }\n}\n","/**\n * PermissionCollection - Collection manager for Permission objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { Permission } from '../models/Permission.js';\n\n/**\n * Collection for managing Permission objects\n */\nexport class PermissionCollection extends SmrtCollection<Permission> {\n static readonly _itemClass = Permission;\n\n /**\n * Find permissions by category\n */\n async findByCategory(category: string): Promise<Permission[]> {\n return await this.list({\n where: { category },\n orderBy: 'slug ASC',\n });\n }\n\n /**\n * Find permission by slug\n */\n async findBySlug(slug: string): Promise<Permission | null> {\n const results = await this.list({\n where: { slug },\n limit: 1,\n });\n return results.length > 0 ? results[0] : null;\n }\n\n /**\n * Batch fetch permissions by IDs\n * Returns a Map of id -> Permission for efficient lookup\n */\n async findByIds(ids: string[]): Promise<Map<string, Permission>> {\n if (ids.length === 0) {\n return new Map();\n }\n\n // Remove duplicates\n const uniqueIds = [...new Set(ids)];\n\n // Build placeholders for SQL IN clause\n const placeholders = uniqueIds.map(() => '?').join(', ');\n const results = await this.query(\n `SELECT * FROM ${this.tableName} WHERE id IN (${placeholders})`,\n uniqueIds,\n );\n\n const map = new Map<string, Permission>();\n for (const perm of results) {\n if (perm.id) {\n map.set(perm.id, perm);\n }\n }\n return map;\n }\n\n /**\n * Get all unique categories\n */\n async getCategories(): Promise<string[]> {\n const results = await this.query(\n `SELECT DISTINCT category FROM ${this.tableName} WHERE category != '' ORDER BY category ASC`,\n );\n return results.map((r) => r.category);\n }\n\n /**\n * Find or create a permission by slug\n */\n async findOrCreate(\n slug: string,\n defaults: Partial<{\n name: string;\n description: string;\n category: string;\n }> = {},\n ): Promise<Permission> {\n const existing = await this.findBySlug(slug);\n if (existing) {\n return existing;\n }\n\n const permission = await this.create({\n slug,\n name: defaults.name ?? slug,\n description: defaults.description ?? '',\n category: defaults.category ?? slug.split('.')[0],\n });\n await permission.save();\n return permission;\n }\n}\n","/**\n * Role model - permission template\n * @packageDocumentation\n */\n\nimport {\n foreignKey,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport type { Role as RoleContract } from '@happyvertical/smrt-types';\n\n/**\n * Constructor options for {@link Role}.\n */\nexport interface RoleOptions extends SmrtObjectOptions {\n tenantId?: string | null;\n name?: string;\n description?: string;\n isSystem?: boolean;\n inheritsToDescendants?: boolean;\n}\n\n/**\n * Role represents a permission template that can be assigned to users.\n *\n * Roles can be:\n * - System roles (tenantId = null): Available to all tenants, cannot be modified\n * - Custom roles (tenantId set): Specific to a tenant, can be customized\n *\n * @example\n * ```typescript\n * // System role (tenantId = null)\n * const adminRole = await roles.create({\n * slug: 'admin',\n * name: 'Administrator',\n * isSystem: true\n * });\n *\n * // Custom tenant role\n * const editorRole = await roles.create({\n * tenantId: tenant.id,\n * slug: 'editor',\n * name: 'Editor',\n * description: 'Can edit content'\n * });\n * ```\n */\n@smrt({\n // #1400: read-only generated surface — RBAC/identity writes go through\n // permission-gated services, not auth-only generated CRUD.\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class Role extends SmrtObject implements RoleContract {\n /**\n * Foreign key to Tenant\n * null = system role available to all tenants\n */\n @foreignKey('Tenant', { nullable: true })\n tenantId?: string | null;\n\n /**\n * Display name for the role\n */\n name: string = '';\n\n /**\n * Description of the role\n */\n description: string = '';\n\n /**\n * Whether this is a system role (cannot be deleted)\n */\n isSystem: boolean = false;\n\n /**\n * Opt-in hierarchical inheritance: when true, an ACTIVE membership holding\n * this role also resolves permissions in descendant tenants where the user\n * has no direct membership (nearest flagged ancestor membership wins).\n *\n * Default false: the role's authority is exact-tenant only, which is the\n * pre-existing resolver behavior. Flag structural roles like a network-level\n * owner/admin; leave per-tenant roles like member/viewer unflagged.\n */\n inheritsToDescendants: boolean = false;\n\n constructor(options: RoleOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.name !== undefined) this.name = options.name;\n if (options.description !== undefined)\n this.description = options.description;\n if (options.isSystem !== undefined) this.isSystem = options.isSystem;\n if (options.inheritsToDescendants !== undefined)\n this.inheritsToDescendants = options.inheritsToDescendants;\n }\n\n /**\n * Check if this is a system-wide role\n */\n isSystemRole(): boolean {\n return this.tenantId === null || this.tenantId === undefined;\n }\n\n /**\n * Check if this is a tenant-specific role\n */\n isTenantRole(): boolean {\n return this.tenantId !== null && this.tenantId !== undefined;\n }\n\n /**\n * Check if this role can be deleted.\n * System roles (isSystem = true) cannot be deleted.\n * @returns true if the role can be deleted\n */\n canDelete(): boolean {\n return !this.isSystem;\n }\n\n /**\n * Delete guard - prevents deletion of system roles.\n * Override the delete method to check isSystem flag first.\n */\n async delete(): Promise<void> {\n if (this.isSystem) {\n throw new Error(\n `Cannot delete system role '${this.slug}'. System roles are protected.`,\n );\n }\n return super.delete();\n }\n}\n","/**\n * RoleCollection - Collection manager for Role objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { Role } from '../models/Role.js';\nimport { DEFAULT_ROLES } from '../types/index.js';\nimport type {\n RolePermissionPatternMatrix,\n SeedRolePermissionsOptions,\n} from './RolePermissionCollection.js';\n\nexport interface SeedSystemRolesOptions\n extends Omit<SeedRolePermissionsOptions, 'catalog'> {\n permissionMatrix?: RolePermissionPatternMatrix;\n seedPermissions?: boolean;\n /**\n * System role slugs to flag `inheritsToDescendants: true` (opt-in\n * hierarchical membership inheritance, e.g. `['owner', 'admin']` for\n * organizational hierarchies). Additive and idempotent: listed slugs are\n * flagged on create and on already-seeded roles, but omitting a slug never\n * unsets a previously flagged role — unflag explicitly via a role update.\n * Unknown slugs throw (a typo here would silently withhold intended\n * authority). Default: no role inherits.\n */\n inheritsToDescendants?: string[];\n}\n\n/**\n * Collection for managing Role objects\n */\nexport class RoleCollection extends SmrtCollection<Role> {\n static readonly _itemClass = Role;\n\n /**\n * Find all system roles (tenantId is null)\n */\n async findSystemRoles(): Promise<Role[]> {\n // Query for roles where tenantId is null\n return await this.query(\n `SELECT * FROM ${this.tableName} WHERE tenant_id IS NULL ORDER BY name ASC`,\n );\n }\n\n /**\n * Find roles available for a tenant (system + tenant-specific)\n */\n async findByTenant(tenantId: string): Promise<Role[]> {\n return await this.query(\n `SELECT * FROM ${this.tableName}\n WHERE tenant_id IS NULL OR tenant_id = ?\n ORDER BY is_system DESC, name ASC`,\n [tenantId],\n );\n }\n\n /**\n * Find tenant-specific roles only\n */\n async findTenantRoles(tenantId: string): Promise<Role[]> {\n return await this.list({\n where: { tenantId },\n orderBy: 'name ASC',\n });\n }\n\n /**\n * Find role by slug within a tenant context\n */\n async findBySlug(slug: string, tenantId?: string): Promise<Role | null> {\n // First check tenant-specific role\n if (tenantId) {\n const tenantRoles = await this.list({\n where: { slug, tenantId },\n limit: 1,\n });\n if (tenantRoles.length > 0) {\n return tenantRoles[0];\n }\n }\n\n // Fall back to system role\n const systemRoles = await this.query(\n `SELECT * FROM ${this.tableName} WHERE slug = ? AND tenant_id IS NULL LIMIT 1`,\n [slug],\n );\n return systemRoles.length > 0 ? systemRoles[0] : null;\n }\n\n /**\n * Seed default system roles\n */\n async seedSystemRoles(options: SeedSystemRolesOptions = {}): Promise<Role[]> {\n const roles: Role[] = [];\n\n const inheritableSlugs = new Set(options.inheritsToDescendants ?? []);\n const knownSlugs = new Set<string>(DEFAULT_ROLES.map((def) => def.slug));\n for (const slug of inheritableSlugs) {\n if (!knownSlugs.has(slug)) {\n throw new Error(\n `Unknown system role slug '${slug}' in inheritsToDescendants. ` +\n `Valid slugs: ${[...knownSlugs].join(', ')}.`,\n );\n }\n }\n\n for (const roleDef of DEFAULT_ROLES) {\n const inheritable = inheritableSlugs.has(roleDef.slug);\n\n // Check if role already exists\n const existing = await this.findBySlug(roleDef.slug);\n if (existing) {\n // Additive-only convergence: flag a listed slug, never unset one.\n if (inheritable && !existing.inheritsToDescendants) {\n existing.inheritsToDescendants = true;\n await existing.save();\n }\n roles.push(existing);\n continue;\n }\n\n // Create new system role\n const role = await this.create({\n slug: roleDef.slug,\n name: roleDef.name,\n description: roleDef.description,\n tenantId: null,\n isSystem: true,\n inheritsToDescendants: inheritable,\n });\n await role.save();\n roles.push(role);\n }\n\n if (options.seedPermissions) {\n const { RolePermissionCollection } = await import(\n './RolePermissionCollection.js'\n );\n const rolePermissions = await RolePermissionCollection.create(\n this.options,\n );\n await rolePermissions.seedRolePermissions(options.permissionMatrix, {\n prune: options.prune,\n syncCatalog: options.syncCatalog,\n tenantId: options.tenantId,\n });\n }\n\n return roles;\n }\n}\n","/**\n * RolePermission model - Role has Permission (join table)\n * @packageDocumentation\n */\n\nimport {\n foreignKey,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\n\n/**\n * Constructor options for {@link RolePermission}.\n */\nexport interface RolePermissionOptions extends SmrtObjectOptions {\n roleId?: string;\n permissionId?: string;\n}\n\n/**\n * RolePermission is a join table linking Roles to Permissions.\n *\n * This enables many-to-many relationship between roles and permissions.\n * A role can have multiple permissions, and a permission can belong to multiple roles.\n *\n * @example\n * ```typescript\n * // Assign a permission to a role\n * const rolePermission = await rolePermissions.create({\n * roleId: adminRole.id,\n * permissionId: createArticlePermission.id\n * });\n * await rolePermission.save();\n * ```\n */\n@smrt({\n // #1400: read-only generated REST + MCP surface — RBAC/identity writes go\n // through permission-gated services, not auth-only generated CRUD. mcp must\n // be explicit: an omitted mcp config generates the FULL tool surface.\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class RolePermission extends SmrtObject {\n /**\n * Foreign key to Role\n */\n @foreignKey('Role', { required: true })\n roleId?: string;\n\n /**\n * Foreign key to Permission\n */\n @foreignKey('Permission', { required: true })\n permissionId?: string;\n\n constructor(options: RolePermissionOptions = {}) {\n super(options);\n if (options.roleId !== undefined) this.roleId = options.roleId;\n if (options.permissionId !== undefined)\n this.permissionId = options.permissionId;\n }\n}\n","/**\n * Manifest-derived permission cataloging and syncing\n * @packageDocumentation\n */\n\nimport { getPackageConfig } from '@happyvertical/smrt-config';\nimport {\n findManifestEntryByQualifiedName,\n ObjectRegistry,\n type SmartObjectDefinition,\n type SmrtClassOptions,\n} from '@happyvertical/smrt-core';\nimport { PermissionCollection } from '../collections/PermissionCollection.js';\nimport {\n isValidPermissionSlug,\n parsePermissionSlug,\n} from '../models/Permission.js';\n\nexport type PermissionCatalogSource = 'manifest' | 'config' | 'runtime';\n\nexport type PostgresPermissionAction =\n | 'SELECT'\n | 'INSERT'\n | 'UPDATE'\n | 'DELETE';\n\nexport interface PostgresPermissionBinding {\n action: Lowercase<PostgresPermissionAction> | PostgresPermissionAction;\n permission?: string;\n schemaName?: string;\n tableName: string;\n tenantField?: string;\n}\n\nexport interface PermissionDefinition {\n category?: string;\n className?: string;\n collection?: string;\n description?: string;\n name?: string;\n postgres?: {\n bindings?: PostgresPermissionBinding[];\n };\n qualifiedName?: string;\n slug: string;\n source?: PermissionCatalogSource;\n}\n\nexport interface PermissionCatalog {\n customPermissions: PermissionDefinition[];\n manifestPermissions: PermissionDefinition[];\n permissions: PermissionDefinition[];\n runtimePermissions: PermissionDefinition[];\n}\n\nexport interface PermissionCatalogSyncResult {\n catalog: PermissionCatalog;\n created: string[];\n unchanged: string[];\n updated: string[];\n}\n\nexport interface UsersConfig extends Record<string, unknown> {\n permissions?: {\n custom?: PermissionDefinition[];\n postgres?: {\n bindings?: PostgresPermissionBinding[];\n enabled?: boolean;\n };\n };\n}\n\nexport interface ConstructorLike {\n name?: string;\n}\n\nexport interface CollectionLike {\n getItemClass?: () => ConstructorLike;\n}\n\nexport type OperationPermissionCollectionInput =\n | string\n | ConstructorLike\n | CollectionLike\n | object;\n\ndeclare global {\n // eslint-disable-next-line no-var\n var __smrtUsersPermissionRegistrations:\n | Map<number, PermissionDefinition[]>\n | undefined;\n // eslint-disable-next-line no-var\n var __smrtUsersPermissionRegistrationCounter: number | undefined;\n}\n\nfunction getRuntimePermissionRegistrations(): Map<\n number,\n PermissionDefinition[]\n> {\n globalThis.__smrtUsersPermissionRegistrations ??= new Map<\n number,\n PermissionDefinition[]\n >();\n return globalThis.__smrtUsersPermissionRegistrations;\n}\n\nfunction toSnakeCase(value: string): string {\n return value\n .replace(/([a-z0-9])([A-Z])/g, '$1_$2')\n .replace(/[-\\s]+/g, '_')\n .toLowerCase();\n}\n\nfunction pluralize(word: string): string {\n if (word.endsWith('y') && !/[aeiou]y$/i.test(word)) {\n return `${word.slice(0, -1)}ies`;\n }\n\n if (\n word.endsWith('s') ||\n word.endsWith('x') ||\n word.endsWith('z') ||\n word.endsWith('ch') ||\n word.endsWith('sh')\n ) {\n return `${word}es`;\n }\n\n return `${word}s`;\n}\n\nfunction deriveCollectionName(className: string): string {\n return pluralize(toSnakeCase(className));\n}\n\nfunction humanizeResource(resource: string): string {\n return resource\n .replace(/[._-]+/g, ' ')\n .replace(/\\b\\w/g, (char) => char.toUpperCase());\n}\n\nfunction capitalize(value: string): string {\n return `${value[0]?.toUpperCase() ?? ''}${value.slice(1)}`;\n}\n\nfunction defaultPermissionName(slug: string): string {\n const parsed = parsePermissionSlug(slug);\n if (!parsed.isValid) {\n return humanizeResource(slug);\n }\n\n return `${humanizeResource(parsed.action)} ${humanizeResource(parsed.resource)}`;\n}\n\nfunction defaultPermissionDescription(slug: string): string {\n const parsed = parsePermissionSlug(slug);\n if (!parsed.isValid) {\n return `Allows ${slug}`;\n }\n\n return `Allows ${humanizeResource(parsed.action).toLowerCase()} access for ${humanizeResource(parsed.resource).toLowerCase()}`;\n}\n\nexport function normalizeOperationPermissionAction(action: string): string {\n const trimmed = action.trim();\n const lower = trimmed.toLowerCase();\n\n if (lower === 'list' || lower === 'get') {\n return 'read';\n }\n\n if (\n lower === 'read' ||\n lower === 'create' ||\n lower === 'update' ||\n lower === 'delete'\n ) {\n return lower;\n }\n\n return trimmed;\n}\n\nfunction isConstructorLike(value: unknown): value is ConstructorLike {\n return typeof value === 'function';\n}\n\nfunction isCollectionLike(value: unknown): value is CollectionLike {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as CollectionLike).getItemClass === 'function'\n );\n}\n\nfunction resolveConstructor(\n value: OperationPermissionCollectionInput,\n): ConstructorLike | undefined {\n if (isConstructorLike(value)) {\n return value;\n }\n\n if (isCollectionLike(value)) {\n return value.getItemClass?.();\n }\n\n if (typeof value === 'object' && value !== null) {\n const ctor = value.constructor;\n if (typeof ctor === 'function' && ctor.name && ctor.name !== 'Object') {\n return ctor;\n }\n }\n\n return undefined;\n}\n\nexport function deriveOperationPermissionCollectionName(\n input: OperationPermissionCollectionInput,\n): string {\n if (typeof input === 'string') {\n const collection = input.trim();\n if (!collection) {\n throw new Error('Operation permission collection must not be empty.');\n }\n return collection;\n }\n\n const ctor = resolveConstructor(input);\n if (!ctor?.name) {\n throw new Error(\n 'Operation permission collection must be a collection slug, model class, model instance, or collection instance.',\n );\n }\n\n const registered =\n ObjectRegistry.getClassByConstructor(\n ctor as Parameters<typeof ObjectRegistry.getClassByConstructor>[0],\n ) ?? ObjectRegistry.getClass(ctor.name);\n const configuredCollection = (\n registered?.config as { collection?: unknown } | undefined\n )?.collection;\n\n if (typeof configuredCollection === 'string' && configuredCollection) {\n return configuredCollection;\n }\n\n if (registered?.collection) {\n return registered.collection;\n }\n\n return deriveCollectionName(registered?.name ?? ctor.name);\n}\n\nexport function deriveOperationPermissionSlug(\n collection: OperationPermissionCollectionInput,\n action: string,\n): string {\n const collectionName = deriveOperationPermissionCollectionName(collection);\n const normalizedAction = normalizeOperationPermissionAction(action);\n if (!normalizedAction) {\n throw new Error('Operation permission action must not be empty.');\n }\n\n return `${collectionName}.${normalizedAction}`;\n}\n\nfunction isCollectionManifestEntry(objectDef?: SmartObjectDefinition): boolean {\n return (\n objectDef?.extends === 'SmrtCollection' ||\n objectDef?.extendsTypeArg !== undefined\n );\n}\n\ninterface ManifestMethodCandidate {\n isPublic?: boolean;\n name?: string;\n}\n\ninterface FieldReadPermissionCandidate {\n _meta?: {\n readPermission?: unknown;\n };\n readPermission?: unknown;\n}\n\ntype MethodCandidate =\n | ManifestMethodCandidate\n | readonly [string, ManifestMethodCandidate];\n\nfunction isMethodEntryTuple(\n method: MethodCandidate,\n): method is readonly [string, ManifestMethodCandidate] {\n return Array.isArray(method);\n}\n\nfunction normalizeMethodCandidate(\n method: MethodCandidate,\n): ManifestMethodCandidate {\n if (isMethodEntryTuple(method)) {\n const [name, definition] = method;\n return { ...definition, name: definition.name ?? name };\n }\n return method;\n}\n\nfunction getPublicCustomMethodNames(\n methodEntries: MethodCandidate[],\n standardActions: readonly string[],\n): string[] {\n return Array.from(\n new Set(\n methodEntries.flatMap((method) => {\n const { isPublic, name } = normalizeMethodCandidate(method);\n if (!name || isPublic !== true || standardActions.includes(name)) {\n return [];\n }\n return [name];\n }),\n ),\n );\n}\n\nfunction getCustomMethodExposureNames(\n config: unknown,\n availableCustomMethods: string[],\n): Set<string> {\n if (!config || config === false) {\n return new Set();\n }\n\n if (config === true || typeof config !== 'object') {\n return new Set(availableCustomMethods);\n }\n\n const rawInclude = (config as { include?: string[] }).include;\n const include = Array.isArray(rawInclude) ? [...rawInclude] : undefined;\n const rawExclude = (config as { exclude?: string[] }).exclude;\n const exclude: string[] = Array.isArray(rawExclude) ? [...rawExclude] : [];\n\n if (!include) {\n return new Set(\n availableCustomMethods.filter(\n (methodName) => !exclude.includes(methodName),\n ),\n );\n }\n\n const baseMethods = include.filter((methodName) =>\n availableCustomMethods.includes(methodName),\n );\n\n return new Set(\n baseMethods.filter((methodName) => !exclude.includes(methodName)),\n );\n}\n\nfunction isOperationEnabled(config: unknown, action: string): boolean {\n if (config === false) {\n return false;\n }\n\n if (config && typeof config === 'object') {\n const include = Array.isArray((config as { include?: string[] }).include)\n ? (config as { include?: string[] }).include\n : undefined;\n const rawExclude = (config as { exclude?: string[] }).exclude;\n const exclude: string[] = Array.isArray(rawExclude) ? [...rawExclude] : [];\n\n if (include && !include.includes(action)) {\n return false;\n }\n if (exclude.includes(action)) {\n return false;\n }\n }\n\n return true;\n}\n\nfunction normalizePostgresAction(\n action: PostgresPermissionBinding['action'],\n): PostgresPermissionAction {\n const normalized = action.toUpperCase();\n if (\n normalized === 'SELECT' ||\n normalized === 'INSERT' ||\n normalized === 'UPDATE' ||\n normalized === 'DELETE'\n ) {\n return normalized;\n }\n\n throw new Error(\n `Unsupported Postgres permission action '${action}'. Expected SELECT, INSERT, UPDATE, or DELETE.`,\n );\n}\n\nfunction normalizeBinding(\n binding: PostgresPermissionBinding,\n fallbackPermission: string,\n): PostgresPermissionBinding {\n return {\n action: normalizePostgresAction(binding.action),\n permission: binding.permission || fallbackPermission,\n schemaName: binding.schemaName,\n tableName: binding.tableName,\n tenantField: binding.tenantField,\n };\n}\n\nfunction mergeStringField(\n fieldName:\n | 'category'\n | 'className'\n | 'collection'\n | 'description'\n | 'name'\n | 'qualifiedName',\n existing: PermissionDefinition,\n incoming: PermissionDefinition,\n slug: string,\n): void {\n const existingValue = existing[fieldName];\n const incomingValue = incoming[fieldName];\n\n if (!incomingValue) {\n return;\n }\n\n if (!existingValue) {\n existing[fieldName] = incomingValue;\n return;\n }\n\n if (existingValue !== incomingValue) {\n throw new Error(\n `Conflicting permission metadata for '${slug}' field '${fieldName}': '${existingValue}' !== '${incomingValue}'`,\n );\n }\n}\n\nfunction mergeBindings(\n existing: PermissionDefinition,\n incoming: PermissionDefinition,\n): void {\n const existingBindings = existing.postgres?.bindings ?? [];\n const incomingBindings = incoming.postgres?.bindings ?? [];\n if (incomingBindings.length === 0) {\n return;\n }\n\n const seen = new Set(\n existingBindings.map((binding) =>\n [\n binding.permission,\n binding.action,\n binding.schemaName ?? '',\n binding.tableName,\n binding.tenantField ?? '',\n ].join('|'),\n ),\n );\n\n const mergedBindings = [...existingBindings];\n for (const binding of incomingBindings) {\n const normalized = normalizeBinding(binding, incoming.slug);\n const key = [\n normalized.permission,\n normalized.action,\n normalized.schemaName ?? '',\n normalized.tableName,\n normalized.tenantField ?? '',\n ].join('|');\n if (!seen.has(key)) {\n seen.add(key);\n mergedBindings.push(normalized);\n }\n }\n\n existing.postgres = {\n bindings: mergedBindings,\n };\n}\n\nfunction normalizeDefinition(\n definition: PermissionDefinition,\n source: PermissionCatalogSource,\n): PermissionDefinition {\n if (!definition.slug || !isValidPermissionSlug(definition.slug.trim())) {\n throw new Error(\n `Invalid permission slug '${definition.slug}'. Expected 'resource.action[.scope...]'.`,\n );\n }\n\n const slug = definition.slug.trim();\n return {\n category: definition.category ?? parsePermissionSlug(slug).resource,\n className: definition.className,\n collection: definition.collection,\n description: definition.description ?? defaultPermissionDescription(slug),\n name: definition.name ?? defaultPermissionName(slug),\n postgres: definition.postgres?.bindings\n ? {\n bindings: definition.postgres.bindings.map((binding) =>\n normalizeBinding(binding, slug),\n ),\n }\n : undefined,\n qualifiedName: definition.qualifiedName,\n slug,\n source,\n };\n}\n\nfunction mergeDefinitionSet(\n current: Map<string, PermissionDefinition>,\n incomingDefinitions: PermissionDefinition[],\n source: PermissionCatalogSource,\n): void {\n for (const rawDefinition of incomingDefinitions) {\n const definition = normalizeDefinition(rawDefinition, source);\n const existing = current.get(definition.slug);\n\n if (!existing) {\n current.set(definition.slug, definition);\n continue;\n }\n\n mergeStringField('category', existing, definition, definition.slug);\n mergeStringField('className', existing, definition, definition.slug);\n mergeStringField('collection', existing, definition, definition.slug);\n mergeStringField('description', existing, definition, definition.slug);\n mergeStringField('name', existing, definition, definition.slug);\n mergeStringField('qualifiedName', existing, definition, definition.slug);\n mergeBindings(existing, definition);\n }\n}\n\nexport function registerPermissionDefinitions(\n definitions: PermissionDefinition[],\n): () => void {\n globalThis.__smrtUsersPermissionRegistrationCounter =\n (globalThis.__smrtUsersPermissionRegistrationCounter ?? 0) + 1;\n const registrationId = globalThis.__smrtUsersPermissionRegistrationCounter;\n getRuntimePermissionRegistrations().set(registrationId, definitions);\n\n return () => {\n getRuntimePermissionRegistrations().delete(registrationId);\n };\n}\n\nexport class PermissionCatalogService {\n constructor(private readonly options: SmrtClassOptions = {}) {}\n\n getUsersConfig(): UsersConfig {\n return getPackageConfig<UsersConfig>('users', {});\n }\n\n getRuntimePermissionDefinitions(): PermissionDefinition[] {\n return Array.from(getRuntimePermissionRegistrations().values()).flat();\n }\n\n getCustomPermissionDefinitions(): PermissionDefinition[] {\n return this.getUsersConfig().permissions?.custom ?? [];\n }\n\n getCatalog(): PermissionCatalog {\n const manifestPermissions = this.getManifestPermissionDefinitions();\n const customPermissions = this.getCustomPermissionDefinitions();\n const runtimePermissions = this.getRuntimePermissionDefinitions();\n\n const merged = new Map<string, PermissionDefinition>();\n mergeDefinitionSet(merged, manifestPermissions, 'manifest');\n mergeDefinitionSet(merged, customPermissions, 'config');\n mergeDefinitionSet(merged, runtimePermissions, 'runtime');\n\n return {\n customPermissions: customPermissions.map((definition) =>\n normalizeDefinition(definition, 'config'),\n ),\n manifestPermissions: manifestPermissions.map((definition) =>\n normalizeDefinition(definition, 'manifest'),\n ),\n permissions: Array.from(merged.values()).sort((left, right) =>\n left.slug.localeCompare(right.slug),\n ),\n runtimePermissions: runtimePermissions.map((definition) =>\n normalizeDefinition(definition, 'runtime'),\n ),\n };\n }\n\n deriveOperationPermissionSlug(\n collection: OperationPermissionCollectionInput,\n action: string,\n ): string {\n return deriveOperationPermissionSlug(collection, action);\n }\n\n hasPermissionSlug(slug: string): boolean {\n return this.getCatalog().permissions.some(\n (permission) => permission.slug === slug,\n );\n }\n\n async syncPermissionCatalog(): Promise<PermissionCatalogSyncResult> {\n const catalog = this.getCatalog();\n const permissions = await PermissionCollection.create(this.options);\n\n const created: string[] = [];\n const unchanged: string[] = [];\n const updated: string[] = [];\n\n for (const definition of catalog.permissions) {\n const existing = await permissions.findBySlug(definition.slug);\n if (!existing) {\n const permission = await permissions.create({\n category:\n definition.category ??\n parsePermissionSlug(definition.slug).resource,\n description: definition.description ?? '',\n name: definition.name ?? definition.slug,\n slug: definition.slug,\n });\n await permission.save();\n created.push(definition.slug);\n continue;\n }\n\n const nextName = definition.name ?? existing.name;\n const nextDescription = definition.description ?? existing.description;\n const nextCategory = definition.category ?? existing.category;\n\n if (\n existing.name === nextName &&\n existing.description === nextDescription &&\n existing.category === nextCategory\n ) {\n unchanged.push(definition.slug);\n continue;\n }\n\n existing.name = nextName;\n existing.description = nextDescription;\n existing.category = nextCategory;\n await existing.save();\n updated.push(definition.slug);\n }\n\n return {\n catalog,\n created,\n unchanged,\n updated,\n };\n }\n\n private getManifestPermissionDefinitions(): PermissionDefinition[] {\n const standardActions = ['list', 'get', 'create', 'update', 'delete'];\n const definitions = new Map<string, PermissionDefinition>();\n\n for (const metadata of ObjectRegistry.getAllObjectMetadata()) {\n const registered =\n ObjectRegistry.getClassByConstructor(metadata.constructor) ??\n ObjectRegistry.getClass(metadata.name);\n const manifestEntry = registered?.qualifiedName\n ? findManifestEntryByQualifiedName(registered.qualifiedName)\n : undefined;\n\n if (isCollectionManifestEntry(manifestEntry)) {\n continue;\n }\n\n const className = metadata.name;\n const qualifiedName = registered?.qualifiedName;\n const objectConfig = manifestEntry?.decoratorConfig ?? metadata.config;\n const rawCollection = (\n objectConfig as { collection?: unknown } | undefined\n )?.collection;\n const configuredCollection =\n typeof rawCollection === 'string' && rawCollection.length > 0\n ? rawCollection\n : undefined;\n const collection =\n configuredCollection ??\n manifestEntry?.collection ??\n deriveCollectionName(metadata.name);\n\n const readExposed =\n isOperationEnabled(objectConfig.api, 'list') ||\n isOperationEnabled(objectConfig.api, 'get') ||\n isOperationEnabled(objectConfig.cli, 'list') ||\n isOperationEnabled(objectConfig.cli, 'get') ||\n isOperationEnabled(objectConfig.mcp, 'list') ||\n isOperationEnabled(objectConfig.mcp, 'get');\n if (readExposed) {\n definitions.set(`${collection}.read`, {\n className,\n collection,\n qualifiedName,\n slug: `${collection}.read`,\n });\n }\n\n const fieldEntries = manifestEntry?.fields\n ? Object.entries(manifestEntry.fields)\n : Array.from(\n (registered?.inheritedFields ?? metadata.fields).entries(),\n );\n for (const [fieldName, fieldDef] of fieldEntries) {\n const field = fieldDef as FieldReadPermissionCandidate;\n const readPermission =\n typeof field.readPermission === 'string'\n ? field.readPermission\n : typeof field._meta?.readPermission === 'string'\n ? field._meta.readPermission\n : undefined;\n if (!readPermission || definitions.has(readPermission)) {\n continue;\n }\n definitions.set(readPermission, {\n className,\n collection,\n description: `Allows reading ${fieldName} on ${humanizeResource(collection).toLowerCase()}`,\n name: `Read ${humanizeResource(fieldName)} on ${humanizeResource(collection)}`,\n qualifiedName,\n slug: readPermission,\n });\n }\n\n for (const action of ['create', 'update', 'delete'] as const) {\n const exposed =\n isOperationEnabled(objectConfig.api, action) ||\n isOperationEnabled(objectConfig.cli, action) ||\n isOperationEnabled(objectConfig.mcp, action);\n if (!exposed) {\n continue;\n }\n\n definitions.set(`${collection}.${action}`, {\n className,\n collection,\n qualifiedName,\n slug: `${collection}.${action}`,\n });\n }\n\n const methodEntries = manifestEntry?.methods\n ? Object.values(manifestEntry.methods)\n : Array.from(metadata.methods.entries());\n const publicCustomMethodNames = getPublicCustomMethodNames(\n methodEntries,\n standardActions,\n );\n const customApiMethods = new Set<string>();\n const customCliMethods = getCustomMethodExposureNames(\n objectConfig.cli,\n publicCustomMethodNames,\n );\n const customMcpMethods = getCustomMethodExposureNames(\n objectConfig.mcp,\n publicCustomMethodNames,\n );\n\n for (const methodName of publicCustomMethodNames) {\n if (isOperationEnabled(objectConfig.api, methodName)) {\n customApiMethods.add(methodName);\n }\n }\n\n const customMethods = new Set<string>([\n ...customApiMethods,\n ...customCliMethods,\n ...customMcpMethods,\n ]);\n\n for (const methodName of customMethods) {\n definitions.set(`${collection}.${methodName}`, {\n className,\n collection,\n description: `Allows ${methodName} on ${humanizeResource(collection).toLowerCase()}`,\n name: `${capitalize(methodName)} ${humanizeResource(collection)}`,\n qualifiedName,\n slug: `${collection}.${methodName}`,\n });\n }\n }\n\n return Array.from(definitions.values()).sort((left, right) =>\n left.slug.localeCompare(right.slug),\n );\n }\n\n static create(options: SmrtClassOptions = {}): PermissionCatalogService {\n return new PermissionCatalogService(options);\n }\n}\n\nexport async function syncPermissionCatalog(\n options: SmrtClassOptions = {},\n): Promise<PermissionCatalogSyncResult> {\n return PermissionCatalogService.create(options).syncPermissionCatalog();\n}\n","/**\n * RolePermissionCollection - Collection manager for RolePermission objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { RolePermission } from '../models/RolePermission.js';\nimport {\n normalizeOperationPermissionAction,\n type PermissionCatalog,\n PermissionCatalogService,\n} from '../services/PermissionCatalogService.js';\nimport { DEFAULT_ROLE_SLUGS, type DefaultRoleSlug } from '../types/index.js';\nimport { PermissionCollection } from './PermissionCollection.js';\nimport { RoleCollection } from './RoleCollection.js';\n\nexport type RolePermissionPatternMatrix = Partial<\n Record<string, readonly string[]>\n>;\n\nexport interface SeedRolePermissionsOptions {\n catalog?: PermissionCatalog;\n /**\n * Remove existing role mappings not selected by the role's current patterns.\n * Defaults false to keep seeding additive and low-risk.\n */\n prune?: boolean;\n /**\n * Sync catalog definitions into the Permission table before assigning role\n * permissions. Defaults true.\n */\n syncCatalog?: boolean;\n tenantId?: string;\n}\n\nexport interface SeedRolePermissionsResult {\n added: Record<string, string[]>;\n matched: Record<string, string[]>;\n missingPermissions: Record<string, string[]>;\n missingRoles: string[];\n removed: Record<string, string[]>;\n unchanged: Record<string, string[]>;\n unmatchedPatterns: Record<string, string[]>;\n}\n\nexport const DEFAULT_ROLE_PERMISSION_PATTERNS: Record<\n DefaultRoleSlug,\n readonly string[]\n> = {\n [DEFAULT_ROLE_SLUGS.OWNER]: ['*'],\n [DEFAULT_ROLE_SLUGS.ADMIN]: ['*'],\n [DEFAULT_ROLE_SLUGS.MEMBER]: ['*.read', '*.list', '*.get', '*.create'],\n [DEFAULT_ROLE_SLUGS.VIEWER]: ['*.read', '*.list', '*.get'],\n};\n\nconst DEFAULT_MEMBER_CREATE_DENIED_RESOURCES = new Set([\n 'group_members',\n 'groupmembers',\n 'group_roles',\n 'grouproles',\n 'groups',\n 'membership_overrides',\n 'membershipoverrides',\n 'memberships',\n 'permissions',\n 'role_permissions',\n 'rolepermissions',\n 'roles',\n 'sessions',\n 'tenant_permission_overrides',\n 'tenantpermissionoverrides',\n 'tenants',\n 'users',\n 'users_magic_link_tokens',\n 'usersmagiclinktokens',\n]);\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nfunction normalizePattern(pattern: string): string {\n const trimmed = pattern.trim();\n if (trimmed === '*' || !trimmed.includes('.')) {\n return trimmed;\n }\n\n const [resource, action, ...rest] = trimmed.split('.');\n if (!resource || !action || rest.length > 0) {\n return trimmed;\n }\n\n return `${resource}.${normalizeOperationPermissionAction(action)}`;\n}\n\nfunction matchesPattern(slug: string, pattern: string): boolean {\n if (pattern === '*') {\n return true;\n }\n\n const regex = new RegExp(\n `^${escapeRegExp(pattern).replaceAll('\\\\*', '.*')}$`,\n );\n return regex.test(slug);\n}\n\nfunction shouldSeedDefaultRolePermission(\n roleSlug: string,\n permissionSlug: string,\n): boolean {\n if (roleSlug !== DEFAULT_ROLE_SLUGS.MEMBER) {\n return true;\n }\n\n const [resource, action, ...rest] = permissionSlug.split('.');\n if (!resource || !action || rest.length > 0) {\n return true;\n }\n\n return (\n action !== 'create' || !DEFAULT_MEMBER_CREATE_DENIED_RESOURCES.has(resource)\n );\n}\n\nfunction emptyResult(): SeedRolePermissionsResult {\n return {\n added: {},\n matched: {},\n missingPermissions: {},\n missingRoles: [],\n removed: {},\n unchanged: {},\n unmatchedPatterns: {},\n };\n}\n\nfunction ensureResultSlot(\n result: SeedRolePermissionsResult,\n roleSlug: string,\n): void {\n result.added[roleSlug] ??= [];\n result.matched[roleSlug] ??= [];\n result.missingPermissions[roleSlug] ??= [];\n result.removed[roleSlug] ??= [];\n result.unchanged[roleSlug] ??= [];\n result.unmatchedPatterns[roleSlug] ??= [];\n}\n\n/**\n * Collection for managing RolePermission objects\n */\nexport class RolePermissionCollection extends SmrtCollection<RolePermission> {\n static readonly _itemClass = RolePermission;\n\n /**\n * Find all permissions for a role\n */\n async findByRole(roleId: string): Promise<RolePermission[]> {\n return await this.list({\n where: { roleId },\n });\n }\n\n /**\n * Find all roles that have a permission\n */\n async findByPermission(permissionId: string): Promise<RolePermission[]> {\n return await this.list({\n where: { permissionId },\n });\n }\n\n /**\n * Check if a role has a specific permission\n */\n async hasPermission(roleId: string, permissionId: string): Promise<boolean> {\n const results = await this.list({\n where: { roleId, permissionId },\n limit: 1,\n });\n return results.length > 0;\n }\n\n /**\n * Add a permission to a role\n */\n async addPermission(\n roleId: string,\n permissionId: string,\n ): Promise<RolePermission> {\n // Check if already exists\n const existing = await this.list({\n where: { roleId, permissionId },\n limit: 1,\n });\n if (existing.length > 0) {\n return existing[0];\n }\n\n const rolePermission = await this.create({ roleId, permissionId });\n await rolePermission.save();\n return rolePermission;\n }\n\n /**\n * Remove a permission from a role\n */\n async removePermission(\n roleId: string,\n permissionId: string,\n ): Promise<boolean> {\n const existing = await this.list({\n where: { roleId, permissionId },\n limit: 1,\n });\n if (existing.length === 0) {\n return false;\n }\n\n await existing[0].delete();\n return true;\n }\n\n /**\n * Get permission IDs for a role\n */\n async getPermissionIds(roleId: string): Promise<string[]> {\n const rolePermissions = await this.findByRole(roleId);\n return rolePermissions.map((rp) => rp.permissionId as string);\n }\n\n /**\n * Seed role-to-permission mappings from catalog-aware glob patterns.\n *\n * Defaults to the system owner/admin/member/viewer matrix. Re-running is\n * idempotent: existing mappings are reported as `unchanged`, new catalog\n * slugs are added, and stale mappings are only removed when `prune` is true.\n */\n async seedRolePermissions(\n matrix: RolePermissionPatternMatrix = DEFAULT_ROLE_PERMISSION_PATTERNS,\n options: SeedRolePermissionsOptions = {},\n ): Promise<SeedRolePermissionsResult> {\n const result = emptyResult();\n const catalogService = PermissionCatalogService.create(this.options);\n const syncResult =\n options.syncCatalog === false\n ? undefined\n : await catalogService.syncPermissionCatalog();\n const catalog =\n options.catalog ?? syncResult?.catalog ?? catalogService.getCatalog();\n const permissions = await PermissionCollection.create(this.options);\n const roles = await RoleCollection.create(this.options);\n const catalogSlugs = catalog.permissions.map(\n (permission) => permission.slug,\n );\n const permissionIdBySlug = new Map<string, string>();\n for (const permission of await permissions.list({})) {\n if (\n typeof permission.id === 'string' &&\n typeof permission.slug === 'string' &&\n permission.slug.length > 0\n ) {\n permissionIdBySlug.set(permission.slug, permission.id);\n }\n }\n\n for (const [roleSlug, rawPatterns] of Object.entries(matrix)) {\n ensureResultSlot(result, roleSlug);\n\n const role = await roles.findBySlug(roleSlug, options.tenantId);\n if (!role?.id) {\n result.missingRoles.push(roleSlug);\n continue;\n }\n\n const patterns = (rawPatterns ?? []).map(normalizePattern);\n const matchedSlugs = new Set<string>();\n for (const pattern of patterns) {\n const matches = catalogSlugs.filter((slug) =>\n matchesPattern(slug, pattern),\n );\n if (matches.length === 0) {\n result.unmatchedPatterns[roleSlug].push(pattern);\n }\n for (const slug of matches) {\n if (!shouldSeedDefaultRolePermission(roleSlug, slug)) {\n continue;\n }\n matchedSlugs.add(slug);\n }\n }\n\n const sortedMatchedSlugs = Array.from(matchedSlugs).sort();\n result.matched[roleSlug].push(...sortedMatchedSlugs);\n\n const existingPermissionIds = new Set(\n await this.getPermissionIds(role.id),\n );\n const targetPermissionIds = new Set<string>();\n\n for (const slug of sortedMatchedSlugs) {\n const permissionId = permissionIdBySlug.get(slug);\n if (!permissionId) {\n result.missingPermissions[roleSlug].push(slug);\n continue;\n }\n\n targetPermissionIds.add(permissionId);\n if (existingPermissionIds.has(permissionId)) {\n result.unchanged[roleSlug].push(slug);\n continue;\n }\n\n await this.addPermission(role.id, permissionId);\n result.added[roleSlug].push(slug);\n }\n\n if (!options.prune) {\n continue;\n }\n\n const existingPermissions = await permissions.findByIds(\n Array.from(existingPermissionIds),\n );\n for (const permissionId of existingPermissionIds) {\n if (targetPermissionIds.has(permissionId)) {\n continue;\n }\n\n await this.removePermission(role.id, permissionId);\n result.removed[roleSlug].push(\n existingPermissions.get(permissionId)?.slug ?? permissionId,\n );\n }\n }\n\n return result;\n }\n}\n","/**\n * Session model - Server-side session management for authenticated users\n * @packageDocumentation\n */\n\nimport {\n field,\n foreignKey,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { SessionStatus } from '../types/index.js';\n\n/**\n * Constructor options for {@link Session}.\n */\nexport interface SessionOptions extends SmrtObjectOptions {\n userId?: string;\n tenantId?: string | null;\n status?: SessionStatus;\n /** Accepts a Date or any value the Date constructor can coerce. */\n expiresAt?: Date | string | number;\n userAgent?: string;\n ipAddress?: string;\n /** Accepts a Date or any value the Date constructor can coerce. */\n lastAccessedAt?: Date | string | number;\n data?: Record<string, unknown>;\n}\n\n/**\n * Default session TTL: 7 days in seconds\n */\nexport const DEFAULT_SESSION_TTL = 7 * 24 * 60 * 60;\n\n/**\n * Generate a cryptographically secure session ID\n */\nexport function generateSessionId(): string {\n // Use crypto.randomUUID for secure, unique session IDs\n return crypto.randomUUID();\n}\n\n/**\n * Session represents an authenticated user session.\n *\n * Sessions are stored server-side and linked to users.\n * A session ID is stored in a cookie and used to look up the session.\n *\n * @example\n * ```typescript\n * const session = await sessions.create({\n * userId: user.id,\n * tenantId: tenant.id,\n * expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),\n * userAgent: request.headers.get('user-agent'),\n * ipAddress: getClientAddress(event)\n * });\n * await session.save();\n * ```\n */\n@smrt({\n // Sessions should not be exposed via public API for security\n api: { include: ['get', 'delete'] },\n mcp: { include: [] },\n cli: true,\n})\nexport class Session extends SmrtObject {\n /**\n * User who owns this session\n */\n @foreignKey('User')\n userId: string = '';\n\n /**\n * Tenant context for this session (for multi-tenant apps)\n * Null means no tenant context selected\n */\n @foreignKey('Tenant', { nullable: true })\n tenantId: string | null = null;\n\n /**\n * Session status\n */\n @field({ type: 'text' })\n status: SessionStatus = SessionStatus.ACTIVE;\n\n /**\n * Session expiration time\n */\n expiresAt: Date = new Date();\n\n /**\n * User agent string from the browser\n */\n userAgent: string = '';\n\n /**\n * IP address of the client\n */\n ipAddress: string = '';\n\n /**\n * Last activity timestamp (updated on each request)\n */\n lastAccessedAt: Date = new Date();\n\n /**\n * Custom session data (JSON serializable)\n */\n data: Record<string, unknown> = {};\n\n constructor(options: SessionOptions = {}) {\n super(options);\n if (options.userId !== undefined) this.userId = options.userId;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.status !== undefined) this.status = options.status;\n if (options.expiresAt !== undefined) {\n this.expiresAt =\n options.expiresAt instanceof Date\n ? options.expiresAt\n : new Date(options.expiresAt);\n }\n if (options.userAgent !== undefined) this.userAgent = options.userAgent;\n if (options.ipAddress !== undefined) this.ipAddress = options.ipAddress;\n if (options.lastAccessedAt !== undefined) {\n this.lastAccessedAt =\n options.lastAccessedAt instanceof Date\n ? options.lastAccessedAt\n : new Date(options.lastAccessedAt);\n }\n if (options.data !== undefined) this.data = options.data;\n }\n\n /**\n * Check if the session is currently valid (active and not expired)\n */\n isValid(): boolean {\n return this.status === SessionStatus.ACTIVE && new Date() < this.expiresAt;\n }\n\n /**\n * Check if the session has expired\n */\n isExpired(): boolean {\n return new Date() >= this.expiresAt;\n }\n\n /**\n * Check if the session was revoked\n */\n isRevoked(): boolean {\n return this.status === SessionStatus.REVOKED;\n }\n\n /**\n * Update the last accessed timestamp\n */\n touch(): void {\n this.lastAccessedAt = new Date();\n }\n\n /**\n * Extend the session expiration by the given TTL (in seconds)\n */\n extend(ttlSeconds: number = DEFAULT_SESSION_TTL): void {\n this.expiresAt = new Date(Date.now() + ttlSeconds * 1000);\n this.touch();\n }\n\n /**\n * Revoke the session\n */\n revoke(): void {\n this.status = SessionStatus.REVOKED;\n }\n\n /**\n * Set the tenant context for this session\n */\n setTenant(tenantId: string | null): void {\n this.tenantId = tenantId;\n }\n\n /**\n * Get or set custom session data\n */\n getData<T>(key: string): T | undefined {\n return this.data[key] as T | undefined;\n }\n\n /**\n * Set custom session data\n */\n setData(key: string, value: unknown): void {\n this.data[key] = value;\n }\n\n /**\n * Remove custom session data\n */\n removeData(key: string): void {\n delete this.data[key];\n }\n}\n","/**\n * SessionCollection - Collection manager for Session objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport {\n DEFAULT_SESSION_TTL,\n generateSessionId,\n Session,\n} from '../models/Session.js';\nimport { SessionStatus } from '../types/index.js';\n\n/**\n * Options for creating a new session\n */\nexport interface CreateSessionOptions {\n /** User ID for the session */\n userId: string;\n /** Optional tenant ID for multi-tenant context */\n tenantId?: string;\n /** Session TTL in seconds (default: 7 days) */\n ttl?: number;\n /** User agent string */\n userAgent?: string;\n /** Client IP address */\n ipAddress?: string;\n /** Custom session data */\n data?: Record<string, unknown>;\n}\n\n/**\n * Collection for managing Session objects\n */\nexport class SessionCollection extends SmrtCollection<Session> {\n static readonly _itemClass = Session;\n\n /**\n * Create a new session with a secure ID\n */\n async createSession(options: CreateSessionOptions): Promise<Session> {\n const ttl = options.ttl ?? DEFAULT_SESSION_TTL;\n const expiresAt = new Date(Date.now() + ttl * 1000);\n\n const session = await this.create({\n id: generateSessionId(),\n userId: options.userId,\n tenantId: options.tenantId ?? null,\n status: SessionStatus.ACTIVE,\n expiresAt,\n userAgent: options.userAgent ?? '',\n ipAddress: options.ipAddress ?? '',\n lastAccessedAt: new Date(),\n data: options.data ?? {},\n });\n\n await session.save();\n return session;\n }\n\n /**\n * Find a valid session by ID\n * Returns null if session doesn't exist, is expired, or is revoked\n */\n async findValidSession(sessionId: string): Promise<Session | null> {\n const session = await this.get(sessionId);\n if (!session) return null;\n\n // Check if session is still valid\n if (!session.isValid()) {\n // If expired but still marked active, flip the status with an atomic\n // guarded UPDATE rather than a read-then-save(). A non-atomic save()\n // rewrites the whole row and could clobber a concurrent revoke/extend on\n // a lock-less backend; the WHERE clause makes the EXPIRED transition a\n // no-op once another writer has already changed the status or expiry.\n if (session.isExpired() && session.status === SessionStatus.ACTIVE) {\n const now = new Date().toISOString();\n await this.db.query(\n `UPDATE ${this.tableName}\n SET status = ?, updated_at = ?\n WHERE id = ? AND status = ? AND expires_at < ?`,\n SessionStatus.EXPIRED,\n now,\n sessionId,\n SessionStatus.ACTIVE,\n now,\n );\n }\n return null;\n }\n\n return session;\n }\n\n /**\n * Update last accessed time and optionally extend session\n */\n async touch(\n sessionId: string,\n extendTtl: boolean = false,\n ttl: number = DEFAULT_SESSION_TTL,\n ): Promise<boolean> {\n const session = await this.findValidSession(sessionId);\n if (!session) return false;\n\n session.touch();\n if (extendTtl) {\n session.extend(ttl);\n }\n await session.save();\n return true;\n }\n\n /**\n * Find all active sessions for a user\n */\n async findByUser(userId: string): Promise<Session[]> {\n const results = await this.list({\n where: {\n userId,\n status: SessionStatus.ACTIVE,\n },\n orderBy: 'last_accessed_at DESC',\n });\n\n // Filter out expired sessions\n return results.filter((session) => session.isValid());\n }\n\n /**\n * Delete all sessions for a user (logout from all devices)\n */\n async deleteUserSessions(userId: string): Promise<number> {\n const sessions = await this.list({\n where: { userId },\n });\n\n let count = 0;\n for (const session of sessions) {\n await session.delete();\n count++;\n }\n\n return count;\n }\n\n /**\n * Revoke all sessions for a user (soft delete)\n */\n async revokeUserSessions(userId: string): Promise<number> {\n const sessions = await this.list({\n where: {\n userId,\n status: SessionStatus.ACTIVE,\n },\n });\n\n let count = 0;\n for (const session of sessions) {\n session.revoke();\n await session.save();\n count++;\n }\n\n return count;\n }\n\n /**\n * Revoke a specific session\n */\n async revokeSession(sessionId: string): Promise<boolean> {\n const session = await this.get(sessionId);\n if (!session) return false;\n\n session.revoke();\n await session.save();\n return true;\n }\n\n /**\n * Delete expired sessions (cleanup job)\n * Returns the number of deleted sessions\n */\n async deleteExpired(): Promise<number> {\n // #1400: a single atomic DELETE. The previous two-pass version listed\n // expired-by-time and revoked sessions separately, so a session that was\n // both got delete()'d twice — the second delete could throw and abort the\n // cleanup job mid-run, leaving expired sessions un-reaped. One statement\n // also avoids hydrating every row just to delete it.\n const now = new Date().toISOString();\n const { rowCount } = await this.db.query(\n `DELETE FROM ${this.tableName}\n WHERE expires_at < ? OR status = ?`,\n now,\n SessionStatus.REVOKED,\n );\n\n return rowCount ?? 0;\n }\n\n /**\n * Count active sessions for a user\n */\n async countUserSessions(userId: string): Promise<number> {\n const sessions = await this.findByUser(userId);\n return sessions.length;\n }\n\n /**\n * Update tenant context for a session (low-level primitive).\n *\n * SECURITY (#1400): this does NOT verify that the session's user is a member\n * of `tenantId` — it is the unguarded storage primitive. Application/route\n * code must go through {@link SessionService.switchTenant}, which fail-closes\n * on a missing/inactive membership before calling this. Calling it directly\n * with an untrusted `tenantId` reintroduces the cross-tenant access bug.\n */\n async setSessionTenant(\n sessionId: string,\n tenantId: string | null,\n ): Promise<boolean> {\n const session = await this.findValidSession(sessionId);\n if (!session) return false;\n\n session.setTenant(tenantId);\n await session.save();\n return true;\n }\n\n /**\n * Set custom session data\n */\n async setSessionData(\n sessionId: string,\n key: string,\n value: unknown,\n ): Promise<boolean> {\n const session = await this.findValidSession(sessionId);\n if (!session) return false;\n\n session.setData(key, value);\n await session.save();\n return true;\n }\n\n /**\n * Get custom session data\n */\n async getSessionData<T>(\n sessionId: string,\n key: string,\n ): Promise<T | undefined> {\n const session = await this.findValidSession(sessionId);\n if (!session) return undefined;\n\n return session.getData<T>(key);\n }\n}\n","/**\n * Tenant model - organizational boundary for multi-tenancy\n * @packageDocumentation\n */\n\nimport {\n field,\n foreignKey,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport type { Tenant as TenantContract } from '@happyvertical/smrt-types';\nimport { TenantStatus } from '../types/index.js';\n\n/**\n * Constructor options for {@link Tenant}.\n */\nexport interface TenantOptions extends SmrtObjectOptions {\n name?: string;\n status?: TenantStatus;\n description?: string;\n parentTenantId?: string | null;\n hierarchyLevel?: number;\n hierarchyPath?: string;\n cascadePermissions?: boolean;\n inheritPermissions?: boolean;\n}\n\n/**\n * Maximum allowed depth for tenant hierarchy.\n * Prevents excessively deep trees that could cause performance issues.\n */\nexport const MAX_TENANT_HIERARCHY_DEPTH = 10;\n\n/**\n * Tenant represents an organizational boundary in the multi-tenant system.\n *\n * Supports hierarchical organization with parent-child relationships.\n * Users can belong to multiple tenants through Memberships.\n * Each tenant can have custom roles in addition to system defaults.\n *\n * ## Hierarchical Tenants\n *\n * Tenants can be organized in a tree structure where child tenants\n * can optionally inherit permissions from their parent tenants.\n *\n * ### Cascade Control\n *\n * Two flags control permission inheritance:\n * - `cascadePermissions`: If true, this tenant pushes its permissions to children\n * - `inheritPermissions`: If true, this tenant accepts permissions from parent\n *\n * Both must be true for inheritance to flow from parent to child.\n *\n * @example\n * ```typescript\n * // Create root tenant\n * const corp = await tenants.create({\n * name: 'Acme Corporation',\n * slug: 'acme-corp',\n * cascadePermissions: true, // Push permissions to children\n * });\n * await corp.save();\n *\n * // Create child tenant that inherits\n * const division = await tenants.create({\n * name: 'Acme West Division',\n * slug: 'acme-west',\n * parentTenantId: corp.id,\n * inheritPermissions: true, // Accept parent permissions\n * });\n * await division.save();\n *\n * // Create independent child (breaks inheritance chain)\n * const independent = await tenants.create({\n * name: 'Acme Labs',\n * slug: 'acme-labs',\n * parentTenantId: corp.id,\n * inheritPermissions: false, // Does NOT inherit from parent\n * });\n * await independent.save();\n * ```\n */\n@smrt({\n tableStrategy: 'sti',\n // #1400: read-only generated surface — tenant create/update (incl. the\n // cascadePermissions/inheritPermissions flags that drive the permission\n // cascade) goes through TenantService, not auth-only generated CRUD.\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class Tenant extends SmrtObject implements TenantContract {\n /**\n * Display name for the tenant\n */\n name: string = '';\n\n /**\n * Tenant status\n */\n @field({ type: 'text' })\n status: TenantStatus = TenantStatus.ACTIVE;\n\n /**\n * Optional description\n */\n description: string = '';\n\n // ============= Hierarchy Fields =============\n\n /**\n * Parent tenant ID for hierarchical organization.\n * Null for root-level tenants.\n */\n @foreignKey('Tenant', { nullable: true })\n parentTenantId?: string | null;\n\n /**\n * Depth in the hierarchy tree (0 = root, 1 = first level child, etc.)\n * Automatically managed by TenantCollection methods.\n */\n hierarchyLevel: number = 0;\n\n /**\n * Materialized path for efficient tree traversal.\n * Format: \"ancestor-id/parent-id\" (path to parent; does not include this tenant's id)\n * Empty string for root tenants.\n * Automatically managed by TenantCollection methods.\n */\n hierarchyPath: string = '';\n\n // ============= Permission Cascade Control =============\n\n /**\n * If true, this tenant's permissions cascade DOWN to child tenants.\n * Children can still opt-out by setting inheritPermissions: false.\n * Default: true\n */\n cascadePermissions: boolean = true;\n\n /**\n * If true, this tenant ACCEPTS permissions from its parent tenant.\n * Parent must also have cascadePermissions: true for inheritance to work.\n * Default: true\n */\n inheritPermissions: boolean = true;\n\n constructor(options: TenantOptions = {}) {\n super(options);\n if (options.name !== undefined) this.name = options.name;\n if (options.status !== undefined) this.status = options.status;\n if (options.description !== undefined)\n this.description = options.description;\n if (options.parentTenantId !== undefined)\n this.parentTenantId = options.parentTenantId;\n if (options.hierarchyLevel !== undefined)\n this.hierarchyLevel = options.hierarchyLevel;\n if (options.hierarchyPath !== undefined)\n this.hierarchyPath = options.hierarchyPath;\n if (options.cascadePermissions !== undefined)\n this.cascadePermissions = options.cascadePermissions;\n if (options.inheritPermissions !== undefined)\n this.inheritPermissions = options.inheritPermissions;\n }\n\n /**\n * Check if tenant is active\n */\n isActive(): boolean {\n return this.status === TenantStatus.ACTIVE;\n }\n\n /**\n * Check if tenant is suspended\n */\n isSuspended(): boolean {\n return this.status === TenantStatus.SUSPENDED;\n }\n\n /**\n * Check if this is a root-level tenant (no parent)\n */\n isRoot(): boolean {\n return !this.parentTenantId;\n }\n\n /**\n * Check if this tenant is configured to cascade permissions to children.\n *\n * Note: This does NOT indicate whether any child tenants actually exist.\n * Use TenantCollection.findChildren() for accurate child lookup.\n */\n canCascadeToChildren(): boolean {\n return this.cascadePermissions;\n }\n\n /**\n * Check if permission inheritance is active for this tenant.\n * Inheritance is active if:\n * - This tenant has a parent AND\n * - This tenant has inheritPermissions: true\n *\n * Note: The parent must also have cascadePermissions: true\n * for actual inheritance to occur. Use PermissionResolver\n * for accurate permission calculation.\n */\n acceptsInheritance(): boolean {\n return !!this.parentTenantId && this.inheritPermissions;\n }\n\n /**\n * Get ancestor IDs from the hierarchy path.\n * Returns an array of tenant IDs from root to immediate parent.\n * Empty array for root tenants.\n */\n getAncestorIds(): string[] {\n if (!this.hierarchyPath) return [];\n return this.hierarchyPath.split('/').filter((id) => id.length > 0);\n }\n}\n","/**\n * TenantCollection - Collection manager for Tenant objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection, type SmrtCreateInput } from '@happyvertical/smrt-core';\nimport { MAX_TENANT_HIERARCHY_DEPTH, Tenant } from '../models/Tenant.js';\nimport { TenantStatus } from '../types/index.js';\n\n/**\n * Error thrown when tenant hierarchy operations fail\n */\nexport class TenantHierarchyError extends Error {\n constructor(\n message: string,\n public readonly code:\n | 'CIRCULAR_REFERENCE'\n | 'MAX_DEPTH_EXCEEDED'\n | 'PARENT_NOT_FOUND'\n | 'INVALID_OPERATION',\n ) {\n super(message);\n this.name = 'TenantHierarchyError';\n }\n}\n\n/**\n * Options for creating a child tenant\n */\nexport interface CreateChildTenantOptions {\n name: string;\n slug?: string;\n description?: string;\n status?: TenantStatus;\n /** Override parent's cascade setting for this child. Default: true */\n inheritPermissions?: boolean;\n /** Whether this child cascades permissions to its children. Default: true */\n cascadePermissions?: boolean;\n}\n\n/**\n * Collection for managing Tenant objects with hierarchical support.\n *\n * Provides methods for:\n * - Basic CRUD operations\n * - Hierarchy management (parent/child relationships)\n * - Tree traversal (ancestors, descendants, siblings)\n * - Hierarchy path maintenance\n */\nexport class TenantCollection extends SmrtCollection<Tenant> {\n static readonly _itemClass = Tenant;\n\n // ============= Basic Query Methods =============\n\n /**\n * Find tenants by status\n */\n async findByStatus(status: TenantStatus): Promise<Tenant[]> {\n return await this.list({\n where: { status },\n orderBy: 'name ASC',\n });\n }\n\n /**\n * Find all active tenants\n */\n async findActive(): Promise<Tenant[]> {\n return await this.findByStatus(TenantStatus.ACTIVE);\n }\n\n /**\n * Find tenant by slug\n */\n async findBySlug(slug: string): Promise<Tenant | null> {\n const results = await this.list({\n where: { slug },\n limit: 1,\n });\n return results.length > 0 ? results[0] : null;\n }\n\n // ============= Hierarchy Query Methods =============\n\n /**\n * Find all root tenants (tenants with no parent)\n */\n async findRoots(): Promise<Tenant[]> {\n return await this.list({\n where: { parentTenantId: null },\n orderBy: 'name ASC',\n });\n }\n\n /**\n * Find direct children of a tenant\n */\n async findChildren(parentTenantId: string): Promise<Tenant[]> {\n return await this.list({\n where: { parentTenantId },\n orderBy: 'name ASC',\n });\n }\n\n /**\n * Find the parent tenant of a given tenant\n */\n async findParent(tenantId: string): Promise<Tenant | null> {\n const tenant = await this.get({ id: tenantId });\n if (!tenant?.parentTenantId) {\n return null;\n }\n return await this.get({ id: tenant.parentTenantId });\n }\n\n /**\n * Get all ancestors of a tenant, from immediate parent to root.\n * Uses the hierarchyPath for efficient lookup.\n */\n async getAncestors(tenantId: string): Promise<Tenant[]> {\n const tenant = await this.get({ id: tenantId });\n if (!tenant) {\n return [];\n }\n\n const ancestorIds = tenant.getAncestorIds();\n if (ancestorIds.length === 0) {\n return [];\n }\n\n // Batch fetch all ancestors\n const ancestorsList = await this.listByIds(ancestorIds);\n const ancestorsMap = new Map(ancestorsList.map((a) => [a.id, a]));\n\n // Return in order from immediate parent to root\n const ancestors: Tenant[] = [];\n for (let i = ancestorIds.length - 1; i >= 0; i--) {\n const ancestor = ancestorsMap.get(ancestorIds[i]);\n if (ancestor) {\n ancestors.push(ancestor);\n }\n }\n\n return ancestors;\n }\n\n /**\n * Get all ancestors in order from root to immediate parent.\n * Reverse of getAncestors.\n */\n async getAncestorsFromRoot(tenantId: string): Promise<Tenant[]> {\n const ancestors = await this.getAncestors(tenantId);\n return ancestors.reverse();\n }\n\n /**\n * Get all descendants of a tenant (all children, grandchildren, etc.)\n * Uses hierarchyPath prefix matching for efficient lookup.\n */\n async getDescendants(tenantId: string): Promise<Tenant[]> {\n const tenant = await this.get({ id: tenantId });\n if (!tenant?.id) {\n return [];\n }\n\n // Build the path prefix to search for\n const pathPrefix = tenant.hierarchyPath\n ? `${tenant.hierarchyPath}/${tenant.id}`\n : tenant.id;\n\n // Use database-level LIKE query for initial filtering\n // This is much more efficient than loading all tenants for large hierarchies\n const candidates = await this.list({\n where: {\n 'hierarchyPath like': `${pathPrefix}%`,\n },\n });\n\n // Apply boundary check in memory to avoid false positives\n // Without the '/' check, \"ancestor/tenant1\" would incorrectly match \"ancestor/tenant123\"\n return candidates.filter(\n (t) =>\n t.hierarchyPath === pathPrefix ||\n t.hierarchyPath?.startsWith(`${pathPrefix}/`),\n );\n }\n\n /**\n * Get siblings of a tenant (other tenants with the same parent)\n */\n async getSiblings(tenantId: string): Promise<Tenant[]> {\n const tenant = await this.get({ id: tenantId });\n if (!tenant) {\n return [];\n }\n\n const siblings = await this.list({\n where: { parentTenantId: tenant.parentTenantId ?? null },\n orderBy: 'name ASC',\n });\n\n // Exclude self from siblings\n return siblings.filter((s) => s.id !== tenantId);\n }\n\n /**\n * Check if a tenant is an ancestor of another tenant\n */\n async isAncestorOf(\n potentialAncestorId: string,\n tenantId: string,\n ): Promise<boolean> {\n const tenant = await this.get({ id: tenantId });\n if (!tenant) {\n return false;\n }\n\n const ancestorIds = tenant.getAncestorIds();\n return ancestorIds.includes(potentialAncestorId);\n }\n\n /**\n * Check if a tenant is a descendant of another tenant\n */\n async isDescendantOf(\n potentialDescendantId: string,\n tenantId: string,\n ): Promise<boolean> {\n return await this.isAncestorOf(tenantId, potentialDescendantId);\n }\n\n // ============= Hierarchy Management Methods =============\n\n /**\n * Create a child tenant under a parent.\n * Automatically sets hierarchyLevel and hierarchyPath.\n */\n async createChild(\n parentTenantId: string,\n options: CreateChildTenantOptions,\n ): Promise<Tenant> {\n const parent = await this.get({ id: parentTenantId });\n if (!parent?.id) {\n throw new TenantHierarchyError(\n `Parent tenant not found: ${parentTenantId}`,\n 'PARENT_NOT_FOUND',\n );\n }\n\n // Check depth limit\n const newLevel = parent.hierarchyLevel + 1;\n if (newLevel >= MAX_TENANT_HIERARCHY_DEPTH) {\n throw new TenantHierarchyError(\n `Maximum hierarchy depth (${MAX_TENANT_HIERARCHY_DEPTH}) exceeded`,\n 'MAX_DEPTH_EXCEEDED',\n );\n }\n\n // Build hierarchy path\n const newPath = parent.hierarchyPath\n ? `${parent.hierarchyPath}/${parent.id}`\n : parent.id;\n\n const child = await this.create({\n name: options.name,\n slug: options.slug,\n description: options.description ?? '',\n status: options.status ?? TenantStatus.ACTIVE,\n parentTenantId: parentTenantId,\n hierarchyLevel: newLevel,\n hierarchyPath: newPath,\n cascadePermissions: options.cascadePermissions ?? true,\n inheritPermissions: options.inheritPermissions ?? true,\n });\n\n await child.save();\n return child;\n }\n\n /**\n * Move a tenant to a new parent.\n * Updates hierarchyLevel and hierarchyPath for the tenant and all descendants.\n */\n async moveToParent(\n tenantId: string,\n newParentId: string | null,\n ): Promise<Tenant> {\n const tenant = await this.get({ id: tenantId });\n if (!tenant?.id) {\n throw new TenantHierarchyError(\n `Tenant not found: ${tenantId}`,\n 'INVALID_OPERATION',\n );\n }\n\n // Validate not moving to self\n if (newParentId === tenantId) {\n throw new TenantHierarchyError(\n 'Cannot move tenant to itself',\n 'CIRCULAR_REFERENCE',\n );\n }\n\n // Validate not moving to a descendant\n if (newParentId) {\n const isDescendant = await this.isDescendantOf(newParentId, tenantId);\n if (isDescendant) {\n throw new TenantHierarchyError(\n 'Cannot move tenant to one of its descendants',\n 'CIRCULAR_REFERENCE',\n );\n }\n }\n\n // Fetch descendants BEFORE any changes (uses current hierarchyPath for lookup)\n const descendants = await this.getDescendants(tenantId);\n\n let newLevel: number;\n let newPath: string;\n\n if (newParentId === null) {\n // Moving to root\n newLevel = 0;\n newPath = '';\n } else {\n const newParent = await this.get({ id: newParentId });\n if (!newParent) {\n throw new TenantHierarchyError(\n `New parent tenant not found: ${newParentId}`,\n 'PARENT_NOT_FOUND',\n );\n }\n\n newLevel = newParent.hierarchyLevel + 1;\n\n // Check depth limit (considering descendants)\n const maxDescendantDepth = descendants.reduce(\n (max, d) => Math.max(max, d.hierarchyLevel - tenant.hierarchyLevel),\n 0,\n );\n\n if (newLevel + maxDescendantDepth >= MAX_TENANT_HIERARCHY_DEPTH) {\n throw new TenantHierarchyError(\n `Moving would exceed maximum hierarchy depth (${MAX_TENANT_HIERARCHY_DEPTH})`,\n 'MAX_DEPTH_EXCEEDED',\n );\n }\n\n newPath = newParent.hierarchyPath\n ? `${newParent.hierarchyPath}/${newParent.id}`\n : newParent.id!;\n }\n\n // Calculate the path prefix for descendants\n const oldPath = tenant.hierarchyPath\n ? `${tenant.hierarchyPath}/${tenant.id}`\n : tenant.id;\n const newPathForDescendants = newPath\n ? `${newPath}/${tenant.id}`\n : tenant.id;\n\n // Update the tenant\n const levelDelta = newLevel - tenant.hierarchyLevel;\n tenant.parentTenantId = newParentId;\n tenant.hierarchyLevel = newLevel;\n tenant.hierarchyPath = newPath;\n await tenant.save();\n\n // Update all descendants (using the list fetched before changes)\n for (const descendant of descendants) {\n // Validate descendant has hierarchyPath - missing path indicates data corruption\n if (!descendant.hierarchyPath) {\n throw new TenantHierarchyError(\n `Descendant tenant ${descendant.id} has no hierarchyPath while updating hierarchy from ${oldPath} to ${newPathForDescendants}`,\n 'INVALID_OPERATION',\n );\n }\n\n // Validate descendant path starts with expected prefix\n if (!descendant.hierarchyPath.startsWith(oldPath)) {\n throw new TenantHierarchyError(\n `Descendant tenant ${descendant.id} has hierarchyPath \"${descendant.hierarchyPath}\" which does not start with expected prefix \"${oldPath}\"`,\n 'INVALID_OPERATION',\n );\n }\n\n // Update path by replacing old prefix with new prefix\n descendant.hierarchyPath =\n newPathForDescendants +\n descendant.hierarchyPath.substring(oldPath.length);\n\n // Adjust level by the same delta\n descendant.hierarchyLevel += levelDelta;\n await descendant.save();\n }\n\n return tenant;\n }\n\n /**\n * Make a tenant a root tenant (remove from hierarchy)\n */\n async makeRoot(tenantId: string): Promise<Tenant> {\n return await this.moveToParent(tenantId, null);\n }\n\n /**\n * Validate that a tenant hierarchy is consistent.\n * Returns validation errors if any.\n */\n async validateHierarchy(tenantId: string): Promise<string[]> {\n const errors: string[] = [];\n const tenant = await this.get({ id: tenantId });\n\n if (!tenant) {\n return [`Tenant not found: ${tenantId}`];\n }\n\n // Check parent exists if set\n if (tenant.parentTenantId) {\n const parent = await this.get({ id: tenant.parentTenantId });\n if (!parent) {\n errors.push(`Parent tenant not found: ${tenant.parentTenantId}`);\n } else {\n // Check hierarchy level\n if (tenant.hierarchyLevel !== parent.hierarchyLevel + 1) {\n errors.push(\n `Hierarchy level mismatch: expected ${parent.hierarchyLevel + 1}, got ${tenant.hierarchyLevel}`,\n );\n }\n\n // Check hierarchy path\n const expectedPath = parent.hierarchyPath\n ? `${parent.hierarchyPath}/${parent.id}`\n : parent.id;\n if (tenant.hierarchyPath !== expectedPath) {\n errors.push(\n `Hierarchy path mismatch: expected \"${expectedPath}\", got \"${tenant.hierarchyPath}\"`,\n );\n }\n }\n } else {\n // Root tenant checks\n if (tenant.hierarchyLevel !== 0) {\n errors.push(\n `Root tenant should have hierarchyLevel 0, got ${tenant.hierarchyLevel}`,\n );\n }\n if (tenant.hierarchyPath !== '') {\n errors.push(\n `Root tenant should have empty hierarchyPath, got \"${tenant.hierarchyPath}\"`,\n );\n }\n }\n\n // Check for circular reference (shouldn't be possible with path, but double-check)\n if (tenant.parentTenantId && tenant.id) {\n const ancestorIds = tenant.getAncestorIds();\n if (ancestorIds.includes(tenant.id)) {\n errors.push('Circular reference detected in hierarchy');\n }\n }\n\n return errors;\n }\n\n /**\n * Get the full hierarchy tree starting from a tenant.\n * Returns a nested structure useful for UI rendering.\n */\n async getTree(\n rootTenantId?: string,\n ): Promise<Array<Tenant & { children: Tenant[] }>> {\n const roots = rootTenantId\n ? [await this.get({ id: rootTenantId })]\n : await this.findRoots();\n\n const buildTree = async (\n tenant: Tenant | null,\n ): Promise<(Tenant & { children: Tenant[] }) | null> => {\n if (!tenant) return null;\n\n const children = await this.findChildren(tenant.id!);\n const childTrees = await Promise.all(children.map(buildTree));\n\n // Use Object.assign to preserve Tenant instance methods while adding children\n const tenantWithChildren = Object.assign(tenant, {\n children: childTrees.filter(Boolean) as Array<\n Tenant & { children: Tenant[] }\n >,\n });\n return tenantWithChildren as Tenant & { children: Tenant[] };\n };\n\n const trees = await Promise.all(roots.map(buildTree));\n return trees.filter(Boolean) as Array<Tenant & { children: Tenant[] }>;\n }\n\n // ============= Override create to handle hierarchy =============\n\n /**\n * Override create to automatically set hierarchy fields for new tenants.\n * Only calculates them when the caller hasn't already supplied\n * `hierarchyLevel`/`hierarchyPath` in the create input — an explicitly\n * provided value is preserved as-is.\n */\n async create(options: SmrtCreateInput<Tenant>): Promise<Tenant> {\n // If parentTenantId is provided and hierarchy fields not already set\n if (options.parentTenantId) {\n // Only calculate when the caller didn't already supply them\n // (a provided hierarchyLevel/hierarchyPath is preserved as-is)\n const needsHierarchyCalc =\n options.hierarchyLevel === undefined ||\n options.hierarchyPath === undefined;\n\n if (needsHierarchyCalc) {\n const parent = await this.get({ id: options.parentTenantId });\n if (!parent?.id) {\n throw new TenantHierarchyError(\n `Parent tenant not found: ${options.parentTenantId}`,\n 'PARENT_NOT_FOUND',\n );\n }\n\n const newLevel = parent.hierarchyLevel + 1;\n if (newLevel >= MAX_TENANT_HIERARCHY_DEPTH) {\n throw new TenantHierarchyError(\n `Maximum hierarchy depth (${MAX_TENANT_HIERARCHY_DEPTH}) exceeded`,\n 'MAX_DEPTH_EXCEEDED',\n );\n }\n\n if (options.hierarchyLevel === undefined) {\n options.hierarchyLevel = newLevel;\n }\n if (options.hierarchyPath === undefined) {\n options.hierarchyPath = parent.hierarchyPath\n ? `${parent.hierarchyPath}/${parent.id}`\n : parent.id;\n }\n }\n } else {\n // Root tenant\n options.hierarchyLevel = options.hierarchyLevel ?? 0;\n options.hierarchyPath = options.hierarchyPath ?? '';\n }\n\n return super.create(options);\n }\n}\n","/**\n * TenantPermissionOverride model - tenant-level permission grant/deny/inherit\n * @packageDocumentation\n */\n\nimport {\n field,\n foreignKey,\n SmrtObject,\n type SmrtObjectOptions,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantPermissionEffect } from '../types/index.js';\n\n/**\n * Constructor options for {@link TenantPermissionOverride}.\n */\nexport interface TenantPermissionOverrideOptions extends SmrtObjectOptions {\n tenantId?: string;\n permissionId?: string;\n effect?: TenantPermissionEffect;\n}\n\n/**\n * TenantPermissionOverride allows setting explicit permission values at the tenant level.\n *\n * This is used in hierarchical tenant structures to control permission inheritance.\n * Each override specifies whether a permission is:\n * - INHERIT: Use the parent tenant's value (default behavior)\n * - GRANT: Explicitly grant at this tenant level\n * - DENY: Explicitly deny at this tenant level (blocks inheritance)\n *\n * ## Resolution Order\n *\n * When resolving effective tenant permissions:\n * 1. Walk down the tenant hierarchy from root to target, accumulating permissions\n * 2. Apply each tenant's overrides (DENY blocks, GRANT adds)\n * 3. INHERIT means \"use whatever the parent resolved to\"\n *\n * @example\n * ```typescript\n * // Explicitly grant a permission at this tenant level\n * const override = await tenantPermissionOverrides.create({\n * tenantId: tenant.id,\n * permissionId: specialPermission.id,\n * effect: TenantPermissionEffect.GRANT\n * });\n *\n * // Block a permission from being inherited (even if parent grants it)\n * const block = await tenantPermissionOverrides.create({\n * tenantId: tenant.id,\n * permissionId: dangerousPermission.id,\n * effect: TenantPermissionEffect.DENY\n * });\n *\n * // Explicitly inherit (same as not having an override, but documents intent)\n * const inherit = await tenantPermissionOverrides.create({\n * tenantId: tenant.id,\n * permissionId: standardPermission.id,\n * effect: TenantPermissionEffect.INHERIT\n * });\n * ```\n */\n@smrt({\n // #1400: read-only generated REST + MCP surface — RBAC/identity writes go\n // through permission-gated services, not auth-only generated CRUD. mcp must\n // be explicit: an omitted mcp config generates the FULL tool surface.\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: true,\n})\nexport class TenantPermissionOverride extends SmrtObject {\n /**\n * Foreign key to Tenant\n */\n @foreignKey('Tenant', { required: true })\n tenantId?: string;\n\n /**\n * Foreign key to Permission\n */\n @foreignKey('Permission', { required: true })\n permissionId?: string;\n\n /**\n * Effect of the override: inherit, grant, or deny\n */\n @field({ type: 'text' })\n effect: TenantPermissionEffect = TenantPermissionEffect.INHERIT;\n\n constructor(options: TenantPermissionOverrideOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.permissionId !== undefined)\n this.permissionId = options.permissionId;\n if (options.effect !== undefined) this.effect = options.effect;\n }\n\n /**\n * Check if this override inherits from parent\n */\n isInherit(): boolean {\n return this.effect === TenantPermissionEffect.INHERIT;\n }\n\n /**\n * Check if this override grants the permission\n */\n isGrant(): boolean {\n return this.effect === TenantPermissionEffect.GRANT;\n }\n\n /**\n * Check if this override denies the permission\n */\n isDeny(): boolean {\n return this.effect === TenantPermissionEffect.DENY;\n }\n\n /**\n * Check if this override has an explicit effect (not inherit)\n */\n hasExplicitEffect(): boolean {\n return this.effect !== TenantPermissionEffect.INHERIT;\n }\n}\n","/**\n * TenantPermissionOverrideCollection - Collection manager for TenantPermissionOverride objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { TenantPermissionOverride } from '../models/TenantPermissionOverride.js';\nimport { TenantPermissionEffect } from '../types/index.js';\n\n/**\n * Result of resolving tenant permission overrides\n */\nexport interface TenantPermissionOverrideResult {\n /** Permission IDs explicitly granted at this tenant level */\n grantedPermissionIds: string[];\n /** Permission IDs explicitly denied at this tenant level */\n deniedPermissionIds: string[];\n /** Permission IDs set to inherit from parent */\n inheritedPermissionIds: string[];\n}\n\n/**\n * Collection for managing TenantPermissionOverride objects.\n *\n * Provides methods for setting and querying tenant-level permission overrides\n * used in hierarchical tenant permission inheritance.\n */\nexport class TenantPermissionOverrideCollection extends SmrtCollection<TenantPermissionOverride> {\n static readonly _itemClass = TenantPermissionOverride;\n\n /**\n * Find all overrides for a tenant\n */\n async findByTenant(tenantId: string): Promise<TenantPermissionOverride[]> {\n return await this.list({\n where: { tenantId },\n });\n }\n\n /**\n * Find grant overrides for a tenant.\n *\n * Filters in memory because the `effect` column is JSON-typed and\n * Postgres rejects bare `json = text` comparisons. A single\n * `findByTenant` call is reused for grant, deny, and inherit lookups.\n */\n async findGrants(tenantId: string): Promise<TenantPermissionOverride[]> {\n const all = await this.findByTenant(tenantId);\n return all.filter((o) => o.effect === TenantPermissionEffect.GRANT);\n }\n\n /**\n * Find deny overrides for a tenant.\n *\n * See `findGrants` for rationale on in-memory filtering.\n */\n async findDenies(tenantId: string): Promise<TenantPermissionOverride[]> {\n const all = await this.findByTenant(tenantId);\n return all.filter((o) => o.effect === TenantPermissionEffect.DENY);\n }\n\n /**\n * Find inherit overrides for a tenant.\n *\n * See `findGrants` for rationale on in-memory filtering.\n */\n async findInherits(tenantId: string): Promise<TenantPermissionOverride[]> {\n const all = await this.findByTenant(tenantId);\n return all.filter((o) => o.effect === TenantPermissionEffect.INHERIT);\n }\n\n /**\n * Get granted permission IDs for a tenant\n */\n async getGrantedPermissionIds(tenantId: string): Promise<string[]> {\n const grants = await this.findGrants(tenantId);\n return grants.map((o) => o.permissionId as string);\n }\n\n /**\n * Get denied permission IDs for a tenant\n */\n async getDeniedPermissionIds(tenantId: string): Promise<string[]> {\n const denies = await this.findDenies(tenantId);\n return denies.map((o) => o.permissionId as string);\n }\n\n /**\n * Get all overrides for a tenant, organized by effect\n */\n async getOverridesByEffect(\n tenantId: string,\n ): Promise<TenantPermissionOverrideResult> {\n const overrides = await this.findByTenant(tenantId);\n\n const result: TenantPermissionOverrideResult = {\n grantedPermissionIds: [],\n deniedPermissionIds: [],\n inheritedPermissionIds: [],\n };\n\n for (const override of overrides) {\n const permId = override.permissionId as string;\n switch (override.effect) {\n case TenantPermissionEffect.GRANT:\n result.grantedPermissionIds.push(permId);\n break;\n case TenantPermissionEffect.DENY:\n result.deniedPermissionIds.push(permId);\n break;\n case TenantPermissionEffect.INHERIT:\n result.inheritedPermissionIds.push(permId);\n break;\n }\n }\n\n return result;\n }\n\n /**\n * Batch get all overrides for multiple tenants, organized by effect.\n * Fetches all overrides in a single query to avoid N+1 query problem.\n *\n * @param tenantIds - Array of tenant IDs to fetch overrides for\n * @returns Map of tenant ID to their permission override results\n */\n async getOverridesByEffectBatch(\n tenantIds: string[],\n ): Promise<Map<string, TenantPermissionOverrideResult>> {\n const resultMap = new Map<string, TenantPermissionOverrideResult>();\n\n // Initialize empty results for all tenant IDs\n for (const tenantId of tenantIds) {\n resultMap.set(tenantId, {\n grantedPermissionIds: [],\n deniedPermissionIds: [],\n inheritedPermissionIds: [],\n });\n }\n\n if (tenantIds.length === 0) {\n return resultMap;\n }\n\n // Fetch all overrides for all tenants in a single query\n const allOverrides = await this.list({\n where: { tenantId: tenantIds },\n });\n\n // Organize overrides by tenant and effect\n for (const override of allOverrides) {\n const tenantId = override.tenantId as string;\n const permId = override.permissionId as string;\n const result = resultMap.get(tenantId);\n\n if (result) {\n switch (override.effect) {\n case TenantPermissionEffect.GRANT:\n result.grantedPermissionIds.push(permId);\n break;\n case TenantPermissionEffect.DENY:\n result.deniedPermissionIds.push(permId);\n break;\n case TenantPermissionEffect.INHERIT:\n result.inheritedPermissionIds.push(permId);\n break;\n }\n }\n }\n\n return resultMap;\n }\n\n /**\n * Set an override for a tenant permission\n */\n async setOverride(\n tenantId: string,\n permissionId: string,\n effect: TenantPermissionEffect,\n ): Promise<TenantPermissionOverride> {\n // Check if override already exists\n const existing = await this.list({\n where: { tenantId, permissionId },\n limit: 1,\n });\n\n if (existing.length > 0) {\n // Update existing override\n existing[0].effect = effect;\n await existing[0].save();\n return existing[0];\n }\n\n // Create new override\n const override = await this.create({ tenantId, permissionId, effect });\n await override.save();\n return override;\n }\n\n /**\n * Remove an override (permission will use default behavior)\n */\n async removeOverride(\n tenantId: string,\n permissionId: string,\n ): Promise<boolean> {\n const existing = await this.list({\n where: { tenantId, permissionId },\n limit: 1,\n });\n\n if (existing.length === 0) {\n return false;\n }\n\n await existing[0].delete();\n return true;\n }\n\n /**\n * Remove all overrides for a tenant\n */\n async removeAllOverrides(tenantId: string): Promise<number> {\n const overrides = await this.findByTenant(tenantId);\n await Promise.all(overrides.map((override) => override.delete()));\n return overrides.length;\n }\n\n /**\n * Grant a permission at the tenant level.\n * Convenience method for setOverride with GRANT effect.\n */\n async grantPermission(\n tenantId: string,\n permissionId: string,\n ): Promise<TenantPermissionOverride> {\n return await this.setOverride(\n tenantId,\n permissionId,\n TenantPermissionEffect.GRANT,\n );\n }\n\n /**\n * Deny a permission at the tenant level.\n * Convenience method for setOverride with DENY effect.\n * This blocks inheritance from parent tenants.\n */\n async denyPermission(\n tenantId: string,\n permissionId: string,\n ): Promise<TenantPermissionOverride> {\n return await this.setOverride(\n tenantId,\n permissionId,\n TenantPermissionEffect.DENY,\n );\n }\n\n /**\n * Set a permission to inherit from parent.\n * Convenience method for setOverride with INHERIT effect.\n * This is the default behavior, so mainly useful to document intent\n * or to reset a previous grant/deny.\n */\n async inheritPermission(\n tenantId: string,\n permissionId: string,\n ): Promise<TenantPermissionOverride> {\n return await this.setOverride(\n tenantId,\n permissionId,\n TenantPermissionEffect.INHERIT,\n );\n }\n\n /**\n * Bulk set overrides for a tenant.\n * Useful for importing or copying permission configurations.\n */\n async bulkSetOverrides(\n tenantId: string,\n overrides: Array<{ permissionId: string; effect: TenantPermissionEffect }>,\n ): Promise<TenantPermissionOverride[]> {\n const results: TenantPermissionOverride[] = [];\n for (const { permissionId, effect } of overrides) {\n const override = await this.setOverride(tenantId, permissionId, effect);\n results.push(override);\n }\n return results;\n }\n\n /**\n * Copy overrides from one tenant to another.\n * Useful when creating child tenants or templates.\n */\n async copyOverrides(\n fromTenantId: string,\n toTenantId: string,\n ): Promise<TenantPermissionOverride[]> {\n const sourceOverrides = await this.findByTenant(fromTenantId);\n const results: TenantPermissionOverride[] = [];\n\n for (const source of sourceOverrides) {\n const override = await this.setOverride(\n toTenantId,\n source.permissionId as string,\n source.effect,\n );\n results.push(override);\n }\n\n return results;\n }\n}\n","import { BackfillTracker } from '@happyvertical/smrt-core/migrations';\nimport { normalizeIdentityEmail } from '@happyvertical/smrt-profiles';\nimport type { getDatabase } from '@happyvertical/sql';\n\ntype DatabaseInterface = Awaited<ReturnType<typeof getDatabase>>;\n\nexport const USER_EMAIL_KEY_BACKFILL_NAME =\n '@happyvertical/smrt-users:user-email-keys:v1';\n\nexport interface DuplicateUserEmailKey {\n emailKey: string;\n userCount: number;\n}\n\nexport interface BackfillUserEmailKeysResult {\n updated: number;\n}\n\nexport type UserEmailKeyBackfillErrorCode =\n | 'duplicate_email'\n | 'transaction_required';\n\n/**\n * Raised before any rows are changed when legacy User emails are ambiguous.\n */\nexport class UserEmailKeyBackfillError extends Error {\n constructor(\n readonly code: UserEmailKeyBackfillErrorCode,\n message: string,\n readonly duplicates: DuplicateUserEmailKey[] = [],\n ) {\n super(message);\n this.name = 'UserEmailKeyBackfillError';\n }\n}\n\n/**\n * Populate the durable normalized-email key for Users created before the\n * `emailKey` field existed.\n *\n * Apply the schema migration first, stop or upgrade legacy writers, then call\n * this once from a single deploy process. The operation is idempotent. It\n * fails before changing any rows when two non-blank emails normalize to the\n * same key, so operators can reconcile those Users explicitly.\n */\nexport async function backfillUserEmailKeys(\n db: DatabaseInterface,\n): Promise<BackfillUserEmailKeysResult> {\n if (!db.transaction) {\n throw new UserEmailKeyBackfillError(\n 'transaction_required',\n 'User email-key backfill requires a root database with transaction().',\n );\n }\n\n const tracker = new BackfillTracker({ db });\n await tracker.initialize();\n\n return db.transaction(async (tx) => {\n BackfillTracker.inheritInitialization(tx, db);\n const result = await tx.query(\n 'SELECT id, email, email_key FROM users ORDER BY id',\n );\n const counts = new Map<string, number>();\n const updates: Array<{ emailKey: string | null; id: string }> = [];\n\n for (const row of result.rows) {\n const id = typeof row.id === 'string' ? row.id : '';\n if (!id) {\n throw new Error('User email-key backfill found a row without an id.');\n }\n const email = typeof row.email === 'string' ? row.email : '';\n const emailKey = email.trim() ? normalizeIdentityEmail(email) : null;\n if (emailKey) counts.set(emailKey, (counts.get(emailKey) ?? 0) + 1);\n const storedKey =\n typeof row.email_key === 'string' ? row.email_key : null;\n if (storedKey !== emailKey) updates.push({ emailKey, id });\n }\n\n const duplicates = [...counts.entries()]\n .filter(([, userCount]) => userCount > 1)\n .map(([emailKey, userCount]) => ({ emailKey, userCount }))\n .sort((left, right) => left.emailKey.localeCompare(right.emailKey));\n if (duplicates.length > 0) {\n throw new UserEmailKeyBackfillError(\n 'duplicate_email',\n 'Cannot backfill User.emailKey because legacy User emails contain normalized duplicates.',\n duplicates,\n );\n }\n\n // Clear every changing key first. This avoids transient unique-index\n // conflicts for valid final states such as two legacy rows whose stale\n // keys were swapped.\n for (const update of updates) {\n await tx.query(\n 'UPDATE users SET email_key = NULL WHERE id = ?',\n update.id,\n );\n }\n for (const update of updates) {\n await tx.query(\n 'UPDATE users SET email_key = ? WHERE id = ?',\n update.emailKey,\n update.id,\n );\n }\n\n await new BackfillTracker({ db: tx }).recordApplied(\n USER_EMAIL_KEY_BACKFILL_NAME,\n {\n description: 'Canonical User email keys are current.',\n packageName: '@happyvertical/smrt-users',\n },\n );\n\n return { updated: updates.length };\n });\n}\n","/**\n * UserCollection - Collection manager for User objects\n * @packageDocumentation\n */\n\nimport { SmrtCollection } from '@happyvertical/smrt-core';\nimport { BackfillTracker } from '@happyvertical/smrt-core/migrations';\nimport {\n AmbiguousOidcIdentityError,\n normalizeIdentityEmail,\n OidcIdentity,\n OidcIdentityCollection,\n type Profile,\n type ProfileCollection,\n} from '@happyvertical/smrt-profiles';\nimport {\n coordinateOidcProvisioning,\n createOidcProvisioningPerson,\n insertOidcProvisioningIdentity,\n isOidcProvisioningRaceConflict,\n saveOidcRaceArbiter,\n} from '@happyvertical/smrt-profiles/internal/oidc-provisioning';\nimport { withSystemContext } from '@happyvertical/smrt-tenancy';\nimport type { getDatabase } from '@happyvertical/sql';\nimport { USER_EMAIL_KEY_BACKFILL_NAME } from '../migrations/backfillUserEmailKeys.js';\nimport { User } from '../models/User.js';\nimport { UserStatus } from '../types/index.js';\n\ntype DatabaseInterface = Awaited<ReturnType<typeof getDatabase>>;\n\n/**\n * OIDC claims used for identity resolution\n */\nexport interface OidcClaims {\n /** Subject identifier from IdP */\n sub: string;\n /** Issuer URL */\n iss: string;\n /** User's email address */\n email?: string;\n /** Whether the IdP verified the email address */\n email_verified?: boolean;\n /** User's display name */\n name?: string;\n /** Preferred username */\n preferred_username?: string;\n}\n\n/** OIDC claims after the provisioning boundary validates and normalizes email. */\nexport type NormalizedOidcClaims = Readonly<OidcClaims & { email: string }>;\n\n/**\n * Result of OIDC identity resolution\n */\nexport interface OidcIdentityResult {\n /** The User record */\n user: User;\n /** The linked Profile */\n profile: Profile;\n /** The OidcIdentity linking profile to IdP */\n oidcIdentity: OidcIdentity;\n /** Whether the profile was newly created */\n created: boolean;\n}\n\n/** Context passed after OIDC claims are validated and before provisioning. */\nexport interface OidcProfileResolverContext {\n /** Transaction-bound database; use this for every resolver read/write. */\n db: DatabaseInterface;\n /**\n * Frozen normalized claim snapshot. OidcLoginService supplies\n * protocol-validated claims; direct collection callers must provide claims\n * from a trusted boundary.\n */\n claims: NormalizedOidcClaims;\n /** Configured provider key. */\n provider: string;\n /** Transaction-bound User collection. */\n users: UserCollection;\n}\n\n/**\n * Resolve a consumer-owned canonical Profile before User/session creation.\n *\n * Return `undefined` for the secure default, `null` to reject login, or a\n * Profile to select it explicitly. For a new issuer/subject, a supplied Profile\n * is still required to be the unique, unowned global Person for the verified\n * email. For an exact existing issuer/subject, `null` still rejects login and a\n * supplied Profile must be the already-linked Profile; the hook cannot rebind\n * identity authority. Stable-link owner and canonical-Person checks still\n * apply. The hook may be retried after a concurrent unique-key race and must\n * therefore be idempotent.\n */\nexport type OidcProfileResolver = (\n context: OidcProfileResolverContext,\n) => Profile | null | undefined | Promise<Profile | null | undefined>;\n\n/** Application authorization for binding a new OIDC identity to an owner. */\nexport interface OidcProfileOwnerAuthorization {\n /** Canonical global Person selected by the application. */\n profile: Profile;\n /** Existing User the application authorizes as that Profile's owner. */\n user: User;\n}\n\n/** Transaction-bound context supplied to the owner authorization callback. */\nexport type OidcProfileOwnerAuthorizerContext = OidcProfileResolverContext;\n\n/**\n * Explicitly authorize first OIDC binding to a pre-provisioned Profile/User.\n *\n * Return `undefined` to preserve SMRT's secure default, `null` to reject the\n * login, or both the canonical Profile and its existing owning User. SMRT\n * reloads and verifies both records inside the provisioning transaction; the\n * returned objects are never trusted as proof of ownership. The callback may\n * be retried after a concurrent unique-key race and must be idempotent.\n */\nexport type OidcProfileOwnerAuthorizer = (\n context: OidcProfileOwnerAuthorizerContext,\n) =>\n | OidcProfileOwnerAuthorization\n | null\n | undefined\n | Promise<OidcProfileOwnerAuthorization | null | undefined>;\n\nexport type OidcProvisioningErrorCode =\n | 'ambiguous_identity'\n | 'concurrency_conflict'\n | 'profile_owned'\n | 'rejected'\n | 'transaction_required'\n | 'user_email_backfill_required'\n | 'user_email_conflict';\n\n/** Fail-closed OIDC identity provisioning error. */\nexport class OidcProvisioningError extends Error {\n constructor(\n readonly code: OidcProvisioningErrorCode,\n message: string,\n options?: { cause?: unknown },\n ) {\n super(message, options);\n this.name = 'OidcProvisioningError';\n }\n}\n\n/**\n * Options for getOrCreateFromOidc\n */\nexport interface GetOrCreateFromOidcOptions {\n /** If false, skip recording login timestamp (default: true) */\n recordLogin?: boolean;\n /**\n * Provision a user even when the IdP explicitly reported the email as\n * unverified (`email_verified: false`). Default false (#1400): refuse to\n * create/resolve a user from a known-unverified address. Has no effect when\n * the claim is absent — an IdP that omits `email_verified` makes no\n * assertion, so it cannot be enforced.\n */\n allowUnverifiedEmail?: boolean;\n /**\n * Optional application resolver invoked inside the provisioning transaction\n * after token/claim validation and before OIDC identity, User, or session\n * creation. Return a canonical Profile, `null` to reject, or `undefined` to\n * use SMRT's secure default.\n */\n resolveProfile?: OidcProfileResolver;\n /**\n * Explicitly authorize a first issuer/subject binding to a pre-provisioned\n * canonical Profile and its existing owner. A successful authorization\n * requires `email_verified === true` and is revalidated atomically by SMRT.\n */\n authorizeProfileOwner?: OidcProfileOwnerAuthorizer;\n}\n\n/**\n * Collection for managing User objects\n */\nexport class UserCollection extends SmrtCollection<User> {\n static readonly _itemClass = User;\n private userEmailKeysReadyPromise: Promise<void> | null = null;\n\n /**\n * Find user by email address\n */\n async findByEmail(email: string): Promise<User | null> {\n const results = await this.list({\n where: { email },\n limit: 1,\n });\n return results.length > 0 ? results[0] : null;\n }\n\n /**\n * Find user by profile ID\n */\n async findByProfile(profileId: string): Promise<User | null> {\n const results = await this.list({\n where: { profileId },\n limit: 1,\n });\n return results.length > 0 ? results[0] : null;\n }\n\n /**\n * Find users by status\n */\n async findByStatus(status: UserStatus): Promise<User[]> {\n return await this.list({\n where: { status },\n orderBy: 'created_at DESC',\n });\n }\n\n /**\n * Find all active users\n */\n async findActive(): Promise<User[]> {\n return await this.findByStatus(UserStatus.ACTIVE);\n }\n\n /**\n * Find all pending users\n */\n async findPending(): Promise<User[]> {\n return await this.findByStatus(UserStatus.PENDING);\n }\n\n /**\n * Get or create user for a profile\n */\n async getOrCreateForProfile(\n profileId: string,\n email: string,\n defaults: Partial<{ status: UserStatus }> = {},\n ): Promise<User> {\n const existing = await this.findByProfile(profileId);\n if (existing) {\n return existing;\n }\n\n const user = await this.create({\n profileId,\n email,\n status: defaults.status ?? UserStatus.ACTIVE,\n });\n await user.save();\n return user;\n }\n\n /**\n * Get or create user from OIDC claims\n *\n * This is the primary method for resolving identity from an OIDC login.\n * It handles the full flow:\n * 1. Find or create Profile from OIDC claims (via smrt-profiles)\n * 2. Link OidcIdentity to the Profile\n * 3. Find or create User linked to the Profile\n *\n * Direct callers must supply claims from a trusted, already validated token\n * boundary. OidcLoginService performs discovery, token exchange, issuer and\n * subject validation, and verified-email source pairing before calling this\n * collection method.\n *\n * @param claims - Trusted OIDC token claims (sub, iss, email, name)\n * @param provider - Provider name (e.g., 'kanidm', 'keycloak', 'google')\n * @param options - Login recording, unverified-email compatibility, Profile\n * resolution, and explicit owner authorization. Hooks run inside the\n * provisioning transaction, may be retried, and must be idempotent. A\n * resolver result must still be the unique, global, unowned Person. An owner\n * authorization must select both the canonical Person and its existing sole\n * approved User owner. Existing identities can only be confirmed, never\n * rebound.\n * @returns User, Profile, OidcIdentity, and whether profile was created\n *\n * @example\n * ```typescript\n * const userCollection = await UserCollection.create({ db: dbConfig });\n *\n * // In your OIDC callback handler:\n * const { user, profile } = await userCollection.getOrCreateFromOidc(\n * {\n * sub: tokenClaims.sub,\n * iss: tokenClaims.iss,\n * email: tokenClaims.email,\n * name: tokenClaims.name,\n * },\n * 'kanidm'\n * );\n *\n * // User and profile are now available\n * // Login was auto-recorded; pass { recordLogin: false } to skip\n * ```\n */\n async getOrCreateFromOidc(\n claims: OidcClaims,\n provider: string,\n options?: GetOrCreateFromOidcOptions,\n ): Promise<OidcIdentityResult> {\n // Validate required OIDC claims at runtime\n const sub = claims?.sub;\n const iss = claims?.iss;\n if (\n typeof sub !== 'string' ||\n sub.trim().length === 0 ||\n typeof iss !== 'string' ||\n iss.trim().length === 0\n ) {\n throw new Error(\n 'Invalid OIDC claims: both \"sub\" and \"iss\" must be non-empty strings.',\n );\n }\n\n // Email is required for user creation and is canonicalized before any\n // resolver or persistence operation.\n const suppliedEmail = claims.email;\n if (typeof suppliedEmail !== 'string' || !suppliedEmail.trim()) {\n throw new Error(\n 'OIDC claims missing required \"email\" for user creation.',\n );\n }\n const email = normalizeIdentityEmail(suppliedEmail);\n\n // #1400: refuse an email the IdP explicitly marked unverified. Only an\n // explicit `email_verified === false` hard-fails; an absent claim makes no\n // assertion and cannot be enforced. Opt out with allowUnverifiedEmail.\n const allowUnverified = options?.allowUnverifiedEmail === true;\n if (claims.email_verified === false && !allowUnverified) {\n throw new Error(\n 'OIDC claims report an unverified email; refusing to provision a user.',\n );\n }\n\n const normalizedClaims: NormalizedOidcClaims = Object.freeze({\n ...claims,\n sub,\n iss,\n email,\n });\n const db = this.requireProvisioningDatabase();\n return coordinateOidcProvisioning({\n db,\n lockKeys: [\n `identity:${OidcIdentity.buildIdentityKey(iss, sub)}`,\n `email:${email}`,\n ],\n isRaceConflict: isUserOidcRaceConflict,\n createTransactionError: (message, cause) =>\n new OidcProvisioningError('transaction_required', message, {\n cause,\n }),\n createConcurrencyError: (cause) =>\n new OidcProvisioningError(\n 'concurrency_conflict',\n 'Concurrent OIDC provisioning did not converge on one identity.',\n { cause },\n ),\n rebindRootResult: (result, rootDb) =>\n this.rebindOidcProvisioningResult(result, rootDb),\n provision: (tx) =>\n withSystemContext(async () => {\n const users =\n tx === db ? this : await UserCollection.create({ db: tx });\n return users.provisionOidcIdentity(\n normalizedClaims,\n provider,\n options,\n tx,\n );\n }),\n });\n }\n\n private requireProvisioningDatabase(): DatabaseInterface {\n const db = this.options.db;\n if (!db || typeof db !== 'object' || !('query' in db)) {\n throw new OidcProvisioningError(\n 'transaction_required',\n 'OIDC provisioning requires an initialized database.',\n );\n }\n return db as DatabaseInterface;\n }\n\n private async provisionOidcIdentity(\n claims: NormalizedOidcClaims,\n provider: string,\n options: GetOrCreateFromOidcOptions | undefined,\n db: DatabaseInterface,\n ): Promise<OidcIdentityResult> {\n const { ProfileCollection } = await import('@happyvertical/smrt-profiles');\n const profileCollection = await ProfileCollection.create({ db });\n const identityCollection = await OidcIdentityCollection.create({ db });\n const existingIdentity = await this.findUniqueOidcIdentity(\n identityCollection,\n claims,\n );\n const createResolverContext = (): OidcProfileResolverContext => ({\n // Never expose the internal snapshot used for retry locks and\n // persistence. A frozen copy keeps an application callback from\n // changing the authenticated identity across attempts.\n claims: Object.freeze({ ...claims }),\n db,\n provider,\n users: this,\n });\n const resolvedProfile = options?.resolveProfile\n ? await options.resolveProfile(createResolverContext())\n : undefined;\n if (resolvedProfile === null) {\n throw new OidcProvisioningError(\n 'rejected',\n 'OIDC provisioning was rejected by the application resolver.',\n );\n }\n const ownerAuthorization = options?.authorizeProfileOwner\n ? await options.authorizeProfileOwner(createResolverContext())\n : undefined;\n if (ownerAuthorization === null) {\n throw new OidcProvisioningError(\n 'rejected',\n 'OIDC provisioning was rejected by the application owner authorizer.',\n );\n }\n const validatedOwnerAuthorization = ownerAuthorization\n ? await this.validateProfileOwnerAuthorization(\n ownerAuthorization,\n claims,\n profileCollection,\n )\n : undefined;\n if (\n validatedOwnerAuthorization &&\n resolvedProfile &&\n requireId(resolvedProfile.id, 'Resolved Profile') !==\n requireId(validatedOwnerAuthorization.profile.id, 'Authorized Profile')\n ) {\n throw new OidcProvisioningError(\n 'rejected',\n 'Application Profile resolution and owner authorization selected different Profiles.',\n );\n }\n\n if (existingIdentity) {\n const profileId = requireId(\n existingIdentity.profileId,\n 'OIDC identity Profile',\n );\n if (\n resolvedProfile &&\n requireId(resolvedProfile.id, 'Resolved Profile') !== profileId\n ) {\n throw new OidcProvisioningError(\n 'rejected',\n 'An application resolver cannot rebind an existing OIDC identity.',\n );\n }\n if (\n validatedOwnerAuthorization &&\n requireId(\n validatedOwnerAuthorization.profile.id,\n 'Authorized Profile',\n ) !== profileId\n ) {\n throw new OidcProvisioningError(\n 'rejected',\n 'An application owner authorizer cannot rebind an existing OIDC identity.',\n );\n }\n const owners = validatedOwnerAuthorization\n ? [validatedOwnerAuthorization.user]\n : await this.findProfileOwners(profileId);\n if (owners.length > 1) {\n throw new OidcProvisioningError(\n 'profile_owned',\n `Profile ${profileId} belongs to multiple Users.`,\n );\n }\n const profile = validatedOwnerAuthorization\n ? validatedOwnerAuthorization.profile\n : await profileCollection.reserveCanonicalIdentityEmail(\n profileId,\n owners[0] ? undefined : claims.email,\n );\n\n const user = await this.finishUserProvisioning(\n profile,\n owners[0],\n claims.email,\n options,\n );\n existingIdentity.email = claims.email;\n existingIdentity.lastUsedAt = new Date();\n await existingIdentity.save();\n return {\n user,\n profile,\n oidcIdentity: existingIdentity,\n created: false,\n };\n }\n\n let profile: Profile | null | undefined =\n validatedOwnerAuthorization?.profile ?? resolvedProfile;\n let created = false;\n\n if (profile && !validatedOwnerAuthorization) {\n if (claims.email_verified !== true) {\n throw new OidcProvisioningError(\n 'rejected',\n 'An application resolver cannot reuse a Profile without a verified OIDC email.',\n );\n }\n profile = await profileCollection.reserveCanonicalIdentityEmail(\n requireId(profile.id, 'Resolved Profile'),\n claims.email,\n );\n } else if (claims.email_verified === true) {\n profile = await profileCollection.findUniqueGlobalPersonByEmail(\n claims.email,\n );\n if (profile) {\n profile = await profileCollection.reserveCanonicalIdentityEmail(\n requireId(profile.id, 'Matched Profile'),\n claims.email,\n );\n }\n } else {\n // Never link an unverified/unspecified email to an existing identity.\n const collision = await profileCollection.findUniqueGlobalPersonByEmail(\n claims.email,\n );\n if (collision) {\n throw new OidcProvisioningError(\n 'rejected',\n `OIDC email ${claims.email} is not verified and already belongs to a Profile.`,\n );\n }\n }\n\n if (!profile) {\n profile = await createOidcProvisioningPerson<Profile>(db, {\n email: claims.email,\n name: claims.name,\n preferredUsername: claims.preferred_username,\n subject: claims.sub,\n });\n created = true;\n }\n\n const profileId = requireId(profile.id, 'OIDC Profile');\n const owners = validatedOwnerAuthorization\n ? [validatedOwnerAuthorization.user]\n : await this.findProfileOwners(profileId);\n if (owners.length > 0) {\n if (validatedOwnerAuthorization) {\n const oidcIdentity = await insertOidcProvisioningIdentity<OidcIdentity>(\n db,\n {\n email: claims.email,\n issuer: claims.iss,\n profileId,\n provider,\n subject: claims.sub,\n },\n );\n const user = await this.finishUserProvisioning(\n profile,\n validatedOwnerAuthorization.user,\n claims.email,\n options,\n );\n return { user, profile, oidcIdentity, created: false };\n }\n // A concurrent first login can commit the exact issuer+subject mapping\n // after this transaction's initial identity lookup but before its\n // profile ownership check. Re-read the durable identity arbiter before\n // rejecting the now-owned Profile so the same identity converges while\n // every different identity still fails closed.\n const concurrentIdentity = await this.findUniqueOidcIdentity(\n identityCollection,\n claims,\n );\n if (owners.length === 1 && concurrentIdentity?.profileId === profileId) {\n const user = await this.finishUserProvisioning(\n profile,\n owners[0],\n claims.email,\n options,\n );\n concurrentIdentity.email = claims.email;\n concurrentIdentity.lastUsedAt = new Date();\n await concurrentIdentity.save();\n return {\n user,\n profile,\n oidcIdentity: concurrentIdentity,\n created: false,\n };\n }\n throw new OidcProvisioningError(\n 'profile_owned',\n `Profile ${profileId} already belongs to another User.`,\n );\n }\n\n // Initial lookup already proved this issuer+subject absent. Insert only:\n // a concurrent winner must surface the unique identity-key conflict so the\n // whole transaction rolls back and retries against the committed winner.\n const oidcIdentity = await insertOidcProvisioningIdentity<OidcIdentity>(\n db,\n {\n email: claims.email,\n issuer: claims.iss,\n profileId,\n provider,\n subject: claims.sub,\n },\n );\n const user = await this.finishUserProvisioning(\n profile,\n undefined,\n claims.email,\n options,\n );\n\n return { user, profile, oidcIdentity, created };\n }\n\n private async findProfileOwners(profileId: string): Promise<User[]> {\n const db = this.requireProvisioningDatabase();\n const lockClause = /^postgres(?:ql)?:/iu.test(db.url ?? '')\n ? ' FOR UPDATE'\n : '';\n const result = await db.query(\n `SELECT id\n FROM users\n WHERE profile_id = ?\n ORDER BY created_at ASC, id ASC\n LIMIT 2${lockClause}`,\n profileId,\n );\n const owners = await Promise.all(\n result.rows.map((row) =>\n typeof row.id === 'string' ? this.get({ id: row.id }) : null,\n ),\n );\n return owners.filter((owner): owner is User => owner !== null);\n }\n\n private async validateProfileOwnerAuthorization(\n authorization: OidcProfileOwnerAuthorization,\n claims: NormalizedOidcClaims,\n profiles: ProfileCollection,\n ): Promise<OidcProfileOwnerAuthorization> {\n if (claims.email_verified !== true) {\n throw new OidcProvisioningError(\n 'rejected',\n 'Owner-authorized OIDC binding requires email_verified to be exactly true.',\n );\n }\n if (\n !authorization ||\n typeof authorization !== 'object' ||\n !authorization.profile ||\n !authorization.user\n ) {\n throw new OidcProvisioningError(\n 'rejected',\n 'Owner-authorized OIDC binding requires both a Profile and User.',\n );\n }\n\n const profileId = requireId(authorization.profile.id, 'Authorized Profile');\n const authorizedUserId = requireId(\n authorization.user.id,\n 'Authorized User',\n );\n const profile = await profiles.reserveCanonicalIdentityEmail(\n profileId,\n claims.email,\n );\n const owners = await this.findProfileOwners(profileId);\n if (owners.length !== 1) {\n throw new OidcProvisioningError(\n 'profile_owned',\n `Owner-authorized Profile ${profileId} must belong to exactly one User.`,\n );\n }\n const owner = owners[0];\n const ownerId = requireId(owner.id, 'Profile owner');\n if (ownerId !== authorizedUserId) {\n throw new OidcProvisioningError(\n 'profile_owned',\n `Authorized User ${authorizedUserId} is not the owner of Profile ${profileId}.`,\n );\n }\n\n const emailUsers = await this.findUsersByNormalizedEmail(claims.email);\n if (\n emailUsers.length !== 1 ||\n requireId(emailUsers[0]?.id, 'Authorized email User') !== ownerId\n ) {\n throw new OidcProvisioningError(\n 'user_email_conflict',\n 'The authorized Profile owner does not have the verified OIDC email.',\n );\n }\n return { profile, user: owner };\n }\n\n private async rebindOidcProvisioningResult(\n result: OidcIdentityResult,\n rootDb: DatabaseInterface,\n ): Promise<OidcIdentityResult> {\n const profileId = requireId(result.profile.id, 'OIDC Profile');\n const identityId = requireId(result.oidcIdentity.id, 'OIDC identity');\n const userId = requireId(result.user.id, 'OIDC User');\n return withSystemContext(async () => {\n const { ProfileCollection } = await import(\n '@happyvertical/smrt-profiles'\n );\n const users = await UserCollection.create({ db: rootDb });\n const profiles = await ProfileCollection.create({ db: rootDb });\n const identities = await OidcIdentityCollection.create({ db: rootDb });\n const [user, profile, oidcIdentity] = await Promise.all([\n users.get({ id: userId }),\n profiles.get({ id: profileId }),\n identities.get({ id: identityId }),\n ]);\n if (!user || !profile || !oidcIdentity) {\n throw new OidcProvisioningError(\n 'concurrency_conflict',\n 'Committed OIDC provisioning result was not found.',\n );\n }\n return { ...result, user, profile, oidcIdentity };\n });\n }\n\n private async findUniqueOidcIdentity(\n identities: OidcIdentityCollection,\n claims: Pick<NormalizedOidcClaims, 'iss' | 'sub'>,\n ): Promise<OidcIdentity | undefined> {\n try {\n return (\n (await identities.findBySubject(claims.iss, claims.sub)) ?? undefined\n );\n } catch (error) {\n if (error instanceof AmbiguousOidcIdentityError) {\n throw new OidcProvisioningError(\n 'ambiguous_identity',\n `Multiple OIDC identities exist for ${claims.iss} subject ${claims.sub}.`,\n { cause: error },\n );\n }\n throw error;\n }\n }\n\n private async finishUserProvisioning(\n profile: Profile,\n existingOwner: User | undefined,\n email: string,\n options: GetOrCreateFromOidcOptions | undefined,\n ): Promise<User> {\n const shouldRecordLogin = options?.recordLogin !== false;\n if (existingOwner) {\n if (shouldRecordLogin) {\n existingOwner.recordLogin();\n await existingOwner.save();\n }\n return existingOwner;\n }\n\n const emailUsers = await this.findUsersByNormalizedEmail(email);\n if (emailUsers.length > 0) {\n throw new OidcProvisioningError(\n 'user_email_conflict',\n `A User already exists for ${email} without the selected Profile.`,\n );\n }\n\n return saveOidcRaceArbiter(() =>\n this.create({\n email,\n ...(shouldRecordLogin ? { lastLoginAt: new Date() } : {}),\n profileId: requireId(profile.id, 'OIDC Profile'),\n status: UserStatus.ACTIVE,\n }),\n );\n }\n\n private async findUsersByNormalizedEmail(email: string): Promise<User[]> {\n const db = this.requireProvisioningDatabase();\n await this.ensureUserEmailKeysReady(db);\n const result = await db.query(\n `SELECT id, email, email_key\n FROM users\n WHERE email_key = ?\n ORDER BY created_at ASC, id ASC\n LIMIT 2`,\n email,\n );\n for (const row of result.rows) {\n this.assertUserEmailKeyCurrent(row, email);\n }\n const users = await Promise.all(\n result.rows.map((row) =>\n typeof row.id === 'string' ? this.get({ id: row.id }) : null,\n ),\n );\n return users.filter((user): user is User => user !== null);\n }\n\n /** Require the deploy-time backfill marker before indexed identity reads. */\n private async ensureUserEmailKeysReady(db: DatabaseInterface): Promise<void> {\n if (!this.userEmailKeysReadyPromise) {\n this.userEmailKeysReadyPromise = this.checkUserEmailKeysReady(db).catch(\n (error) => {\n this.userEmailKeysReadyPromise = null;\n throw error;\n },\n );\n }\n return this.userEmailKeysReadyPromise;\n }\n\n private async checkUserEmailKeysReady(db: DatabaseInterface): Promise<void> {\n if (\n await new BackfillTracker({ db }).isApplied(USER_EMAIL_KEY_BACKFILL_NAME)\n )\n return;\n throw new OidcProvisioningError(\n 'user_email_backfill_required',\n 'User email keys are not marked ready; run backfillUserEmailKeys() before OIDC provisioning.',\n );\n }\n\n private assertUserEmailKeyCurrent(\n row: Record<string, unknown>,\n normalizedEmail?: string,\n ): void {\n const storedEmail = typeof row.email === 'string' ? row.email : '';\n const expectedKey = storedEmail.trim()\n ? normalizeIdentityEmail(storedEmail)\n : null;\n const storedKey = typeof row.email_key === 'string' ? row.email_key : null;\n if (\n storedKey !== expectedKey ||\n (normalizedEmail !== undefined && expectedKey !== normalizedEmail)\n ) {\n throw new OidcProvisioningError(\n 'user_email_backfill_required',\n `User ${String(row.id ?? '<unknown>')} has a missing or stale email key; run backfillUserEmailKeys() before OIDC provisioning.`,\n );\n }\n }\n}\n\nfunction requireId(value: unknown, label: string): string {\n if (typeof value !== 'string' || !value) {\n throw new Error(`${label} did not produce an id.`);\n }\n return value;\n}\n\nfunction isUserOidcRaceConflict(error: unknown): boolean {\n return isOidcProvisioningRaceConflict(error, {\n messagePatterns: [/users[._].*profile_id|users_profile_id/iu],\n });\n}\n","export const encoder = new TextEncoder();\nexport const decoder = new TextDecoder();\nconst MAX_INT32 = 2 ** 32;\nexport function concat(...buffers) {\n const size = buffers.reduce((acc, { length }) => acc + length, 0);\n const buf = new Uint8Array(size);\n let i = 0;\n for (const buffer of buffers) {\n buf.set(buffer, i);\n i += buffer.length;\n }\n return buf;\n}\nfunction writeUInt32BE(buf, value, offset) {\n if (value < 0 || value >= MAX_INT32) {\n throw new RangeError(`value must be >= 0 and <= ${MAX_INT32 - 1}. Received ${value}`);\n }\n buf.set([value >>> 24, value >>> 16, value >>> 8, value & 0xff], offset);\n}\nexport function uint64be(value) {\n const high = Math.floor(value / MAX_INT32);\n const low = value % MAX_INT32;\n const buf = new Uint8Array(8);\n writeUInt32BE(buf, high, 0);\n writeUInt32BE(buf, low, 4);\n return buf;\n}\nexport function uint32be(value) {\n const buf = new Uint8Array(4);\n writeUInt32BE(buf, value);\n return buf;\n}\nexport function encode(string) {\n const bytes = new Uint8Array(string.length);\n for (let i = 0; i < string.length; i++) {\n const code = string.charCodeAt(i);\n if (code > 127) {\n throw new TypeError('non-ASCII string encountered in encode()');\n }\n bytes[i] = code;\n }\n return bytes;\n}\n","export function encodeBase64(input) {\n if (Uint8Array.prototype.toBase64) {\n return input.toBase64();\n }\n const CHUNK_SIZE = 0x8000;\n const arr = [];\n for (let i = 0; i < input.length; i += CHUNK_SIZE) {\n arr.push(String.fromCharCode.apply(null, input.subarray(i, i + CHUNK_SIZE)));\n }\n return btoa(arr.join(''));\n}\nexport function decodeBase64(encoded) {\n if (Uint8Array.fromBase64) {\n return Uint8Array.fromBase64(encoded);\n }\n const binary = atob(encoded);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes;\n}\n","import { encoder, decoder } from '../lib/buffer_utils.js';\nimport { encodeBase64, decodeBase64 } from '../lib/base64.js';\nexport function decode(input) {\n if (Uint8Array.fromBase64) {\n return Uint8Array.fromBase64(typeof input === 'string' ? input : decoder.decode(input), {\n alphabet: 'base64url',\n });\n }\n let encoded = input;\n if (encoded instanceof Uint8Array) {\n encoded = decoder.decode(encoded);\n }\n encoded = encoded.replace(/-/g, '+').replace(/_/g, '/');\n try {\n return decodeBase64(encoded);\n }\n catch {\n throw new TypeError('The input to be decoded is not correctly encoded.');\n }\n}\nexport function encode(input) {\n let unencoded = input;\n if (typeof unencoded === 'string') {\n unencoded = encoder.encode(unencoded);\n }\n if (Uint8Array.prototype.toBase64) {\n return unencoded.toBase64({ alphabet: 'base64url', omitPadding: true });\n }\n return encodeBase64(unencoded).replace(/=/g, '').replace(/\\+/g, '-').replace(/\\//g, '_');\n}\n","const unusable = (name, prop = 'algorithm.name') => new TypeError(`CryptoKey does not support this operation, its ${prop} must be ${name}`);\nconst isAlgorithm = (algorithm, name) => algorithm.name === name;\nfunction getHashLength(hash) {\n return parseInt(hash.name.slice(4), 10);\n}\nfunction checkHashLength(algorithm, expected) {\n const actual = getHashLength(algorithm.hash);\n if (actual !== expected)\n throw unusable(`SHA-${expected}`, 'algorithm.hash');\n}\nfunction getNamedCurve(alg) {\n switch (alg) {\n case 'ES256':\n return 'P-256';\n case 'ES384':\n return 'P-384';\n case 'ES512':\n return 'P-521';\n default:\n throw new Error('unreachable');\n }\n}\nfunction checkUsage(key, usage) {\n if (usage && !key.usages.includes(usage)) {\n throw new TypeError(`CryptoKey does not support this operation, its usages must include ${usage}.`);\n }\n}\nexport function checkSigCryptoKey(key, alg, usage) {\n switch (alg) {\n case 'HS256':\n case 'HS384':\n case 'HS512': {\n if (!isAlgorithm(key.algorithm, 'HMAC'))\n throw unusable('HMAC');\n checkHashLength(key.algorithm, parseInt(alg.slice(2), 10));\n break;\n }\n case 'RS256':\n case 'RS384':\n case 'RS512': {\n if (!isAlgorithm(key.algorithm, 'RSASSA-PKCS1-v1_5'))\n throw unusable('RSASSA-PKCS1-v1_5');\n checkHashLength(key.algorithm, parseInt(alg.slice(2), 10));\n break;\n }\n case 'PS256':\n case 'PS384':\n case 'PS512': {\n if (!isAlgorithm(key.algorithm, 'RSA-PSS'))\n throw unusable('RSA-PSS');\n checkHashLength(key.algorithm, parseInt(alg.slice(2), 10));\n break;\n }\n case 'Ed25519':\n case 'EdDSA': {\n if (!isAlgorithm(key.algorithm, 'Ed25519'))\n throw unusable('Ed25519');\n break;\n }\n case 'ML-DSA-44':\n case 'ML-DSA-65':\n case 'ML-DSA-87': {\n if (!isAlgorithm(key.algorithm, alg))\n throw unusable(alg);\n break;\n }\n case 'ES256':\n case 'ES384':\n case 'ES512': {\n if (!isAlgorithm(key.algorithm, 'ECDSA'))\n throw unusable('ECDSA');\n const expected = getNamedCurve(alg);\n const actual = key.algorithm.namedCurve;\n if (actual !== expected)\n throw unusable(expected, 'algorithm.namedCurve');\n break;\n }\n default:\n throw new TypeError('CryptoKey does not support this operation');\n }\n checkUsage(key, usage);\n}\nexport function checkEncCryptoKey(key, alg, usage) {\n switch (alg) {\n case 'A128GCM':\n case 'A192GCM':\n case 'A256GCM': {\n if (!isAlgorithm(key.algorithm, 'AES-GCM'))\n throw unusable('AES-GCM');\n const expected = parseInt(alg.slice(1, 4), 10);\n const actual = key.algorithm.length;\n if (actual !== expected)\n throw unusable(expected, 'algorithm.length');\n break;\n }\n case 'A128KW':\n case 'A192KW':\n case 'A256KW': {\n if (!isAlgorithm(key.algorithm, 'AES-KW'))\n throw unusable('AES-KW');\n const expected = parseInt(alg.slice(1, 4), 10);\n const actual = key.algorithm.length;\n if (actual !== expected)\n throw unusable(expected, 'algorithm.length');\n break;\n }\n case 'ECDH': {\n switch (key.algorithm.name) {\n case 'ECDH':\n case 'X25519':\n break;\n default:\n throw unusable('ECDH or X25519');\n }\n break;\n }\n case 'PBES2-HS256+A128KW':\n case 'PBES2-HS384+A192KW':\n case 'PBES2-HS512+A256KW':\n if (!isAlgorithm(key.algorithm, 'PBKDF2'))\n throw unusable('PBKDF2');\n break;\n case 'RSA-OAEP':\n case 'RSA-OAEP-256':\n case 'RSA-OAEP-384':\n case 'RSA-OAEP-512': {\n if (!isAlgorithm(key.algorithm, 'RSA-OAEP'))\n throw unusable('RSA-OAEP');\n checkHashLength(key.algorithm, parseInt(alg.slice(9), 10) || 1);\n break;\n }\n default:\n throw new TypeError('CryptoKey does not support this operation');\n }\n checkUsage(key, usage);\n}\n","function message(msg, actual, ...types) {\n types = types.filter(Boolean);\n if (types.length > 2) {\n const last = types.pop();\n msg += `one of type ${types.join(', ')}, or ${last}.`;\n }\n else if (types.length === 2) {\n msg += `one of type ${types[0]} or ${types[1]}.`;\n }\n else {\n msg += `of type ${types[0]}.`;\n }\n if (actual == null) {\n msg += ` Received ${actual}`;\n }\n else if (typeof actual === 'function' && actual.name) {\n msg += ` Received function ${actual.name}`;\n }\n else if (typeof actual === 'object' && actual != null) {\n if (actual.constructor?.name) {\n msg += ` Received an instance of ${actual.constructor.name}`;\n }\n }\n return msg;\n}\nexport const invalidKeyInput = (actual, ...types) => message('Key must be ', actual, ...types);\nexport const withAlg = (alg, actual, ...types) => message(`Key for the ${alg} algorithm must be `, actual, ...types);\n","export class JOSEError extends Error {\n static code = 'ERR_JOSE_GENERIC';\n code = 'ERR_JOSE_GENERIC';\n constructor(message, options) {\n super(message, options);\n this.name = this.constructor.name;\n Error.captureStackTrace?.(this, this.constructor);\n }\n}\nexport class JWTClaimValidationFailed extends JOSEError {\n static code = 'ERR_JWT_CLAIM_VALIDATION_FAILED';\n code = 'ERR_JWT_CLAIM_VALIDATION_FAILED';\n claim;\n reason;\n payload;\n constructor(message, payload, claim = 'unspecified', reason = 'unspecified') {\n super(message, { cause: { claim, reason, payload } });\n this.claim = claim;\n this.reason = reason;\n this.payload = payload;\n }\n}\nexport class JWTExpired extends JOSEError {\n static code = 'ERR_JWT_EXPIRED';\n code = 'ERR_JWT_EXPIRED';\n claim;\n reason;\n payload;\n constructor(message, payload, claim = 'unspecified', reason = 'unspecified') {\n super(message, { cause: { claim, reason, payload } });\n this.claim = claim;\n this.reason = reason;\n this.payload = payload;\n }\n}\nexport class JOSEAlgNotAllowed extends JOSEError {\n static code = 'ERR_JOSE_ALG_NOT_ALLOWED';\n code = 'ERR_JOSE_ALG_NOT_ALLOWED';\n}\nexport class JOSENotSupported extends JOSEError {\n static code = 'ERR_JOSE_NOT_SUPPORTED';\n code = 'ERR_JOSE_NOT_SUPPORTED';\n}\nexport class JWEDecryptionFailed extends JOSEError {\n static code = 'ERR_JWE_DECRYPTION_FAILED';\n code = 'ERR_JWE_DECRYPTION_FAILED';\n constructor(message = 'decryption operation failed', options) {\n super(message, options);\n }\n}\nexport class JWEInvalid extends JOSEError {\n static code = 'ERR_JWE_INVALID';\n code = 'ERR_JWE_INVALID';\n}\nexport class JWSInvalid extends JOSEError {\n static code = 'ERR_JWS_INVALID';\n code = 'ERR_JWS_INVALID';\n}\nexport class JWTInvalid extends JOSEError {\n static code = 'ERR_JWT_INVALID';\n code = 'ERR_JWT_INVALID';\n}\nexport class JWKInvalid extends JOSEError {\n static code = 'ERR_JWK_INVALID';\n code = 'ERR_JWK_INVALID';\n}\nexport class JWKSInvalid extends JOSEError {\n static code = 'ERR_JWKS_INVALID';\n code = 'ERR_JWKS_INVALID';\n}\nexport class JWKSNoMatchingKey extends JOSEError {\n static code = 'ERR_JWKS_NO_MATCHING_KEY';\n code = 'ERR_JWKS_NO_MATCHING_KEY';\n constructor(message = 'no applicable key found in the JSON Web Key Set', options) {\n super(message, options);\n }\n}\nexport class JWKSMultipleMatchingKeys extends JOSEError {\n [Symbol.asyncIterator];\n static code = 'ERR_JWKS_MULTIPLE_MATCHING_KEYS';\n code = 'ERR_JWKS_MULTIPLE_MATCHING_KEYS';\n constructor(message = 'multiple matching keys found in the JSON Web Key Set', options) {\n super(message, options);\n }\n}\nexport class JWKSTimeout extends JOSEError {\n static code = 'ERR_JWKS_TIMEOUT';\n code = 'ERR_JWKS_TIMEOUT';\n constructor(message = 'request timed out', options) {\n super(message, options);\n }\n}\nexport class JWSSignatureVerificationFailed extends JOSEError {\n static code = 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED';\n code = 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED';\n constructor(message = 'signature verification failed', options) {\n super(message, options);\n }\n}\n","export function assertCryptoKey(key) {\n if (!isCryptoKey(key)) {\n throw new Error('CryptoKey instance expected');\n }\n}\nexport const isCryptoKey = (key) => {\n if (key?.[Symbol.toStringTag] === 'CryptoKey')\n return true;\n try {\n return key instanceof CryptoKey;\n }\n catch {\n return false;\n }\n};\nexport const isKeyObject = (key) => key?.[Symbol.toStringTag] === 'KeyObject';\nexport const isKeyLike = (key) => isCryptoKey(key) || isKeyObject(key);\n","import { decode } from '../util/base64url.js';\nexport const unprotected = Symbol();\nexport function assertNotSet(value, name) {\n if (value) {\n throw new TypeError(`${name} can only be called once`);\n }\n}\nexport function decodeBase64url(value, label, ErrorClass) {\n try {\n return decode(value);\n }\n catch {\n throw new ErrorClass(`Failed to base64url decode the ${label}`);\n }\n}\nexport async function digest(algorithm, data) {\n const subtleDigest = `SHA-${algorithm.slice(-3)}`;\n return new Uint8Array(await crypto.subtle.digest(subtleDigest, data));\n}\n","const isObjectLike = (value) => typeof value === 'object' && value !== null;\nexport function isObject(input) {\n if (!isObjectLike(input) || Object.prototype.toString.call(input) !== '[object Object]') {\n return false;\n }\n if (Object.getPrototypeOf(input) === null) {\n return true;\n }\n let proto = input;\n while (Object.getPrototypeOf(proto) !== null) {\n proto = Object.getPrototypeOf(proto);\n }\n return Object.getPrototypeOf(input) === proto;\n}\nexport function isDisjoint(...headers) {\n const sources = headers.filter(Boolean);\n if (sources.length === 0 || sources.length === 1) {\n return true;\n }\n let acc;\n for (const header of sources) {\n const parameters = Object.keys(header);\n if (!acc || acc.size === 0) {\n acc = new Set(parameters);\n continue;\n }\n for (const parameter of parameters) {\n if (acc.has(parameter)) {\n return false;\n }\n acc.add(parameter);\n }\n }\n return true;\n}\nexport const isJWK = (key) => isObject(key) && typeof key.kty === 'string';\nexport const isPrivateJWK = (key) => key.kty !== 'oct' &&\n ((key.kty === 'AKP' && typeof key.priv === 'string') || typeof key.d === 'string');\nexport const isPublicJWK = (key) => key.kty !== 'oct' && key.d === undefined && key.priv === undefined;\nexport const isSecretJWK = (key) => key.kty === 'oct' && typeof key.k === 'string';\n","import { JOSENotSupported } from '../util/errors.js';\nimport { checkSigCryptoKey } from './crypto_key.js';\nimport { invalidKeyInput } from './invalid_key_input.js';\nexport function checkKeyLength(alg, key) {\n if (alg.startsWith('RS') || alg.startsWith('PS')) {\n const { modulusLength } = key.algorithm;\n if (typeof modulusLength !== 'number' || modulusLength < 2048) {\n throw new TypeError(`${alg} requires key modulusLength to be 2048 bits or larger`);\n }\n }\n}\nfunction subtleAlgorithm(alg, algorithm) {\n const hash = `SHA-${alg.slice(-3)}`;\n switch (alg) {\n case 'HS256':\n case 'HS384':\n case 'HS512':\n return { hash, name: 'HMAC' };\n case 'PS256':\n case 'PS384':\n case 'PS512':\n return { hash, name: 'RSA-PSS', saltLength: parseInt(alg.slice(-3), 10) >> 3 };\n case 'RS256':\n case 'RS384':\n case 'RS512':\n return { hash, name: 'RSASSA-PKCS1-v1_5' };\n case 'ES256':\n case 'ES384':\n case 'ES512':\n return { hash, name: 'ECDSA', namedCurve: algorithm.namedCurve };\n case 'Ed25519':\n case 'EdDSA':\n return { name: 'Ed25519' };\n case 'ML-DSA-44':\n case 'ML-DSA-65':\n case 'ML-DSA-87':\n return { name: alg };\n default:\n throw new JOSENotSupported(`alg ${alg} is not supported either by JOSE or your javascript runtime`);\n }\n}\nasync function getSigKey(alg, key, usage) {\n if (key instanceof Uint8Array) {\n if (!alg.startsWith('HS')) {\n throw new TypeError(invalidKeyInput(key, 'CryptoKey', 'KeyObject', 'JSON Web Key'));\n }\n return crypto.subtle.importKey('raw', key, { hash: `SHA-${alg.slice(-3)}`, name: 'HMAC' }, false, [usage]);\n }\n checkSigCryptoKey(key, alg, usage);\n return key;\n}\nexport async function sign(alg, key, data) {\n const cryptoKey = await getSigKey(alg, key, 'sign');\n checkKeyLength(alg, cryptoKey);\n const signature = await crypto.subtle.sign(subtleAlgorithm(alg, cryptoKey.algorithm), cryptoKey, data);\n return new Uint8Array(signature);\n}\nexport async function verify(alg, key, signature, data) {\n const cryptoKey = await getSigKey(alg, key, 'verify');\n checkKeyLength(alg, cryptoKey);\n const algorithm = subtleAlgorithm(alg, cryptoKey.algorithm);\n try {\n return await crypto.subtle.verify(algorithm, cryptoKey, signature, data);\n }\n catch {\n return false;\n }\n}\n","import { JOSENotSupported } from '../util/errors.js';\nconst unsupportedAlg = 'Invalid or unsupported JWK \"alg\" (Algorithm) Parameter value';\nfunction subtleMapping(jwk) {\n let algorithm;\n let keyUsages;\n switch (jwk.kty) {\n case 'AKP': {\n switch (jwk.alg) {\n case 'ML-DSA-44':\n case 'ML-DSA-65':\n case 'ML-DSA-87':\n algorithm = { name: jwk.alg };\n keyUsages = jwk.priv ? ['sign'] : ['verify'];\n break;\n default:\n throw new JOSENotSupported(unsupportedAlg);\n }\n break;\n }\n case 'RSA': {\n switch (jwk.alg) {\n case 'PS256':\n case 'PS384':\n case 'PS512':\n algorithm = { name: 'RSA-PSS', hash: `SHA-${jwk.alg.slice(-3)}` };\n keyUsages = jwk.d ? ['sign'] : ['verify'];\n break;\n case 'RS256':\n case 'RS384':\n case 'RS512':\n algorithm = { name: 'RSASSA-PKCS1-v1_5', hash: `SHA-${jwk.alg.slice(-3)}` };\n keyUsages = jwk.d ? ['sign'] : ['verify'];\n break;\n case 'RSA-OAEP':\n case 'RSA-OAEP-256':\n case 'RSA-OAEP-384':\n case 'RSA-OAEP-512':\n algorithm = {\n name: 'RSA-OAEP',\n hash: `SHA-${parseInt(jwk.alg.slice(-3), 10) || 1}`,\n };\n keyUsages = jwk.d ? ['decrypt', 'unwrapKey'] : ['encrypt', 'wrapKey'];\n break;\n default:\n throw new JOSENotSupported(unsupportedAlg);\n }\n break;\n }\n case 'EC': {\n switch (jwk.alg) {\n case 'ES256':\n case 'ES384':\n case 'ES512':\n algorithm = {\n name: 'ECDSA',\n namedCurve: { ES256: 'P-256', ES384: 'P-384', ES512: 'P-521' }[jwk.alg],\n };\n keyUsages = jwk.d ? ['sign'] : ['verify'];\n break;\n case 'ECDH-ES':\n case 'ECDH-ES+A128KW':\n case 'ECDH-ES+A192KW':\n case 'ECDH-ES+A256KW':\n algorithm = { name: 'ECDH', namedCurve: jwk.crv };\n keyUsages = jwk.d ? ['deriveBits'] : [];\n break;\n default:\n throw new JOSENotSupported(unsupportedAlg);\n }\n break;\n }\n case 'OKP': {\n switch (jwk.alg) {\n case 'Ed25519':\n case 'EdDSA':\n algorithm = { name: 'Ed25519' };\n keyUsages = jwk.d ? ['sign'] : ['verify'];\n break;\n case 'ECDH-ES':\n case 'ECDH-ES+A128KW':\n case 'ECDH-ES+A192KW':\n case 'ECDH-ES+A256KW':\n algorithm = { name: jwk.crv };\n keyUsages = jwk.d ? ['deriveBits'] : [];\n break;\n default:\n throw new JOSENotSupported(unsupportedAlg);\n }\n break;\n }\n default:\n throw new JOSENotSupported('Invalid or unsupported JWK \"kty\" (Key Type) Parameter value');\n }\n return { algorithm, keyUsages };\n}\nexport async function jwkToKey(jwk) {\n if (!jwk.alg) {\n throw new TypeError('\"alg\" argument is required when \"jwk.alg\" is not present');\n }\n const { algorithm, keyUsages } = subtleMapping(jwk);\n const keyData = { ...jwk };\n if (keyData.kty !== 'AKP') {\n delete keyData.alg;\n }\n delete keyData.use;\n return crypto.subtle.importKey('jwk', keyData, algorithm, jwk.ext ?? (jwk.d || jwk.priv ? false : true), jwk.key_ops ?? keyUsages);\n}\n","import { isJWK } from './type_checks.js';\nimport { decode } from '../util/base64url.js';\nimport { jwkToKey } from './jwk_to_key.js';\nimport { isCryptoKey, isKeyObject } from './is_key_like.js';\nconst unusableForAlg = 'given KeyObject instance cannot be used for this algorithm';\nlet cache;\nconst handleJWK = async (key, jwk, alg, freeze = false) => {\n cache ||= new WeakMap();\n let cached = cache.get(key);\n if (cached?.[alg]) {\n return cached[alg];\n }\n const cryptoKey = await jwkToKey({ ...jwk, alg });\n if (freeze)\n Object.freeze(key);\n if (!cached) {\n cache.set(key, { [alg]: cryptoKey });\n }\n else {\n cached[alg] = cryptoKey;\n }\n return cryptoKey;\n};\nconst handleKeyObject = (keyObject, alg) => {\n cache ||= new WeakMap();\n let cached = cache.get(keyObject);\n if (cached?.[alg]) {\n return cached[alg];\n }\n const isPublic = keyObject.type === 'public';\n const extractable = isPublic ? true : false;\n let cryptoKey;\n if (keyObject.asymmetricKeyType === 'x25519') {\n switch (alg) {\n case 'ECDH-ES':\n case 'ECDH-ES+A128KW':\n case 'ECDH-ES+A192KW':\n case 'ECDH-ES+A256KW':\n break;\n default:\n throw new TypeError(unusableForAlg);\n }\n cryptoKey = keyObject.toCryptoKey(keyObject.asymmetricKeyType, extractable, isPublic ? [] : ['deriveBits']);\n }\n if (keyObject.asymmetricKeyType === 'ed25519') {\n if (alg !== 'EdDSA' && alg !== 'Ed25519') {\n throw new TypeError(unusableForAlg);\n }\n cryptoKey = keyObject.toCryptoKey(keyObject.asymmetricKeyType, extractable, [\n isPublic ? 'verify' : 'sign',\n ]);\n }\n switch (keyObject.asymmetricKeyType) {\n case 'ml-dsa-44':\n case 'ml-dsa-65':\n case 'ml-dsa-87': {\n if (alg !== keyObject.asymmetricKeyType.toUpperCase()) {\n throw new TypeError(unusableForAlg);\n }\n cryptoKey = keyObject.toCryptoKey(keyObject.asymmetricKeyType, extractable, [\n isPublic ? 'verify' : 'sign',\n ]);\n }\n }\n if (keyObject.asymmetricKeyType === 'rsa') {\n let hash;\n switch (alg) {\n case 'RSA-OAEP':\n hash = 'SHA-1';\n break;\n case 'RS256':\n case 'PS256':\n case 'RSA-OAEP-256':\n hash = 'SHA-256';\n break;\n case 'RS384':\n case 'PS384':\n case 'RSA-OAEP-384':\n hash = 'SHA-384';\n break;\n case 'RS512':\n case 'PS512':\n case 'RSA-OAEP-512':\n hash = 'SHA-512';\n break;\n default:\n throw new TypeError(unusableForAlg);\n }\n if (alg.startsWith('RSA-OAEP')) {\n return keyObject.toCryptoKey({\n name: 'RSA-OAEP',\n hash,\n }, extractable, isPublic ? ['encrypt'] : ['decrypt']);\n }\n cryptoKey = keyObject.toCryptoKey({\n name: alg.startsWith('PS') ? 'RSA-PSS' : 'RSASSA-PKCS1-v1_5',\n hash,\n }, extractable, [isPublic ? 'verify' : 'sign']);\n }\n if (keyObject.asymmetricKeyType === 'ec') {\n const nist = new Map([\n ['prime256v1', 'P-256'],\n ['secp384r1', 'P-384'],\n ['secp521r1', 'P-521'],\n ]);\n const namedCurve = nist.get(keyObject.asymmetricKeyDetails?.namedCurve);\n if (!namedCurve) {\n throw new TypeError(unusableForAlg);\n }\n const expectedCurve = { ES256: 'P-256', ES384: 'P-384', ES512: 'P-521' };\n if (expectedCurve[alg] && namedCurve === expectedCurve[alg]) {\n cryptoKey = keyObject.toCryptoKey({\n name: 'ECDSA',\n namedCurve,\n }, extractable, [isPublic ? 'verify' : 'sign']);\n }\n if (alg.startsWith('ECDH-ES')) {\n cryptoKey = keyObject.toCryptoKey({\n name: 'ECDH',\n namedCurve,\n }, extractable, isPublic ? [] : ['deriveBits']);\n }\n }\n if (!cryptoKey) {\n throw new TypeError(unusableForAlg);\n }\n if (!cached) {\n cache.set(keyObject, { [alg]: cryptoKey });\n }\n else {\n cached[alg] = cryptoKey;\n }\n return cryptoKey;\n};\nexport async function normalizeKey(key, alg) {\n if (key instanceof Uint8Array) {\n return key;\n }\n if (isCryptoKey(key)) {\n return key;\n }\n if (isKeyObject(key)) {\n if (key.type === 'secret') {\n return key.export();\n }\n if ('toCryptoKey' in key && typeof key.toCryptoKey === 'function') {\n try {\n return handleKeyObject(key, alg);\n }\n catch (err) {\n if (err instanceof TypeError) {\n throw err;\n }\n }\n }\n let jwk = key.export({ format: 'jwk' });\n return handleJWK(key, jwk, alg);\n }\n if (isJWK(key)) {\n if (key.k) {\n return decode(key.k);\n }\n return handleJWK(key, key, alg, true);\n }\n throw new Error('unreachable');\n}\n","import { decode as decodeBase64URL } from '../util/base64url.js';\nimport { fromSPKI, fromPKCS8, fromX509 } from '../lib/asn1.js';\nimport { jwkToKey } from '../lib/jwk_to_key.js';\nimport { JOSENotSupported } from '../util/errors.js';\nimport { isObject } from '../lib/type_checks.js';\nexport async function importSPKI(spki, alg, options) {\n if (typeof spki !== 'string' || spki.indexOf('-----BEGIN PUBLIC KEY-----') !== 0) {\n throw new TypeError('\"spki\" must be SPKI formatted string');\n }\n return fromSPKI(spki, alg, options);\n}\nexport async function importX509(x509, alg, options) {\n if (typeof x509 !== 'string' || x509.indexOf('-----BEGIN CERTIFICATE-----') !== 0) {\n throw new TypeError('\"x509\" must be X.509 formatted string');\n }\n return fromX509(x509, alg, options);\n}\nexport async function importPKCS8(pkcs8, alg, options) {\n if (typeof pkcs8 !== 'string' || pkcs8.indexOf('-----BEGIN PRIVATE KEY-----') !== 0) {\n throw new TypeError('\"pkcs8\" must be PKCS#8 formatted string');\n }\n return fromPKCS8(pkcs8, alg, options);\n}\nexport async function importJWK(jwk, alg, options) {\n if (!isObject(jwk)) {\n throw new TypeError('JWK must be an object');\n }\n let ext;\n alg ??= jwk.alg;\n ext ??= options?.extractable ?? jwk.ext;\n switch (jwk.kty) {\n case 'oct':\n if (typeof jwk.k !== 'string' || !jwk.k) {\n throw new TypeError('missing \"k\" (Key Value) Parameter value');\n }\n return decodeBase64URL(jwk.k);\n case 'RSA':\n if ('oth' in jwk && jwk.oth !== undefined) {\n throw new JOSENotSupported('RSA JWK \"oth\" (Other Primes Info) Parameter value is not supported');\n }\n return jwkToKey({ ...jwk, alg, ext });\n case 'AKP': {\n if (typeof jwk.alg !== 'string' || !jwk.alg) {\n throw new TypeError('missing \"alg\" (Algorithm) Parameter value');\n }\n if (alg !== undefined && alg !== jwk.alg) {\n throw new TypeError('JWK alg and alg option value mismatch');\n }\n return jwkToKey({ ...jwk, ext });\n }\n case 'EC':\n case 'OKP':\n return jwkToKey({ ...jwk, alg, ext });\n default:\n throw new JOSENotSupported('Unsupported \"kty\" (Key Type) Parameter value');\n }\n}\n","import { JOSENotSupported, JWEInvalid, JWSInvalid } from '../util/errors.js';\nexport function validateCrit(Err, recognizedDefault, recognizedOption, protectedHeader, joseHeader) {\n if (joseHeader.crit !== undefined && protectedHeader?.crit === undefined) {\n throw new Err('\"crit\" (Critical) Header Parameter MUST be integrity protected');\n }\n if (!protectedHeader || protectedHeader.crit === undefined) {\n return new Set();\n }\n if (!Array.isArray(protectedHeader.crit) ||\n protectedHeader.crit.length === 0 ||\n protectedHeader.crit.some((input) => typeof input !== 'string' || input.length === 0)) {\n throw new Err('\"crit\" (Critical) Header Parameter MUST be an array of non-empty strings when present');\n }\n let recognized;\n if (recognizedOption !== undefined) {\n recognized = new Map([...Object.entries(recognizedOption), ...recognizedDefault.entries()]);\n }\n else {\n recognized = recognizedDefault;\n }\n for (const parameter of protectedHeader.crit) {\n if (!recognized.has(parameter)) {\n throw new JOSENotSupported(`Extension Header Parameter \"${parameter}\" is not recognized`);\n }\n if (joseHeader[parameter] === undefined) {\n throw new Err(`Extension Header Parameter \"${parameter}\" is missing`);\n }\n if (recognized.get(parameter) && protectedHeader[parameter] === undefined) {\n throw new Err(`Extension Header Parameter \"${parameter}\" MUST be integrity protected`);\n }\n }\n return new Set(protectedHeader.crit);\n}\n","export function validateAlgorithms(option, algorithms) {\n if (algorithms !== undefined &&\n (!Array.isArray(algorithms) || algorithms.some((s) => typeof s !== 'string'))) {\n throw new TypeError(`\"${option}\" option must be an array of strings`);\n }\n if (!algorithms) {\n return undefined;\n }\n return new Set(algorithms);\n}\n","import { withAlg as invalidKeyInput } from './invalid_key_input.js';\nimport { isKeyLike } from './is_key_like.js';\nimport * as jwk from './type_checks.js';\nconst tag = (key) => key?.[Symbol.toStringTag];\nconst jwkMatchesOp = (alg, key, usage) => {\n if (key.use !== undefined) {\n let expected;\n switch (usage) {\n case 'sign':\n case 'verify':\n expected = 'sig';\n break;\n case 'encrypt':\n case 'decrypt':\n expected = 'enc';\n break;\n }\n if (key.use !== expected) {\n throw new TypeError(`Invalid key for this operation, its \"use\" must be \"${expected}\" when present`);\n }\n }\n if (key.alg !== undefined && key.alg !== alg) {\n throw new TypeError(`Invalid key for this operation, its \"alg\" must be \"${alg}\" when present`);\n }\n if (Array.isArray(key.key_ops)) {\n let expectedKeyOp;\n switch (true) {\n case usage === 'sign' || usage === 'verify':\n case alg === 'dir':\n case alg.includes('CBC-HS'):\n expectedKeyOp = usage;\n break;\n case alg.startsWith('PBES2'):\n expectedKeyOp = 'deriveBits';\n break;\n case /^A\\d{3}(?:GCM)?(?:KW)?$/.test(alg):\n if (!alg.includes('GCM') && alg.endsWith('KW')) {\n expectedKeyOp = usage === 'encrypt' ? 'wrapKey' : 'unwrapKey';\n }\n else {\n expectedKeyOp = usage;\n }\n break;\n case usage === 'encrypt' && alg.startsWith('RSA'):\n expectedKeyOp = 'wrapKey';\n break;\n case usage === 'decrypt':\n expectedKeyOp = alg.startsWith('RSA') ? 'unwrapKey' : 'deriveBits';\n break;\n }\n if (expectedKeyOp && key.key_ops?.includes?.(expectedKeyOp) === false) {\n throw new TypeError(`Invalid key for this operation, its \"key_ops\" must include \"${expectedKeyOp}\" when present`);\n }\n }\n return true;\n};\nconst symmetricTypeCheck = (alg, key, usage) => {\n if (key instanceof Uint8Array)\n return;\n if (jwk.isJWK(key)) {\n if (jwk.isSecretJWK(key) && jwkMatchesOp(alg, key, usage))\n return;\n throw new TypeError(`JSON Web Key for symmetric algorithms must have JWK \"kty\" (Key Type) equal to \"oct\" and the JWK \"k\" (Key Value) present`);\n }\n if (!isKeyLike(key)) {\n throw new TypeError(invalidKeyInput(alg, key, 'CryptoKey', 'KeyObject', 'JSON Web Key', 'Uint8Array'));\n }\n if (key.type !== 'secret') {\n throw new TypeError(`${tag(key)} instances for symmetric algorithms must be of type \"secret\"`);\n }\n};\nconst asymmetricTypeCheck = (alg, key, usage) => {\n if (jwk.isJWK(key)) {\n switch (usage) {\n case 'decrypt':\n case 'sign':\n if (jwk.isPrivateJWK(key) && jwkMatchesOp(alg, key, usage))\n return;\n throw new TypeError(`JSON Web Key for this operation must be a private JWK`);\n case 'encrypt':\n case 'verify':\n if (jwk.isPublicJWK(key) && jwkMatchesOp(alg, key, usage))\n return;\n throw new TypeError(`JSON Web Key for this operation must be a public JWK`);\n }\n }\n if (!isKeyLike(key)) {\n throw new TypeError(invalidKeyInput(alg, key, 'CryptoKey', 'KeyObject', 'JSON Web Key'));\n }\n if (key.type === 'secret') {\n throw new TypeError(`${tag(key)} instances for asymmetric algorithms must not be of type \"secret\"`);\n }\n if (key.type === 'public') {\n switch (usage) {\n case 'sign':\n throw new TypeError(`${tag(key)} instances for asymmetric algorithm signing must be of type \"private\"`);\n case 'decrypt':\n throw new TypeError(`${tag(key)} instances for asymmetric algorithm decryption must be of type \"private\"`);\n }\n }\n if (key.type === 'private') {\n switch (usage) {\n case 'verify':\n throw new TypeError(`${tag(key)} instances for asymmetric algorithm verifying must be of type \"public\"`);\n case 'encrypt':\n throw new TypeError(`${tag(key)} instances for asymmetric algorithm encryption must be of type \"public\"`);\n }\n }\n};\nexport function checkKeyType(alg, key, usage) {\n switch (alg.substring(0, 2)) {\n case 'A1':\n case 'A2':\n case 'di':\n case 'HS':\n case 'PB':\n symmetricTypeCheck(alg, key, usage);\n break;\n default:\n asymmetricTypeCheck(alg, key, usage);\n }\n}\n","import { decode as b64u } from '../../util/base64url.js';\nimport { verify } from '../../lib/signing.js';\nimport { JOSEAlgNotAllowed, JWSInvalid, JWSSignatureVerificationFailed } from '../../util/errors.js';\nimport { concat, encoder, decoder, encode } from '../../lib/buffer_utils.js';\nimport { decodeBase64url } from '../../lib/helpers.js';\nimport { isDisjoint } from '../../lib/type_checks.js';\nimport { isObject } from '../../lib/type_checks.js';\nimport { checkKeyType } from '../../lib/check_key_type.js';\nimport { validateCrit } from '../../lib/validate_crit.js';\nimport { validateAlgorithms } from '../../lib/validate_algorithms.js';\nimport { normalizeKey } from '../../lib/normalize_key.js';\nexport async function flattenedVerify(jws, key, options) {\n if (!isObject(jws)) {\n throw new JWSInvalid('Flattened JWS must be an object');\n }\n if (jws.protected === undefined && jws.header === undefined) {\n throw new JWSInvalid('Flattened JWS must have either of the \"protected\" or \"header\" members');\n }\n if (jws.protected !== undefined && typeof jws.protected !== 'string') {\n throw new JWSInvalid('JWS Protected Header incorrect type');\n }\n if (jws.payload === undefined) {\n throw new JWSInvalid('JWS Payload missing');\n }\n if (typeof jws.signature !== 'string') {\n throw new JWSInvalid('JWS Signature missing or incorrect type');\n }\n if (jws.header !== undefined && !isObject(jws.header)) {\n throw new JWSInvalid('JWS Unprotected Header incorrect type');\n }\n let parsedProt = {};\n if (jws.protected) {\n try {\n const protectedHeader = b64u(jws.protected);\n parsedProt = JSON.parse(decoder.decode(protectedHeader));\n }\n catch {\n throw new JWSInvalid('JWS Protected Header is invalid');\n }\n }\n if (!isDisjoint(parsedProt, jws.header)) {\n throw new JWSInvalid('JWS Protected and JWS Unprotected Header Parameter names must be disjoint');\n }\n const joseHeader = {\n ...parsedProt,\n ...jws.header,\n };\n const extensions = validateCrit(JWSInvalid, new Map([['b64', true]]), options?.crit, parsedProt, joseHeader);\n let b64 = true;\n if (extensions.has('b64')) {\n b64 = parsedProt.b64;\n if (typeof b64 !== 'boolean') {\n throw new JWSInvalid('The \"b64\" (base64url-encode payload) Header Parameter must be a boolean');\n }\n }\n const { alg } = joseHeader;\n if (typeof alg !== 'string' || !alg) {\n throw new JWSInvalid('JWS \"alg\" (Algorithm) Header Parameter missing or invalid');\n }\n const algorithms = options && validateAlgorithms('algorithms', options.algorithms);\n if (algorithms && !algorithms.has(alg)) {\n throw new JOSEAlgNotAllowed('\"alg\" (Algorithm) Header Parameter value not allowed');\n }\n if (b64) {\n if (typeof jws.payload !== 'string') {\n throw new JWSInvalid('JWS Payload must be a string');\n }\n }\n else if (typeof jws.payload !== 'string' && !(jws.payload instanceof Uint8Array)) {\n throw new JWSInvalid('JWS Payload must be a string or an Uint8Array instance');\n }\n let resolvedKey = false;\n if (typeof key === 'function') {\n key = await key(parsedProt, jws);\n resolvedKey = true;\n }\n checkKeyType(alg, key, 'verify');\n const data = concat(jws.protected !== undefined ? encode(jws.protected) : new Uint8Array(), encode('.'), typeof jws.payload === 'string'\n ? b64\n ? encode(jws.payload)\n : encoder.encode(jws.payload)\n : jws.payload);\n const signature = decodeBase64url(jws.signature, 'signature', JWSInvalid);\n const k = await normalizeKey(key, alg);\n const verified = await verify(alg, k, signature, data);\n if (!verified) {\n throw new JWSSignatureVerificationFailed();\n }\n let payload;\n if (b64) {\n payload = decodeBase64url(jws.payload, 'payload', JWSInvalid);\n }\n else if (typeof jws.payload === 'string') {\n payload = encoder.encode(jws.payload);\n }\n else {\n payload = jws.payload;\n }\n const result = { payload };\n if (jws.protected !== undefined) {\n result.protectedHeader = parsedProt;\n }\n if (jws.header !== undefined) {\n result.unprotectedHeader = jws.header;\n }\n if (resolvedKey) {\n return { ...result, key: k };\n }\n return result;\n}\n","import { flattenedVerify } from '../flattened/verify.js';\nimport { JWSInvalid } from '../../util/errors.js';\nimport { decoder } from '../../lib/buffer_utils.js';\nexport async function compactVerify(jws, key, options) {\n if (jws instanceof Uint8Array) {\n jws = decoder.decode(jws);\n }\n if (typeof jws !== 'string') {\n throw new JWSInvalid('Compact JWS must be a string or Uint8Array');\n }\n const { 0: protectedHeader, 1: payload, 2: signature, length } = jws.split('.');\n if (length !== 3) {\n throw new JWSInvalid('Invalid Compact JWS');\n }\n const verified = await flattenedVerify({ payload, protected: protectedHeader, signature }, key, options);\n const result = { payload: verified.payload, protectedHeader: verified.protectedHeader };\n if (typeof key === 'function') {\n return { ...result, key: verified.key };\n }\n return result;\n}\n","import { JWTClaimValidationFailed, JWTExpired, JWTInvalid } from '../util/errors.js';\nimport { encoder, decoder } from './buffer_utils.js';\nimport { isObject } from './type_checks.js';\nconst epoch = (date) => Math.floor(date.getTime() / 1000);\nconst minute = 60;\nconst hour = minute * 60;\nconst day = hour * 24;\nconst week = day * 7;\nconst year = day * 365.25;\nconst REGEX = /^(\\+|\\-)? ?(\\d+|\\d+\\.\\d+) ?(seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)(?: (ago|from now))?$/i;\nexport function secs(str) {\n const matched = REGEX.exec(str);\n if (!matched || (matched[4] && matched[1])) {\n throw new TypeError('Invalid time period format');\n }\n const value = parseFloat(matched[2]);\n const unit = matched[3].toLowerCase();\n let numericDate;\n switch (unit) {\n case 'sec':\n case 'secs':\n case 'second':\n case 'seconds':\n case 's':\n numericDate = Math.round(value);\n break;\n case 'minute':\n case 'minutes':\n case 'min':\n case 'mins':\n case 'm':\n numericDate = Math.round(value * minute);\n break;\n case 'hour':\n case 'hours':\n case 'hr':\n case 'hrs':\n case 'h':\n numericDate = Math.round(value * hour);\n break;\n case 'day':\n case 'days':\n case 'd':\n numericDate = Math.round(value * day);\n break;\n case 'week':\n case 'weeks':\n case 'w':\n numericDate = Math.round(value * week);\n break;\n default:\n numericDate = Math.round(value * year);\n break;\n }\n if (matched[1] === '-' || matched[4] === 'ago') {\n return -numericDate;\n }\n return numericDate;\n}\nfunction validateInput(label, input) {\n if (!Number.isFinite(input)) {\n throw new TypeError(`Invalid ${label} input`);\n }\n return input;\n}\nconst normalizeTyp = (value) => {\n if (value.includes('/')) {\n return value.toLowerCase();\n }\n return `application/${value.toLowerCase()}`;\n};\nconst checkAudiencePresence = (audPayload, audOption) => {\n if (typeof audPayload === 'string') {\n return audOption.includes(audPayload);\n }\n if (Array.isArray(audPayload)) {\n return audOption.some(Set.prototype.has.bind(new Set(audPayload)));\n }\n return false;\n};\nexport function validateClaimsSet(protectedHeader, encodedPayload, options = {}) {\n let payload;\n try {\n payload = JSON.parse(decoder.decode(encodedPayload));\n }\n catch {\n }\n if (!isObject(payload)) {\n throw new JWTInvalid('JWT Claims Set must be a top-level JSON object');\n }\n const { typ } = options;\n if (typ &&\n (typeof protectedHeader.typ !== 'string' ||\n normalizeTyp(protectedHeader.typ) !== normalizeTyp(typ))) {\n throw new JWTClaimValidationFailed('unexpected \"typ\" JWT header value', payload, 'typ', 'check_failed');\n }\n const { requiredClaims = [], issuer, subject, audience, maxTokenAge } = options;\n const presenceCheck = [...requiredClaims];\n if (maxTokenAge !== undefined)\n presenceCheck.push('iat');\n if (audience !== undefined)\n presenceCheck.push('aud');\n if (subject !== undefined)\n presenceCheck.push('sub');\n if (issuer !== undefined)\n presenceCheck.push('iss');\n for (const claim of new Set(presenceCheck.reverse())) {\n if (!(claim in payload)) {\n throw new JWTClaimValidationFailed(`missing required \"${claim}\" claim`, payload, claim, 'missing');\n }\n }\n if (issuer &&\n !(Array.isArray(issuer) ? issuer : [issuer]).includes(payload.iss)) {\n throw new JWTClaimValidationFailed('unexpected \"iss\" claim value', payload, 'iss', 'check_failed');\n }\n if (subject && payload.sub !== subject) {\n throw new JWTClaimValidationFailed('unexpected \"sub\" claim value', payload, 'sub', 'check_failed');\n }\n if (audience &&\n !checkAudiencePresence(payload.aud, typeof audience === 'string' ? [audience] : audience)) {\n throw new JWTClaimValidationFailed('unexpected \"aud\" claim value', payload, 'aud', 'check_failed');\n }\n let tolerance;\n switch (typeof options.clockTolerance) {\n case 'string':\n tolerance = secs(options.clockTolerance);\n break;\n case 'number':\n tolerance = options.clockTolerance;\n break;\n case 'undefined':\n tolerance = 0;\n break;\n default:\n throw new TypeError('Invalid clockTolerance option type');\n }\n const { currentDate } = options;\n const now = epoch(currentDate || new Date());\n if ((payload.iat !== undefined || maxTokenAge) && typeof payload.iat !== 'number') {\n throw new JWTClaimValidationFailed('\"iat\" claim must be a number', payload, 'iat', 'invalid');\n }\n if (payload.nbf !== undefined) {\n if (typeof payload.nbf !== 'number') {\n throw new JWTClaimValidationFailed('\"nbf\" claim must be a number', payload, 'nbf', 'invalid');\n }\n if (payload.nbf > now + tolerance) {\n throw new JWTClaimValidationFailed('\"nbf\" claim timestamp check failed', payload, 'nbf', 'check_failed');\n }\n }\n if (payload.exp !== undefined) {\n if (typeof payload.exp !== 'number') {\n throw new JWTClaimValidationFailed('\"exp\" claim must be a number', payload, 'exp', 'invalid');\n }\n if (payload.exp <= now - tolerance) {\n throw new JWTExpired('\"exp\" claim timestamp check failed', payload, 'exp', 'check_failed');\n }\n }\n if (maxTokenAge) {\n const age = now - payload.iat;\n const max = typeof maxTokenAge === 'number' ? maxTokenAge : secs(maxTokenAge);\n if (age - tolerance > max) {\n throw new JWTExpired('\"iat\" claim timestamp check failed (too far in the past)', payload, 'iat', 'check_failed');\n }\n if (age < 0 - tolerance) {\n throw new JWTClaimValidationFailed('\"iat\" claim timestamp check failed (it should be in the past)', payload, 'iat', 'check_failed');\n }\n }\n return payload;\n}\nexport class JWTClaimsBuilder {\n #payload;\n constructor(payload) {\n if (!isObject(payload)) {\n throw new TypeError('JWT Claims Set MUST be an object');\n }\n this.#payload = structuredClone(payload);\n }\n data() {\n return encoder.encode(JSON.stringify(this.#payload));\n }\n get iss() {\n return this.#payload.iss;\n }\n set iss(value) {\n this.#payload.iss = value;\n }\n get sub() {\n return this.#payload.sub;\n }\n set sub(value) {\n this.#payload.sub = value;\n }\n get aud() {\n return this.#payload.aud;\n }\n set aud(value) {\n this.#payload.aud = value;\n }\n set jti(value) {\n this.#payload.jti = value;\n }\n set nbf(value) {\n if (typeof value === 'number') {\n this.#payload.nbf = validateInput('setNotBefore', value);\n }\n else if (value instanceof Date) {\n this.#payload.nbf = validateInput('setNotBefore', epoch(value));\n }\n else {\n this.#payload.nbf = epoch(new Date()) + secs(value);\n }\n }\n set exp(value) {\n if (typeof value === 'number') {\n this.#payload.exp = validateInput('setExpirationTime', value);\n }\n else if (value instanceof Date) {\n this.#payload.exp = validateInput('setExpirationTime', epoch(value));\n }\n else {\n this.#payload.exp = epoch(new Date()) + secs(value);\n }\n }\n set iat(value) {\n if (value === undefined) {\n this.#payload.iat = epoch(new Date());\n }\n else if (value instanceof Date) {\n this.#payload.iat = validateInput('setIssuedAt', epoch(value));\n }\n else if (typeof value === 'string') {\n this.#payload.iat = validateInput('setIssuedAt', epoch(new Date()) + secs(value));\n }\n else {\n this.#payload.iat = validateInput('setIssuedAt', value);\n }\n }\n}\n","import { compactVerify } from '../jws/compact/verify.js';\nimport { validateClaimsSet } from '../lib/jwt_claims_set.js';\nimport { JWTInvalid } from '../util/errors.js';\nexport async function jwtVerify(jwt, key, options) {\n const verified = await compactVerify(jwt, key, options);\n if (verified.protectedHeader.crit?.includes('b64') && verified.protectedHeader.b64 === false) {\n throw new JWTInvalid('JWTs MUST NOT use unencoded payload');\n }\n const payload = validateClaimsSet(verified.protectedHeader, verified.payload, options);\n const result = { payload, protectedHeader: verified.protectedHeader };\n if (typeof key === 'function') {\n return { ...result, key: verified.key };\n }\n return result;\n}\n","import { encode as b64u } from '../../util/base64url.js';\nimport { sign } from '../../lib/signing.js';\nimport { isDisjoint } from '../../lib/type_checks.js';\nimport { JWSInvalid } from '../../util/errors.js';\nimport { concat, encode } from '../../lib/buffer_utils.js';\nimport { checkKeyType } from '../../lib/check_key_type.js';\nimport { validateCrit } from '../../lib/validate_crit.js';\nimport { normalizeKey } from '../../lib/normalize_key.js';\nimport { assertNotSet } from '../../lib/helpers.js';\nexport class FlattenedSign {\n #payload;\n #protectedHeader;\n #unprotectedHeader;\n constructor(payload) {\n if (!(payload instanceof Uint8Array)) {\n throw new TypeError('payload must be an instance of Uint8Array');\n }\n this.#payload = payload;\n }\n setProtectedHeader(protectedHeader) {\n assertNotSet(this.#protectedHeader, 'setProtectedHeader');\n this.#protectedHeader = protectedHeader;\n return this;\n }\n setUnprotectedHeader(unprotectedHeader) {\n assertNotSet(this.#unprotectedHeader, 'setUnprotectedHeader');\n this.#unprotectedHeader = unprotectedHeader;\n return this;\n }\n async sign(key, options) {\n if (!this.#protectedHeader && !this.#unprotectedHeader) {\n throw new JWSInvalid('either setProtectedHeader or setUnprotectedHeader must be called before #sign()');\n }\n if (!isDisjoint(this.#protectedHeader, this.#unprotectedHeader)) {\n throw new JWSInvalid('JWS Protected and JWS Unprotected Header Parameter names must be disjoint');\n }\n const joseHeader = {\n ...this.#protectedHeader,\n ...this.#unprotectedHeader,\n };\n const extensions = validateCrit(JWSInvalid, new Map([['b64', true]]), options?.crit, this.#protectedHeader, joseHeader);\n let b64 = true;\n if (extensions.has('b64')) {\n b64 = this.#protectedHeader.b64;\n if (typeof b64 !== 'boolean') {\n throw new JWSInvalid('The \"b64\" (base64url-encode payload) Header Parameter must be a boolean');\n }\n }\n const { alg } = joseHeader;\n if (typeof alg !== 'string' || !alg) {\n throw new JWSInvalid('JWS \"alg\" (Algorithm) Header Parameter missing or invalid');\n }\n checkKeyType(alg, key, 'sign');\n let payloadS;\n let payloadB;\n if (b64) {\n payloadS = b64u(this.#payload);\n payloadB = encode(payloadS);\n }\n else {\n payloadB = this.#payload;\n payloadS = '';\n }\n let protectedHeaderString;\n let protectedHeaderBytes;\n if (this.#protectedHeader) {\n protectedHeaderString = b64u(JSON.stringify(this.#protectedHeader));\n protectedHeaderBytes = encode(protectedHeaderString);\n }\n else {\n protectedHeaderString = '';\n protectedHeaderBytes = new Uint8Array();\n }\n const data = concat(protectedHeaderBytes, encode('.'), payloadB);\n const k = await normalizeKey(key, alg);\n const signature = await sign(alg, k, data);\n const jws = {\n signature: b64u(signature),\n payload: payloadS,\n };\n if (this.#unprotectedHeader) {\n jws.header = this.#unprotectedHeader;\n }\n if (this.#protectedHeader) {\n jws.protected = protectedHeaderString;\n }\n return jws;\n }\n}\n","import { FlattenedSign } from '../flattened/sign.js';\nexport class CompactSign {\n #flattened;\n constructor(payload) {\n this.#flattened = new FlattenedSign(payload);\n }\n setProtectedHeader(protectedHeader) {\n this.#flattened.setProtectedHeader(protectedHeader);\n return this;\n }\n async sign(key, options) {\n const jws = await this.#flattened.sign(key, options);\n if (jws.payload === undefined) {\n throw new TypeError('use the flattened module for creating JWS with b64: false');\n }\n return `${jws.protected}.${jws.payload}.${jws.signature}`;\n }\n}\n","import { CompactSign } from '../jws/compact/sign.js';\nimport { JWTInvalid } from '../util/errors.js';\nimport { JWTClaimsBuilder } from '../lib/jwt_claims_set.js';\nexport class SignJWT {\n #protectedHeader;\n #jwt;\n constructor(payload = {}) {\n this.#jwt = new JWTClaimsBuilder(payload);\n }\n setIssuer(issuer) {\n this.#jwt.iss = issuer;\n return this;\n }\n setSubject(subject) {\n this.#jwt.sub = subject;\n return this;\n }\n setAudience(audience) {\n this.#jwt.aud = audience;\n return this;\n }\n setJti(jwtId) {\n this.#jwt.jti = jwtId;\n return this;\n }\n setNotBefore(input) {\n this.#jwt.nbf = input;\n return this;\n }\n setExpirationTime(input) {\n this.#jwt.exp = input;\n return this;\n }\n setIssuedAt(input) {\n this.#jwt.iat = input;\n return this;\n }\n setProtectedHeader(protectedHeader) {\n this.#protectedHeader = protectedHeader;\n return this;\n }\n async sign(key, options) {\n const sig = new CompactSign(this.#jwt.data());\n sig.setProtectedHeader(this.#protectedHeader);\n if (Array.isArray(this.#protectedHeader?.crit) &&\n this.#protectedHeader.crit.includes('b64') &&\n this.#protectedHeader.b64 === false) {\n throw new JWTInvalid('JWTs MUST NOT use unencoded payload');\n }\n return sig.sign(key, options);\n }\n}\n","import { importJWK } from '../key/import.js';\nimport { JWKSInvalid, JOSENotSupported, JWKSNoMatchingKey, JWKSMultipleMatchingKeys, } from '../util/errors.js';\nimport { isObject } from '../lib/type_checks.js';\nfunction getKtyFromAlg(alg) {\n switch (typeof alg === 'string' && alg.slice(0, 2)) {\n case 'RS':\n case 'PS':\n return 'RSA';\n case 'ES':\n return 'EC';\n case 'Ed':\n return 'OKP';\n case 'ML':\n return 'AKP';\n default:\n throw new JOSENotSupported('Unsupported \"alg\" value for a JSON Web Key Set');\n }\n}\nfunction isJWKSLike(jwks) {\n return (jwks &&\n typeof jwks === 'object' &&\n Array.isArray(jwks.keys) &&\n jwks.keys.every(isJWKLike));\n}\nfunction isJWKLike(key) {\n return isObject(key);\n}\nclass LocalJWKSet {\n #jwks;\n #cached = new WeakMap();\n constructor(jwks) {\n if (!isJWKSLike(jwks)) {\n throw new JWKSInvalid('JSON Web Key Set malformed');\n }\n this.#jwks = structuredClone(jwks);\n }\n jwks() {\n return this.#jwks;\n }\n async getKey(protectedHeader, token) {\n const { alg, kid } = { ...protectedHeader, ...token?.header };\n const kty = getKtyFromAlg(alg);\n const candidates = this.#jwks.keys.filter((jwk) => {\n let candidate = kty === jwk.kty;\n if (candidate && typeof kid === 'string') {\n candidate = kid === jwk.kid;\n }\n if (candidate && (typeof jwk.alg === 'string' || kty === 'AKP')) {\n candidate = alg === jwk.alg;\n }\n if (candidate && typeof jwk.use === 'string') {\n candidate = jwk.use === 'sig';\n }\n if (candidate && Array.isArray(jwk.key_ops)) {\n candidate = jwk.key_ops.includes('verify');\n }\n if (candidate) {\n switch (alg) {\n case 'ES256':\n candidate = jwk.crv === 'P-256';\n break;\n case 'ES384':\n candidate = jwk.crv === 'P-384';\n break;\n case 'ES512':\n candidate = jwk.crv === 'P-521';\n break;\n case 'Ed25519':\n case 'EdDSA':\n candidate = jwk.crv === 'Ed25519';\n break;\n }\n }\n return candidate;\n });\n const { 0: jwk, length } = candidates;\n if (length === 0) {\n throw new JWKSNoMatchingKey();\n }\n if (length !== 1) {\n const error = new JWKSMultipleMatchingKeys();\n const _cached = this.#cached;\n error[Symbol.asyncIterator] = async function* () {\n for (const jwk of candidates) {\n try {\n yield await importWithAlgCache(_cached, jwk, alg);\n }\n catch { }\n }\n };\n throw error;\n }\n return importWithAlgCache(this.#cached, jwk, alg);\n }\n}\nasync function importWithAlgCache(cache, jwk, alg) {\n const cached = cache.get(jwk) || cache.set(jwk, {}).get(jwk);\n if (cached[alg] === undefined) {\n const key = await importJWK({ ...jwk, ext: true }, alg);\n if (key instanceof Uint8Array || key.type !== 'public') {\n throw new JWKSInvalid('JSON Web Key Set members must be public keys');\n }\n cached[alg] = key;\n }\n return cached[alg];\n}\nexport function createLocalJWKSet(jwks) {\n const set = new LocalJWKSet(jwks);\n const localJWKSet = async (protectedHeader, token) => set.getKey(protectedHeader, token);\n Object.defineProperties(localJWKSet, {\n jwks: {\n value: () => structuredClone(set.jwks()),\n enumerable: false,\n configurable: false,\n writable: false,\n },\n });\n return localJWKSet;\n}\n","import { JOSEError, JWKSNoMatchingKey, JWKSTimeout } from '../util/errors.js';\nimport { createLocalJWKSet } from './local.js';\nimport { isObject } from '../lib/type_checks.js';\nfunction isCloudflareWorkers() {\n return (typeof WebSocketPair !== 'undefined' ||\n (typeof navigator !== 'undefined' && navigator.userAgent === 'Cloudflare-Workers') ||\n (typeof EdgeRuntime !== 'undefined' && EdgeRuntime === 'vercel'));\n}\nlet USER_AGENT;\nif (typeof navigator === 'undefined' || !navigator.userAgent?.startsWith?.('Mozilla/5.0 ')) {\n const NAME = 'jose';\n const VERSION = 'v6.2.3';\n USER_AGENT = `${NAME}/${VERSION}`;\n}\nexport const customFetch = Symbol();\nasync function fetchJwks(url, headers, signal, fetchImpl = fetch) {\n const response = await fetchImpl(url, {\n method: 'GET',\n signal,\n redirect: 'manual',\n headers,\n }).catch((err) => {\n if (err.name === 'TimeoutError') {\n throw new JWKSTimeout();\n }\n throw err;\n });\n if (response.status !== 200) {\n throw new JOSEError('Expected 200 OK from the JSON Web Key Set HTTP response');\n }\n try {\n return await response.json();\n }\n catch {\n throw new JOSEError('Failed to parse the JSON Web Key Set HTTP response as JSON');\n }\n}\nexport const jwksCache = Symbol();\nfunction isFreshJwksCache(input, cacheMaxAge) {\n if (typeof input !== 'object' || input === null) {\n return false;\n }\n if (!('uat' in input) || typeof input.uat !== 'number' || Date.now() - input.uat >= cacheMaxAge) {\n return false;\n }\n if (!('jwks' in input) ||\n !isObject(input.jwks) ||\n !Array.isArray(input.jwks.keys) ||\n !Array.prototype.every.call(input.jwks.keys, isObject)) {\n return false;\n }\n return true;\n}\nclass RemoteJWKSet {\n #url;\n #timeoutDuration;\n #cooldownDuration;\n #cacheMaxAge;\n #jwksTimestamp;\n #pendingFetch;\n #headers;\n #customFetch;\n #local;\n #cache;\n constructor(url, options) {\n if (!(url instanceof URL)) {\n throw new TypeError('url must be an instance of URL');\n }\n this.#url = new URL(url.href);\n this.#timeoutDuration =\n typeof options?.timeoutDuration === 'number' ? options?.timeoutDuration : 5000;\n this.#cooldownDuration =\n typeof options?.cooldownDuration === 'number' ? options?.cooldownDuration : 30000;\n this.#cacheMaxAge = typeof options?.cacheMaxAge === 'number' ? options?.cacheMaxAge : 600000;\n this.#headers = new Headers(options?.headers);\n if (USER_AGENT && !this.#headers.has('User-Agent')) {\n this.#headers.set('User-Agent', USER_AGENT);\n }\n if (!this.#headers.has('accept')) {\n this.#headers.set('accept', 'application/json');\n this.#headers.append('accept', 'application/jwk-set+json');\n }\n this.#customFetch = options?.[customFetch];\n if (options?.[jwksCache] !== undefined) {\n this.#cache = options?.[jwksCache];\n if (isFreshJwksCache(options?.[jwksCache], this.#cacheMaxAge)) {\n this.#jwksTimestamp = this.#cache.uat;\n this.#local = createLocalJWKSet(this.#cache.jwks);\n }\n }\n }\n pendingFetch() {\n return !!this.#pendingFetch;\n }\n coolingDown() {\n return typeof this.#jwksTimestamp === 'number'\n ? Date.now() < this.#jwksTimestamp + this.#cooldownDuration\n : false;\n }\n fresh() {\n return typeof this.#jwksTimestamp === 'number'\n ? Date.now() < this.#jwksTimestamp + this.#cacheMaxAge\n : false;\n }\n jwks() {\n return this.#local?.jwks();\n }\n async getKey(protectedHeader, token) {\n if (!this.#local || !this.fresh()) {\n await this.reload();\n }\n try {\n return await this.#local(protectedHeader, token);\n }\n catch (err) {\n if (err instanceof JWKSNoMatchingKey) {\n if (this.coolingDown() === false) {\n await this.reload();\n return this.#local(protectedHeader, token);\n }\n }\n throw err;\n }\n }\n async reload() {\n if (this.#pendingFetch && isCloudflareWorkers()) {\n this.#pendingFetch = undefined;\n }\n this.#pendingFetch ||= fetchJwks(this.#url.href, this.#headers, AbortSignal.timeout(this.#timeoutDuration), this.#customFetch)\n .then((json) => {\n this.#local = createLocalJWKSet(json);\n if (this.#cache) {\n this.#cache.uat = Date.now();\n this.#cache.jwks = json;\n }\n this.#jwksTimestamp = Date.now();\n this.#pendingFetch = undefined;\n })\n .catch((err) => {\n this.#pendingFetch = undefined;\n throw err;\n });\n await this.#pendingFetch;\n }\n}\nexport function createRemoteJWKSet(url, options) {\n const set = new RemoteJWKSet(url, options);\n const remoteJWKSet = async (protectedHeader, token) => set.getKey(protectedHeader, token);\n Object.defineProperties(remoteJWKSet, {\n coolingDown: {\n get: () => set.coolingDown(),\n enumerable: true,\n configurable: false,\n },\n fresh: {\n get: () => set.fresh(),\n enumerable: true,\n configurable: false,\n },\n reload: {\n value: () => set.reload(),\n enumerable: true,\n configurable: false,\n writable: false,\n },\n reloading: {\n get: () => set.pendingFetch(),\n enumerable: true,\n configurable: false,\n },\n jwks: {\n value: () => set.jwks(),\n enumerable: true,\n configurable: false,\n writable: false,\n },\n });\n return remoteJWKSet;\n}\n","export { compactDecrypt } from './jwe/compact/decrypt.js';\nexport { flattenedDecrypt } from './jwe/flattened/decrypt.js';\nexport { generalDecrypt } from './jwe/general/decrypt.js';\nexport { GeneralEncrypt } from './jwe/general/encrypt.js';\nexport { compactVerify } from './jws/compact/verify.js';\nexport { flattenedVerify } from './jws/flattened/verify.js';\nexport { generalVerify } from './jws/general/verify.js';\nexport { jwtVerify } from './jwt/verify.js';\nexport { jwtDecrypt } from './jwt/decrypt.js';\nexport { CompactEncrypt } from './jwe/compact/encrypt.js';\nexport { FlattenedEncrypt } from './jwe/flattened/encrypt.js';\nexport { CompactSign } from './jws/compact/sign.js';\nexport { FlattenedSign } from './jws/flattened/sign.js';\nexport { GeneralSign } from './jws/general/sign.js';\nexport { SignJWT } from './jwt/sign.js';\nexport { EncryptJWT } from './jwt/encrypt.js';\nexport { calculateJwkThumbprint, calculateJwkThumbprintUri } from './jwk/thumbprint.js';\nexport { EmbeddedJWK } from './jwk/embedded.js';\nexport { createLocalJWKSet } from './jwks/local.js';\nexport { createRemoteJWKSet, jwksCache, customFetch } from './jwks/remote.js';\nexport { UnsecuredJWT } from './jwt/unsecured.js';\nexport { exportPKCS8, exportSPKI, exportJWK } from './key/export.js';\nexport { importSPKI, importPKCS8, importX509, importJWK } from './key/import.js';\nexport { decodeProtectedHeader } from './util/decode_protected_header.js';\nexport { decodeJwt } from './util/decode_jwt.js';\nimport * as errors from './util/errors.js';\nexport { errors };\nexport { generateKeyPair } from './key/generate_key_pair.js';\nexport { generateSecret } from './key/generate_secret.js';\nimport * as base64url from './util/base64url.js';\nexport { base64url };\nexport const cryptoRuntime = 'WebCryptoAPI';\n","/**\n * OidcLoginService - OAuth2/OIDC authorization-code login with PKCE.\n *\n * The service is provider-generic and works with standards-compliant IdPs such\n * as Kanidm, Dex, Keycloak, Authentik, Google, and others. It handles provider\n * discovery, authorization URL construction, token exchange, ID token\n * verification, and SMRT User/Profile creation.\n *\n * @packageDocumentation\n */\n\nimport { getPackageConfig } from '@happyvertical/smrt-config';\nimport type { SmrtClassOptions } from '@happyvertical/smrt-core';\nimport {\n createRemoteJWKSet,\n customFetch,\n type JWTPayload,\n type JWTVerifyGetKey,\n jwtVerify,\n} from 'jose';\nimport {\n type OidcClaims,\n type OidcIdentityResult,\n type OidcProfileOwnerAuthorizer,\n type OidcProfileResolver,\n UserCollection,\n} from '../collections/UserCollection.js';\n\nconst DEFAULT_SCOPES = ['openid', 'profile', 'email'] as const;\nconst DEFAULT_CLOCK_TOLERANCE_SECONDS = 15;\nconst DEFAULT_METADATA_CACHE_TTL_MS = 5 * 60 * 1000;\nconst WELL_KNOWN_PATH = '/.well-known/openid-configuration';\n\nexport type OidcProviderKind = 'kanidm' | 'dex' | 'generic';\n\nexport type OidcTokenEndpointAuthMethod =\n | 'client_secret_basic'\n | 'client_secret_post'\n | 'none';\n\nexport interface OidcProviderConfig {\n /**\n * Optional preset label for documentation and UI.\n *\n * Kanidm and Dex are standards-compliant OIDC providers, so the preset does\n * not change protocol behavior; it lets apps name their intent clearly.\n */\n kind?: OidcProviderKind;\n /** OIDC issuer URL, e.g. https://id.example.com/dex */\n issuer: string;\n /** OAuth2/OIDC client ID registered with the provider */\n clientId: string;\n /** OAuth2/OIDC client secret for confidential clients */\n clientSecret?: string;\n /**\n * Redirect URI registered with the provider.\n *\n * SvelteKit helpers can derive this from the current request when omitted.\n */\n redirectUri?: string;\n /** Scopes to request. Defaults to openid profile email. */\n scopes?: string[];\n /** Override the discovery document URL when needed. */\n discoveryUrl?: string;\n /** Additional authorization request parameters. */\n authorizationParams?: Record<string, string | number | boolean | undefined>;\n /**\n * Client authentication method for the token endpoint.\n *\n * Defaults to client_secret_basic when clientSecret exists, otherwise none.\n */\n tokenEndpointAuthMethod?: OidcTokenEndpointAuthMethod;\n}\n\nexport interface OidcProviderResolutionOptions {\n defaultProvider?: string;\n providers?: Record<string, OidcProviderConfig>;\n}\n\nexport interface UsersOidcConfig extends OidcProviderResolutionOptions {\n /** Seconds before login transaction cookies expire. */\n transactionTtl?: number;\n}\n\nexport interface OidcProviderResolution {\n provider: OidcProviderConfig;\n providerName: string;\n}\n\nexport interface ResolvedOidcProviderConfig extends OidcProviderConfig {\n redirectUri: string;\n}\n\nexport interface OidcProviderMetadata {\n authorization_endpoint: string;\n end_session_endpoint?: string;\n issuer: string;\n jwks_uri: string;\n token_endpoint: string;\n userinfo_endpoint?: string;\n [key: string]: unknown;\n}\n\nexport interface OidcTransaction {\n codeVerifier: string;\n createdAt: number;\n nonce: string;\n provider: string;\n returnTo?: string;\n state: string;\n}\n\nexport interface CreateAuthorizationUrlOptions {\n authorizationParams?: Record<string, string | number | boolean | undefined>;\n transaction?: OidcTransaction;\n}\n\nexport interface OidcTokenSet {\n accessToken?: string;\n expiresAt?: Date;\n idToken: string;\n refreshToken?: string;\n scope?: string;\n tokenType?: string;\n}\n\nexport interface OidcCallbackResult {\n claims: OidcClaims;\n tokens: OidcTokenSet;\n}\n\nexport interface OidcLoginResult extends OidcIdentityResult {\n claims: OidcClaims;\n tokens: OidcTokenSet;\n}\n\nexport interface OidcLoginServiceOptions extends SmrtClassOptions {\n /** Provider key, e.g. kanidm or dex */\n providerName: string;\n /** Fully resolved provider config. redirectUri is required here. */\n provider: ResolvedOidcProviderConfig;\n /** Optional fetch override for tests or custom runtimes. */\n fetch?: typeof fetch;\n /** JWT clock tolerance passed to jose. Defaults to 15 seconds. */\n clockTolerance?: number | string;\n /** Provider metadata cache TTL in milliseconds. Defaults to 5 minutes. */\n metadataCacheTtlMs?: number;\n /**\n * Application Profile resolver invoked after claims validation and before\n * OIDC identity, User, or session creation.\n */\n resolveProfile?: OidcProfileResolver;\n /**\n * Explicitly authorize first binding to a pre-provisioned canonical Profile\n * and its existing User owner after protocol claim validation.\n */\n authorizeProfileOwner?: OidcProfileOwnerAuthorizer;\n}\n\nexport class OidcLoginError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'OidcLoginError';\n }\n}\n\ninterface UsersAuthPackageConfig extends Record<string, unknown> {\n auth?: {\n oidc?: UsersOidcConfig;\n };\n}\n\ninterface TokenEndpointResponse {\n access_token?: unknown;\n error?: unknown;\n error_description?: unknown;\n expires_in?: unknown;\n id_token?: unknown;\n refresh_token?: unknown;\n scope?: unknown;\n token_type?: unknown;\n}\n\ntype OidcClaimSource = JWTPayload | Record<string, unknown>;\n\nexport function getUsersOidcConfig(): UsersOidcConfig {\n const config = getPackageConfig<UsersAuthPackageConfig>('users', {});\n return config.auth?.oidc ?? {};\n}\n\nfunction normalizeIssuer(issuer: string): string {\n return issuer.replace(/\\/+$/u, '');\n}\n\nfunction resolveDiscoveryUrl(provider: OidcProviderConfig): string {\n if (provider.discoveryUrl) return provider.discoveryUrl;\n return `${normalizeIssuer(provider.issuer)}${WELL_KNOWN_PATH}`;\n}\n\nfunction bytesToBase64Url(bytes: Uint8Array): string {\n let binary = '';\n for (const byte of bytes) {\n binary += String.fromCharCode(byte);\n }\n\n return btoa(binary)\n .replaceAll('+', '-')\n .replaceAll('/', '_')\n .replace(/=+$/u, '');\n}\n\nfunction base64UrlToBytes(value: string): Uint8Array {\n const normalized = value.replaceAll('-', '+').replaceAll('_', '/');\n const padded = normalized.padEnd(\n normalized.length + ((4 - (normalized.length % 4)) % 4),\n '=',\n );\n const binary = atob(padded);\n const bytes = new Uint8Array(binary.length);\n\n for (let index = 0; index < binary.length; index += 1) {\n bytes[index] = binary.charCodeAt(index);\n }\n\n return bytes;\n}\n\nfunction randomBase64Url(byteLength = 32): string {\n const bytes = new Uint8Array(byteLength);\n crypto.getRandomValues(bytes);\n return bytesToBase64Url(bytes);\n}\n\nasync function pkceChallenge(codeVerifier: string): Promise<string> {\n const bytes = new TextEncoder().encode(codeVerifier);\n const digest = await crypto.subtle.digest('SHA-256', bytes);\n return bytesToBase64Url(new Uint8Array(digest));\n}\n\nfunction bytesToBase64(bytes: Uint8Array): string {\n let binary = '';\n\n for (const byte of bytes) {\n binary += String.fromCharCode(byte);\n }\n\n return btoa(binary);\n}\n\nfunction formUrlEncode(value: string): string {\n const params = new URLSearchParams();\n params.set('value', value);\n return params.toString().slice('value='.length);\n}\n\nfunction encodeBasicAuth(clientId: string, clientSecret: string): string {\n const credentials = `${formUrlEncode(clientId)}:${formUrlEncode(clientSecret)}`;\n return bytesToBase64(new TextEncoder().encode(credentials));\n}\n\nfunction asString(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\nfunction asBoolean(value: unknown): boolean | undefined {\n if (typeof value === 'boolean') return value;\n if (value === 'true') return true;\n if (value === 'false') return false;\n return undefined;\n}\n\nfunction ensureFetch(fetchOverride?: typeof fetch): typeof fetch {\n const runtimeFetch = fetchOverride ?? globalThis.fetch;\n if (!runtimeFetch) {\n throw new OidcLoginError('OIDC login requires a fetch implementation.');\n }\n return runtimeFetch;\n}\n\nfunction requireString(value: unknown, field: string): string {\n if (typeof value !== 'string' || value.length === 0) {\n throw new OidcLoginError(`OIDC provider metadata missing \"${field}\".`);\n }\n return value;\n}\n\nasync function readJsonResponse<T>(\n response: Response,\n context: string,\n): Promise<T> {\n try {\n return (await response.json()) as T;\n } catch {\n throw new OidcLoginError(`${context} returned invalid JSON.`);\n }\n}\n\nfunction normalizeClaims(\n payload: OidcClaimSource,\n issuer: string,\n sourceName = 'OIDC claims',\n): OidcClaims {\n const sub = asString(payload.sub);\n if (!sub) {\n throw new OidcLoginError(`${sourceName} missing required \"sub\" claim.`);\n }\n\n return {\n sub,\n iss: asString(payload.iss) ?? issuer,\n email: asString(payload.email),\n email_verified: asBoolean(payload.email_verified),\n name: asString(payload.name),\n preferred_username: asString(payload.preferred_username),\n };\n}\n\nfunction mergeClaims(\n idTokenClaims: OidcClaims,\n userInfoClaims: OidcClaims,\n): OidcClaims {\n if (userInfoClaims.sub !== idTokenClaims.sub) {\n throw new OidcLoginError('OIDC userinfo subject does not match ID token.');\n }\n\n const useIdTokenEmail = idTokenClaims.email !== undefined;\n return {\n ...idTokenClaims,\n // Email verification is meaningful only for the email asserted by the\n // same source. Never borrow an ID-token verification flag for a userinfo\n // address (or vice versa).\n email: useIdTokenEmail ? idTokenClaims.email : userInfoClaims.email,\n email_verified: useIdTokenEmail\n ? idTokenClaims.email_verified\n : userInfoClaims.email_verified,\n name: idTokenClaims.name ?? userInfoClaims.name,\n preferred_username:\n idTokenClaims.preferred_username ?? userInfoClaims.preferred_username,\n };\n}\n\nfunction validateAuthorizedParty(payload: JWTPayload, clientId: string): void {\n const authorizedParty = asString(payload.azp);\n\n if (authorizedParty && authorizedParty !== clientId) {\n throw new OidcLoginError('OIDC authorized party validation failed.');\n }\n\n if (\n Array.isArray(payload.aud) &&\n payload.aud.length > 1 &&\n authorizedParty !== clientId\n ) {\n throw new OidcLoginError('OIDC authorized party validation failed.');\n }\n}\n\nfunction getTokenEndpointAuthMethod(\n provider: OidcProviderConfig,\n): OidcTokenEndpointAuthMethod {\n return (\n provider.tokenEndpointAuthMethod ??\n (provider.clientSecret ? 'client_secret_basic' : 'none')\n );\n}\n\nfunction getScopes(provider: OidcProviderConfig): string[] {\n return provider.scopes?.length ? provider.scopes : [...DEFAULT_SCOPES];\n}\n\nfunction parseTokenResponse(json: TokenEndpointResponse): OidcTokenSet {\n if (typeof json.id_token !== 'string') {\n throw new OidcLoginError('OIDC token response missing \"id_token\".');\n }\n\n return {\n accessToken: asString(json.access_token),\n expiresAt:\n typeof json.expires_in === 'number'\n ? new Date(Date.now() + json.expires_in * 1000)\n : undefined,\n idToken: json.id_token,\n refreshToken: asString(json.refresh_token),\n scope: asString(json.scope),\n tokenType: asString(json.token_type),\n };\n}\n\nfunction parseTransaction(value: unknown): OidcTransaction {\n if (!value || typeof value !== 'object') {\n throw new OidcLoginError('Invalid OIDC login transaction.');\n }\n\n const record = value as Partial<OidcTransaction>;\n if (\n !record.provider ||\n !record.state ||\n !record.nonce ||\n !record.codeVerifier ||\n typeof record.createdAt !== 'number'\n ) {\n throw new OidcLoginError('Invalid OIDC login transaction.');\n }\n\n return {\n codeVerifier: record.codeVerifier,\n createdAt: record.createdAt,\n nonce: record.nonce,\n provider: record.provider,\n returnTo: asString(record.returnTo),\n state: record.state,\n };\n}\n\nexport function encodeOidcTransaction(transaction: OidcTransaction): string {\n const json = JSON.stringify(transaction);\n return bytesToBase64Url(new TextEncoder().encode(json));\n}\n\nexport function decodeOidcTransaction(value: string): OidcTransaction {\n try {\n const json = new TextDecoder().decode(base64UrlToBytes(value));\n return parseTransaction(JSON.parse(json));\n } catch (error) {\n if (error instanceof OidcLoginError) throw error;\n throw new OidcLoginError('Invalid OIDC login transaction.');\n }\n}\n\nexport function resolveOidcProviderConfig(\n providerName?: string,\n options: OidcProviderResolutionOptions = {},\n): OidcProviderResolution {\n const oidcConfig = getUsersOidcConfig();\n const providers = {\n ...(oidcConfig.providers ?? {}),\n ...(options.providers ?? {}),\n };\n const resolvedProviderName =\n providerName ?? options.defaultProvider ?? oidcConfig.defaultProvider;\n\n if (!resolvedProviderName) {\n throw new OidcLoginError(\n 'No OIDC provider specified. Pass a provider name or configure packages.users.auth.oidc.defaultProvider.',\n );\n }\n\n const provider = providers[resolvedProviderName];\n if (!provider) {\n throw new OidcLoginError(\n `Unknown OIDC provider \"${resolvedProviderName}\". Configure it under packages.users.auth.oidc.providers.`,\n );\n }\n\n if (!provider.issuer || !provider.clientId) {\n throw new OidcLoginError(\n `OIDC provider \"${resolvedProviderName}\" requires issuer and clientId.`,\n );\n }\n\n return {\n provider,\n providerName: resolvedProviderName,\n };\n}\n\nexport class OidcLoginService {\n private readonly classOptions: SmrtClassOptions;\n private readonly clockTolerance: number | string;\n private readonly fetchImpl: typeof fetch;\n private readonly metadataCacheTtlMs: number;\n private readonly authorizeProfileOwner?: OidcProfileOwnerAuthorizer;\n private readonly resolveProfile?: OidcProfileResolver;\n private metadataFetchedAt = 0;\n private metadataPromise?: Promise<OidcProviderMetadata>;\n private remoteJwks?: JWTVerifyGetKey;\n readonly provider: ResolvedOidcProviderConfig;\n readonly providerName: string;\n\n constructor(options: OidcLoginServiceOptions) {\n const {\n clockTolerance,\n fetch: fetchOverride,\n metadataCacheTtlMs,\n provider,\n providerName,\n authorizeProfileOwner,\n resolveProfile,\n ...classOptions\n } = options;\n\n if (!provider.redirectUri) {\n throw new OidcLoginError(\n `OIDC provider \"${providerName}\" requires a redirectUri.`,\n );\n }\n\n this.classOptions = classOptions;\n this.clockTolerance = clockTolerance ?? DEFAULT_CLOCK_TOLERANCE_SECONDS;\n this.fetchImpl = ensureFetch(fetchOverride);\n this.metadataCacheTtlMs =\n metadataCacheTtlMs ?? DEFAULT_METADATA_CACHE_TTL_MS;\n this.authorizeProfileOwner = authorizeProfileOwner;\n this.resolveProfile = resolveProfile;\n this.provider = {\n ...provider,\n issuer: normalizeIssuer(provider.issuer),\n };\n this.providerName = providerName;\n }\n\n createTransaction(returnTo?: string): OidcTransaction {\n return {\n codeVerifier: randomBase64Url(32),\n createdAt: Date.now(),\n nonce: randomBase64Url(32),\n provider: this.providerName,\n returnTo,\n state: randomBase64Url(32),\n };\n }\n\n async getMetadata(): Promise<OidcProviderMetadata> {\n const cacheExpired =\n this.metadataFetchedAt > 0 &&\n Date.now() - this.metadataFetchedAt > this.metadataCacheTtlMs;\n\n if (!this.metadataPromise || cacheExpired) {\n this.metadataPromise = this.fetchMetadata()\n .then((metadata) => {\n this.metadataFetchedAt = Date.now();\n this.remoteJwks = undefined;\n return metadata;\n })\n .catch((error) => {\n this.metadataPromise = undefined;\n throw error;\n });\n }\n\n return this.metadataPromise;\n }\n\n private async fetchMetadata(): Promise<OidcProviderMetadata> {\n const discoveryUrl = resolveDiscoveryUrl(this.provider);\n const response = await this.fetchImpl(discoveryUrl, {\n headers: { accept: 'application/json' },\n });\n\n if (!response.ok) {\n throw new OidcLoginError(\n `OIDC discovery failed for \"${this.providerName}\" (${response.status}).`,\n );\n }\n\n const metadata = await readJsonResponse<Partial<OidcProviderMetadata>>(\n response,\n 'OIDC discovery',\n );\n const issuer = requireString(\n metadata.issuer ?? this.provider.issuer,\n 'issuer',\n );\n\n if (normalizeIssuer(issuer) !== this.provider.issuer) {\n throw new OidcLoginError(\n `OIDC discovery issuer mismatch for \"${this.providerName}\".`,\n );\n }\n\n return {\n ...metadata,\n authorization_endpoint: requireString(\n metadata.authorization_endpoint,\n 'authorization_endpoint',\n ),\n issuer,\n jwks_uri: requireString(metadata.jwks_uri, 'jwks_uri'),\n token_endpoint: requireString(metadata.token_endpoint, 'token_endpoint'),\n };\n }\n\n async createAuthorizationUrl(\n options: CreateAuthorizationUrlOptions = {},\n ): Promise<{ transaction: OidcTransaction; url: URL }> {\n const metadata = await this.getMetadata();\n const transaction = options.transaction ?? this.createTransaction();\n const codeChallenge = await pkceChallenge(transaction.codeVerifier);\n const url = new URL(metadata.authorization_endpoint);\n\n url.searchParams.set('response_type', 'code');\n url.searchParams.set('client_id', this.provider.clientId);\n url.searchParams.set('redirect_uri', this.provider.redirectUri);\n url.searchParams.set('scope', getScopes(this.provider).join(' '));\n url.searchParams.set('state', transaction.state);\n url.searchParams.set('nonce', transaction.nonce);\n url.searchParams.set('code_challenge', codeChallenge);\n url.searchParams.set('code_challenge_method', 'S256');\n\n for (const [key, value] of Object.entries({\n ...this.provider.authorizationParams,\n ...options.authorizationParams,\n })) {\n if (value !== undefined) {\n url.searchParams.set(key, String(value));\n }\n }\n\n return { transaction, url };\n }\n\n async exchangeCallback(\n callbackUrl: string | URL,\n transaction: OidcTransaction,\n ): Promise<OidcCallbackResult> {\n const code = this.validateCallback(callbackUrl, transaction);\n const tokens = await this.exchangeCode(code, transaction);\n const idTokenClaims = await this.verifyIdToken(\n tokens.idToken,\n transaction.nonce,\n );\n const claims = await this.enrichClaimsFromUserInfo(idTokenClaims, tokens);\n\n return { claims, tokens };\n }\n\n async completeLogin(\n callbackUrl: string | URL,\n transaction: OidcTransaction,\n ): Promise<OidcLoginResult> {\n const { claims, tokens } = await this.exchangeCallback(\n callbackUrl,\n transaction,\n );\n const users = await UserCollection.create(this.classOptions);\n const result = await users.getOrCreateFromOidc(claims, this.providerName, {\n authorizeProfileOwner: this.authorizeProfileOwner,\n resolveProfile: this.resolveProfile,\n });\n\n return {\n ...result,\n claims,\n tokens,\n };\n }\n\n private validateCallback(\n callbackUrl: string | URL,\n transaction: OidcTransaction,\n ): string {\n if (transaction.provider !== this.providerName) {\n throw new OidcLoginError('OIDC login transaction provider mismatch.');\n }\n\n const url = callbackUrl instanceof URL ? callbackUrl : new URL(callbackUrl);\n const error = url.searchParams.get('error');\n if (error) {\n const description = url.searchParams.get('error_description');\n throw new OidcLoginError(\n description\n ? `OIDC provider returned \"${error}\": ${description}`\n : `OIDC provider returned \"${error}\".`,\n );\n }\n\n const state = url.searchParams.get('state');\n if (!state || state !== transaction.state) {\n throw new OidcLoginError('OIDC state validation failed.');\n }\n\n const responseIssuer = url.searchParams.get('iss');\n if (\n responseIssuer &&\n normalizeIssuer(responseIssuer) !== this.provider.issuer\n ) {\n throw new OidcLoginError('OIDC response issuer validation failed.');\n }\n\n const code = url.searchParams.get('code');\n if (!code) {\n throw new OidcLoginError('OIDC callback missing authorization code.');\n }\n\n return code;\n }\n\n private async exchangeCode(\n code: string,\n transaction: OidcTransaction,\n ): Promise<OidcTokenSet> {\n const metadata = await this.getMetadata();\n const body = new URLSearchParams({\n code,\n code_verifier: transaction.codeVerifier,\n grant_type: 'authorization_code',\n redirect_uri: this.provider.redirectUri,\n });\n const headers = new Headers({\n accept: 'application/json',\n 'content-type': 'application/x-www-form-urlencoded',\n });\n\n const authMethod = getTokenEndpointAuthMethod(this.provider);\n if (authMethod === 'client_secret_basic') {\n if (!this.provider.clientSecret) {\n throw new OidcLoginError(\n `OIDC provider \"${this.providerName}\" is missing clientSecret.`,\n );\n }\n headers.set(\n 'authorization',\n `Basic ${encodeBasicAuth(this.provider.clientId, this.provider.clientSecret)}`,\n );\n } else {\n body.set('client_id', this.provider.clientId);\n if (authMethod === 'client_secret_post') {\n if (!this.provider.clientSecret) {\n throw new OidcLoginError(\n `OIDC provider \"${this.providerName}\" is missing clientSecret.`,\n );\n }\n body.set('client_secret', this.provider.clientSecret);\n }\n }\n\n const response = await this.fetchImpl(metadata.token_endpoint, {\n body,\n headers,\n method: 'POST',\n });\n const json = await readJsonResponse<TokenEndpointResponse>(\n response,\n 'OIDC token endpoint',\n );\n\n if (!response.ok) {\n const providerError = asString(json.error);\n const description = asString(json.error_description);\n throw new OidcLoginError(\n description\n ? `OIDC token exchange failed (${providerError ?? response.status}): ${description}`\n : `OIDC token exchange failed (${providerError ?? response.status}).`,\n );\n }\n\n return parseTokenResponse(json);\n }\n\n private async enrichClaimsFromUserInfo(\n claims: OidcClaims,\n tokens: OidcTokenSet,\n ): Promise<OidcClaims> {\n if (claims.email || !tokens.accessToken) {\n return claims;\n }\n\n const metadata = await this.getMetadata();\n if (!metadata.userinfo_endpoint) {\n return claims;\n }\n\n const tokenType = tokens.tokenType ?? 'Bearer';\n const response = await this.fetchImpl(metadata.userinfo_endpoint, {\n headers: {\n accept: 'application/json',\n authorization: `${tokenType} ${tokens.accessToken}`,\n },\n });\n\n if (!response.ok) {\n throw new OidcLoginError(\n `OIDC userinfo request failed (${response.status}).`,\n );\n }\n\n const userInfo = await readJsonResponse<Record<string, unknown>>(\n response,\n 'OIDC userinfo endpoint',\n );\n\n return mergeClaims(\n claims,\n normalizeClaims(userInfo, metadata.issuer, 'OIDC userinfo'),\n );\n }\n\n private async verifyIdToken(\n idToken: string,\n expectedNonce: string,\n ): Promise<OidcClaims> {\n const metadata = await this.getMetadata();\n this.remoteJwks ??= createRemoteJWKSet(new URL(metadata.jwks_uri), {\n [customFetch]: this.fetchImpl,\n });\n\n const { payload } = await jwtVerify(idToken, this.remoteJwks, {\n audience: this.provider.clientId,\n clockTolerance: this.clockTolerance,\n issuer: metadata.issuer,\n });\n\n validateAuthorizedParty(payload, this.provider.clientId);\n\n if (payload.nonce !== expectedNonce) {\n throw new OidcLoginError('OIDC nonce validation failed.');\n }\n\n return normalizeClaims(payload, metadata.issuer, 'OIDC ID token');\n }\n}\n","/**\n * PermissionResolver - Resolves effective permissions for a user in a tenant\n * @packageDocumentation\n */\n\nimport type { SmrtClassOptions } from '@happyvertical/smrt-core';\nimport { GroupMemberCollection } from '../collections/GroupMemberCollection.js';\nimport { GroupRoleCollection } from '../collections/GroupRoleCollection.js';\nimport { MembershipCollection } from '../collections/MembershipCollection.js';\nimport { MembershipOverrideCollection } from '../collections/MembershipOverrideCollection.js';\nimport { PermissionCollection } from '../collections/PermissionCollection.js';\nimport { RoleCollection } from '../collections/RoleCollection.js';\nimport { RolePermissionCollection } from '../collections/RolePermissionCollection.js';\nimport { TenantCollection } from '../collections/TenantCollection.js';\nimport { TenantPermissionOverrideCollection } from '../collections/TenantPermissionOverrideCollection.js';\nimport type { Membership } from '../models/Membership.js';\nimport { MAX_TENANT_HIERARCHY_DEPTH, type Tenant } from '../models/Tenant.js';\n\n/**\n * Permission resolution result\n */\nexport interface PermissionResolutionResult {\n /** Set of granted permission slugs */\n permissions: Set<string>;\n /** Membership ID used for resolution */\n membershipId: string | null;\n /** Role ID from membership */\n roleId: string | null;\n /** Group IDs that contributed permissions */\n groupIds: string[];\n /** Permission IDs explicitly denied */\n deniedPermissionIds: string[];\n /**\n * Ancestor tenant id the resolving membership belongs to, when resolution\n * used opt-in hierarchical inheritance (no direct membership in the target\n * tenant; nearest ACTIVE ancestor membership whose role has\n * `inheritsToDescendants: true`). `null` for direct-membership resolution.\n */\n inheritedFromTenantId: string | null;\n}\n\nexport interface PermissionResolutionOptions {\n /**\n * Membership row already resolved by the caller for this user/tenant.\n * Passing this lets request-scoped session loaders avoid re-querying the\n * same membership row before resolving permissions.\n *\n * Pass the RAW lookup result: an inactive row pins resolution to the empty\n * set (a suspended/pending direct membership stays effective), while an\n * explicit `null` asserts \"no direct membership row exists\" and makes the\n * resolver consider opt-in ancestor-membership inheritance. Leaving it\n * `undefined` lets the resolver perform its own lookup.\n */\n membership?: Membership | null;\n}\n\n/**\n * Tenant permission inheritance chain result\n */\nexport interface TenantPermissionInheritanceResult {\n /** Effective tenant permissions (slugs) after inheritance resolution */\n permissions: Set<string>;\n /** Tenant IDs that contributed to the inheritance chain */\n contributingTenantIds: string[];\n /** Whether inheritance was active (at least one tenant in chain had inheritPermissions: true) */\n inheritanceActive: boolean;\n /**\n * Permission slugs explicitly DENY'd by a `TenantPermissionOverride` anywhere\n * in the tenant hierarchy. These are a HARD, tenant-wide block:\n * `resolvePermissions` subtracts them AFTER role + group grants are applied,\n * so a tenant-DENY overrides a permission a role/group otherwise grants. A\n * more-specific membership-override GRANT can still re-add a slug listed here;\n * a membership-override DENY stays absolute. (This set is independent of the\n * net `permissions` above, which only reflects DENY's effect on the inherited\n * cascade.)\n */\n deniedPermissions: Set<string>;\n}\n\n/**\n * PermissionResolver resolves the effective permissions for a user in a tenant.\n *\n * Resolution algorithm:\n * 1. Resolve tenant hierarchy permissions (if hierarchical tenants are used)\n * 2. Get user's membership in the tenant\n * 3. Get base permissions from membership's role\n * 4. Get user's groups in the tenant\n * 5. Add permissions from group roles\n * 6. Apply membership overrides (grant/deny)\n *\n * DENY overrides take precedence over GRANT overrides at every level.\n *\n * ## Hierarchical Tenant Permissions\n *\n * When tenants are organized hierarchically, permissions can cascade from\n * parent tenants to child tenants. The cascade is controlled by:\n * - Parent's `cascadePermissions`: If true, parent pushes permissions down\n * - Child's `inheritPermissions`: If true, child accepts parent's permissions\n *\n * Child tenants can override inherited permissions using TenantPermissionOverride:\n * - INHERIT: Use parent's value (default)\n * - GRANT: Explicitly grant at this level\n * - DENY: Explicitly block (even if parent grants)\n *\n * ## Hierarchical Membership-Role Inheritance (opt-in)\n *\n * Independent of the tenant-level cascade above, membership-role authority\n * can follow the hierarchy DOWN when a role is explicitly flagged\n * `inheritsToDescendants: true`: a user with no direct membership in the\n * target tenant resolves through the nearest ACTIVE ancestor membership\n * holding such a role. A direct membership row in the target tenant always\n * wins (including inactive rows, which resolve empty), child tenant-DENY\n * overrides still subtract from inherited grants, and with no flagged role\n * the resolver behaves exactly as before. See `resolvePermissions`.\n *\n * @example\n * ```typescript\n * const resolver = new PermissionResolver(options);\n * await resolver.initialize();\n *\n * // Check single permission\n * const canCreate = await resolver.hasPermission(userId, tenantId, 'articles.create');\n *\n * // Get all permissions\n * const result = await resolver.resolvePermissions(userId, tenantId);\n * console.log(result.permissions); // Set<string>\n *\n * // Resolve tenant-level permissions only (without user context)\n * const tenantPerms = await resolver.resolveTenantPermissions(tenantId);\n * ```\n */\nexport class PermissionResolver {\n private options: SmrtClassOptions;\n private membershipCollection!: MembershipCollection;\n private roleCollection!: RoleCollection;\n private rolePermissionCollection!: RolePermissionCollection;\n private membershipOverrideCollection!: MembershipOverrideCollection;\n private groupMemberCollection!: GroupMemberCollection;\n private groupRoleCollection!: GroupRoleCollection;\n private permissionCollection!: PermissionCollection;\n private tenantCollection!: TenantCollection;\n private tenantPermissionOverrideCollection!: TenantPermissionOverrideCollection;\n\n constructor(options: SmrtClassOptions) {\n this.options = options;\n }\n\n /**\n * Initialize collections\n *\n * Each collection is created via the inherited static `SmrtCollection.create()`\n * factory, which is generically typed to return the concrete subclass instance.\n */\n async initialize(): Promise<void> {\n this.membershipCollection = await MembershipCollection.create(this.options);\n this.roleCollection = await RoleCollection.create(this.options);\n this.rolePermissionCollection = await RolePermissionCollection.create(\n this.options,\n );\n this.membershipOverrideCollection =\n await MembershipOverrideCollection.create(this.options);\n this.groupMemberCollection = await GroupMemberCollection.create(\n this.options,\n );\n this.groupRoleCollection = await GroupRoleCollection.create(this.options);\n this.permissionCollection = await PermissionCollection.create(this.options);\n this.tenantCollection = await TenantCollection.create(this.options);\n this.tenantPermissionOverrideCollection =\n await TenantPermissionOverrideCollection.create(this.options);\n }\n\n // ============= Tenant Hierarchy Permission Resolution =============\n\n /**\n * Resolve effective permissions for a tenant, considering hierarchy inheritance.\n *\n * Algorithm:\n * 1. Get the tenant and its ancestors (from root to immediate parent)\n * 2. Batch fetch all permission overrides for the entire chain (single query)\n * 3. Walk down the chain, building up permissions:\n * - Start with root tenant's permissions\n * - For each child: if parent.cascadePermissions && child.inheritPermissions:\n * - Merge parent's permissions\n * - Apply child's overrides (GRANT adds, DENY removes)\n * 4. Return the final effective permission set\n */\n async resolveTenantPermissions(\n tenantId: string,\n ): Promise<TenantPermissionInheritanceResult> {\n const result: TenantPermissionInheritanceResult = {\n permissions: new Set<string>(),\n contributingTenantIds: [],\n inheritanceActive: false,\n deniedPermissions: new Set<string>(),\n };\n\n const tenant = await this.tenantCollection.get({ id: tenantId });\n if (!tenant) {\n return result;\n }\n\n // Get the inheritance chain from root to this tenant\n const ancestors =\n await this.tenantCollection.getAncestorsFromRoot(tenantId);\n const chain: Tenant[] = [...ancestors, tenant];\n\n // Batch fetch all permission overrides for the entire chain (single query)\n const chainTenantIds = chain.map((t) => t.id!);\n const allOverridesMap =\n await this.tenantPermissionOverrideCollection.getOverridesByEffectBatch(\n chainTenantIds,\n );\n\n // Build a set of permission IDs for batch lookup. Also track every\n // tenant-level DENY id across the chain: tenant-DENY is a hard, tenant-wide\n // block that `resolvePermissions` subtracts from role/group grants too, so\n // it must be reported independently of the net inherited `permissions` set\n // (which only reflects DENY's effect on the cascade, not on roles).\n const allPermissionIds = new Set<string>();\n const deniedPermissionIds = new Set<string>();\n for (const overrides of allOverridesMap.values()) {\n for (const id of overrides.grantedPermissionIds) allPermissionIds.add(id);\n for (const id of overrides.deniedPermissionIds) {\n allPermissionIds.add(id);\n deniedPermissionIds.add(id);\n }\n }\n\n // Process each tenant in the chain\n let inheritedPermissions = new Set<string>();\n\n for (let i = 0; i < chain.length; i++) {\n const current = chain[i];\n const isFirst = i === 0;\n const previous = isFirst ? null : chain[i - 1];\n\n // Check if inheritance is active for this tenant\n const shouldInherit =\n !isFirst && previous?.cascadePermissions && current.inheritPermissions;\n\n if (shouldInherit) {\n result.inheritanceActive = true;\n }\n\n // Get this tenant's permission overrides from the batch result\n const overrides = allOverridesMap.get(current.id!) ?? {\n grantedPermissionIds: [],\n deniedPermissionIds: [],\n inheritedPermissionIds: [],\n };\n\n // Build this tenant's effective permissions\n const currentPermissions = new Set<string>();\n\n // Track if this tenant actually contributed to the permission set\n let contributed = false;\n\n // Start with inherited permissions if applicable\n if (shouldInherit && inheritedPermissions.size > 0) {\n for (const permId of inheritedPermissions) {\n currentPermissions.add(permId);\n }\n contributed = true;\n }\n\n // Apply grants\n for (const permId of overrides.grantedPermissionIds) {\n currentPermissions.add(permId);\n contributed = true;\n }\n\n // Apply denies (remove) - only counts as contribution if something was actually removed\n for (const permId of overrides.deniedPermissionIds) {\n if (currentPermissions.has(permId)) {\n currentPermissions.delete(permId);\n contributed = true;\n }\n }\n\n // Only add to contributingTenantIds if this tenant actually affected the final set\n if (contributed && !result.contributingTenantIds.includes(current.id!)) {\n result.contributingTenantIds.push(current.id!);\n }\n\n // This becomes the inherited set for the next iteration\n inheritedPermissions = currentPermissions;\n }\n\n // Batch fetch all permissions to get slugs\n if (allPermissionIds.size > 0) {\n const permissionsMap = await this.permissionCollection.findByIds(\n Array.from(allPermissionIds),\n );\n\n // Convert IDs to slugs in the result\n for (const permId of inheritedPermissions) {\n const perm = permissionsMap.get(permId);\n if (perm?.slug) {\n result.permissions.add(perm.slug);\n }\n }\n\n // Record the slugs DENY'd at any tenant level so callers can enforce the\n // hard tenant-wide block over role/group grants — but NET against the\n // cascade's own resolution: a slug a more-specific tenant GRANT re-added\n // (so it survives in the net-granted `inheritedPermissions`) must NOT be\n // in the hard-block set, because the cascade already decided GRANT\n // (most-specific wins). Without this guard a parent-DENY + child-GRANT\n // slug would wrongly override the child GRANT and any role/group grant.\n for (const permId of deniedPermissionIds) {\n if (inheritedPermissions.has(permId)) continue;\n const perm = permissionsMap.get(permId);\n if (perm?.slug) {\n result.deniedPermissions.add(perm.slug);\n }\n }\n }\n\n return result;\n }\n\n /**\n * Get the inheritance chain for a tenant (for debugging/display purposes)\n */\n async getTenantInheritanceChain(\n tenantId: string,\n ): Promise<Array<{ tenant: Tenant; inherits: boolean; cascades: boolean }>> {\n const tenant = await this.tenantCollection.get({ id: tenantId });\n if (!tenant) {\n return [];\n }\n\n const ancestors =\n await this.tenantCollection.getAncestorsFromRoot(tenantId);\n const chain: Array<{\n tenant: Tenant;\n inherits: boolean;\n cascades: boolean;\n }> = [];\n\n for (let i = 0; i < ancestors.length; i++) {\n const current = ancestors[i];\n const next = i + 1 < ancestors.length ? ancestors[i + 1] : tenant;\n\n chain.push({\n tenant: current,\n inherits: false, // Root or ancestor doesn't inherit in this context\n cascades: current.cascadePermissions && next.inheritPermissions,\n });\n }\n\n // Add the target tenant\n chain.push({\n tenant: tenant,\n inherits: tenant.inheritPermissions && ancestors.length > 0,\n cascades: tenant.cascadePermissions,\n });\n\n return chain;\n }\n\n /**\n * Resolve all effective permissions for a user in a tenant.\n *\n * Precedence (broad -> specific, most-specific wins):\n * tenant-inherited (cascade)\n * -> role\n * -> group roles\n * -> tenant-DENY (removes; overrides role/group grants, tenant-wide)\n * -> membership GRANT (re-adds; most specific, can win over a tenant-DENY)\n * -> membership DENY (absolute; always wins)\n *\n * ## Membership selection\n *\n * A direct membership row in the target tenant always pins resolution to\n * itself: active rows resolve normally, inactive (pending/suspended) rows\n * resolve to the empty set — an explicit direct membership is authoritative\n * even when it attenuates or suspends a user who holds broader authority on\n * an ancestor tenant.\n *\n * Only when NO direct membership row exists does the resolver consider\n * opt-in hierarchical inheritance: it walks the tenant's ancestors (nearest\n * first) and resolves through the nearest ACTIVE ancestor membership whose\n * role has `inheritsToDescendants: true`. All later layers then run\n * unchanged against the TARGET tenant — the tenant cascade and tenant-DENY\n * block come from the target tenant (so a child tenant can still carve\n * authority out of an inherited role), group roles remain exact-tenant\n * (only groups the user belongs to in the target tenant contribute), and\n * membership GRANT/DENY overrides travel with the ancestor membership used.\n * With no role flagged `inheritsToDescendants`, resolution is identical to\n * the pre-inheritance behavior.\n *\n * Algorithm:\n * 1. Get membership (direct, or nearest inheritable ancestor membership)\n * and collect all permission IDs from all sources\n * 2. Batch fetch all permissions in a single query\n * 3. Apply permissions from role, then groups\n * 4. Subtract tenant-level DENY'd slugs (hard tenant-wide block)\n * 5. Apply membership GRANT overrides (can re-add a tenant-DENY'd slug)\n * 6. Subtract membership DENY overrides (absolute precedence)\n */\n async resolvePermissions(\n userId: string,\n tenantId: string,\n options: PermissionResolutionOptions = {},\n ): Promise<PermissionResolutionResult> {\n const result: PermissionResolutionResult = {\n permissions: new Set<string>(),\n membershipId: null,\n roleId: null,\n groupIds: [],\n deniedPermissionIds: [],\n inheritedFromTenantId: null,\n };\n\n // 1. Get membership, reusing a request-scoped row when the caller already\n // resolved it for this exact user/tenant.\n let membership =\n options.membership === undefined\n ? await this.membershipCollection.findByUserAndTenant(userId, tenantId)\n : options.membership;\n\n if (membership) {\n // A direct membership row pins resolution to itself: a mismatched\n // caller-provided row is rejected, and an inactive row resolves empty\n // (suspension/pending in the target tenant is effective even for users\n // holding inheritable authority on an ancestor).\n if (membership.userId !== userId || membership.tenantId !== tenantId) {\n return result;\n }\n if (!membership.isActive()) {\n return result;\n }\n } else {\n // No direct membership row: opt-in nearest-ancestor inheritance.\n membership = await this.resolveInheritedMembership(userId, tenantId);\n if (!membership) {\n return result;\n }\n result.inheritedFromTenantId = membership.tenantId ?? null;\n }\n\n result.membershipId = membership.id ?? null;\n result.roleId = membership.roleId ?? null;\n\n const tenantPermissions = await this.resolveTenantPermissions(tenantId);\n for (const slug of tenantPermissions.permissions) {\n result.permissions.add(slug);\n }\n\n if (!membership.roleId) {\n return result;\n }\n\n // 2. Collect all permission IDs from all sources (no awaits in loops)\n const allPermissionIds: Set<string> = new Set();\n const rolePermissionIds: string[] = [];\n const groupRolePermissionIds: Map<string, string[]> = new Map();\n\n // 2a. Get base role permissions\n const baseRolePermIds =\n await this.rolePermissionCollection.getPermissionIds(membership.roleId);\n for (const id of baseRolePermIds) {\n allPermissionIds.add(id);\n rolePermissionIds.push(id);\n }\n\n // 2b. Get user's groups in the tenant (scoped to prevent cross-tenant leakage)\n const groupIds = await this.groupMemberCollection.getGroupIdsForTenant(\n userId,\n tenantId,\n );\n result.groupIds = groupIds;\n\n // 2c. Collect permissions from group roles\n for (const groupId of groupIds) {\n const groupRoleIds = await this.groupRoleCollection.getRoleIds(groupId);\n for (const roleId of groupRoleIds) {\n const permIds =\n await this.rolePermissionCollection.getPermissionIds(roleId);\n for (const id of permIds) {\n allPermissionIds.add(id);\n }\n groupRolePermissionIds.set(roleId, permIds);\n }\n }\n\n // 2d. Get membership overrides\n if (!membership.id) {\n return result;\n }\n const membershipId = membership.id;\n const grantedPermissionIds =\n await this.membershipOverrideCollection.getGrantedPermissionIds(\n membershipId,\n );\n const deniedPermissionIds =\n await this.membershipOverrideCollection.getDeniedPermissionIds(\n membershipId,\n );\n\n result.deniedPermissionIds = deniedPermissionIds;\n\n for (const id of grantedPermissionIds) {\n allPermissionIds.add(id);\n }\n for (const id of deniedPermissionIds) {\n allPermissionIds.add(id);\n }\n\n // 3. Batch fetch all permissions in a single query\n const permissionsMap = await this.permissionCollection.findByIds(\n Array.from(allPermissionIds),\n );\n\n // Build ID to slug mapping\n const permissionIdToSlug = new Map<string, string>();\n for (const [id, perm] of permissionsMap) {\n if (perm.slug) {\n permissionIdToSlug.set(id, perm.slug);\n }\n }\n\n // 4. Apply permissions from role\n for (const permId of rolePermissionIds) {\n const slug = permissionIdToSlug.get(permId);\n if (slug) {\n result.permissions.add(slug);\n }\n }\n\n // 5. Apply permissions from group roles\n for (const permIds of groupRolePermissionIds.values()) {\n for (const permId of permIds) {\n const slug = permissionIdToSlug.get(permId);\n if (slug) {\n result.permissions.add(slug);\n }\n }\n }\n\n // 6. Subtract tenant-level DENY'd slugs. A tenant-DENY is a HARD,\n // tenant-wide block that overrides role and group grants too — sitting just\n // above the most-specific membership overrides in precedence. It runs\n // BEFORE the membership GRANT pass so a more-specific membership GRANT (step\n // 7) can deliberately re-add a slug a tenant DENYs.\n for (const slug of tenantPermissions.deniedPermissions) {\n result.permissions.delete(slug);\n }\n\n // 7. Apply granted membership overrides (most specific GRANT; can re-add a\n // tenant-DENY'd slug).\n for (const permId of grantedPermissionIds) {\n const slug = permissionIdToSlug.get(permId);\n if (slug) {\n result.permissions.add(slug);\n }\n }\n\n // 8. Remove denied membership overrides (DENY takes absolute precedence over\n // tenant, role, group, and granted membership permissions).\n for (const permId of deniedPermissionIds) {\n const slug = permissionIdToSlug.get(permId);\n if (slug) {\n result.permissions.delete(slug);\n }\n }\n\n return result;\n }\n\n /**\n * Find the membership to resolve through when the user has no direct\n * membership row in the target tenant.\n *\n * Walks the target tenant's ancestor chain (from the materialized\n * `hierarchyPath`, nearest ancestor first) and returns the nearest ACTIVE\n * ancestor membership whose role is explicitly flagged\n * `inheritsToDescendants: true`. Ancestor memberships that are inactive or\n * hold an unflagged role are skipped — they neither confer nor block\n * inheritance from higher ancestors. Attenuating a user in a specific\n * tenant is expressed with a direct membership row there (which pins\n * resolution) or a tenant-level DENY, not with an intermediate unflagged\n * membership.\n *\n * Fails closed (returns null, resolving to the empty set) when the tenant\n * is missing or its `hierarchyPath` is malformed: deeper than\n * `MAX_TENANT_HIERARCHY_DEPTH`, self-referential, containing duplicate\n * ancestor ids, or inconsistent with the actual `parentTenantId` chain\n * (e.g. a stale path naming an unrelated tenant).\n */\n private async resolveInheritedMembership(\n userId: string,\n tenantId: string,\n ): Promise<Membership | null> {\n const tenant = await this.tenantCollection.get({ id: tenantId });\n if (!tenant?.id) {\n return null;\n }\n\n // Ordered root -> immediate parent, per the materialized path.\n const ancestorIds = tenant.getAncestorIds();\n if (ancestorIds.length === 0) {\n return null;\n }\n\n // Malformed hierarchy paths fail closed: a well-formed path is bounded by\n // MAX_TENANT_HIERARCHY_DEPTH (at most MAX-1 ancestors), never contains the\n // tenant itself, and never repeats an ancestor.\n if (\n ancestorIds.length >= MAX_TENANT_HIERARCHY_DEPTH ||\n ancestorIds.includes(tenant.id) ||\n new Set(ancestorIds).size !== ancestorIds.length\n ) {\n return null;\n }\n\n // The materialized path is an authorization source here, so verify it\n // against the actual parent chain before trusting it: batch-load the\n // ancestors and require an unbroken parentTenantId link\n // root -> ... -> immediate parent -> target. A stale or inconsistent path\n // (e.g. naming a tenant that is not really an ancestor) fails closed.\n const ancestors = await this.tenantCollection.listByIds(ancestorIds);\n const ancestorsById = new Map(\n ancestors.map((ancestor) => [ancestor.id, ancestor]),\n );\n let expectedParentId: string | null = null;\n for (const ancestorId of ancestorIds) {\n const ancestor = ancestorsById.get(ancestorId);\n if (!ancestor?.id) {\n return null;\n }\n if ((ancestor.parentTenantId ?? null) !== expectedParentId) {\n return null;\n }\n expectedParentId = ancestor.id;\n }\n if ((tenant.parentTenantId ?? null) !== expectedParentId) {\n return null;\n }\n\n // One query for the user's ACTIVE memberships, intersected with the\n // ancestor chain. UNIQUE(userId, tenantId) guarantees at most one row per\n // ancestor.\n const activeMemberships =\n await this.membershipCollection.findActiveByUser(userId);\n if (activeMemberships.length === 0) {\n return null;\n }\n const membershipByTenantId = new Map<string, Membership>();\n for (const row of activeMemberships) {\n if (row.tenantId && row.userId === userId) {\n membershipByTenantId.set(row.tenantId, row);\n }\n }\n\n // Candidates ordered nearest ancestor first.\n const candidates: Membership[] = [];\n for (let i = ancestorIds.length - 1; i >= 0; i--) {\n const candidate = membershipByTenantId.get(ancestorIds[i]);\n if (candidate?.roleId) {\n candidates.push(candidate);\n }\n }\n if (candidates.length === 0) {\n return null;\n }\n\n // Batch load candidate roles; only an explicit `inheritsToDescendants:\n // true` role confers inheritance, and the nearest such membership wins.\n const roleIds = [\n ...new Set(candidates.map((candidate) => candidate.roleId as string)),\n ];\n const roles = await this.roleCollection.listByIds(roleIds);\n const inheritableRoleIds = new Set<string>();\n for (const role of roles) {\n if (role.id && role.inheritsToDescendants === true) {\n inheritableRoleIds.add(role.id);\n }\n }\n\n for (const candidate of candidates) {\n if (candidate.roleId && inheritableRoleIds.has(candidate.roleId)) {\n return candidate;\n }\n }\n return null;\n }\n\n /**\n * Check if a user has a specific permission in a tenant\n */\n async hasPermission(\n userId: string,\n tenantId: string,\n permissionSlug: string,\n options: PermissionResolutionOptions = {},\n ): Promise<boolean> {\n const result = await this.resolvePermissions(userId, tenantId, options);\n return result.permissions.has(permissionSlug);\n }\n\n /**\n * Check if a user has all of the specified permissions\n */\n async hasAllPermissions(\n userId: string,\n tenantId: string,\n permissionSlugs: string[],\n options: PermissionResolutionOptions = {},\n ): Promise<boolean> {\n const result = await this.resolvePermissions(userId, tenantId, options);\n return permissionSlugs.every((slug) => result.permissions.has(slug));\n }\n\n /**\n * Check if a user has any of the specified permissions\n */\n async hasAnyPermission(\n userId: string,\n tenantId: string,\n permissionSlugs: string[],\n options: PermissionResolutionOptions = {},\n ): Promise<boolean> {\n const result = await this.resolvePermissions(userId, tenantId, options);\n return permissionSlugs.some((slug) => result.permissions.has(slug));\n }\n\n /**\n * Static factory method\n */\n static async create(options: SmrtClassOptions): Promise<PermissionResolver> {\n const resolver = new PermissionResolver(options);\n await resolver.initialize();\n return resolver;\n }\n}\n","/**\n * SessionService - Higher-level session management combining sessions with user/permission loading\n * @packageDocumentation\n */\n\nimport type { SmrtClassOptions } from '@happyvertical/smrt-core';\nimport { MembershipCollection } from '../collections/MembershipCollection.js';\nimport {\n type CreateSessionOptions,\n SessionCollection,\n} from '../collections/SessionCollection.js';\nimport { UserCollection } from '../collections/UserCollection.js';\nimport type { Membership } from '../models/Membership.js';\nimport { DEFAULT_SESSION_TTL, type Session } from '../models/Session.js';\nimport type { User } from '../models/User.js';\nimport { PermissionResolver } from './PermissionResolver.js';\n\n/**\n * Session context with user and permissions\n */\nexport interface SessionContext {\n /** The User record */\n user: User;\n /** Active membership for the current tenant, if one was resolved */\n membership?: Membership | null;\n /** Resolved permission slugs */\n permissions: string[];\n /** Tenant ID from session (if any) */\n tenantId: string | null;\n /** Session ID */\n sessionId: string;\n}\n\n/**\n * Result of {@link SessionService.switchTenant}.\n *\n * A successful switch into a non-null tenant ROTATES the session id (#1354\n * follow-up): a brand-new {@link Session} is minted and the old one is revoked,\n * so a captured pre-switch id stops validating. Callers MUST persist `sessionId`\n * (e.g. re-set the session cookie) after a rotation.\n */\nexport interface SwitchTenantResult {\n /** Whether the switch succeeded (true also for a `null` clear). */\n switched: boolean;\n /**\n * The session id to use going forward: the NEW id after a rotation, the\n * unchanged id after a `null` clear, or `null` when the switch failed\n * (unknown session or non-member — fail-closed).\n */\n sessionId: string | null;\n /** The resulting session (new on rotation; existing on clear; null on failure). */\n session: Session | null;\n /** True only when a fresh session id was minted (non-null tenant switch). */\n rotated: boolean;\n}\n\n/**\n * Options for SessionService\n */\nexport interface SessionServiceOptions extends SmrtClassOptions {\n /** Default session TTL in seconds (default: 7 days) */\n defaultTTL?: number;\n /** Cookie name (default: 'sid') */\n cookieName?: string;\n /** Whether to auto-extend sessions on access (default: false) */\n autoExtend?: boolean;\n}\n\n/**\n * SessionService provides high-level session management that combines\n * session storage with user and permission loading.\n *\n * This is the main service to use for session-based authentication.\n *\n * @example\n * ```typescript\n * const sessionService = await SessionService.create({\n * db: { type: 'sqlite', url: 'app.db' },\n * defaultTTL: 7 * 24 * 60 * 60, // 7 days\n * });\n *\n * // Create session after login\n * const sessionId = await sessionService.createSession(userId, tenantId);\n *\n * // Load session context on each request\n * const context = await sessionService.loadSessionContext(sessionId);\n * if (context) {\n * console.log('User:', context.user.email);\n * console.log('Permissions:', context.permissions);\n * }\n *\n * // Destroy session on logout\n * await sessionService.destroySession(sessionId);\n * ```\n */\nexport class SessionService {\n private options: SessionServiceOptions;\n private sessionCollection!: SessionCollection;\n private userCollection!: UserCollection;\n private membershipCollection!: MembershipCollection;\n private permissionResolver!: PermissionResolver;\n private defaultTTL: number;\n private autoExtend: boolean;\n\n constructor(options: SessionServiceOptions) {\n this.options = options;\n this.defaultTTL = options.defaultTTL ?? DEFAULT_SESSION_TTL;\n this.autoExtend = options.autoExtend ?? false;\n }\n\n /**\n * Initialize collections\n */\n async initialize(): Promise<void> {\n this.sessionCollection = await SessionCollection.create(this.options);\n this.userCollection = await UserCollection.create(this.options);\n this.membershipCollection = await MembershipCollection.create(this.options);\n this.permissionResolver = await PermissionResolver.create(this.options);\n }\n\n /**\n * Create a new session for a user\n *\n * @param userId - The user ID\n * @param tenantId - Optional tenant context\n * @param options - Additional session options\n * @returns The session ID\n */\n async createSession(\n userId: string,\n tenantId?: string,\n options?: Partial<CreateSessionOptions>,\n ): Promise<string> {\n const session = await this.sessionCollection.createSession({\n userId,\n tenantId,\n ttl: options?.ttl ?? this.defaultTTL,\n userAgent: options?.userAgent,\n ipAddress: options?.ipAddress,\n data: options?.data,\n });\n\n return session.id as string;\n }\n\n /**\n * Load full session context (user + permissions)\n *\n * Returns null if session is invalid or user doesn't exist\n */\n async loadSessionContext(sessionId: string): Promise<SessionContext | null> {\n // Find valid session\n const session = await this.sessionCollection.findValidSession(sessionId);\n if (!session) return null;\n\n // Load user\n const user = await this.userCollection.get(session.userId);\n if (!user?.isActive()) return null;\n\n // Resolve permissions (only if tenant context exists)\n let permissions: string[] = [];\n let membership: Membership | null = null;\n if (session.tenantId) {\n const resolvedMembership =\n await this.membershipCollection.findByUserAndTenant(\n session.userId,\n session.tenantId,\n );\n membership = resolvedMembership?.isActive() ? resolvedMembership : null;\n\n // Pass the RAW row: an inactive direct membership pins resolution to\n // the empty set, while a missing row (null) lets the resolver apply\n // opt-in ancestor-membership inheritance (`Role.inheritsToDescendants`).\n const result = await this.permissionResolver.resolvePermissions(\n session.userId,\n session.tenantId,\n { membership: resolvedMembership },\n );\n permissions = Array.from(result.permissions);\n }\n\n // Touch session (and optionally extend)\n if (this.autoExtend) {\n session.extend(this.defaultTTL);\n } else {\n session.touch();\n }\n await session.save();\n\n return {\n user,\n membership,\n permissions,\n tenantId: session.tenantId,\n sessionId: session.id as string,\n };\n }\n\n /**\n * Get the initialized database connection backing this session service.\n */\n getDatabase() {\n return this.sessionCollection.db;\n }\n\n /**\n * Refresh session (extend expiry, update lastAccessed)\n */\n async refreshSession(sessionId: string): Promise<boolean> {\n return this.sessionCollection.touch(sessionId, true, this.defaultTTL);\n }\n\n /**\n * Destroy a session (revoke it)\n */\n async destroySession(sessionId: string): Promise<boolean> {\n return this.sessionCollection.revokeSession(sessionId);\n }\n\n /**\n * Destroy all sessions for a user (logout from all devices)\n */\n async destroyAllUserSessions(userId: string): Promise<number> {\n return this.sessionCollection.revokeUserSessions(userId);\n }\n\n /**\n * Switch tenant context for a session.\n *\n * A session's `tenantId` is the tenant-isolation key for every `@TenantScoped`\n * query, so it must never be set to a tenant the session's user is not an\n * active member of — otherwise a caller could read/write another tenant's data\n * by feeding an arbitrary id here (e.g. straight from untrusted form data).\n *\n * Fail-closed (#1400): the user's ACTIVE membership in the target tenant is\n * verified BEFORE any write. A non-member switch returns\n * `{ switched: false, ... }` and mutates nothing.\n *\n * Session-id ROTATION (#1354 follow-up): a successful switch into a non-null\n * tenant mints a BRAND-NEW session (fresh secure id, fresh TTL) for the same\n * user with the new tenant, then REVOKES the old session — so any captured\n * pre-switch session id immediately stops validating, shrinking the blast\n * radius of a leaked id across a privilege/tenant boundary. The device context\n * (user agent, IP, custom data) carries over to the new session. Callers MUST\n * persist the returned `sessionId` (e.g. re-set the cookie).\n *\n * Passing `null` clears the tenant context, is always allowed, and stays\n * in-place (no rotation — there is no privilege boundary being crossed).\n *\n * @returns A {@link SwitchTenantResult}; check `switched` for success.\n */\n async switchTenant(\n sessionId: string,\n tenantId: string | null,\n ): Promise<SwitchTenantResult> {\n const failClosed: SwitchTenantResult = {\n switched: false,\n sessionId: null,\n session: null,\n rotated: false,\n };\n\n const session = await this.sessionCollection.findValidSession(sessionId);\n if (!session) return failClosed;\n\n // Clearing the tenant context crosses no privilege boundary, so keep the\n // existing session in place (no rotation needed).\n if (tenantId === null) {\n const ok = await this.sessionCollection.setSessionTenant(sessionId, null);\n if (!ok) return failClosed;\n const updated = await this.sessionCollection.findValidSession(sessionId);\n return {\n switched: true,\n sessionId,\n session: updated,\n rotated: false,\n };\n }\n\n // Fail-closed membership check BEFORE any write.\n const membership = await this.membershipCollection.findByUserAndTenant(\n session.userId,\n tenantId,\n );\n if (!membership?.isActive()) {\n return failClosed;\n }\n\n // Rotate FAIL-CLOSED: revoke the old session FIRST, then mint the fresh one.\n // If the second write fails the caller ends up needing to re-auth (the old\n // id is already invalid) rather than retaining a still-valid stale-tenant\n // session — so a captured pre-switch id provably stops validating before we\n // ever report success.\n await this.sessionCollection.revokeSession(sessionId);\n const rotated = await this.sessionCollection.createSession({\n userId: session.userId,\n tenantId,\n ttl: this.defaultTTL,\n userAgent: session.userAgent,\n ipAddress: session.ipAddress,\n data: session.data,\n });\n\n return {\n switched: true,\n sessionId: rotated.id as string,\n session: rotated,\n rotated: true,\n };\n }\n\n /**\n * Get all active sessions for a user (for \"manage sessions\" UI)\n */\n async getUserSessions(userId: string) {\n return this.sessionCollection.findByUser(userId);\n }\n\n /**\n * Clean up expired sessions (run periodically)\n */\n async cleanupExpiredSessions(): Promise<number> {\n return this.sessionCollection.deleteExpired();\n }\n\n /**\n * Check if a permission is granted for the session\n */\n async hasPermission(sessionId: string, permission: string): Promise<boolean> {\n const context = await this.loadSessionContext(sessionId);\n if (!context) return false;\n return context.permissions.includes(permission);\n }\n\n /**\n * Get session data\n */\n async getSessionData<T>(\n sessionId: string,\n key: string,\n ): Promise<T | undefined> {\n return this.sessionCollection.getSessionData<T>(sessionId, key);\n }\n\n /**\n * Set session data\n */\n async setSessionData(\n sessionId: string,\n key: string,\n value: unknown,\n ): Promise<boolean> {\n return this.sessionCollection.setSessionData(sessionId, key, value);\n }\n\n /**\n * Static factory method\n */\n static async create(options: SessionServiceOptions): Promise<SessionService> {\n const service = new SessionService(options);\n await service.initialize();\n return service;\n }\n}\n","/**\n * MobileAuthService — server side of the SMRT mobile `/api/mobile` contract\n * (ADR 0001 Phase 3.5, issue #1748).\n *\n * Implements server-brokered OIDC/PKCE for native mobile clients on top of\n * the existing building blocks: {@link OidcLoginService} for the protocol\n * work, {@link SessionService} for the opaque bearer session (the session id\n * IS the bearer token — the same convention `TerminalAuthService` ships), and\n * the membership/role/tenant collections for the session bootstrap.\n *\n * Wire DTOs come from `@happyvertical/smrt-mobile-contract` — the owning\n * package for both the Kotlin client contract and these TypeScript types.\n *\n * ## Stateless completion: the state token\n *\n * The mobile flow has no cookie jar, so the web flow's HMAC-signed\n * transaction cookie cannot carry `state`/`nonce`/`codeVerifier` across the\n * redirect. Instead (per the frozen contract) the CLIENT persists and echoes\n * `state` + `codeVerifier`, and the server stays stateless by making the\n * OAuth `state` value itself a signed token: base64url JSON carrying the\n * nonce, provider, and creation time, plus an HMAC-SHA256 signature when a\n * secret is available (`stateSecret` option, defaulting to the provider's\n * `clientSecret` — the same fallback the web transaction cookie uses). The\n * client treats `state` as opaque; `MobileSessionManager` just compares the\n * redirect's `state` to the persisted one and echoes it back.\n *\n * The `codeVerifier` deliberately NEVER rides inside the state token: state\n * appears in the authorization URL (browser history, proxy logs), and a\n * verifier there would defeat PKCE. It stays client-held — returned once in\n * the `auth/start` response body and echoed once in the `auth/complete`\n * request body, both over TLS POSTs. The nonce, by contrast, is already a\n * query parameter of the authorization URL, so embedding it in `state`\n * leaks nothing while letting the server enforce full ID-token nonce\n * verification without server-side pending state.\n *\n * @packageDocumentation\n */\n\nimport type { SmrtClassOptions } from '@happyvertical/smrt-core';\nimport type {\n MobileAuthCompleteRequest,\n MobileAuthSession,\n MobileAuthStartRequest,\n MobileAuthStartResponse,\n MobileSessionBootstrap,\n MobileTenantOption,\n MobileUserSummary,\n} from '@happyvertical/smrt-mobile-contract';\nimport { MembershipCollection } from '../collections/MembershipCollection.js';\nimport { RoleCollection } from '../collections/RoleCollection.js';\nimport { TenantCollection } from '../collections/TenantCollection.js';\nimport {\n type OidcClaims,\n UserCollection,\n} from '../collections/UserCollection.js';\nimport type { Membership } from '../models/Membership.js';\nimport type { Role } from '../models/Role.js';\nimport type { Tenant } from '../models/Tenant.js';\nimport {\n OidcLoginError,\n OidcLoginService,\n type OidcProviderResolutionOptions,\n type OidcTokenSet,\n type OidcTransaction,\n resolveOidcProviderConfig,\n} from './OidcLoginService.js';\nimport type { SessionContext } from './SessionService.js';\nimport { SessionService } from './SessionService.js';\n\n/** 30 days — mobile sessions outlive browser sessions by design. */\nconst DEFAULT_MOBILE_SESSION_TTL_SECONDS = 30 * 24 * 60 * 60;\n/** 10 minutes — matches the web flow's transaction-cookie TTL. */\nconst DEFAULT_MOBILE_TRANSACTION_TTL_SECONDS = 10 * 60;\n/** Tolerated forward clock skew when checking state-token freshness. */\nconst STATE_TOKEN_CLOCK_SKEW_MS = 60 * 1000;\n/** Upper bound for a client-supplied correlation `state` value. */\nconst MAX_CLIENT_STATE_LENGTH = 512;\n/** Cap for the per-(provider, redirectUri) OIDC service cache. */\nconst MAX_OIDC_SERVICE_CACHE = 16;\n/** Cap for IdP-sourced strings persisted in the session data blob. */\nconst MAX_STORED_CLAIM_LENGTH = 1024;\n\n/**\n * Machine-readable error codes carried on {@link MobileAuthError} and in the\n * JSON error body (`{ error, code }`) the SvelteKit handlers emit.\n */\nexport type MobileAuthErrorCode =\n | 'invalid_request'\n | 'unknown_provider'\n | 'invalid_redirect_uri'\n | 'missing_code_verifier'\n | 'invalid_state'\n | 'expired_transaction'\n | 'exchange_failed'\n | 'signin_not_permitted'\n | 'missing_bearer_token'\n | 'invalid_bearer_token'\n | 'provider_unavailable'\n | 'server_misconfigured';\n\n/**\n * HTTP-mapped mobile-auth failure. `status` drives the response code; `code`\n * gives clients a stable discriminator (messages may change).\n */\nexport class MobileAuthError extends Error {\n readonly status: number;\n readonly code: MobileAuthErrorCode;\n\n constructor(status: number, code: MobileAuthErrorCode, message: string) {\n super(message);\n this.name = 'MobileAuthError';\n this.status = status;\n this.code = code;\n }\n}\n\n/** Context handed to the {@link MobileAuthServiceOptions.resolveUser} hook. */\nexport interface MobileLoginContext {\n claims: OidcClaims;\n tokens: OidcTokenSet;\n providerName: string;\n}\n\n/** Minimal user identity a {@link MobileAuthServiceOptions.resolveUser} hook returns. */\nexport interface MobileResolvedUser {\n id: string;\n email?: string | null;\n}\n\n/** Context handed to the {@link MobileAuthServiceOptions.resolveTenantId} hook. */\nexport interface MobileTenantContext {\n userId: string;\n claims: OidcClaims;\n providerName: string;\n}\n\n/** Context handed to the {@link MobileAuthServiceOptions.buildExtras} hook. */\nexport interface MobileBootstrapContext {\n /** Full session context (user, membership, tenantId, sessionId). */\n session: SessionContext;\n /**\n * The session's resolved permission slugs. Any MODEL JSON placed into\n * `extras` must be projected with `toPublicJSON({ permissions })` using\n * THIS set, or the response leaks fields the generated routes redact\n * (`@field({ readPermission })`, #1822). Fail closed.\n */\n permissions: string[];\n tenants: MobileTenantOption[];\n activeTenant: MobileTenantOption | null;\n}\n\n/** Request metadata recorded onto the minted session. */\nexport interface MobileRequestMeta {\n userAgent?: string;\n ipAddress?: string;\n}\n\nexport interface MobileAuthServiceOptions\n extends SmrtClassOptions,\n OidcProviderResolutionOptions {\n /** Optional fetch override for tests or custom runtimes. */\n fetch?: typeof fetch;\n /** JWT clock tolerance passed to jose. */\n clockTolerance?: number | string;\n /**\n * Allowed mobile redirect URIs. Entries are exact matches, except entries\n * ending in `/` which allow any sub-path of that prefix. When omitted or\n * empty, any structurally valid mobile redirect URI is accepted (https,\n * loopback http, or a private app scheme — RFC 8252); configure this in\n * production so authorization responses cannot be pointed at an\n * attacker-controlled URI.\n */\n redirectUris?: string[];\n /** Mobile bearer session TTL in seconds. Default: 30 days. */\n sessionTtl?: number;\n /**\n * Auth handshake (start → complete) TTL in seconds. Default: 10 minutes,\n * matching the web flow's transaction cookie.\n */\n transactionTtl?: number;\n /**\n * HMAC secret for state-token integrity. Defaults to the resolved\n * provider's `clientSecret`.\n *\n * State signing binds the `nonce`/provider/createdAt in the OAuth `state`\n * so they cannot be forged, so it is REQUIRED by default: if neither\n * `stateSecret` nor the provider's `clientSecret` is available, sign-in\n * fails closed (500 `server_misconfigured`). A public OIDC client (no\n * client secret — the PKCE case) just supplies a `stateSecret`; it is a\n * server-side HMAC key unrelated to OAuth client authentication, so any\n * deployment can set one. Set {@link allowUnsignedState} to opt into\n * unsigned tokens (NOT recommended — then `redirectUris` is the only\n * defense against state forgery).\n */\n stateSecret?: string;\n /**\n * Permit unsigned state tokens when no `stateSecret`/`clientSecret` is\n * configured. Default false (fail closed). Only enable for local\n * development or a deployment that accepts the state-forgery risk;\n * production should configure a `stateSecret` instead.\n */\n allowUnsignedState?: boolean;\n /**\n * Include descendant tenants reachable through ACTIVE memberships whose\n * role has `inheritsToDescendants: true` (#1867) in the bootstrap tenant\n * list. Default true.\n */\n includeInheritedTenants?: boolean;\n /**\n * Provision a user even when the IdP explicitly reported the email as\n * unverified. Passed through to `UserCollection.getOrCreateFromOidc`.\n */\n allowUnverifiedEmail?: boolean;\n /**\n * Map verified IdP claims to a SMRT user. The default provisions (or\n * resolves) the user via `UserCollection.getOrCreateFromOidc`. Return\n * `null`/`undefined` to REFUSE sign-in (403 `signin_not_permitted`) —\n * invite-gated apps resolve against their own membership rules here\n * without any user row being created. THROWING (vs returning null) is\n * treated as an unexpected server error and surfaces as a generic 500;\n * translate expected denials into a `null` return or a thrown\n * {@link MobileAuthError}.\n */\n resolveUser?: (\n context: MobileLoginContext,\n ) => Promise<MobileResolvedUser | null | undefined>;\n /**\n * Choose the tenant the minted session is bound to. Return a tenant id,\n * `null` for an explicitly tenant-less session, or `undefined` to fall\n * back to the default (the first direct ACTIVE membership's tenant,\n * sorted by tenant name). The session's tenant is the isolation key for\n * every `@TenantScoped` query, so only return tenants the user can\n * actually resolve permissions in.\n */\n resolveTenantId?: (\n context: MobileTenantContext,\n ) => Promise<string | null | undefined>;\n /**\n * App-defined `extras` object for the session bootstrap. Must be a plain\n * JSON object (the Kotlin client decodes it as `JsonObject`). See\n * {@link MobileBootstrapContext.permissions} for the read-permission\n * redaction requirement on model JSON.\n */\n buildExtras?: (\n context: MobileBootstrapContext,\n ) => Promise<Record<string, unknown> | null | undefined>;\n}\n\n/** Result of {@link MobileAuthService.logout}. */\nexport interface MobileLogoutResult {\n ok: true;\n /** Whether a live session was actually revoked. */\n destroyed: boolean;\n}\n\n/**\n * Payload encoded into the OAuth `state` value. Short keys keep the\n * authorization URL compact.\n */\ninterface MobileStatePayload {\n /** Version discriminator. */\n v: 1;\n /** Random entropy (CSRF binding for the client's opaque comparison). */\n s: string;\n /** OIDC nonce echoed for ID-token verification at complete time. */\n n: string;\n /** Provider name the handshake was started for. */\n p: string;\n /** Creation time (epoch ms) for TTL enforcement. */\n t: number;\n /** Optional client-supplied correlation value (opaque passthrough). */\n c?: string;\n}\n\nfunction bytesToBase64Url(bytes: Uint8Array): string {\n let binary = '';\n for (const byte of bytes) {\n binary += String.fromCharCode(byte);\n }\n return btoa(binary)\n .replaceAll('+', '-')\n .replaceAll('/', '_')\n .replace(/=+$/u, '');\n}\n\nfunction base64UrlToBytes(value: string): Uint8Array {\n const normalized = value.replaceAll('-', '+').replaceAll('_', '/');\n const padded = normalized.padEnd(\n normalized.length + ((4 - (normalized.length % 4)) % 4),\n '=',\n );\n const binary = atob(padded);\n const bytes = new Uint8Array(binary.length);\n for (let index = 0; index < binary.length; index += 1) {\n bytes[index] = binary.charCodeAt(index);\n }\n return bytes;\n}\n\nfunction randomBase64Url(byteLength = 16): string {\n const bytes = new Uint8Array(byteLength);\n crypto.getRandomValues(bytes);\n return bytesToBase64Url(bytes);\n}\n\nasync function hmacBase64Url(payload: string, secret: string): Promise<string> {\n const key = await crypto.subtle.importKey(\n 'raw',\n new TextEncoder().encode(secret),\n { hash: 'SHA-256', name: 'HMAC' },\n false,\n ['sign'],\n );\n const signature = await crypto.subtle.sign(\n 'HMAC',\n key,\n new TextEncoder().encode(payload),\n );\n return bytesToBase64Url(new Uint8Array(signature));\n}\n\nfunction timingSafeEqual(left: string, right: string): boolean {\n const leftBytes = new TextEncoder().encode(left);\n const rightBytes = new TextEncoder().encode(right);\n let diff = leftBytes.length ^ rightBytes.length;\n const length = Math.max(leftBytes.length, rightBytes.length);\n for (let index = 0; index < length; index += 1) {\n diff |= (leftBytes[index] ?? 0) ^ (rightBytes[index] ?? 0);\n }\n return diff === 0;\n}\n\nasync function encodeMobileState(\n payload: MobileStatePayload,\n secret: string | undefined,\n): Promise<string> {\n const encoded = bytesToBase64Url(\n new TextEncoder().encode(JSON.stringify(payload)),\n );\n if (!secret) return encoded;\n return `${encoded}.${await hmacBase64Url(encoded, secret)}`;\n}\n\nasync function decodeMobileState(\n raw: string,\n secret: string | undefined,\n): Promise<MobileStatePayload> {\n const invalid = () =>\n new MobileAuthError(401, 'invalid_state', 'Invalid mobile auth state.');\n\n let encoded = raw;\n if (secret) {\n const separator = raw.lastIndexOf('.');\n if (separator < 0) throw invalid();\n encoded = raw.slice(0, separator);\n const signature = raw.slice(separator + 1);\n const expected = await hmacBase64Url(encoded, secret);\n if (!timingSafeEqual(signature, expected)) throw invalid();\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(new TextDecoder().decode(base64UrlToBytes(encoded)));\n } catch {\n throw invalid();\n }\n\n if (!parsed || typeof parsed !== 'object') throw invalid();\n const record = parsed as Partial<MobileStatePayload>;\n if (\n record.v !== 1 ||\n typeof record.s !== 'string' ||\n record.s.length === 0 ||\n typeof record.n !== 'string' ||\n record.n.length === 0 ||\n typeof record.p !== 'string' ||\n record.p.length === 0 ||\n typeof record.t !== 'number' ||\n !Number.isFinite(record.t) ||\n (record.c !== undefined && typeof record.c !== 'string')\n ) {\n throw invalid();\n }\n\n return {\n v: 1,\n s: record.s,\n n: record.n,\n p: record.p,\n t: record.t,\n c: record.c,\n };\n}\n\n/**\n * Pull the token out of an `Authorization: Bearer <token>` header. Returns\n * `null` when the header is missing or malformed.\n */\nexport function readMobileBearerToken(\n authorizationHeader: string | null | undefined,\n): string | null {\n const match = authorizationHeader?.match(/^Bearer\\s+(.+)$/iu);\n const token = match?.[1]?.trim();\n return token && token.length > 0 ? token : null;\n}\n\n// Schemes that could execute script or read local resources if a redirect\n// value ever reached a browser context. Rejected regardless of allow list.\nconst DANGEROUS_REDIRECT_SCHEMES = new Set([\n 'javascript:',\n 'data:',\n 'vbscript:',\n 'file:',\n 'blob:',\n 'about:',\n]);\n\n// Well-known IANA schemes that are neither `https`, loopback `http`, nor a\n// private-use app scheme (RFC 8252 §7.1). A mobile OAuth redirect would never\n// legitimately use one of these, so reject them even when no `redirectUris`\n// allow list is configured — an app-owned custom scheme (e.g.\n// `com.example.app://`) still passes.\nconst NON_APP_REDIRECT_SCHEMES = new Set([\n 'ftp:',\n 'ftps:',\n 'ws:',\n 'wss:',\n 'mailto:',\n 'tel:',\n 'sms:',\n 'gopher:',\n 'ldap:',\n 'ldaps:',\n]);\n\nfunction isLoopbackHost(hostname: string): boolean {\n const host = hostname.toLowerCase();\n return (\n host === 'localhost' ||\n host === '127.0.0.1' ||\n host === '::1' ||\n host === '[::1]'\n );\n}\n\nfunction normalizeOptionalString(value: unknown): string | undefined {\n return typeof value === 'string' && value.trim().length > 0\n ? value.trim()\n : undefined;\n}\n\n/**\n * Validate a mobile redirect URI: absolute, a safe scheme (https, loopback\n * http, or a private app scheme per RFC 8252), and — when an allow list is\n * configured — present on it. Returns the normalized URI.\n */\nexport function validateMobileRedirectUri(\n value: unknown,\n allowList: string[] = [],\n): string {\n const redirectUri = normalizeOptionalString(value);\n if (!redirectUri) {\n throw new MobileAuthError(\n 400,\n 'invalid_redirect_uri',\n 'Missing mobile redirect URI.',\n );\n }\n\n let parsed: URL;\n try {\n parsed = new URL(redirectUri);\n } catch {\n throw new MobileAuthError(\n 400,\n 'invalid_redirect_uri',\n 'Mobile redirect URI must be an absolute URL.',\n );\n }\n\n const scheme = parsed.protocol.toLowerCase();\n if (\n DANGEROUS_REDIRECT_SCHEMES.has(scheme) ||\n NON_APP_REDIRECT_SCHEMES.has(scheme)\n ) {\n throw new MobileAuthError(\n 400,\n 'invalid_redirect_uri',\n 'Mobile redirect URI uses an unsupported scheme.',\n );\n }\n // Plain http is only permitted for native loopback redirects (RFC 8252).\n // https and private-use app schemes (e.g. com.example.app://) are allowed.\n if (scheme === 'http:' && !isLoopbackHost(parsed.hostname)) {\n throw new MobileAuthError(\n 400,\n 'invalid_redirect_uri',\n 'Mobile redirect URI must use https, a loopback address, or an app scheme.',\n );\n }\n\n const entries = allowList.map((entry) => entry.trim()).filter(Boolean);\n if (entries.length > 0) {\n const allowed = entries.some((entry) =>\n redirectUriMatchesAllowEntry(parsed, redirectUri, entry),\n );\n if (!allowed) {\n throw new MobileAuthError(\n 400,\n 'invalid_redirect_uri',\n 'Mobile redirect URI is not allowed.',\n );\n }\n }\n\n return redirectUri;\n}\n\n/**\n * Does a validated redirect URI match one allow-list entry?\n *\n * - A NON-prefix entry (no trailing `/`) is an exact string match.\n * - A prefix entry (trailing `/`) matches on the PARSED URL — same protocol,\n * same host (incl. port), and the candidate's NORMALIZED pathname starting\n * with the entry's pathname. Comparing normalized pathnames is what defeats\n * path traversal: `http://127.0.0.1/cb/../evil/` parses to pathname\n * `/evil/`, which does not start with `/cb/`, so a raw-string\n * `startsWith('http://127.0.0.1/cb/')` false-positive can't slip a code to\n * an unregistered path.\n */\nfunction redirectUriMatchesAllowEntry(\n parsedCandidate: URL,\n candidate: string,\n entry: string,\n): boolean {\n if (!entry.endsWith('/')) {\n return candidate === entry;\n }\n let parsedEntry: URL;\n try {\n parsedEntry = new URL(entry);\n } catch {\n return false;\n }\n return (\n parsedCandidate.protocol === parsedEntry.protocol &&\n parsedCandidate.host === parsedEntry.host &&\n parsedCandidate.pathname.startsWith(parsedEntry.pathname)\n );\n}\n\nfunction isModuleNotFoundError(error: unknown): boolean {\n return (\n error instanceof Error &&\n ((error as NodeJS.ErrnoException).code === 'ERR_MODULE_NOT_FOUND' ||\n /Cannot find package '@happyvertical\\/smrt-tenancy'/.test(error.message))\n );\n}\n\n/**\n * Run `fn` inside smrt-tenancy's system context when the package is\n * available (mirrors `SessionPermissionContext`'s tolerance). Mobile session\n * resolution and tenant enumeration are inherently CROSS-tenant reads —\n * under strict tenancy interceptors they must not be pinned to any single\n * tenant context.\n */\nasync function runWithSystemContext<T>(fn: () => Promise<T>): Promise<T> {\n try {\n const tenancy = await import('@happyvertical/smrt-tenancy');\n return await tenancy.withSystemContext(fn);\n } catch (error) {\n if (isModuleNotFoundError(error)) {\n return await fn();\n }\n throw error;\n }\n}\n\ninterface TenantOptionSources {\n memberships: Membership[];\n tenantById: Map<string, Tenant>;\n roleById: Map<string, Role>;\n}\n\n/**\n * Server implementation of the `/api/mobile` auth + session contract.\n *\n * Framework-agnostic: methods take plain wire DTOs and header strings and\n * return wire DTOs or throw {@link MobileAuthError}. The SvelteKit adapters\n * live in `@happyvertical/smrt-users/sveltekit`\n * (`createMobileAuthHandlers`).\n */\nexport class MobileAuthService {\n private readonly options: MobileAuthServiceOptions;\n private readonly classOptions: SmrtClassOptions;\n private readonly sessionTtl: number;\n private readonly transactionTtl: number;\n private sessionService!: SessionService;\n private userCollection!: UserCollection;\n private membershipCollection!: MembershipCollection;\n private roleCollection!: RoleCollection;\n private tenantCollection!: TenantCollection;\n /** Per-(provider, redirectUri) service cache preserving metadata caches. */\n private readonly oidcServices = new Map<string, OidcLoginService>();\n\n constructor(options: MobileAuthServiceOptions) {\n const {\n fetch: _fetch,\n clockTolerance: _clockTolerance,\n redirectUris: _redirectUris,\n sessionTtl,\n transactionTtl,\n stateSecret: _stateSecret,\n allowUnsignedState: _allowUnsignedState,\n includeInheritedTenants: _includeInheritedTenants,\n allowUnverifiedEmail: _allowUnverifiedEmail,\n resolveUser: _resolveUser,\n resolveTenantId: _resolveTenantId,\n buildExtras: _buildExtras,\n defaultProvider: _defaultProvider,\n providers: _providers,\n ...classOptions\n } = options;\n this.options = options;\n this.classOptions = classOptions;\n this.sessionTtl =\n sessionTtl && sessionTtl > 0\n ? Math.floor(sessionTtl)\n : DEFAULT_MOBILE_SESSION_TTL_SECONDS;\n this.transactionTtl =\n transactionTtl && transactionTtl > 0\n ? Math.floor(transactionTtl)\n : DEFAULT_MOBILE_TRANSACTION_TTL_SECONDS;\n }\n\n private async initialize(): Promise<void> {\n this.sessionService = await SessionService.create({\n ...this.classOptions,\n defaultTTL: this.sessionTtl,\n });\n this.userCollection = await UserCollection.create(this.classOptions);\n this.membershipCollection = await MembershipCollection.create(\n this.classOptions,\n );\n this.roleCollection = await RoleCollection.create(this.classOptions);\n this.tenantCollection = await TenantCollection.create(this.classOptions);\n }\n\n static async create(\n options: MobileAuthServiceOptions,\n ): Promise<MobileAuthService> {\n const service = new MobileAuthService(options);\n await service.initialize();\n return service;\n }\n\n /**\n * The underlying {@link SessionService}. Exposed so route guards can share\n * it with `withSessionPermissionContext` instead of minting a second one.\n */\n getSessionService(): SessionService {\n return this.sessionService;\n }\n\n private resolveProvider(requested: string | null | undefined) {\n try {\n return resolveOidcProviderConfig(\n normalizeOptionalString(requested),\n this.options,\n );\n } catch (error) {\n const message =\n error instanceof OidcLoginError\n ? error.message\n : 'Unknown mobile auth provider.';\n throw new MobileAuthError(400, 'unknown_provider', message);\n }\n }\n\n private getOidcService(\n providerName: string,\n redirectUri: string,\n ): OidcLoginService {\n const key = `${providerName}\\n${redirectUri}`;\n const cached = this.oidcServices.get(key);\n if (cached) {\n // LRU: re-insert so the hot entry moves to the newest slot and eviction\n // drops a genuinely-cold provider rather than the one in active use.\n this.oidcServices.delete(key);\n this.oidcServices.set(key, cached);\n return cached;\n }\n\n const { provider } = this.resolveProvider(providerName);\n const service = new OidcLoginService({\n ...this.classOptions,\n fetch: this.options.fetch,\n clockTolerance: this.options.clockTolerance,\n provider: { ...provider, redirectUri },\n providerName,\n });\n\n // Bounded cache: redirect URIs are allow-list validated before we get\n // here, but a no-allow-list dev setup must not grow this unboundedly.\n // Map iteration is insertion-order, so with re-insert-on-hit above the\n // first key is the least-recently-used entry.\n if (this.oidcServices.size >= MAX_OIDC_SERVICE_CACHE) {\n const lru = this.oidcServices.keys().next().value;\n if (lru !== undefined) this.oidcServices.delete(lru);\n }\n this.oidcServices.set(key, service);\n return service;\n }\n\n /**\n * The HMAC secret for state signing, or `undefined` when unsigned tokens\n * are explicitly permitted via {@link MobileAuthServiceOptions.allowUnsignedState}.\n * Fails closed (throws → 500 `server_misconfigured`) when no secret is\n * available and unsigned tokens are not opted in, so a deployment can never\n * silently fall back to forgeable state tokens.\n */\n private stateSecretFor(provider: {\n clientSecret?: string;\n }): string | undefined {\n const secret = this.options.stateSecret ?? provider.clientSecret;\n if (secret && secret.length > 0) {\n return secret;\n }\n if (this.options.allowUnsignedState === true) {\n return undefined;\n }\n throw new MobileAuthError(\n 500,\n 'server_misconfigured',\n 'Mobile auth is not configured for signed state. Set a stateSecret (or provider clientSecret), or opt into allowUnsignedState.',\n );\n }\n\n /**\n * `POST /api/mobile/auth/start` — begin the server-brokered PKCE\n * handshake. Returns the authorization URL plus the `state` and\n * `codeVerifier` the client must persist and echo back on complete.\n */\n async start(input: MobileAuthStartRequest): Promise<MobileAuthStartResponse> {\n const resolution = this.resolveProvider(input.providerId);\n const redirectUri = validateMobileRedirectUri(\n input.redirectUri,\n this.options.redirectUris,\n );\n\n const clientState = normalizeOptionalString(input.state);\n if (clientState && clientState.length > MAX_CLIENT_STATE_LENGTH) {\n throw new MobileAuthError(\n 400,\n 'invalid_request',\n `Client state exceeds ${MAX_CLIENT_STATE_LENGTH} characters.`,\n );\n }\n\n const service = this.getOidcService(resolution.providerName, redirectUri);\n const base = service.createTransaction();\n const state = await encodeMobileState(\n {\n v: 1,\n s: randomBase64Url(16),\n n: base.nonce,\n p: resolution.providerName,\n t: Date.now(),\n c: clientState,\n },\n this.stateSecretFor(resolution.provider),\n );\n const transaction: OidcTransaction = { ...base, state };\n\n const scopes = Array.isArray(input.scopes)\n ? input.scopes\n .filter((scope): scope is string => typeof scope === 'string')\n .map((scope) => scope.trim())\n .filter(Boolean)\n : [];\n const loginHint = normalizeOptionalString(input.loginHint);\n\n let url: URL;\n try {\n ({ url } = await service.createAuthorizationUrl({\n transaction,\n authorizationParams: {\n ...(scopes.length > 0 ? { scope: scopes.join(' ') } : {}),\n ...(loginHint ? { login_hint: loginHint } : {}),\n },\n }));\n } catch (error) {\n const message =\n error instanceof OidcLoginError\n ? error.message\n : 'Mobile auth provider is unavailable.';\n throw new MobileAuthError(502, 'provider_unavailable', message);\n }\n\n return {\n providerId: resolution.providerName,\n authorizationUrl: url.toString(),\n state,\n codeVerifier: transaction.codeVerifier,\n nonce: transaction.nonce,\n redirectUri,\n };\n }\n\n /**\n * `POST /api/mobile/auth/complete` — exchange the authorization code (with\n * the echoed `state` + `codeVerifier`) for a mobile bearer session.\n */\n async complete(\n input: MobileAuthCompleteRequest,\n meta: MobileRequestMeta = {},\n ): Promise<MobileAuthSession> {\n const code = normalizeOptionalString(input.code);\n if (!code) {\n throw new MobileAuthError(\n 400,\n 'invalid_request',\n 'Missing authorization code.',\n );\n }\n const rawState = normalizeOptionalString(input.state);\n if (!rawState) {\n throw new MobileAuthError(\n 401,\n 'invalid_state',\n 'Missing mobile auth state.',\n );\n }\n const codeVerifier = normalizeOptionalString(input.codeVerifier);\n if (!codeVerifier) {\n throw new MobileAuthError(\n 400,\n 'missing_code_verifier',\n 'Missing PKCE code verifier.',\n );\n }\n const redirectUri = validateMobileRedirectUri(\n input.redirectUri,\n this.options.redirectUris,\n );\n\n // The state token names the provider the handshake was started for; a\n // client-supplied providerId must agree with it.\n const requestedProvider = normalizeOptionalString(input.providerId);\n const probe = this.resolveProvider(requestedProvider);\n const payload = await decodeMobileState(\n rawState,\n this.stateSecretFor(probe.provider),\n );\n if (payload.p !== probe.providerName) {\n throw new MobileAuthError(\n 401,\n 'invalid_state',\n 'Mobile auth state does not match the requested provider.',\n );\n }\n const age = Date.now() - payload.t;\n if (age > this.transactionTtl * 1000 || age < -STATE_TOKEN_CLOCK_SKEW_MS) {\n throw new MobileAuthError(\n 401,\n 'expired_transaction',\n 'Mobile auth handshake has expired. Start a new sign-in.',\n );\n }\n\n const transaction: OidcTransaction = {\n codeVerifier,\n createdAt: payload.t,\n nonce: payload.n,\n provider: payload.p,\n state: rawState,\n };\n\n const service = this.getOidcService(probe.providerName, redirectUri);\n const callbackUrl = new URL(redirectUri);\n callbackUrl.searchParams.set('code', code);\n callbackUrl.searchParams.set('state', rawState);\n\n let claims: OidcClaims;\n let tokens: OidcTokenSet;\n try {\n ({ claims, tokens } = await service.exchangeCallback(\n callbackUrl,\n transaction,\n ));\n } catch (error) {\n const message =\n error instanceof OidcLoginError\n ? error.message\n : 'Mobile auth code exchange failed.';\n throw new MobileAuthError(401, 'exchange_failed', message);\n }\n\n const resolved = await this.resolveUserFromLogin({\n claims,\n tokens,\n providerName: probe.providerName,\n });\n if (!resolved?.id) {\n throw new MobileAuthError(\n 403,\n 'signin_not_permitted',\n 'This account is not permitted to sign in.',\n );\n }\n\n const tenants = await this.listTenantOptions(resolved.id);\n const tenantId = await this.resolveSessionTenantId(\n { userId: resolved.id, claims, providerName: probe.providerName },\n tenants,\n );\n\n const sessionId = await this.sessionService.createSession(\n resolved.id,\n tenantId ?? undefined,\n {\n ttl: this.sessionTtl,\n userAgent: meta.userAgent,\n ipAddress: meta.ipAddress,\n data: {\n source: 'mobile',\n oidcProvider: probe.providerName,\n // Cap the stored IdP-sourced strings so a hostile/misbehaving IdP\n // cannot bloat every session row (iss is already constrained to the\n // configured issuer; sub is bounded defensively).\n oidcIssuer: claims.iss.slice(0, MAX_STORED_CLAIM_LENGTH),\n oidcSubject: claims.sub.slice(0, MAX_STORED_CLAIM_LENGTH),\n },\n },\n );\n\n const email = resolved.email ?? claims.email ?? '';\n return {\n accessToken: sessionId,\n tokenType: 'Bearer',\n expiresAt: new Date(Date.now() + this.sessionTtl * 1000).toISOString(),\n user: this.toUserSummary(resolved.id, email),\n activeTenant: tenants.find((tenant) => tenant.id === tenantId) ?? null,\n tenants,\n };\n }\n\n private async resolveUserFromLogin(\n context: MobileLoginContext,\n ): Promise<MobileResolvedUser | null | undefined> {\n if (this.options.resolveUser) {\n return this.options.resolveUser(context);\n }\n const result = await this.userCollection.getOrCreateFromOidc(\n context.claims,\n context.providerName,\n { allowUnverifiedEmail: this.options.allowUnverifiedEmail },\n );\n const id = result.user.id as string | undefined;\n if (!id) return null;\n return { id, email: result.user.email };\n }\n\n private async resolveSessionTenantId(\n context: MobileTenantContext,\n tenants: MobileTenantOption[],\n ): Promise<string | null> {\n if (this.options.resolveTenantId) {\n const chosen = await this.options.resolveTenantId(context);\n if (chosen !== undefined) return chosen;\n }\n // Default: the first DIRECT membership tenant (options are sorted by\n // name; inherited descendants are excluded because `switchTenant` — and\n // any flow that verifies direct membership — must accept the default).\n const direct = await runWithSystemContext(() =>\n this.membershipCollection.findActiveByUser(context.userId),\n );\n const directIds = new Set(\n direct\n .map((membership) => membership.tenantId)\n .filter((id): id is string => Boolean(id)),\n );\n return tenants.find((tenant) => directIds.has(tenant.id))?.id ?? null;\n }\n\n /**\n * `GET /api/mobile/session` — bootstrap the app from a bearer token.\n * Throws 401 when the token is missing, unknown, expired, or revoked.\n */\n async bootstrap(\n authorizationHeader: string | null | undefined,\n ): Promise<MobileSessionBootstrap> {\n const context = await this.resolveSessionContext(authorizationHeader);\n const userId = context.user.id as string;\n const tenants = await this.listTenantOptions(userId);\n const activeTenant =\n tenants.find((tenant) => tenant.id === context.tenantId) ?? null;\n\n const extras = this.options.buildExtras\n ? await this.options.buildExtras({\n session: context,\n permissions: context.permissions,\n tenants,\n activeTenant,\n })\n : undefined;\n\n return {\n user: this.toUserSummary(userId, context.user.email ?? ''),\n activeTenant,\n tenants,\n ...(extras ? { extras } : {}),\n };\n }\n\n /**\n * Resolve a bearer header into a full {@link SessionContext} (user,\n * membership, resolved permissions, tenant). Throws {@link MobileAuthError}\n * with the 401 semantics the mobile client's re-auth flow expects.\n */\n async resolveSessionContext(\n authorizationHeader: string | null | undefined,\n ): Promise<SessionContext> {\n const token = readMobileBearerToken(authorizationHeader);\n if (!token) {\n throw new MobileAuthError(\n 401,\n 'missing_bearer_token',\n 'Missing mobile bearer token.',\n );\n }\n // System context: session loading resolves permissions across the\n // user's memberships, which strict tenancy interceptors reject outside\n // an explicit context. Request-scoped tenant entry belongs to the route\n // guard (`withSessionPermissionContext`), not here.\n const context = await runWithSystemContext(() =>\n this.sessionService.loadSessionContext(token),\n );\n if (!context?.user?.id) {\n throw new MobileAuthError(\n 401,\n 'invalid_bearer_token',\n 'Invalid or expired mobile bearer token.',\n );\n }\n return context;\n }\n\n /**\n * `DELETE /api/mobile/session` — revoke the bearer session. Idempotent:\n * a missing or unknown token reports `destroyed: false` with 200.\n */\n async logout(\n authorizationHeader: string | null | undefined,\n ): Promise<MobileLogoutResult> {\n const token = readMobileBearerToken(authorizationHeader);\n if (!token) return { ok: true, destroyed: false };\n const destroyed = await this.sessionService.destroySession(token);\n return { ok: true, destroyed };\n }\n\n /**\n * The user's selectable tenants: direct ACTIVE memberships plus — when\n * `includeInheritedTenants` is on (default) — descendant tenants reachable\n * through an ACTIVE membership whose role has `inheritsToDescendants:\n * true` (#1867). Selection mirrors the permission resolver: the NEAREST\n * flagged ancestor membership labels an inherited option, unflagged or\n * inactive ancestors neither confer nor block, and ANY direct membership\n * row on a tenant pins it (active → its own role; inactive → excluded,\n * since a pinned inactive row resolves to the empty permission set).\n *\n * This list is informational — per-request authorization always re-runs\n * through `PermissionResolver`, which additionally fail-closes on\n * malformed hierarchy paths.\n */\n async listTenantOptions(userId: string): Promise<MobileTenantOption[]> {\n // One system-context entry for the whole cross-tenant computation (session\n // resolution and tenant enumeration span tenants, so a single-tenant\n // context would be wrong); the nested reads below inherit it.\n return runWithSystemContext(() => this.buildTenantOptions(userId));\n }\n\n private async buildTenantOptions(\n userId: string,\n ): Promise<MobileTenantOption[]> {\n const { memberships, tenantById, roleById } =\n await this.loadTenantOptionSources(userId);\n\n const active = memberships.filter(\n (membership) => membership.isActive() && membership.tenantId,\n );\n // Pin from ALL rows (any status), NOT just active ones: a direct\n // membership row always pins its tenant in the resolver, and an INACTIVE\n // direct row pins to the empty set. So an inactive direct row on a\n // descendant must EXCLUDE that descendant from the inherited list (below)\n // — narrowing this to active-only would wrongly surface it as inherited.\n const pinnedTenantIds = new Set(\n memberships\n .map((membership) => membership.tenantId)\n .filter((id): id is string => Boolean(id)),\n );\n\n const options: MobileTenantOption[] = [];\n for (const membership of active) {\n const tenant = tenantById.get(membership.tenantId as string);\n if (!tenant?.id) continue;\n const role = membership.roleId\n ? roleById.get(membership.roleId)\n : undefined;\n options.push(this.toTenantOption(tenant, role));\n }\n\n if (this.options.includeInheritedTenants !== false) {\n const activeByTenant = new Map<string, Membership>();\n for (const membership of active) {\n activeByTenant.set(membership.tenantId as string, membership);\n }\n const isFlagged = (membership: Membership | undefined): boolean => {\n const role = membership?.roleId\n ? roleById.get(membership.roleId)\n : undefined;\n return role?.inheritsToDescendants === true;\n };\n\n // Batch the descendant reads across all flagged memberships instead of\n // awaiting one per membership in series.\n const flaggedTenantIds = active\n .filter(isFlagged)\n .map((membership) => membership.tenantId as string);\n const descendantLists = await Promise.all(\n flaggedTenantIds.map((tenantId) =>\n this.tenantCollection.getDescendants(tenantId),\n ),\n );\n\n const candidates = new Map<string, Tenant>();\n for (const descendants of descendantLists) {\n for (const descendant of descendants) {\n if (!descendant.id) continue;\n if (pinnedTenantIds.has(descendant.id)) continue;\n if (!candidates.has(descendant.id)) {\n candidates.set(descendant.id, descendant);\n }\n }\n }\n\n for (const descendant of candidates.values()) {\n // Nearest ancestor first (getAncestorIds is root → parent order).\n const ancestorIds = descendant.getAncestorIds();\n for (let i = ancestorIds.length - 1; i >= 0; i -= 1) {\n const ancestorMembership = activeByTenant.get(ancestorIds[i]);\n if (!isFlagged(ancestorMembership)) continue;\n const role = roleById.get(ancestorMembership?.roleId as string);\n options.push(this.toTenantOption(descendant, role));\n break;\n }\n }\n }\n\n return options.sort(\n (a, b) =>\n (a.name ?? '').localeCompare(b.name ?? '', undefined, {\n sensitivity: 'base',\n }) || a.id.localeCompare(b.id),\n );\n }\n\n private async loadTenantOptionSources(\n userId: string,\n ): Promise<TenantOptionSources> {\n const memberships = await this.membershipCollection.findByUser(userId);\n const active = memberships.filter(\n (membership) => membership.isActive() && membership.tenantId,\n );\n\n const tenantIds = [\n ...new Set(active.map((membership) => membership.tenantId as string)),\n ];\n const roleIds = [\n ...new Set(\n active\n .map((membership) => membership.roleId)\n .filter((id): id is string => Boolean(id)),\n ),\n ];\n\n const [tenants, roles] = await Promise.all([\n tenantIds.length > 0\n ? this.tenantCollection.listByIds(tenantIds)\n : Promise.resolve([]),\n roleIds.length > 0\n ? this.roleCollection.listByIds(roleIds)\n : Promise.resolve([]),\n ]);\n\n return {\n memberships,\n tenantById: new Map(\n tenants\n .filter((tenant): tenant is Tenant => Boolean(tenant?.id))\n .map((tenant) => [tenant.id as string, tenant]),\n ),\n roleById: new Map(\n roles\n .filter((role): role is Role => Boolean(role?.id))\n .map((role) => [role.id as string, role]),\n ),\n };\n }\n\n private toTenantOption(tenant: Tenant, role: Role | undefined) {\n return {\n id: tenant.id as string,\n name: tenant.name ?? '',\n slug: tenant.slug ?? '',\n roleSlug: role?.slug ?? '',\n roleLabel: role?.name || role?.slug || '',\n } satisfies MobileTenantOption;\n }\n\n private toUserSummary(id: string, email: string): MobileUserSummary {\n return { id, email, label: email || id };\n }\n}\n","/**\n * Request-scoped session permission context helpers\n * @packageDocumentation\n */\n\nimport { AsyncLocalStorage } from 'node:async_hooks';\nimport { getPackageConfig } from '@happyvertical/smrt-config';\nimport type { SmrtClassOptions } from '@happyvertical/smrt-core';\nimport type { DatabaseInterface } from '@happyvertical/sql';\nimport type { Membership } from '../models/Membership.js';\nimport { PermissionResolver } from './PermissionResolver.js';\nimport {\n type SessionContext,\n SessionService,\n type SessionServiceOptions,\n} from './SessionService.js';\n\ninterface QueryableDatabase extends DatabaseInterface {\n beginTransaction?: () => Promise<TransactionDatabase>;\n url: string;\n}\n\ninterface TransactionDatabase extends QueryableDatabase {\n commit: () => Promise<void>;\n isActive: () => boolean;\n rollback: () => Promise<void>;\n}\n\nexport interface SessionPermissionRuntimeContext {\n database?: QueryableDatabase;\n permissions: string[];\n permissionSet: Set<string>;\n membership: SessionContext['membership'];\n postgresRls: boolean;\n session: SessionContext | null;\n sessionId: string | null;\n superAdminBypass: boolean;\n systemContext: boolean;\n tenantId: string | null;\n user: SessionContext['user'] | null;\n userId: string | null;\n}\n\nexport interface SessionPermissionRuntimeOptions extends SmrtClassOptions {\n enterTenantContext?: boolean;\n postgresRls?: boolean;\n sessionId?: string | null;\n sessionService?: SessionService;\n superAdminBypass?: boolean;\n systemContext?: boolean;\n}\n\n/**\n * Options for {@link withPrincipalPermissionContext}.\n *\n * Where {@link SessionPermissionRuntimeOptions} sources the principal from a\n * loaded session, this sources it directly from a `(userId, tenantId)` pair —\n * used to run work AS a bound principal (e.g. an agent persona's `runAsUserId`)\n * with that principal's *live* resolved permissions.\n */\nexport interface PrincipalPermissionRuntimeOptions extends SmrtClassOptions {\n /** Enter tenant context so tenant auto-filtering applies on every adapter. */\n enterTenantContext?: boolean;\n /**\n * Raw membership row for the `(userId, tenantId)` pair, forwarded to\n * {@link PermissionResolver.resolvePermissions}. Only consulted when\n * `permissions` is not supplied. Pass `null` to assert \"no direct membership\"\n * (enabling opt-in ancestor inheritance); leave `undefined` to let the\n * resolver look it up.\n */\n membership?: Membership | null;\n /**\n * Pre-resolved permission slugs. When omitted, the principal's permissions\n * are resolved LIVE via {@link PermissionResolver.resolvePermissions} so role\n * changes reflect on the next call.\n */\n permissions?: string[];\n /** Opt into Postgres RLS transaction wrapping (defaults to package config). */\n postgresRls?: boolean;\n /** Reuse an initialized resolver instead of creating one per call. */\n resolver?: PermissionResolver;\n /** Tenant the principal acts within. `null` resolves to no permissions. */\n tenantId: string | null;\n /** The user whose live permissions bound this execution. */\n userId: string;\n}\n\n/**\n * The controls shared by both permission-context entry points that influence\n * transaction wrapping and tenant-context entry.\n */\ninterface PermissionRuntimeControls {\n enterTenantContext?: boolean;\n postgresRls?: boolean;\n systemContext?: boolean;\n}\n\ntype PermissionRuntimeDatabaseConfig =\n | SmrtClassOptions['db']\n | SmrtClassOptions['persistence'];\n\ndeclare global {\n // eslint-disable-next-line no-var\n var __smrtGetRequestPermissionContext:\n | (() => SessionPermissionRuntimeContext | undefined)\n | undefined;\n // eslint-disable-next-line no-var\n var __smrtGetRequestScopedDatabase:\n | (() => QueryableDatabase | undefined)\n | undefined;\n}\n\n// globalThis key for the shared AsyncLocalStorage. Bundler pipelines can\n// evaluate this module more than once from a single copy on disk; a\n// module-local storage would strand context entered through one instance\n// (the `globalThis.__smrtGetRequestPermissionContext` getter below only helps\n// readers reach the FIRST instance's storage — a second instance running\n// `.run()` would still be invisible). Symbol.for() resolves identically in\n// every instance, so all of them share the one storage.\nconst PERMISSION_STORAGE_KEY = Symbol.for(\n 'smrt:session-permission-context-storage',\n);\n\nfunction resolvePermissionContextStorage(): AsyncLocalStorage<SessionPermissionRuntimeContext> {\n const root = globalThis as typeof globalThis & {\n [PERMISSION_STORAGE_KEY]?: AsyncLocalStorage<SessionPermissionRuntimeContext>;\n };\n root[PERMISSION_STORAGE_KEY] ??=\n new AsyncLocalStorage<SessionPermissionRuntimeContext>();\n return root[PERMISSION_STORAGE_KEY];\n}\n\nconst requestPermissionContextStorage = resolvePermissionContextStorage();\n\nexport function getCurrentSessionPermissionContext():\n | SessionPermissionRuntimeContext\n | undefined {\n return requestPermissionContextStorage.getStore();\n}\n\nexport function getRequestScopedDatabase(): QueryableDatabase | undefined {\n return getCurrentSessionPermissionContext()?.database;\n}\n\nglobalThis.__smrtGetRequestPermissionContext ??=\n getCurrentSessionPermissionContext;\nglobalThis.__smrtGetRequestScopedDatabase ??= getRequestScopedDatabase;\n\nfunction isProbablyPostgres(\n configDb: SmrtClassOptions['db'],\n database: QueryableDatabase,\n): boolean {\n if (\n configDb &&\n typeof configDb === 'object' &&\n !('query' in configDb) &&\n 'type' in configDb &&\n configDb.type === 'postgres'\n ) {\n return true;\n }\n\n if (typeof database.url === 'string' && database.url.startsWith('postgres')) {\n return true;\n }\n\n return (database.constructor?.name || '').toLowerCase().includes('postgres');\n}\n\nasync function setPostgresSessionVariables(\n database: QueryableDatabase,\n context: SessionPermissionRuntimeContext,\n): Promise<void> {\n await database.query(\n \"SELECT set_config('smrt.tenant_id', $1, true)\",\n context.tenantId ?? '',\n );\n await database.query(\n \"SELECT set_config('smrt.user_id', $1, true)\",\n context.userId ?? '',\n );\n await database.query(\n \"SELECT set_config('smrt.session_id', $1, true)\",\n context.sessionId ?? '',\n );\n await database.query(\n \"SELECT set_config('smrt.permissions', $1, true)\",\n JSON.stringify(context.permissions),\n );\n await database.query(\n \"SELECT set_config('smrt.super_admin_bypass', $1, true)\",\n context.superAdminBypass ? 'true' : 'false',\n );\n await database.query(\n \"SELECT set_config('smrt.system_context', $1, true)\",\n context.systemContext ? 'true' : 'false',\n );\n}\n\nfunction isModuleNotFoundError(error: unknown): boolean {\n return (\n error instanceof Error &&\n ((error as NodeJS.ErrnoException).code === 'ERR_MODULE_NOT_FOUND' ||\n /Cannot find package '@happyvertical\\/smrt-tenancy'/.test(error.message))\n );\n}\n\nasync function runWithOptionalTenantContext<T>(\n controls: PermissionRuntimeControls,\n context: SessionPermissionRuntimeContext,\n fn: () => Promise<T>,\n): Promise<T> {\n if (controls.systemContext) {\n try {\n const tenancy = await import('@happyvertical/smrt-tenancy');\n return await tenancy.withSystemContext(fn);\n } catch (error) {\n if (isModuleNotFoundError(error)) {\n return await fn();\n }\n throw error;\n }\n }\n\n if (!controls.enterTenantContext || !context.tenantId) {\n return await fn();\n }\n\n try {\n const tenancy = await import('@happyvertical/smrt-tenancy');\n return await tenancy.withTenant(\n {\n database: context.database,\n permissions: context.permissionSet,\n superAdminBypass: context.superAdminBypass,\n tenantId: context.tenantId,\n user: context.user ?? undefined,\n userId: context.userId ?? undefined,\n },\n fn,\n );\n } catch (error) {\n if (isModuleNotFoundError(error)) {\n return await fn();\n }\n throw error;\n }\n}\n\n/**\n * Shared runtime: given an already-resolved principal `spec` and the base\n * database, optionally open a Postgres RLS transaction, publish the principal\n * onto the DB session (`set_config(..., is_local=true)`), and run `fn` inside\n * the request-scoped ALS store (and optional tenant context).\n *\n * Both {@link withSessionPermissionContext} (principal from a loaded session)\n * and {@link withPrincipalPermissionContext} (principal from a `userId` +\n * live-resolved permissions) funnel through here, so the transaction lifecycle\n * and the exact DB-session publication stay identical no matter which door the\n * principal entered through.\n */\nasync function runWithinPermissionRuntime<T>(\n controls: PermissionRuntimeControls,\n configuredDb: PermissionRuntimeDatabaseConfig,\n baseDatabase: QueryableDatabase,\n spec: {\n permissions: string[];\n membership: SessionContext['membership'];\n session: SessionContext | null;\n sessionId: string | null;\n superAdminBypass: boolean;\n systemContext: boolean;\n tenantId: string | null;\n user: SessionContext['user'] | null;\n userId: string | null;\n },\n fn: (context: SessionPermissionRuntimeContext) => Promise<T>,\n): Promise<T> {\n const config = getPackageConfig<{\n permissions?: { postgres?: { enabled?: boolean } };\n }>('users', {});\n const usePostgresRls =\n (controls.postgresRls ?? config.permissions?.postgres?.enabled ?? false) &&\n isProbablyPostgres(configuredDb, baseDatabase);\n\n let transaction: TransactionDatabase | undefined;\n let rolledBack = false;\n if (usePostgresRls) {\n if (!baseDatabase.beginTransaction) {\n throw new Error(\n 'Postgres RLS requires a database adapter that supports beginTransaction().',\n );\n }\n transaction = await baseDatabase.beginTransaction();\n }\n\n const runtimeContext: SessionPermissionRuntimeContext = {\n database: transaction ?? baseDatabase,\n permissions: spec.permissions,\n permissionSet: new Set(spec.permissions),\n membership: spec.membership,\n postgresRls: usePostgresRls,\n session: spec.session,\n sessionId: spec.sessionId,\n superAdminBypass: spec.superAdminBypass,\n systemContext: spec.systemContext,\n tenantId: spec.tenantId,\n user: spec.user,\n userId: spec.userId,\n };\n\n try {\n if (transaction) {\n await setPostgresSessionVariables(transaction, runtimeContext);\n }\n\n return await requestPermissionContextStorage.run(runtimeContext, async () =>\n runWithOptionalTenantContext(controls, runtimeContext, () =>\n fn(runtimeContext),\n ),\n );\n } catch (error) {\n if (transaction) {\n const active = transaction.isActive ? transaction.isActive() : true;\n if (active) {\n rolledBack = true;\n await transaction.rollback();\n }\n }\n throw error;\n } finally {\n if (transaction && !rolledBack) {\n const active = transaction.isActive ? transaction.isActive() : true;\n if (active) {\n await transaction.commit();\n }\n }\n }\n}\n\nexport async function withSessionPermissionContext<T>(\n options: SessionPermissionRuntimeOptions,\n fn: (context: SessionPermissionRuntimeContext) => Promise<T>,\n): Promise<T> {\n const {\n enterTenantContext: _enterTenantContext,\n postgresRls: _postgresRls,\n sessionId: _sessionId,\n sessionService: _sessionService,\n superAdminBypass: _superAdminBypass,\n systemContext: _systemContext,\n ...sessionServiceOptions\n } = options;\n const sessionService =\n options.sessionService ??\n (await SessionService.create({\n ...sessionServiceOptions,\n } as SessionServiceOptions));\n const configuredDb =\n (sessionServiceOptions as SessionServiceOptions).db ??\n (sessionServiceOptions as SessionServiceOptions).persistence;\n const session =\n options.sessionId === undefined || options.sessionId === null\n ? null\n : await sessionService.loadSessionContext(options.sessionId);\n const baseDatabase = sessionService.getDatabase() as QueryableDatabase;\n\n return runWithinPermissionRuntime(\n {\n enterTenantContext: options.enterTenantContext,\n postgresRls: options.postgresRls,\n systemContext: options.systemContext,\n },\n configuredDb,\n baseDatabase,\n {\n permissions: session?.permissions ?? [],\n membership: session?.membership ?? null,\n session,\n sessionId: session?.sessionId ?? null,\n superAdminBypass: options.superAdminBypass ?? false,\n systemContext: options.systemContext ?? false,\n tenantId: session?.tenantId ?? null,\n user: session?.user ?? null,\n userId: session?.user.id ?? null,\n },\n fn,\n );\n}\n\n/**\n * Run `fn` AS a bound principal — a `(userId, tenantId)` pair rather than a\n * session — inside a session-permission context carrying that principal's\n * **live** resolved permissions.\n *\n * This is the door-agnostic mechanism behind \"execute as principal\": it does\n * NOT introduce a new authorization layer. It resolves the principal's\n * permissions through the standard {@link PermissionResolver} cascade (unless\n * the caller passes a pre-resolved set) and publishes `(smrt.user_id,\n * smrt.tenant_id, smrt.permissions[])` onto the DB session exactly like\n * {@link withSessionPermissionContext}, so the manifest-derived Postgres RLS\n * policies bound every query on that session per-`(table, action)`.\n *\n * A principal run never bypasses: `super_admin_bypass` and `system_context`\n * are always published as `false`. Resolution runs on the base connection\n * BEFORE any RLS transaction opens (identical ordering to session loading), so\n * the trusted cascade is never bounded by the not-yet-published principal.\n *\n * With RLS off (SQLite/dev) the same permission set is still resolved and\n * carried, but the DB has no per-operation teeth — callers relying on this on a\n * non-Postgres adapter must additionally assert the catalog permission for the\n * `(collection, action)` at their tool/exec seam (see\n * `assertOperationPermission`).\n */\nexport async function withPrincipalPermissionContext<T>(\n options: PrincipalPermissionRuntimeOptions,\n fn: (context: SessionPermissionRuntimeContext) => Promise<T>,\n): Promise<T> {\n const {\n enterTenantContext: _enterTenantContext,\n membership,\n permissions: providedPermissions,\n postgresRls: _postgresRls,\n resolver: providedResolver,\n tenantId,\n userId,\n ...serviceOptions\n } = options;\n\n // Reuse SessionService purely as the blessed database accessor — no session\n // is loaded on this path; the principal comes straight from (userId,\n // tenantId).\n const sessionService = await SessionService.create({\n ...serviceOptions,\n } as SessionServiceOptions);\n const configuredDb =\n (serviceOptions as SessionServiceOptions).db ??\n (serviceOptions as SessionServiceOptions).persistence;\n const baseDatabase = sessionService.getDatabase() as QueryableDatabase;\n\n // Resolve the bound principal's LIVE permission set unless the caller passed\n // one explicitly. Distinguish \"not provided\" (`undefined` → resolve live)\n // from \"explicitly empty\" (`[]` → run with ZERO permissions): a truthy test\n // would treat an intentional empty set as \"not provided\" and fail open. A\n // missing tenant likewise resolves to no permissions (fail closed).\n let permissions: string[];\n if (providedPermissions !== undefined) {\n permissions = providedPermissions;\n } else if (tenantId) {\n const resolver =\n providedResolver ??\n (await PermissionResolver.create({\n ...serviceOptions,\n } as SmrtClassOptions));\n const resolved = await resolver.resolvePermissions(\n userId,\n tenantId,\n membership === undefined ? {} : { membership },\n );\n permissions = Array.from(resolved.permissions);\n } else {\n permissions = [];\n }\n\n return runWithinPermissionRuntime(\n {\n enterTenantContext: options.enterTenantContext,\n postgresRls: options.postgresRls,\n systemContext: false,\n },\n configuredDb,\n baseDatabase,\n {\n permissions,\n membership: null,\n session: null,\n sessionId: null,\n superAdminBypass: false,\n systemContext: false,\n tenantId: tenantId ?? null,\n user: null,\n userId,\n },\n fn,\n );\n}\n","/**\n * Reusable operation-level permission guard for hand-written server surfaces.\n * @packageDocumentation\n */\n\nimport type { SmrtClassOptions } from '@happyvertical/smrt-core';\nimport {\n deriveOperationPermissionSlug,\n type OperationPermissionCollectionInput,\n type PermissionCatalog,\n PermissionCatalogService,\n} from './PermissionCatalogService.js';\nimport {\n type PermissionResolutionOptions,\n PermissionResolver,\n} from './PermissionResolver.js';\nimport { getCurrentSessionPermissionContext } from './SessionPermissionContext.js';\n\nexport type OperationPermissionDenyReason =\n | 'invalid_operation'\n | 'missing_principal'\n | 'resolution_error'\n | 'unknown_permission'\n | 'permission_denied';\n\nexport type OperationPermissionAllowReason =\n | 'permission_granted'\n | 'super_admin_bypass'\n | 'system_context_bypass';\n\nexport interface OperationPermissionDecision {\n allowed: boolean;\n permission: string | null;\n reason: OperationPermissionAllowReason | OperationPermissionDenyReason;\n error?: unknown;\n}\n\nexport interface OperationPermissionOptions\n extends SmrtClassOptions,\n PermissionResolutionOptions {\n /**\n * Collection slug, model class, model instance, or collection instance.\n */\n collection: OperationPermissionCollectionInput;\n /**\n * Operation action. `list` and `get` normalize to the catalog's `.read`.\n */\n action: string;\n /**\n * Default true. When false, a super-admin tenant context must still hold the\n * resolved permission.\n */\n allowSuperAdminBypass?: boolean;\n /**\n * Default true. System context represents trusted server-side execution.\n */\n allowSystemContextBypass?: boolean;\n catalog?: PermissionCatalog;\n onDeny?: 'return' | 'throw';\n resolver?: PermissionResolver;\n tenantId?: string | null;\n userId?: string | null;\n /**\n * When provided, authorize against THIS exact permission set instead of\n * re-resolving live RBAC. Pass the published principal set\n * (`context.permissionSet`) so an RLS-off catalog gate enforces the same\n * snapshot authority the RLS-on session published — keeping the authority\n * bound adapter-independent. Without it a mid-run role grant, or a reduced\n * pre-resolved `permissions` set, could let SQLite/dev allow an operation the\n * Postgres RLS path for the same principal context would deny.\n */\n permissionSet?: ReadonlySet<string> | readonly string[];\n}\n\nexport class OperationPermissionError extends Error {\n readonly decision: OperationPermissionDecision;\n readonly permission: string | null;\n readonly status = 403;\n\n constructor(decision: OperationPermissionDecision) {\n super(\n decision.permission\n ? `Permission denied for '${decision.permission}'.`\n : 'Permission denied.',\n );\n this.name = 'OperationPermissionError';\n this.decision = decision;\n this.permission = decision.permission;\n }\n}\n\ninterface RuntimeBypassContext {\n superAdminBypass: boolean;\n systemContext: boolean;\n}\n\nfunction isModuleNotFoundError(error: unknown): boolean {\n return (\n error instanceof Error &&\n ((error as NodeJS.ErrnoException).code === 'ERR_MODULE_NOT_FOUND' ||\n /Cannot find package '@happyvertical\\/smrt-tenancy'/.test(error.message))\n );\n}\n\nasync function readTenancyBypassContext(): Promise<RuntimeBypassContext> {\n try {\n const tenancy = await import('@happyvertical/smrt-tenancy');\n return {\n superAdminBypass: tenancy.isSuperAdminBypass(),\n systemContext: tenancy.isSystemContext(),\n };\n } catch (error) {\n if (!isModuleNotFoundError(error)) {\n throw error;\n }\n\n return {\n superAdminBypass: false,\n systemContext: false,\n };\n }\n}\n\nfunction deny(\n permission: string | null,\n reason: OperationPermissionDenyReason,\n error?: unknown,\n): OperationPermissionDecision {\n return {\n allowed: false,\n error,\n permission,\n reason,\n };\n}\n\nfunction allow(\n permission: string,\n reason: OperationPermissionAllowReason,\n): OperationPermissionDecision {\n return {\n allowed: true,\n permission,\n reason,\n };\n}\n\nfunction hasCatalogSlug(\n catalog: PermissionCatalog,\n permission: string,\n): boolean {\n return catalog.permissions.some(\n (definition) => definition.slug === permission,\n );\n}\n\nexport async function checkOperationPermission(\n options: OperationPermissionOptions,\n): Promise<OperationPermissionDecision> {\n let permission: string;\n try {\n permission = deriveOperationPermissionSlug(\n options.collection,\n options.action,\n );\n } catch (error) {\n return deny(null, 'invalid_operation', error);\n }\n\n const catalog =\n options.catalog ?? PermissionCatalogService.create(options).getCatalog();\n if (!hasCatalogSlug(catalog, permission)) {\n return deny(permission, 'unknown_permission');\n }\n\n const sessionContext = getCurrentSessionPermissionContext();\n let tenancyContext: RuntimeBypassContext;\n try {\n tenancyContext = await readTenancyBypassContext();\n } catch (error) {\n return deny(permission, 'resolution_error', error);\n }\n const systemContext =\n sessionContext?.systemContext === true || tenancyContext.systemContext;\n const superAdminBypass =\n sessionContext?.superAdminBypass === true ||\n tenancyContext.superAdminBypass;\n\n if (systemContext && options.allowSystemContextBypass !== false) {\n return allow(permission, 'system_context_bypass');\n }\n\n if (superAdminBypass && options.allowSuperAdminBypass !== false) {\n return allow(permission, 'super_admin_bypass');\n }\n\n const userId = options.userId ?? sessionContext?.userId ?? null;\n const tenantId = options.tenantId ?? sessionContext?.tenantId ?? null;\n if (!userId || !tenantId) {\n return deny(permission, 'missing_principal');\n }\n\n // Explicit set wins over a live re-resolve: authorize against the exact\n // published principal set so the gate matches what an RLS session enforces\n // for the same context (adapter-independent, snapshot-consistent).\n if (options.permissionSet !== undefined) {\n const granted = Array.isArray(options.permissionSet)\n ? options.permissionSet.includes(permission)\n : (options.permissionSet as ReadonlySet<string>).has(permission);\n return granted\n ? allow(permission, 'permission_granted')\n : deny(permission, 'permission_denied');\n }\n\n try {\n const resolver =\n options.resolver ?? (await PermissionResolver.create(options));\n const hasPermission = await resolver.hasPermission(\n userId,\n tenantId,\n permission,\n { membership: options.membership },\n );\n return hasPermission\n ? allow(permission, 'permission_granted')\n : deny(permission, 'permission_denied');\n } catch (error) {\n return deny(permission, 'resolution_error', error);\n }\n}\n\nexport async function hasOperationPermission(\n options: OperationPermissionOptions,\n): Promise<boolean> {\n return (await checkOperationPermission(options)).allowed;\n}\n\nexport async function assertOperationPermission(\n options: OperationPermissionOptions,\n): Promise<OperationPermissionDecision> {\n const decision = await checkOperationPermission(options);\n if (decision.allowed || options.onDeny === 'return') {\n return decision;\n }\n\n throw new OperationPermissionError(decision);\n}\n","/**\n * TerminalAuthService — device-code grant for terminal/CLI logins.\n *\n * Flow:\n * 1. CLI calls `createRequest(origin)` → user code (short, displayed) + device\n * code (long, kept secret) + verification URL.\n * 2. User opens the verification URL in a browser, signs in normally, and\n * POSTs the user code via `approveRequest({ user, tenantId, userCode })`.\n * 3. CLI polls `exchangeDeviceCode(deviceCode)`; once approved, gets back the\n * bearer session id (an opaque token usable as `Authorization: Bearer …`).\n * 4. Server-side request handlers resolve incoming bearer tokens via\n * `loadBearerSession(token)`, and revoke them via `destroyBearerSession`.\n *\n * The service is framework-agnostic: it does not set cookies, parse Request\n * objects, or render pages. SvelteKit glue lives in the package's\n * `./sveltekit` subpath.\n *\n * @packageDocumentation\n */\n\nimport { createHash, randomBytes } from 'node:crypto';\nimport type { SmrtClassOptions } from '@happyvertical/smrt-core';\nimport { UsersCliAuthRequestCollection } from '../collections/CliAuthRequestCollection.js';\nimport type {\n CliAuthRequestStatus,\n UsersCliAuthRequest,\n} from '../models/CliAuthRequest.js';\nimport type { User } from '../models/User.js';\nimport { type SessionContext, SessionService } from './SessionService.js';\n\n/** Default polling interval the CLI should honour while waiting for approval. */\nexport const DEFAULT_CLI_AUTH_POLL_INTERVAL_SECONDS = 2;\n/** Default lifetime of a pending request (10 minutes). */\nexport const DEFAULT_CLI_AUTH_REQUEST_TTL_SECONDS = 10 * 60;\n/** Default lifetime of the session minted on approval (30 days). */\nexport const DEFAULT_CLI_SESSION_TTL_SECONDS = 30 * 24 * 60 * 60;\n/** Default cap on consecutive failed approve attempts per user before lockout. */\nexport const DEFAULT_CLI_AUTH_MAX_APPROVE_ATTEMPTS = 5;\n/** Default sliding window for counting failed approve attempts (5 minutes). */\nexport const DEFAULT_CLI_AUTH_APPROVE_ATTEMPT_WINDOW_SECONDS = 5 * 60;\n\n/**\n * Options for {@link TerminalAuthService}.\n */\nexport interface TerminalAuthServiceOptions extends SmrtClassOptions {\n /**\n * Cookie name passed through to {@link SessionService}. Should match the\n * site's normal session cookie name so the same SessionService can\n * resolve both browser cookies and CLI bearer tokens.\n */\n sessionCookieName?: string;\n /**\n * Prefix prepended to randomly generated user codes — purely for human\n * recognition (\"WG-1A2B3C4D\"). Defaults to no prefix.\n */\n userCodePrefix?: string;\n /** TTL of a pending request, in seconds. */\n requestTtlSeconds?: number;\n /** TTL of the session minted on approval, in seconds. */\n sessionTtlSeconds?: number;\n /** Polling interval hint returned to clients. */\n pollIntervalSeconds?: number;\n /**\n * Path on the site's origin where the approval page is mounted. Used to\n * build the verification URL returned to the CLI. Defaults to\n * `/terminal-login`.\n */\n verificationPath?: string;\n /** Whether the underlying SessionService should auto-extend on access. */\n sessionAutoExtend?: boolean;\n /**\n * Maximum consecutive failed approve attempts allowed per user inside the\n * sliding window before the user is locked out from approving any further\n * codes. Defaults to {@link DEFAULT_CLI_AUTH_MAX_APPROVE_ATTEMPTS}.\n *\n * User codes are only 32 bits of entropy; without this throttle, an\n * authenticated attacker could brute-force pending codes and hijack\n * another user's CLI session by approving it themselves.\n */\n maxApproveAttempts?: number;\n /**\n * Sliding window (seconds) over which {@link maxApproveAttempts} counts.\n * Defaults to {@link DEFAULT_CLI_AUTH_APPROVE_ATTEMPT_WINDOW_SECONDS}.\n */\n approveAttemptWindowSeconds?: number;\n}\n\n/**\n * What `createRequest` returns to the CLI.\n */\nexport interface CliAuthStartResult {\n deviceCode: string;\n expiresAt: string;\n interval: number;\n userCode: string;\n verificationUrl: string;\n}\n\n/**\n * What `exchangeDeviceCode` returns to the polling CLI.\n */\nexport type CliAuthTokenResult =\n | { status: 'pending'; expiresAt: string; interval: number }\n | { status: 'expired' }\n | {\n status: 'approved';\n accessToken: string;\n expiresIn: number;\n tokenType: 'Bearer';\n };\n\n/**\n * Inputs for `approveRequest`. The caller is responsible for authenticating\n * the user via its existing browser session before calling this.\n */\nexport interface ApproveCliAuthRequestInput {\n userCode: string;\n user: Pick<User, 'id' | 'email'>;\n tenantId: string | null | undefined;\n ipAddress?: string;\n userAgent?: string;\n}\n\nfunction base64url(bytes: Buffer): string {\n return bytes.toString('base64url');\n}\n\nfunction hashDeviceCode(deviceCode: string): string {\n return createHash('sha256').update(deviceCode).digest('hex');\n}\n\nfunction isExpired(request: UsersCliAuthRequest): boolean {\n return new Date(request.expiresAt).getTime() <= Date.now();\n}\n\n/**\n * High-level orchestration of the terminal device-code flow.\n */\nexport class TerminalAuthService {\n private readonly options: TerminalAuthServiceOptions;\n private readonly userCodePrefix: string;\n private readonly requestTtlSeconds: number;\n private readonly sessionTtlSeconds: number;\n private readonly pollIntervalSeconds: number;\n private readonly verificationPath: string;\n private readonly maxApproveAttempts: number;\n private readonly approveAttemptWindowMs: number;\n private readonly failedApprovesByUser = new Map<\n string,\n { count: number; firstAttemptAt: number }\n >();\n private requestCollection!: UsersCliAuthRequestCollection;\n private sessionService!: SessionService;\n\n constructor(options: TerminalAuthServiceOptions) {\n this.options = options;\n // Lookups uppercase the submitted code before querying, so the stored\n // form must also be uppercase — normalize once here so consumers can\n // pass any case.\n this.userCodePrefix = (options.userCodePrefix ?? '').trim().toUpperCase();\n this.requestTtlSeconds =\n options.requestTtlSeconds ?? DEFAULT_CLI_AUTH_REQUEST_TTL_SECONDS;\n this.sessionTtlSeconds =\n options.sessionTtlSeconds ?? DEFAULT_CLI_SESSION_TTL_SECONDS;\n this.pollIntervalSeconds =\n options.pollIntervalSeconds ?? DEFAULT_CLI_AUTH_POLL_INTERVAL_SECONDS;\n this.maxApproveAttempts =\n options.maxApproveAttempts ?? DEFAULT_CLI_AUTH_MAX_APPROVE_ATTEMPTS;\n this.approveAttemptWindowMs =\n (options.approveAttemptWindowSeconds ??\n DEFAULT_CLI_AUTH_APPROVE_ATTEMPT_WINDOW_SECONDS) * 1000;\n const verificationPath = options.verificationPath ?? '/terminal-login';\n this.verificationPath = verificationPath.startsWith('/')\n ? verificationPath\n : `/${verificationPath}`;\n }\n\n async initialize(): Promise<void> {\n this.requestCollection = await UsersCliAuthRequestCollection.create(\n this.options,\n );\n this.sessionService = await SessionService.create({\n ...this.options,\n autoExtend: this.options.sessionAutoExtend ?? true,\n cookieName: this.options.sessionCookieName,\n defaultTTL: this.sessionTtlSeconds,\n });\n }\n\n static async create(\n options: TerminalAuthServiceOptions,\n ): Promise<TerminalAuthService> {\n const service = new TerminalAuthService(options);\n await service.initialize();\n return service;\n }\n\n private makeUserCode(): string {\n const raw = randomBytes(6).toString('hex').toUpperCase().slice(0, 8);\n return this.userCodePrefix ? `${this.userCodePrefix}-${raw}` : raw;\n }\n\n /**\n * Generate a user code that is not currently in use by another *active*\n * (pending or approved-but-unclaimed) request. Collisions are vanishingly\n * rare (1 in 2^32 per attempt) but happen at scale; retrying keeps the\n * affected CLI from being locked out.\n */\n private async makeUniqueUserCode(maxAttempts = 5): Promise<string> {\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n const candidate = this.makeUserCode();\n const existing = await this.requestCollection.findByUserCode(candidate);\n if (!existing || existing.status === 'expired') {\n return candidate;\n }\n }\n throw new TerminalAuthError(\n 'Unable to generate a unique terminal user code; try again.',\n );\n }\n\n /**\n * Start a new request. Returns the device code the CLI keeps secret, the\n * user code the human types into the browser, and the verification URL to\n * open. The device code is stored only as a hash.\n */\n async createRequest(origin: string): Promise<CliAuthStartResult> {\n const trimmedOrigin = origin.replace(/\\/+$/u, '');\n const deviceCode = base64url(randomBytes(32));\n const userCode = await this.makeUniqueUserCode();\n const expiresAt = new Date(Date.now() + this.requestTtlSeconds * 1000);\n\n const request = await this.requestCollection.create({\n deviceCodeHash: hashDeviceCode(deviceCode),\n expiresAt,\n status: 'pending',\n userCode,\n });\n await request.save();\n\n return {\n deviceCode,\n expiresAt: expiresAt.toISOString(),\n interval: this.pollIntervalSeconds,\n userCode,\n verificationUrl: `${trimmedOrigin}${this.verificationPath}?code=${encodeURIComponent(userCode)}`,\n };\n }\n\n /**\n * Look up a request by user code. Performs lazy expiry: pending requests\n * past their TTL are flipped to `expired` and persisted.\n */\n async getRequestForUserCode(\n userCode: string,\n ): Promise<UsersCliAuthRequest | null> {\n const request = await this.requestCollection.findByUserCode(userCode);\n if (!request) return null;\n\n if (request.status === 'pending' && isExpired(request)) {\n request.status = 'expired';\n await request.save();\n }\n return request;\n }\n\n /**\n * Mark a pending request as approved and mint a bearer session bound to the\n * approving user. Idempotent: re-approving an already-approved request is a\n * no-op. Throws if the user/tenant are missing, the request is unknown, or\n * the request has expired.\n */\n async approveRequest(\n input: ApproveCliAuthRequestInput,\n ): Promise<UsersCliAuthRequest> {\n if (!input.user.id || !input.tenantId) {\n throw new TerminalAuthError(\n 'An authenticated tenant session is required.',\n );\n }\n\n this.assertApproveRateLimit(input.user.id);\n\n const request = await this.getRequestForUserCode(input.userCode);\n if (!request) {\n this.recordFailedApprove(input.user.id);\n throw new TerminalAuthError('Terminal login request not found.');\n }\n if (request.status === 'approved') {\n // Idempotent success — don't penalize a re-approval.\n return request;\n }\n if (request.status !== 'pending' || isExpired(request)) {\n request.status = 'expired';\n await request.save();\n this.recordFailedApprove(input.user.id);\n throw new TerminalAuthError('Terminal login request has expired.');\n }\n\n const sessionId = await this.sessionService.createSession(\n input.user.id,\n input.tenantId,\n {\n data: {\n approvedBy: input.user.email ?? input.user.id,\n kind: 'terminal',\n },\n ipAddress: input.ipAddress,\n ttl: this.sessionTtlSeconds,\n userAgent: input.userAgent,\n },\n );\n\n request.approvedAt = new Date();\n request.sessionId = sessionId;\n request.status = 'approved';\n request.tenantId = input.tenantId;\n request.userId = input.user.id;\n await request.save();\n this.failedApprovesByUser.delete(input.user.id);\n return request;\n }\n\n /**\n * Throw {@link TerminalAuthRateLimitError} if the user has exceeded the\n * configured failed-approve budget inside the current sliding window.\n */\n private assertApproveRateLimit(userId: string): void {\n const tracking = this.failedApprovesByUser.get(userId);\n if (!tracking) return;\n\n const elapsedMs = Date.now() - tracking.firstAttemptAt;\n if (elapsedMs >= this.approveAttemptWindowMs) {\n this.failedApprovesByUser.delete(userId);\n return;\n }\n\n if (tracking.count >= this.maxApproveAttempts) {\n const retryAfterSeconds = Math.max(\n 1,\n Math.ceil((this.approveAttemptWindowMs - elapsedMs) / 1000),\n );\n throw new TerminalAuthRateLimitError(\n 'Too many failed terminal-login attempts. Try again later.',\n retryAfterSeconds,\n );\n }\n }\n\n /** Increment the failed-approve counter for `userId`, starting the window if needed. */\n private recordFailedApprove(userId: string): void {\n const tracking = this.failedApprovesByUser.get(userId);\n if (!tracking) {\n this.failedApprovesByUser.set(userId, {\n count: 1,\n firstAttemptAt: Date.now(),\n });\n return;\n }\n tracking.count += 1;\n }\n\n /**\n * Exchange a device code (the secret the CLI keeps) for an access token\n * once the request has been approved. Returns `{ status: 'pending' }` while\n * the CLI should keep polling, and `{ status: 'expired' }` if the request\n * was never approved within its TTL.\n */\n async exchangeDeviceCode(deviceCode: string): Promise<CliAuthTokenResult> {\n const request = await this.requestCollection.findByDeviceCodeHash(\n hashDeviceCode(deviceCode),\n );\n if (!request) {\n return { status: 'expired' };\n }\n\n // Approved requests stay claimable even after the original request TTL —\n // the session has its own (longer) lifetime, and the CLI may poll just\n // after approval but after `expiresAt`. Check this before TTL expiry so\n // freshly minted sessions don't get orphaned.\n if (request.status === 'approved' && request.sessionId) {\n return {\n accessToken: request.sessionId,\n expiresIn: this.sessionTtlSeconds,\n status: 'approved',\n tokenType: 'Bearer',\n };\n }\n\n if (isExpired(request)) {\n if (request.status === 'pending') {\n request.status = 'expired';\n await request.save();\n }\n return { status: 'expired' };\n }\n\n if (request.status === 'pending') {\n return {\n expiresAt: new Date(request.expiresAt).toISOString(),\n interval: this.pollIntervalSeconds,\n status: 'pending',\n };\n }\n\n return { status: 'expired' };\n }\n\n /**\n * Resolve a bearer token issued by this service into a full session\n * context — load the user, permissions, and tenant just like the cookie\n * path does. Returns `null` if the token is unknown, expired, or revoked.\n */\n async loadBearerSession(token: string): Promise<SessionContext | null> {\n return this.sessionService.loadSessionContext(token);\n }\n\n /**\n * Revoke a bearer token (CLI logout). Returns true if a session was\n * actually revoked.\n */\n async destroyBearerSession(token: string): Promise<boolean> {\n return this.sessionService.destroySession(token);\n }\n\n /**\n * Delete expired pending requests (cleanup job).\n */\n async cleanupExpiredRequests(): Promise<number> {\n return this.requestCollection.deleteExpired();\n }\n\n /** The default polling interval clients should use. Exposed for tests. */\n get pollInterval(): number {\n return this.pollIntervalSeconds;\n }\n}\n\n/**\n * Error class for terminal auth failures the caller is expected to surface to\n * the user (vs. unexpected internal errors).\n */\nexport class TerminalAuthError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'TerminalAuthError';\n }\n}\n\n/**\n * Thrown when a user has exceeded the configured failed-approve budget. The\n * caller (SvelteKit handler, REST adapter, etc.) is expected to surface this\n * as HTTP 429 with `Retry-After: retryAfterSeconds`.\n */\nexport class TerminalAuthRateLimitError extends TerminalAuthError {\n readonly retryAfterSeconds: number;\n\n constructor(message: string, retryAfterSeconds: number) {\n super(message);\n this.name = 'TerminalAuthRateLimitError';\n this.retryAfterSeconds = retryAfterSeconds;\n }\n}\n\nexport type { CliAuthRequestStatus };\n"],"x_google_ignoreList":[30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55],"mappings":"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"}
|