@shipfox/api-auth 17.0.0 → 19.0.0

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.
Files changed (99) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +44 -0
  3. package/README.md +24 -9
  4. package/dist/core/administration.d.ts +15 -0
  5. package/dist/core/administration.d.ts.map +1 -1
  6. package/dist/core/administration.js +44 -2
  7. package/dist/core/administration.js.map +1 -1
  8. package/dist/core/auth.d.ts.map +1 -1
  9. package/dist/core/auth.js +5 -3
  10. package/dist/core/auth.js.map +1 -1
  11. package/dist/core/entities/agent-access.d.ts +75 -0
  12. package/dist/core/entities/agent-access.d.ts.map +1 -0
  13. package/dist/core/entities/agent-access.js +3 -0
  14. package/dist/core/entities/agent-access.js.map +1 -0
  15. package/dist/core/errors.d.ts +3 -0
  16. package/dist/core/errors.d.ts.map +1 -1
  17. package/dist/core/errors.js +6 -0
  18. package/dist/core/errors.js.map +1 -1
  19. package/dist/core/runner-session-token.d.ts.map +1 -1
  20. package/dist/core/runner-session-token.js +2 -1
  21. package/dist/core/runner-session-token.js.map +1 -1
  22. package/dist/db/admin-user-moderation.d.ts.map +1 -1
  23. package/dist/db/admin-user-moderation.js +30 -26
  24. package/dist/db/admin-user-moderation.js.map +1 -1
  25. package/dist/db/admin-user-summary.d.ts +22 -0
  26. package/dist/db/admin-user-summary.d.ts.map +1 -1
  27. package/dist/db/admin-user-summary.js +75 -1
  28. package/dist/db/admin-user-summary.js.map +1 -1
  29. package/dist/db/admin-users.d.ts +11 -0
  30. package/dist/db/admin-users.d.ts.map +1 -1
  31. package/dist/db/admin-users.js +4 -1
  32. package/dist/db/admin-users.js.map +1 -1
  33. package/dist/db/agent-access.d.ts +159 -0
  34. package/dist/db/agent-access.d.ts.map +1 -0
  35. package/dist/db/agent-access.js +267 -0
  36. package/dist/db/agent-access.js.map +1 -0
  37. package/dist/db/db.d.ts +2604 -238
  38. package/dist/db/db.d.ts.map +1 -1
  39. package/dist/db/db.js +7 -0
  40. package/dist/db/db.js.map +1 -1
  41. package/dist/db/schema/agent-access.d.ts +1204 -0
  42. package/dist/db/schema/agent-access.d.ts.map +1 -0
  43. package/dist/db/schema/agent-access.js +267 -0
  44. package/dist/db/schema/agent-access.js.map +1 -0
  45. package/dist/db/schema/users.d.ts.map +1 -1
  46. package/dist/db/schema/users.js +8 -3
  47. package/dist/db/schema/users.js.map +1 -1
  48. package/dist/index.d.ts +3 -2
  49. package/dist/index.d.ts.map +1 -1
  50. package/dist/index.js +2 -2
  51. package/dist/index.js.map +1 -1
  52. package/dist/metrics/instance.d.ts +1 -1
  53. package/dist/metrics/instance.d.ts.map +1 -1
  54. package/dist/metrics/instance.js.map +1 -1
  55. package/dist/presentation/routes/administration.d.ts.map +1 -1
  56. package/dist/presentation/routes/administration.js +123 -20
  57. package/dist/presentation/routes/administration.js.map +1 -1
  58. package/dist/presentation/routes/email-verification/verify-email-resend.d.ts.map +1 -1
  59. package/dist/presentation/routes/email-verification/verify-email-resend.js +6 -1
  60. package/dist/presentation/routes/email-verification/verify-email-resend.js.map +1 -1
  61. package/dist/presentation/routes/rate-limit.d.ts.map +1 -1
  62. package/dist/presentation/routes/rate-limit.js +10 -0
  63. package/dist/presentation/routes/rate-limit.js.map +1 -1
  64. package/dist/presentation/routes/registration/signup.d.ts.map +1 -1
  65. package/dist/presentation/routes/registration/signup.js +59 -50
  66. package/dist/presentation/routes/registration/signup.js.map +1 -1
  67. package/dist/tsconfig.test.tsbuildinfo +1 -1
  68. package/drizzle/0003_brainy_luckman.sql +6 -0
  69. package/drizzle/0004_yummy_runaways.sql +110 -0
  70. package/drizzle/meta/0003_snapshot.json +844 -0
  71. package/drizzle/meta/0004_snapshot.json +1751 -0
  72. package/drizzle/meta/_journal.json +14 -0
  73. package/package.json +7 -7
  74. package/src/core/admin-role.test.ts +70 -0
  75. package/src/core/administration.ts +78 -1
  76. package/src/core/auth.ts +3 -5
  77. package/src/core/entities/agent-access.ts +80 -0
  78. package/src/core/errors.ts +7 -0
  79. package/src/core/runner-session-token.test.ts +2 -0
  80. package/src/core/runner-session-token.ts +1 -0
  81. package/src/db/admin-user-moderation.ts +53 -45
  82. package/src/db/admin-user-summary.test.ts +228 -0
  83. package/src/db/admin-user-summary.ts +143 -1
  84. package/src/db/admin-users.ts +21 -1
  85. package/src/db/agent-access.test.ts +370 -0
  86. package/src/db/agent-access.ts +629 -0
  87. package/src/db/db.ts +14 -0
  88. package/src/db/schema/agent-access.ts +273 -0
  89. package/src/db/schema/users.ts +9 -3
  90. package/src/index.ts +6 -0
  91. package/src/metrics/instance.ts +1 -0
  92. package/src/presentation/routes/administration.test.ts +359 -2
  93. package/src/presentation/routes/administration.ts +142 -20
  94. package/src/presentation/routes/email-verification/verify-email-resend.ts +7 -1
  95. package/src/presentation/routes/rate-limit.ts +4 -0
  96. package/src/presentation/routes/registration/signup.ts +52 -41
  97. package/test/globalSetup.ts +1 -1
  98. package/test/routes.ts +3 -1
  99. package/tsconfig.build.tsbuildinfo +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/auth.ts"],"sourcesContent":["import {type AdminRole, emailSchema} from '@shipfox/api-auth-dto';\nimport {\n confirmEmailChallenge,\n consumeEmailChallengeProof,\n createEmailChallenge,\n resendEmailChallenge,\n} from '@shipfox/api-email-challenges';\nimport {\n type WorkspacesInterModuleClient,\n workspacesInterModuleContract,\n} from '@shipfox/api-workspaces-dto/inter-module';\nimport {isInterModuleKnownError} from '@shipfox/inter-module';\nimport {userAccessTokenKey} from '@shipfox/node-auth-root-key';\nimport {durationToSeconds} from '@shipfox/node-jwt';\nimport {generateOpaqueToken, hashOpaqueToken} from '@shipfox/node-tokens';\nimport {config} from '#config.js';\nimport {consumePasswordReset, createPasswordReset} from '#db/password-resets.js';\nimport {\n createRefreshTokenForActiveUser,\n findActiveRefreshTokenByHash,\n findRefreshTokenByHash,\n revokeRefreshSession,\n revokeRefreshTokensForUser,\n rotateRefreshToken,\n} from '#db/refresh-tokens.js';\nimport {\n createUser as createDbUser,\n findUserByEmail,\n findUserById,\n markEmailVerified,\n provisionUser as provisionDbUser,\n updateUserPassword,\n} from '#db/users.js';\nimport {type AuthTokenRefreshOutcome, recordTokenRefreshed} from '#metrics/index.js';\nimport {getCurrentAdminRole} from './admin-role.js';\nimport type {RefreshToken} from './entities/refresh-token.js';\nimport type {User} from './entities/user.js';\nimport {\n AuthDependencyUnavailableError,\n EmailNotVerifiedError,\n EmailTakenError,\n ImpersonationDisabledError,\n InvalidCredentialsError,\n InvitationEmailMismatchError,\n SignupNotAllowedError,\n TokenAlreadyUsedError,\n TokenExpiredError,\n TokenInvalidError,\n UserNotFoundError,\n} from './errors.js';\nimport {signUserToken, type TokenMembership, verifyUserToken} from './jwt.js';\nimport {hashPassword, verifyPassword} from './password.js';\nimport type {SignupPolicy} from './ports.js';\nimport {createEnvironmentSignupPolicy} from './signup-policy.js';\n\nconst RESET_TTL_HOURS = 1;\nconst PASSWORD_VERIFICATION_PURPOSE = 'password-verification';\nconst DEFAULT_SIGNUP_NOT_ALLOWED_MESSAGE =\n 'This Shipfox deployment does not accept new accounts right now.';\n\nconst defaultSignupPolicy: SignupPolicy = createEnvironmentSignupPolicy();\n\nasync function assertSignupAllowed(params: {\n signupPolicy?: SignupPolicy | undefined;\n email: string;\n emailVerified: boolean;\n source: string;\n}): Promise<void> {\n const result = await (params.signupPolicy ?? defaultSignupPolicy).isSignupAllowed({\n email: params.email,\n emailVerified: params.emailVerified,\n source: params.source,\n });\n\n if (!result.allowed) {\n throw new SignupNotAllowedError(\n result.message ?? DEFAULT_SIGNUP_NOT_ALLOWED_MESSAGE,\n result.format,\n );\n }\n}\n\nlet dummyHashCache: string | undefined;\nasync function getDummyHash(): Promise<string> {\n if (!dummyHashCache) {\n dummyHashCache = await hashPassword({password: 'dummy-for-timing-parity'});\n }\n return dummyHashCache;\n}\n\nfunction hoursFromNow(hours: number): Date {\n return new Date(Date.now() + hours * 60 * 60 * 1000);\n}\n\nfunction daysFromNow(days: number): Date {\n return new Date(Date.now() + days * 24 * 60 * 60 * 1000);\n}\n\nfunction recordRefreshOutcome(outcome: AuthTokenRefreshOutcome): void {\n recordTokenRefreshed(outcome);\n}\n\ntype TokenMemberships = TokenMembership[];\n\nasync function loadTokenMemberships(\n userId: string,\n workspaces: WorkspacesInterModuleClient,\n): Promise<TokenMemberships> {\n const memberships = await workspaces\n .listMembershipsForTokenClaims({userId})\n .catch((error: unknown) => {\n throw new AuthDependencyUnavailableError('workspaces', error);\n });\n return memberships.memberships;\n}\n\nasync function signAccessToken(\n user: User,\n memberships: TokenMemberships,\n refreshSessionId: string,\n): Promise<string> {\n return await signUserToken({\n userId: user.id,\n email: user.email,\n name: user.name,\n memberships,\n refreshSessionId,\n secret: userAccessTokenKey(),\n expiresIn: config.AUTH_JWT_EXPIRES_IN,\n });\n}\n\nfunction isWithinRotationGrace(refreshToken: RefreshToken): boolean {\n if (!refreshToken.rotatedAt) return false;\n const graceMs = config.AUTH_REFRESH_ROTATION_GRACE_SECONDS * 1000;\n return Date.now() - refreshToken.rotatedAt.getTime() <= graceMs;\n}\n\nasync function createRefreshSession(\n user: User,\n refreshSessionId: string,\n): Promise<{refreshToken: string; refreshSessionId: string}> {\n const refreshToken = generateOpaqueToken('refreshToken');\n const session = await createRefreshTokenForActiveUser({\n sessionId: refreshSessionId,\n userId: user.id,\n hashedToken: hashOpaqueToken(refreshToken),\n expiresAt: daysFromNow(config.AUTH_REFRESH_TOKEN_EXPIRES_IN_DAYS),\n });\n if (!session) throw new InvalidCredentialsError();\n return {refreshToken, refreshSessionId: session.sessionId};\n}\n\nasync function createSessionTokens(\n user: User,\n workspaces: WorkspacesInterModuleClient,\n): Promise<{token: string; refreshToken: string; adminRole: AdminRole | null}> {\n const refreshSessionId = crypto.randomUUID();\n const memberships = await loadTokenMemberships(user.id, workspaces);\n const token = await signAccessToken(user, memberships, refreshSessionId);\n const adminRole = await getCurrentAdminRole({userId: user.id});\n const {refreshToken} = await createRefreshSession(user, refreshSessionId);\n return {token, refreshToken, adminRole};\n}\n\nfunction passwordResetLink(rawToken: string): string {\n return `${config.CLIENT_BASE_URL}/auth/reset?token=${rawToken}`;\n}\n\nfunction invitationErrorFromKnownError(\n error: unknown,\n method: 'preflightInvitationAcceptance' | 'acceptInvitation',\n): Error {\n const contractMethod = workspacesInterModuleContract.methods[method];\n if (!isInterModuleKnownError(contractMethod, error)) return error as Error;\n\n if (error.code === 'invitation-token-invalid')\n return new TokenInvalidError('Invitation token is invalid');\n if (error.code === 'invitation-token-used') return new TokenAlreadyUsedError();\n if (error.code === 'invitation-token-expired') return new TokenExpiredError();\n return new InvitationEmailMismatchError();\n}\n\nfunction invitationErrorMessage(\n code:\n | 'invitation-token-invalid'\n | 'invitation-token-used'\n | 'invitation-token-expired'\n | 'invitation-email-mismatch',\n): string {\n if (code === 'invitation-token-invalid') return 'Invitation token is invalid';\n if (code === 'invitation-token-used') return 'Invitation has already been accepted';\n if (code === 'invitation-token-expired') return 'Invitation has expired';\n return 'Signup email does not match the invitation';\n}\n\nexport interface SignupParams {\n email: string;\n password: string;\n name?: string | undefined;\n signupPolicy?: SignupPolicy | undefined;\n}\n\nexport interface ProvisionUserParams {\n email: string;\n name?: string | null | undefined;\n signupPolicy?: SignupPolicy | undefined;\n}\n\n/**\n * Creates a verified, password-less user for an external identity provider.\n * Existing users are returned unchanged, including their password and profile.\n */\nexport async function provisionUser(params: ProvisionUserParams): Promise<User> {\n const email = emailSchema.parse(params.email);\n const existing = await findUserByEmail({email});\n if (existing) return existing;\n\n await assertSignupAllowed({\n signupPolicy: params.signupPolicy,\n email,\n emailVerified: true,\n source: 'external-identity',\n });\n\n return await provisionDbUser({\n email,\n name: params.name ?? null,\n });\n}\n\nexport type SignupResult = User & {\n emailChallenge: {id: string; nextResendAvailableAt: Date};\n};\n\nexport async function signup(params: SignupParams & {sourceIp?: string}): Promise<SignupResult> {\n const email = emailSchema.parse(params.email);\n const existing = await findUserByEmail({email});\n if (existing) {\n const canResumeVerification =\n existing.status === 'active' &&\n existing.emailVerifiedAt === null &&\n existing.hashedPassword !== null &&\n (await verifyPassword({password: params.password, hash: existing.hashedPassword}));\n if (canResumeVerification) {\n const emailChallenge = await createEmailChallenge({\n email: existing.email,\n purpose: PASSWORD_VERIFICATION_PURPOSE,\n continuation: existing.id,\n idempotencyKey: existing.id,\n sourceIp: params.sourceIp ?? '0.0.0.0',\n });\n return {...existing, emailChallenge};\n }\n throw new EmailTakenError(email);\n }\n\n await assertSignupAllowed({\n signupPolicy: params.signupPolicy,\n email,\n emailVerified: false,\n source: 'password',\n });\n\n const hashedPassword = await hashPassword({password: params.password});\n const user = await createDbUser({\n email,\n hashedPassword,\n name: params.name ?? null,\n signedUp: {viaInvitation: false},\n });\n\n const emailChallenge = await createEmailChallenge({\n email: user.email,\n purpose: PASSWORD_VERIFICATION_PURPOSE,\n continuation: user.id,\n idempotencyKey: user.id,\n sourceIp: params.sourceIp ?? '0.0.0.0',\n });\n\n return {...user, emailChallenge};\n}\n\nexport interface SignupWithInvitationParams extends SignupParams {\n invitationToken: string;\n workspaces: WorkspacesInterModuleClient;\n}\n\nexport interface SignupWithInvitationResult extends LoginResult {\n membership: {id: string; userId: string; workspaceId: string} | null;\n acceptError?: {code: string; message: string};\n}\n\nexport async function signupWithInvitation(\n params: SignupWithInvitationParams,\n): Promise<SignupWithInvitationResult> {\n // Step 1: Pre-validate the invitation BEFORE any user write so an invalid\n // token never produces an orphan user. Re-validation happens again inside\n // acceptWorkspaceInvitation (race-safe) after the user is created.\n try {\n await params.workspaces.preflightInvitationAcceptance({\n token: params.invitationToken,\n email: params.email,\n });\n } catch (error) {\n throw invitationErrorFromKnownError(error, 'preflightInvitationAcceptance');\n }\n\n // Step 2: Create the user as verified. The invitation email is the proof of\n // ownership, and this avoids a second auth-table write just to mark verified.\n const existing = await findUserByEmail({email: params.email});\n if (existing) {\n throw new EmailTakenError(params.email);\n }\n const hashedPassword = await hashPassword({password: params.password});\n const user = await createDbUser({\n email: params.email,\n hashedPassword,\n name: params.name ?? null,\n emailVerifiedAt: new Date(),\n signedUp: {viaInvitation: true},\n });\n\n // Step 3: Accept the invitation through the workspaces module boundary.\n // This is intentionally not one database transaction with auth; treat it like\n // calling another service. If it fails after user creation, return a verified\n // signed-in user and an explicit retryable accept error.\n let membership: SignupWithInvitationResult['membership'] = null;\n let acceptError: SignupWithInvitationResult['acceptError'];\n try {\n const result = await params.workspaces.acceptInvitation({\n token: params.invitationToken,\n userId: user.id,\n email: user.email,\n name: user.name,\n });\n membership = {\n id: result.membership.id,\n userId: result.membership.userId,\n workspaceId: result.membership.workspaceId,\n };\n } catch (error) {\n if (isInterModuleKnownError(workspacesInterModuleContract.methods.acceptInvitation, error)) {\n acceptError = {code: error.code, message: invitationErrorMessage(error.code)};\n } else {\n acceptError = {\n code: 'AcceptFailed',\n message: 'Could not accept the invitation; please retry from the invite link.',\n };\n }\n }\n\n // Step 4: Issue session. createSessionTokens reads memberships through the\n // workspaces module API, so a successful accept is reflected in the JWT.\n const {token, refreshToken, adminRole} = await createSessionTokens(user, params.workspaces);\n\n if (acceptError) {\n return {token, refreshToken, user, membership, acceptError, adminRole};\n }\n return {token, refreshToken, user, membership, adminRole};\n}\n\nexport interface CreateUserParams extends SignupParams {\n verified: boolean;\n}\n\nexport async function createUser(params: CreateUserParams): Promise<User> {\n const existing = await findUserByEmail({email: params.email});\n if (existing) {\n throw new EmailTakenError(params.email);\n }\n\n const hashedPassword = await hashPassword({password: params.password});\n const user = await createDbUser({\n email: params.email,\n hashedPassword,\n name: params.name ?? null,\n emailVerifiedAt: params.verified ? new Date() : null,\n });\n\n return user;\n}\n\nexport interface LoginParams {\n email: string;\n password: string;\n workspaces: WorkspacesInterModuleClient;\n}\n\nexport interface LoginResult {\n token: string;\n refreshToken: string;\n user: User;\n adminRole: AdminRole | null;\n}\n\nexport async function login(params: LoginParams): Promise<LoginResult> {\n const user = await findUserByEmail({email: params.email});\n\n if (!user) {\n await verifyPassword({password: params.password, hash: await getDummyHash()});\n throw new InvalidCredentialsError();\n }\n\n const hasPassword = user.hashedPassword !== null;\n const ok = await verifyPassword({\n password: params.password,\n hash: user.hashedPassword ?? (await getDummyHash()),\n });\n if (!hasPassword || !ok || user.status !== 'active') {\n throw new InvalidCredentialsError();\n }\n\n if (user.emailVerifiedAt === null) {\n throw new EmailNotVerifiedError();\n }\n\n const {token, refreshToken, adminRole} = await createSessionTokens(user, params.workspaces);\n\n return {token, refreshToken, user, adminRole};\n}\n\nexport interface CreateSessionForUserParams {\n userId?: string | undefined;\n email?: string | undefined;\n workspaces: WorkspacesInterModuleClient;\n}\n\nexport interface CreateSessionForUserResult {\n token: string;\n refreshToken: string;\n user: User;\n adminRole: AdminRole | null;\n}\n\nexport type CreateSessionForUserError =\n | AuthDependencyUnavailableError\n | EmailNotVerifiedError\n | InvalidCredentialsError\n | UserNotFoundError;\n\nexport async function createSessionForUser(\n params: CreateSessionForUserParams,\n): Promise<CreateSessionForUserResult> {\n const user = params.userId\n ? await findUserById({id: params.userId})\n : params.email\n ? await findUserByEmail({email: emailSchema.parse(params.email)})\n : undefined;\n\n if (!user) {\n throw new UserNotFoundError(params.userId ?? params.email ?? 'unknown');\n }\n if (user.emailVerifiedAt === null) {\n throw new EmailNotVerifiedError();\n }\n if (user.status !== 'active') {\n throw new InvalidCredentialsError();\n }\n\n const {token, refreshToken, adminRole} = await createSessionTokens(user, params.workspaces);\n\n return {token, refreshToken, user, adminRole};\n}\n\n/**\n * Cap for impersonated session tokens: the TTL is min(`AUTH_JWT_EXPIRES_IN`,\n * this), so an impersonated window can never outlive 15 minutes no matter how\n * the deployment configures ordinary access tokens.\n */\nexport const IMPERSONATION_MAX_TTL_SECONDS = 15 * 60;\n\nexport interface CreateImpersonatedSessionTokenParams {\n targetUserId: string;\n /** The administrator the token is minted for; signed into the `impersonatorId` claim. */\n impersonatorId: string;\n workspaces: WorkspacesInterModuleClient;\n /**\n * Re-sign lifetime override used by an idempotent replay: the remaining\n * lifetime to the stored `expires_at`, so a replay never extends the window.\n * Defaults to min(`AUTH_JWT_EXPIRES_IN`, 15 minutes).\n */\n expiresIn?: string | undefined;\n}\n\nexport interface CreateImpersonatedSessionTokenResult {\n token: string;\n expiresAt: Date;\n user: User;\n}\n\nfunction impersonationTtlSeconds(): number {\n const configuredSeconds = durationToSeconds(config.AUTH_JWT_EXPIRES_IN);\n const ttlSeconds = Math.min(configuredSeconds, IMPERSONATION_MAX_TTL_SECONDS);\n if (ttlSeconds <= 0) {\n throw new TypeError(\n `AUTH_JWT_EXPIRES_IN must be a valid duration of at least 1 second, got ${config.AUTH_JWT_EXPIRES_IN}`,\n );\n }\n return ttlSeconds;\n}\n\n/**\n * Mints an access-token-only impersonated session for a target user: the same\n * eligibility as login (active account, verified email), the target's real\n * membership claims, a capped TTL, the `impersonatorId` claim, and **no**\n * `refreshSessionId`. It creates no refresh session and sets no cookie, so\n * nothing persisted can resurrect the session after the token window.\n */\nexport async function createImpersonatedSessionToken(\n params: CreateImpersonatedSessionTokenParams,\n): Promise<CreateImpersonatedSessionTokenResult> {\n // Rule 1 lives in the mint primitive as well as the command entry, so the\n // exported package API can never bypass the kill switch: the flag is a\n // configuration read that also holds on the in-transaction replay path.\n if (!config.AUTH_IMPERSONATION_ENABLED) throw new ImpersonationDisabledError();\n\n const user = await findUserById({id: params.targetUserId});\n if (!user) {\n throw new UserNotFoundError(params.targetUserId);\n }\n if (user.emailVerifiedAt === null) {\n throw new EmailNotVerifiedError();\n }\n if (user.status !== 'active') {\n throw new InvalidCredentialsError();\n }\n\n const memberships = await loadTokenMemberships(user.id, params.workspaces);\n const ttlSeconds =\n params.expiresIn === undefined\n ? impersonationTtlSeconds()\n : Math.min(durationToSeconds(params.expiresIn), IMPERSONATION_MAX_TTL_SECONDS);\n if (ttlSeconds <= 0) {\n throw new TypeError(\n `Impersonation token TTL must be at least 1 second, got ${params.expiresIn}`,\n );\n }\n\n const token = await signUserToken({\n userId: user.id,\n email: user.email,\n name: user.name,\n memberships,\n impersonatorId: params.impersonatorId,\n secret: userAccessTokenKey(),\n expiresIn: `${ttlSeconds}s`,\n });\n\n // The advertised expiry is the token's actual signed `exp`, never a\n // clock-derived estimate: the signer stamps `iat`/`exp` in whole seconds, so\n // `Date.now() + ttl` could drift up to a second from the signed claims in\n // either direction. Deriving `expiresAt` from the signed token keeps the\n // response, the stored command result, and the bearer token exactly aligned,\n // which is what lets a replay re-sign with a TTL that never extends the\n // window (`exp` of the re-signed token is at most the original `exp`).\n const claims = await verifyUserToken({token, secret: userAccessTokenKey()});\n return {token, expiresAt: new Date(claims.exp * 1000), user};\n}\n\nexport interface RefreshAccessTokenResult {\n token: string;\n /** Undefined on a grace-window hit: keep the existing cookie instead of rotating it. */\n refreshToken: string | undefined;\n user: User;\n adminRole: AdminRole | null;\n}\n\nexport async function refreshAccessToken(params: {\n refreshToken: string;\n workspaces: WorkspacesInterModuleClient;\n}): Promise<RefreshAccessTokenResult> {\n const currentHashedToken = hashOpaqueToken(params.refreshToken);\n const current = await findRefreshTokenByHash({hashedToken: currentHashedToken});\n if (!current) {\n recordRefreshOutcome('rejected');\n throw new TokenInvalidError('Refresh token is invalid or expired');\n }\n\n const user = await findUserById({id: current.userId});\n if (user?.status !== 'active') {\n await revokeRefreshSession({sessionId: current.sessionId, userId: current.userId});\n recordRefreshOutcome('rejected');\n throw new TokenInvalidError('Refresh token is invalid or expired');\n }\n const adminRole = await getCurrentAdminRole({userId: user.id});\n\n // Within the grace window a rotated token means a concurrent refresh (e.g. a\n // second tab); past it, reuse of a retired token means a compromised session.\n if (current.rotatedAt) {\n if (isWithinRotationGrace(current)) {\n const memberships = await loadTokenMemberships(user.id, params.workspaces);\n const token = await signAccessToken(user, memberships, current.sessionId);\n recordRefreshOutcome('grace');\n return {token, refreshToken: undefined, user, adminRole};\n }\n await revokeRefreshTokensForUser({userId: user.id});\n recordRefreshOutcome('rejected');\n throw new TokenInvalidError('Refresh token reused after rotation');\n }\n\n // Keep the external membership snapshot ahead of rotation so an outage\n // cannot retire a refresh token that the caller can still retry.\n const memberships = await loadTokenMemberships(user.id, params.workspaces);\n\n // Losing the CAS requires a state check: another request may have rotated,\n // revoked, or expired the token before this refresh could claim it.\n const nextRefreshToken = generateOpaqueToken('refreshToken');\n const rotated = await rotateRefreshToken({\n userId: current.userId,\n id: current.id,\n currentHashedToken,\n nextHashedToken: hashOpaqueToken(nextRefreshToken),\n expiresAt: daysFromNow(config.AUTH_REFRESH_TOKEN_EXPIRES_IN_DAYS),\n });\n if (!rotated) {\n const latestUser = await findUserById({id: current.userId});\n if (latestUser?.status !== 'active') {\n recordRefreshOutcome('rejected');\n throw new TokenInvalidError('Refresh token is invalid or expired');\n }\n const latest = await findRefreshTokenByHash({hashedToken: currentHashedToken});\n if (!latest || !isWithinRotationGrace(latest)) {\n recordRefreshOutcome('rejected');\n throw new TokenInvalidError('Refresh token is invalid or expired');\n }\n const token = await signAccessToken(user, memberships, latest.sessionId);\n recordRefreshOutcome('grace');\n return {token, refreshToken: undefined, user, adminRole};\n }\n\n const token = await signAccessToken(user, memberships, current.sessionId);\n recordRefreshOutcome('rotated');\n return {token, refreshToken: nextRefreshToken, user, adminRole};\n}\n\nexport interface ConfirmEmailVerificationResult {\n token: string;\n refreshToken: string;\n user: User;\n adminRole: AdminRole | null;\n}\n\nexport interface ResendEmailVerificationResult {\n nextResendAvailableAt: Date;\n}\n\nexport async function confirmEmailVerification(params: {\n email: string;\n challengeId: string;\n code: string;\n workspaces: WorkspacesInterModuleClient;\n}): Promise<ConfirmEmailVerificationResult> {\n const user = await findUserByEmail({email: emailSchema.parse(params.email)});\n if (!user) throw new TokenInvalidError('Verification code is invalid or expired');\n await confirmEmailChallenge({id: params.challengeId, code: params.code, continuation: user.id});\n await consumeEmailChallengeProof({\n id: params.challengeId,\n purpose: PASSWORD_VERIFICATION_PURPOSE,\n continuation: user.id,\n });\n\n const verifiedUser = await markEmailVerified({userId: user.id});\n if (verifiedUser?.status !== 'active') {\n throw new TokenInvalidError('Verification code is invalid or expired');\n }\n\n const {token, refreshToken, adminRole} = await createSessionTokens(\n verifiedUser,\n params.workspaces,\n );\n\n return {token, refreshToken, user: verifiedUser, adminRole};\n}\n\nexport async function resendEmailVerification(params: {\n email: string;\n challengeId: string;\n sourceIp: string;\n}): Promise<ResendEmailVerificationResult> {\n const user = await findUserByEmail({email: emailSchema.parse(params.email)});\n if (!user || user.emailVerifiedAt || user.status !== 'active') {\n return {nextResendAvailableAt: new Date()};\n }\n const result = await resendEmailChallenge({\n id: params.challengeId,\n continuation: user.id,\n sourceIp: params.sourceIp,\n });\n\n return {nextResendAvailableAt: result.nextResendAvailableAt};\n}\n\nexport async function requestPasswordReset(params: {email: string}): Promise<void> {\n const user = await findUserByEmail({email: params.email});\n if (user?.status !== 'active' || user.hashedPassword === null) {\n return;\n }\n\n const rawToken = generateOpaqueToken('passwordReset');\n await createPasswordReset({\n userId: user.id,\n hashedToken: hashOpaqueToken(rawToken),\n expiresAt: hoursFromNow(RESET_TTL_HOURS),\n sendEmail: {\n email: user.email,\n resetLink: passwordResetLink(rawToken),\n expiresInHours: RESET_TTL_HOURS,\n },\n });\n}\n\nexport interface ConfirmPasswordResetResult {\n token: string;\n refreshToken: string;\n user: User;\n adminRole: AdminRole | null;\n}\n\nexport async function confirmPasswordReset(params: {\n token: string;\n newPassword: string;\n workspaces: WorkspacesInterModuleClient;\n}): Promise<ConfirmPasswordResetResult> {\n const consumed = await consumePasswordReset({hashedToken: hashOpaqueToken(params.token)});\n if (!consumed) {\n throw new TokenInvalidError('Reset token is invalid or expired');\n }\n\n const existingUser = await findUserById({id: consumed.userId});\n if (existingUser?.status !== 'active' || existingUser.hashedPassword === null) {\n throw new TokenInvalidError('Reset token is invalid or expired');\n }\n\n const hashedPassword = await hashPassword({password: params.newPassword});\n const user = await updateUserPassword({userId: consumed.userId, hashedPassword});\n if (user?.status !== 'active') {\n throw new TokenInvalidError('Reset token is invalid or expired');\n }\n\n await revokeRefreshTokensForUser({userId: consumed.userId});\n\n const {token, refreshToken, adminRole} = await createSessionTokens(user, params.workspaces);\n\n return {token, refreshToken, user, adminRole};\n}\n\nexport async function changePassword(params: {\n userId: string;\n currentPassword: string;\n newPassword: string;\n refreshToken?: string | undefined;\n}): Promise<void> {\n const user = await findUserById({id: params.userId});\n if (!user) {\n throw new UserNotFoundError(params.userId);\n }\n\n const hasPassword = user.hashedPassword !== null;\n const ok = await verifyPassword({\n password: params.currentPassword,\n hash: user.hashedPassword ?? (await getDummyHash()),\n });\n if (!hasPassword || !ok) {\n throw new InvalidCredentialsError();\n }\n\n const hashedPassword = await hashPassword({password: params.newPassword});\n const currentRefreshSession = params.refreshToken\n ? await findActiveRefreshTokenByHash({hashedToken: hashOpaqueToken(params.refreshToken)})\n : undefined;\n const exceptRefreshTokenId =\n currentRefreshSession?.userId === user.id ? currentRefreshSession.id : undefined;\n\n await updateUserPassword({userId: user.id, hashedPassword});\n await revokeRefreshTokensForUser({userId: user.id, exceptRefreshTokenId});\n}\n\nexport async function logout(params: {refreshToken?: string | undefined}): Promise<void> {\n if (!params.refreshToken) return;\n const refreshToken = await findRefreshTokenByHash({\n hashedToken: hashOpaqueToken(params.refreshToken),\n });\n if (!refreshToken) return;\n await revokeRefreshSession({sessionId: refreshToken.sessionId, userId: refreshToken.userId});\n}\n\nexport interface GetCurrentUserResult {\n user: User;\n}\n\nexport async function getCurrentUser(params: {userId: string}): Promise<GetCurrentUserResult> {\n const user = await findUserById({id: params.userId});\n if (!user) {\n throw new UserNotFoundError(params.userId);\n }\n\n return {user};\n}\n"],"names":["emailSchema","confirmEmailChallenge","consumeEmailChallengeProof","createEmailChallenge","resendEmailChallenge","workspacesInterModuleContract","isInterModuleKnownError","userAccessTokenKey","durationToSeconds","generateOpaqueToken","hashOpaqueToken","config","consumePasswordReset","createPasswordReset","createRefreshTokenForActiveUser","findActiveRefreshTokenByHash","findRefreshTokenByHash","revokeRefreshSession","revokeRefreshTokensForUser","rotateRefreshToken","createUser","createDbUser","findUserByEmail","findUserById","markEmailVerified","provisionUser","provisionDbUser","updateUserPassword","recordTokenRefreshed","getCurrentAdminRole","AuthDependencyUnavailableError","EmailNotVerifiedError","EmailTakenError","ImpersonationDisabledError","InvalidCredentialsError","InvitationEmailMismatchError","SignupNotAllowedError","TokenAlreadyUsedError","TokenExpiredError","TokenInvalidError","UserNotFoundError","signUserToken","verifyUserToken","hashPassword","verifyPassword","createEnvironmentSignupPolicy","RESET_TTL_HOURS","PASSWORD_VERIFICATION_PURPOSE","DEFAULT_SIGNUP_NOT_ALLOWED_MESSAGE","defaultSignupPolicy","assertSignupAllowed","params","result","signupPolicy","isSignupAllowed","email","emailVerified","source","allowed","message","format","dummyHashCache","getDummyHash","password","hoursFromNow","hours","Date","now","daysFromNow","days","recordRefreshOutcome","outcome","loadTokenMemberships","userId","workspaces","memberships","listMembershipsForTokenClaims","catch","error","signAccessToken","user","refreshSessionId","id","name","secret","expiresIn","AUTH_JWT_EXPIRES_IN","isWithinRotationGrace","refreshToken","rotatedAt","graceMs","AUTH_REFRESH_ROTATION_GRACE_SECONDS","getTime","createRefreshSession","session","sessionId","hashedToken","expiresAt","AUTH_REFRESH_TOKEN_EXPIRES_IN_DAYS","createSessionTokens","crypto","randomUUID","token","adminRole","passwordResetLink","rawToken","CLIENT_BASE_URL","invitationErrorFromKnownError","method","contractMethod","methods","code","invitationErrorMessage","parse","existing","signup","canResumeVerification","status","emailVerifiedAt","hashedPassword","hash","emailChallenge","purpose","continuation","idempotencyKey","sourceIp","signedUp","viaInvitation","signupWithInvitation","preflightInvitationAcceptance","invitationToken","membership","acceptError","acceptInvitation","workspaceId","verified","login","hasPassword","ok","createSessionForUser","undefined","IMPERSONATION_MAX_TTL_SECONDS","impersonationTtlSeconds","configuredSeconds","ttlSeconds","Math","min","TypeError","createImpersonatedSessionToken","AUTH_IMPERSONATION_ENABLED","targetUserId","impersonatorId","claims","exp","refreshAccessToken","currentHashedToken","current","nextRefreshToken","rotated","nextHashedToken","latestUser","latest","confirmEmailVerification","challengeId","verifiedUser","resendEmailVerification","nextResendAvailableAt","requestPasswordReset","sendEmail","resetLink","expiresInHours","confirmPasswordReset","consumed","existingUser","newPassword","changePassword","currentPassword","currentRefreshSession","exceptRefreshTokenId","logout","getCurrentUser"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/core/auth.ts"],"sourcesContent":["import {type AdminRole, emailSchema} from '@shipfox/api-auth-dto';\nimport {\n confirmEmailChallenge,\n consumeEmailChallengeProof,\n createEmailChallenge,\n resendEmailChallenge,\n} from '@shipfox/api-email-challenges';\nimport {\n type WorkspacesInterModuleClient,\n workspacesInterModuleContract,\n} from '@shipfox/api-workspaces-dto/inter-module';\nimport {isInterModuleKnownError} from '@shipfox/inter-module';\nimport {userAccessTokenKey} from '@shipfox/node-auth-root-key';\nimport {durationToSeconds} from '@shipfox/node-jwt';\nimport {generateOpaqueToken, hashOpaqueToken} from '@shipfox/node-tokens';\nimport {config} from '#config.js';\nimport {consumePasswordReset, createPasswordReset} from '#db/password-resets.js';\nimport {\n createRefreshTokenForActiveUser,\n findActiveRefreshTokenByHash,\n findRefreshTokenByHash,\n revokeRefreshSession,\n revokeRefreshTokensForUser,\n rotateRefreshToken,\n} from '#db/refresh-tokens.js';\nimport {\n createUser as createDbUser,\n findUserByEmail,\n findUserById,\n markEmailVerified,\n provisionUser as provisionDbUser,\n updateUserPassword,\n} from '#db/users.js';\nimport {type AuthTokenRefreshOutcome, recordTokenRefreshed} from '#metrics/index.js';\nimport {getCurrentAdminRole} from './admin-role.js';\nimport type {RefreshToken} from './entities/refresh-token.js';\nimport type {User} from './entities/user.js';\nimport {\n AuthDependencyUnavailableError,\n EmailNotVerifiedError,\n EmailTakenError,\n ImpersonationDisabledError,\n InvalidCredentialsError,\n InvitationEmailMismatchError,\n SignupNotAllowedError,\n TokenAlreadyUsedError,\n TokenExpiredError,\n TokenInvalidError,\n UserNotFoundError,\n} from './errors.js';\nimport {signUserToken, type TokenMembership, verifyUserToken} from './jwt.js';\nimport {hashPassword, verifyPassword} from './password.js';\nimport type {SignupPolicy} from './ports.js';\nimport {createEnvironmentSignupPolicy} from './signup-policy.js';\n\nconst RESET_TTL_HOURS = 1;\nconst PASSWORD_VERIFICATION_PURPOSE = 'password-verification';\nconst DEFAULT_SIGNUP_NOT_ALLOWED_MESSAGE =\n 'This Shipfox deployment does not accept new accounts right now.';\n\nconst defaultSignupPolicy: SignupPolicy = createEnvironmentSignupPolicy();\n\nasync function assertSignupAllowed(params: {\n signupPolicy?: SignupPolicy | undefined;\n email: string;\n emailVerified: boolean;\n source: string;\n}): Promise<void> {\n const result = await (params.signupPolicy ?? defaultSignupPolicy).isSignupAllowed({\n email: params.email,\n emailVerified: params.emailVerified,\n source: params.source,\n });\n\n if (!result.allowed) {\n throw new SignupNotAllowedError(\n result.message ?? DEFAULT_SIGNUP_NOT_ALLOWED_MESSAGE,\n result.format,\n );\n }\n}\n\nlet dummyHashCache: string | undefined;\nasync function getDummyHash(): Promise<string> {\n if (!dummyHashCache) {\n dummyHashCache = await hashPassword({password: 'dummy-for-timing-parity'});\n }\n return dummyHashCache;\n}\n\nfunction hoursFromNow(hours: number): Date {\n return new Date(Date.now() + hours * 60 * 60 * 1000);\n}\n\nfunction daysFromNow(days: number): Date {\n return new Date(Date.now() + days * 24 * 60 * 60 * 1000);\n}\n\nfunction recordRefreshOutcome(outcome: AuthTokenRefreshOutcome): void {\n recordTokenRefreshed(outcome);\n}\n\ntype TokenMemberships = TokenMembership[];\n\nasync function loadTokenMemberships(\n userId: string,\n workspaces: WorkspacesInterModuleClient,\n): Promise<TokenMemberships> {\n const memberships = await workspaces\n .listMembershipsForTokenClaims({userId})\n .catch((error: unknown) => {\n throw new AuthDependencyUnavailableError('workspaces', error);\n });\n return memberships.memberships;\n}\n\nasync function signAccessToken(\n user: User,\n memberships: TokenMemberships,\n refreshSessionId: string,\n): Promise<string> {\n return await signUserToken({\n userId: user.id,\n email: user.email,\n name: user.name,\n memberships,\n refreshSessionId,\n secret: userAccessTokenKey(),\n expiresIn: config.AUTH_JWT_EXPIRES_IN,\n });\n}\n\nfunction isWithinRotationGrace(refreshToken: RefreshToken): boolean {\n if (!refreshToken.rotatedAt) return false;\n const graceMs = config.AUTH_REFRESH_ROTATION_GRACE_SECONDS * 1000;\n return Date.now() - refreshToken.rotatedAt.getTime() <= graceMs;\n}\n\nasync function createRefreshSession(\n user: User,\n refreshSessionId: string,\n): Promise<{refreshToken: string; refreshSessionId: string}> {\n const refreshToken = generateOpaqueToken('refreshToken');\n const session = await createRefreshTokenForActiveUser({\n sessionId: refreshSessionId,\n userId: user.id,\n hashedToken: hashOpaqueToken(refreshToken),\n expiresAt: daysFromNow(config.AUTH_REFRESH_TOKEN_EXPIRES_IN_DAYS),\n });\n if (!session) throw new InvalidCredentialsError();\n return {refreshToken, refreshSessionId: session.sessionId};\n}\n\nasync function createSessionTokens(\n user: User,\n workspaces: WorkspacesInterModuleClient,\n): Promise<{token: string; refreshToken: string; adminRole: AdminRole | null}> {\n const refreshSessionId = crypto.randomUUID();\n const memberships = await loadTokenMemberships(user.id, workspaces);\n const token = await signAccessToken(user, memberships, refreshSessionId);\n const adminRole = await getCurrentAdminRole({userId: user.id});\n const {refreshToken} = await createRefreshSession(user, refreshSessionId);\n return {token, refreshToken, adminRole};\n}\n\nfunction passwordResetLink(rawToken: string): string {\n return `${config.CLIENT_BASE_URL}/auth/reset?token=${rawToken}`;\n}\n\nfunction invitationErrorFromKnownError(\n error: unknown,\n method: 'preflightInvitationAcceptance' | 'acceptInvitation',\n): Error {\n const contractMethod = workspacesInterModuleContract.methods[method];\n if (!isInterModuleKnownError(contractMethod, error)) return error as Error;\n\n if (error.code === 'invitation-token-invalid')\n return new TokenInvalidError('Invitation token is invalid');\n if (error.code === 'invitation-token-used') return new TokenAlreadyUsedError();\n if (error.code === 'invitation-token-expired') return new TokenExpiredError();\n return new InvitationEmailMismatchError();\n}\n\nfunction invitationErrorMessage(\n code:\n | 'invitation-token-invalid'\n | 'invitation-token-used'\n | 'invitation-token-expired'\n | 'invitation-email-mismatch',\n): string {\n if (code === 'invitation-token-invalid') return 'Invitation token is invalid';\n if (code === 'invitation-token-used') return 'Invitation has already been accepted';\n if (code === 'invitation-token-expired') return 'Invitation has expired';\n return 'Signup email does not match the invitation';\n}\n\nexport interface SignupParams {\n email: string;\n password: string;\n name?: string | undefined;\n signupPolicy?: SignupPolicy | undefined;\n}\n\nexport interface ProvisionUserParams {\n email: string;\n name?: string | null | undefined;\n signupPolicy?: SignupPolicy | undefined;\n}\n\n/**\n * Creates a verified, password-less user for an external identity provider.\n * Existing users are returned unchanged, including their password and profile.\n */\nexport async function provisionUser(params: ProvisionUserParams): Promise<User> {\n const email = emailSchema.parse(params.email);\n const existing = await findUserByEmail({email});\n if (existing) return existing;\n\n await assertSignupAllowed({\n signupPolicy: params.signupPolicy,\n email,\n emailVerified: true,\n source: 'external-identity',\n });\n\n return await provisionDbUser({\n email,\n name: params.name ?? null,\n });\n}\n\nexport type SignupResult = User & {\n emailChallenge: {id: string; nextResendAvailableAt: Date};\n};\n\nexport async function signup(params: SignupParams & {sourceIp?: string}): Promise<SignupResult> {\n const email = emailSchema.parse(params.email);\n const existing = await findUserByEmail({email});\n if (existing) {\n const canResumeVerification =\n existing.status === 'active' &&\n existing.emailVerifiedAt === null &&\n existing.hashedPassword !== null &&\n (await verifyPassword({password: params.password, hash: existing.hashedPassword}));\n if (canResumeVerification) {\n const emailChallenge = await createEmailChallenge({\n email: existing.email,\n purpose: PASSWORD_VERIFICATION_PURPOSE,\n continuation: existing.id,\n idempotencyKey: existing.id,\n sourceIp: params.sourceIp ?? '0.0.0.0',\n });\n return {...existing, emailChallenge};\n }\n throw new EmailTakenError(email);\n }\n\n await assertSignupAllowed({\n signupPolicy: params.signupPolicy,\n email,\n emailVerified: false,\n source: 'password',\n });\n\n const hashedPassword = await hashPassword({password: params.password});\n const user = await createDbUser({\n email,\n hashedPassword,\n name: params.name ?? null,\n signedUp: {viaInvitation: false},\n });\n\n const emailChallenge = await createEmailChallenge({\n email: user.email,\n purpose: PASSWORD_VERIFICATION_PURPOSE,\n continuation: user.id,\n idempotencyKey: user.id,\n sourceIp: params.sourceIp ?? '0.0.0.0',\n });\n\n return {...user, emailChallenge};\n}\n\nexport interface SignupWithInvitationParams extends SignupParams {\n invitationToken: string;\n workspaces: WorkspacesInterModuleClient;\n}\n\nexport interface SignupWithInvitationResult extends LoginResult {\n membership: {id: string; userId: string; workspaceId: string} | null;\n acceptError?: {code: string; message: string};\n}\n\nexport async function signupWithInvitation(\n params: SignupWithInvitationParams,\n): Promise<SignupWithInvitationResult> {\n // Step 1: Pre-validate the invitation BEFORE any user write so an invalid\n // token never produces an orphan user. Re-validation happens again inside\n // acceptWorkspaceInvitation (race-safe) after the user is created.\n try {\n await params.workspaces.preflightInvitationAcceptance({\n token: params.invitationToken,\n email: params.email,\n });\n } catch (error) {\n throw invitationErrorFromKnownError(error, 'preflightInvitationAcceptance');\n }\n\n // Step 2: Create the user as verified. The invitation email is the proof of\n // ownership, and this avoids a second auth-table write just to mark verified.\n const existing = await findUserByEmail({email: params.email});\n if (existing) {\n throw new EmailTakenError(params.email);\n }\n const hashedPassword = await hashPassword({password: params.password});\n const user = await createDbUser({\n email: params.email,\n hashedPassword,\n name: params.name ?? null,\n emailVerifiedAt: new Date(),\n signedUp: {viaInvitation: true},\n });\n\n // Step 3: Accept the invitation through the workspaces module boundary.\n // This is intentionally not one database transaction with auth; treat it like\n // calling another service. If it fails after user creation, return a verified\n // signed-in user and an explicit retryable accept error.\n let membership: SignupWithInvitationResult['membership'] = null;\n let acceptError: SignupWithInvitationResult['acceptError'];\n try {\n const result = await params.workspaces.acceptInvitation({\n token: params.invitationToken,\n userId: user.id,\n email: user.email,\n name: user.name,\n });\n membership = {\n id: result.membership.id,\n userId: result.membership.userId,\n workspaceId: result.membership.workspaceId,\n };\n } catch (error) {\n if (isInterModuleKnownError(workspacesInterModuleContract.methods.acceptInvitation, error)) {\n acceptError = {code: error.code, message: invitationErrorMessage(error.code)};\n } else {\n acceptError = {\n code: 'AcceptFailed',\n message: 'Could not accept the invitation; please retry from the invite link.',\n };\n }\n }\n\n // Step 4: Issue session. createSessionTokens reads memberships through the\n // workspaces module API, so a successful accept is reflected in the JWT.\n const {token, refreshToken, adminRole} = await createSessionTokens(user, params.workspaces);\n\n if (acceptError) {\n return {token, refreshToken, user, membership, acceptError, adminRole};\n }\n return {token, refreshToken, user, membership, adminRole};\n}\n\nexport interface CreateUserParams extends SignupParams {\n verified: boolean;\n}\n\nexport async function createUser(params: CreateUserParams): Promise<User> {\n const existing = await findUserByEmail({email: params.email});\n if (existing) {\n throw new EmailTakenError(params.email);\n }\n\n const hashedPassword = await hashPassword({password: params.password});\n const user = await createDbUser({\n email: params.email,\n hashedPassword,\n name: params.name ?? null,\n emailVerifiedAt: params.verified ? new Date() : null,\n });\n\n return user;\n}\n\nexport interface LoginParams {\n email: string;\n password: string;\n workspaces: WorkspacesInterModuleClient;\n}\n\nexport interface LoginResult {\n token: string;\n refreshToken: string;\n user: User;\n adminRole: AdminRole | null;\n}\n\nexport async function login(params: LoginParams): Promise<LoginResult> {\n const user = await findUserByEmail({email: params.email});\n\n if (!user) {\n await verifyPassword({password: params.password, hash: await getDummyHash()});\n throw new InvalidCredentialsError();\n }\n\n const hasPassword = user.hashedPassword !== null;\n const ok = await verifyPassword({\n password: params.password,\n hash: user.hashedPassword ?? (await getDummyHash()),\n });\n if (!hasPassword || !ok || user.status !== 'active') {\n throw new InvalidCredentialsError();\n }\n\n if (user.emailVerifiedAt === null) {\n throw new EmailNotVerifiedError();\n }\n\n const {token, refreshToken, adminRole} = await createSessionTokens(user, params.workspaces);\n\n return {token, refreshToken, user, adminRole};\n}\n\nexport interface CreateSessionForUserParams {\n userId?: string | undefined;\n email?: string | undefined;\n workspaces: WorkspacesInterModuleClient;\n}\n\nexport interface CreateSessionForUserResult {\n token: string;\n refreshToken: string;\n user: User;\n adminRole: AdminRole | null;\n}\n\nexport type CreateSessionForUserError =\n | AuthDependencyUnavailableError\n | EmailNotVerifiedError\n | InvalidCredentialsError\n | UserNotFoundError;\n\nexport async function createSessionForUser(\n params: CreateSessionForUserParams,\n): Promise<CreateSessionForUserResult> {\n let user: User | undefined;\n if (params.userId) user = await findUserById({id: params.userId});\n else if (params.email) user = await findUserByEmail({email: emailSchema.parse(params.email)});\n\n if (!user) {\n throw new UserNotFoundError(params.userId ?? params.email ?? 'unknown');\n }\n if (user.emailVerifiedAt === null) {\n throw new EmailNotVerifiedError();\n }\n if (user.status !== 'active') {\n throw new InvalidCredentialsError();\n }\n\n const {token, refreshToken, adminRole} = await createSessionTokens(user, params.workspaces);\n\n return {token, refreshToken, user, adminRole};\n}\n\n/**\n * Cap for impersonated session tokens: the TTL is min(`AUTH_JWT_EXPIRES_IN`,\n * this), so an impersonated window can never outlive 15 minutes no matter how\n * the deployment configures ordinary access tokens.\n */\nexport const IMPERSONATION_MAX_TTL_SECONDS = 15 * 60;\n\nexport interface CreateImpersonatedSessionTokenParams {\n targetUserId: string;\n /** The administrator the token is minted for; signed into the `impersonatorId` claim. */\n impersonatorId: string;\n workspaces: WorkspacesInterModuleClient;\n /**\n * Re-sign lifetime override used by an idempotent replay: the remaining\n * lifetime to the stored `expires_at`, so a replay never extends the window.\n * Defaults to min(`AUTH_JWT_EXPIRES_IN`, 15 minutes).\n */\n expiresIn?: string | undefined;\n}\n\nexport interface CreateImpersonatedSessionTokenResult {\n token: string;\n expiresAt: Date;\n user: User;\n}\n\nfunction impersonationTtlSeconds(): number {\n const configuredSeconds = durationToSeconds(config.AUTH_JWT_EXPIRES_IN);\n const ttlSeconds = Math.min(configuredSeconds, IMPERSONATION_MAX_TTL_SECONDS);\n if (ttlSeconds <= 0) {\n throw new TypeError(\n `AUTH_JWT_EXPIRES_IN must be a valid duration of at least 1 second, got ${config.AUTH_JWT_EXPIRES_IN}`,\n );\n }\n return ttlSeconds;\n}\n\n/**\n * Mints an access-token-only impersonated session for a target user: the same\n * eligibility as login (active account, verified email), the target's real\n * membership claims, a capped TTL, the `impersonatorId` claim, and **no**\n * `refreshSessionId`. It creates no refresh session and sets no cookie, so\n * nothing persisted can resurrect the session after the token window.\n */\nexport async function createImpersonatedSessionToken(\n params: CreateImpersonatedSessionTokenParams,\n): Promise<CreateImpersonatedSessionTokenResult> {\n // Rule 1 lives in the mint primitive as well as the command entry, so the\n // exported package API can never bypass the kill switch: the flag is a\n // configuration read that also holds on the in-transaction replay path.\n if (!config.AUTH_IMPERSONATION_ENABLED) throw new ImpersonationDisabledError();\n\n const user = await findUserById({id: params.targetUserId});\n if (!user) {\n throw new UserNotFoundError(params.targetUserId);\n }\n if (user.emailVerifiedAt === null) {\n throw new EmailNotVerifiedError();\n }\n if (user.status !== 'active') {\n throw new InvalidCredentialsError();\n }\n\n const memberships = await loadTokenMemberships(user.id, params.workspaces);\n const ttlSeconds =\n params.expiresIn === undefined\n ? impersonationTtlSeconds()\n : Math.min(durationToSeconds(params.expiresIn), IMPERSONATION_MAX_TTL_SECONDS);\n if (ttlSeconds <= 0) {\n throw new TypeError(\n `Impersonation token TTL must be at least 1 second, got ${params.expiresIn}`,\n );\n }\n\n const token = await signUserToken({\n userId: user.id,\n email: user.email,\n name: user.name,\n memberships,\n impersonatorId: params.impersonatorId,\n secret: userAccessTokenKey(),\n expiresIn: `${ttlSeconds}s`,\n });\n\n // The advertised expiry is the token's actual signed `exp`, never a\n // clock-derived estimate: the signer stamps `iat`/`exp` in whole seconds, so\n // `Date.now() + ttl` could drift up to a second from the signed claims in\n // either direction. Deriving `expiresAt` from the signed token keeps the\n // response, the stored command result, and the bearer token exactly aligned,\n // which is what lets a replay re-sign with a TTL that never extends the\n // window (`exp` of the re-signed token is at most the original `exp`).\n const claims = await verifyUserToken({token, secret: userAccessTokenKey()});\n return {token, expiresAt: new Date(claims.exp * 1000), user};\n}\n\nexport interface RefreshAccessTokenResult {\n token: string;\n /** Undefined on a grace-window hit: keep the existing cookie instead of rotating it. */\n refreshToken: string | undefined;\n user: User;\n adminRole: AdminRole | null;\n}\n\nexport async function refreshAccessToken(params: {\n refreshToken: string;\n workspaces: WorkspacesInterModuleClient;\n}): Promise<RefreshAccessTokenResult> {\n const currentHashedToken = hashOpaqueToken(params.refreshToken);\n const current = await findRefreshTokenByHash({hashedToken: currentHashedToken});\n if (!current) {\n recordRefreshOutcome('rejected');\n throw new TokenInvalidError('Refresh token is invalid or expired');\n }\n\n const user = await findUserById({id: current.userId});\n if (user?.status !== 'active') {\n await revokeRefreshSession({sessionId: current.sessionId, userId: current.userId});\n recordRefreshOutcome('rejected');\n throw new TokenInvalidError('Refresh token is invalid or expired');\n }\n const adminRole = await getCurrentAdminRole({userId: user.id});\n\n // Within the grace window a rotated token means a concurrent refresh (e.g. a\n // second tab); past it, reuse of a retired token means a compromised session.\n if (current.rotatedAt) {\n if (isWithinRotationGrace(current)) {\n const memberships = await loadTokenMemberships(user.id, params.workspaces);\n const token = await signAccessToken(user, memberships, current.sessionId);\n recordRefreshOutcome('grace');\n return {token, refreshToken: undefined, user, adminRole};\n }\n await revokeRefreshTokensForUser({userId: user.id});\n recordRefreshOutcome('rejected');\n throw new TokenInvalidError('Refresh token reused after rotation');\n }\n\n // Keep the external membership snapshot ahead of rotation so an outage\n // cannot retire a refresh token that the caller can still retry.\n const memberships = await loadTokenMemberships(user.id, params.workspaces);\n\n // Losing the CAS requires a state check: another request may have rotated,\n // revoked, or expired the token before this refresh could claim it.\n const nextRefreshToken = generateOpaqueToken('refreshToken');\n const rotated = await rotateRefreshToken({\n userId: current.userId,\n id: current.id,\n currentHashedToken,\n nextHashedToken: hashOpaqueToken(nextRefreshToken),\n expiresAt: daysFromNow(config.AUTH_REFRESH_TOKEN_EXPIRES_IN_DAYS),\n });\n if (!rotated) {\n const latestUser = await findUserById({id: current.userId});\n if (latestUser?.status !== 'active') {\n recordRefreshOutcome('rejected');\n throw new TokenInvalidError('Refresh token is invalid or expired');\n }\n const latest = await findRefreshTokenByHash({hashedToken: currentHashedToken});\n if (!latest || !isWithinRotationGrace(latest)) {\n recordRefreshOutcome('rejected');\n throw new TokenInvalidError('Refresh token is invalid or expired');\n }\n const token = await signAccessToken(user, memberships, latest.sessionId);\n recordRefreshOutcome('grace');\n return {token, refreshToken: undefined, user, adminRole};\n }\n\n const token = await signAccessToken(user, memberships, current.sessionId);\n recordRefreshOutcome('rotated');\n return {token, refreshToken: nextRefreshToken, user, adminRole};\n}\n\nexport interface ConfirmEmailVerificationResult {\n token: string;\n refreshToken: string;\n user: User;\n adminRole: AdminRole | null;\n}\n\nexport interface ResendEmailVerificationResult {\n nextResendAvailableAt: Date;\n}\n\nexport async function confirmEmailVerification(params: {\n email: string;\n challengeId: string;\n code: string;\n workspaces: WorkspacesInterModuleClient;\n}): Promise<ConfirmEmailVerificationResult> {\n const user = await findUserByEmail({email: emailSchema.parse(params.email)});\n if (!user) throw new TokenInvalidError('Verification code is invalid or expired');\n await confirmEmailChallenge({id: params.challengeId, code: params.code, continuation: user.id});\n await consumeEmailChallengeProof({\n id: params.challengeId,\n purpose: PASSWORD_VERIFICATION_PURPOSE,\n continuation: user.id,\n });\n\n const verifiedUser = await markEmailVerified({userId: user.id});\n if (verifiedUser?.status !== 'active') {\n throw new TokenInvalidError('Verification code is invalid or expired');\n }\n\n const {token, refreshToken, adminRole} = await createSessionTokens(\n verifiedUser,\n params.workspaces,\n );\n\n return {token, refreshToken, user: verifiedUser, adminRole};\n}\n\nexport async function resendEmailVerification(params: {\n email: string;\n challengeId: string;\n sourceIp: string;\n}): Promise<ResendEmailVerificationResult> {\n const user = await findUserByEmail({email: emailSchema.parse(params.email)});\n if (!user || user.emailVerifiedAt || user.status !== 'active') {\n return {nextResendAvailableAt: new Date()};\n }\n const result = await resendEmailChallenge({\n id: params.challengeId,\n continuation: user.id,\n sourceIp: params.sourceIp,\n });\n\n return {nextResendAvailableAt: result.nextResendAvailableAt};\n}\n\nexport async function requestPasswordReset(params: {email: string}): Promise<void> {\n const user = await findUserByEmail({email: params.email});\n if (user?.status !== 'active' || user.hashedPassword === null) {\n return;\n }\n\n const rawToken = generateOpaqueToken('passwordReset');\n await createPasswordReset({\n userId: user.id,\n hashedToken: hashOpaqueToken(rawToken),\n expiresAt: hoursFromNow(RESET_TTL_HOURS),\n sendEmail: {\n email: user.email,\n resetLink: passwordResetLink(rawToken),\n expiresInHours: RESET_TTL_HOURS,\n },\n });\n}\n\nexport interface ConfirmPasswordResetResult {\n token: string;\n refreshToken: string;\n user: User;\n adminRole: AdminRole | null;\n}\n\nexport async function confirmPasswordReset(params: {\n token: string;\n newPassword: string;\n workspaces: WorkspacesInterModuleClient;\n}): Promise<ConfirmPasswordResetResult> {\n const consumed = await consumePasswordReset({hashedToken: hashOpaqueToken(params.token)});\n if (!consumed) {\n throw new TokenInvalidError('Reset token is invalid or expired');\n }\n\n const existingUser = await findUserById({id: consumed.userId});\n if (existingUser?.status !== 'active' || existingUser.hashedPassword === null) {\n throw new TokenInvalidError('Reset token is invalid or expired');\n }\n\n const hashedPassword = await hashPassword({password: params.newPassword});\n const user = await updateUserPassword({userId: consumed.userId, hashedPassword});\n if (user?.status !== 'active') {\n throw new TokenInvalidError('Reset token is invalid or expired');\n }\n\n await revokeRefreshTokensForUser({userId: consumed.userId});\n\n const {token, refreshToken, adminRole} = await createSessionTokens(user, params.workspaces);\n\n return {token, refreshToken, user, adminRole};\n}\n\nexport async function changePassword(params: {\n userId: string;\n currentPassword: string;\n newPassword: string;\n refreshToken?: string | undefined;\n}): Promise<void> {\n const user = await findUserById({id: params.userId});\n if (!user) {\n throw new UserNotFoundError(params.userId);\n }\n\n const hasPassword = user.hashedPassword !== null;\n const ok = await verifyPassword({\n password: params.currentPassword,\n hash: user.hashedPassword ?? (await getDummyHash()),\n });\n if (!hasPassword || !ok) {\n throw new InvalidCredentialsError();\n }\n\n const hashedPassword = await hashPassword({password: params.newPassword});\n const currentRefreshSession = params.refreshToken\n ? await findActiveRefreshTokenByHash({hashedToken: hashOpaqueToken(params.refreshToken)})\n : undefined;\n const exceptRefreshTokenId =\n currentRefreshSession?.userId === user.id ? currentRefreshSession.id : undefined;\n\n await updateUserPassword({userId: user.id, hashedPassword});\n await revokeRefreshTokensForUser({userId: user.id, exceptRefreshTokenId});\n}\n\nexport async function logout(params: {refreshToken?: string | undefined}): Promise<void> {\n if (!params.refreshToken) return;\n const refreshToken = await findRefreshTokenByHash({\n hashedToken: hashOpaqueToken(params.refreshToken),\n });\n if (!refreshToken) return;\n await revokeRefreshSession({sessionId: refreshToken.sessionId, userId: refreshToken.userId});\n}\n\nexport interface GetCurrentUserResult {\n user: User;\n}\n\nexport async function getCurrentUser(params: {userId: string}): Promise<GetCurrentUserResult> {\n const user = await findUserById({id: params.userId});\n if (!user) {\n throw new UserNotFoundError(params.userId);\n }\n\n return {user};\n}\n"],"names":["emailSchema","confirmEmailChallenge","consumeEmailChallengeProof","createEmailChallenge","resendEmailChallenge","workspacesInterModuleContract","isInterModuleKnownError","userAccessTokenKey","durationToSeconds","generateOpaqueToken","hashOpaqueToken","config","consumePasswordReset","createPasswordReset","createRefreshTokenForActiveUser","findActiveRefreshTokenByHash","findRefreshTokenByHash","revokeRefreshSession","revokeRefreshTokensForUser","rotateRefreshToken","createUser","createDbUser","findUserByEmail","findUserById","markEmailVerified","provisionUser","provisionDbUser","updateUserPassword","recordTokenRefreshed","getCurrentAdminRole","AuthDependencyUnavailableError","EmailNotVerifiedError","EmailTakenError","ImpersonationDisabledError","InvalidCredentialsError","InvitationEmailMismatchError","SignupNotAllowedError","TokenAlreadyUsedError","TokenExpiredError","TokenInvalidError","UserNotFoundError","signUserToken","verifyUserToken","hashPassword","verifyPassword","createEnvironmentSignupPolicy","RESET_TTL_HOURS","PASSWORD_VERIFICATION_PURPOSE","DEFAULT_SIGNUP_NOT_ALLOWED_MESSAGE","defaultSignupPolicy","assertSignupAllowed","params","result","signupPolicy","isSignupAllowed","email","emailVerified","source","allowed","message","format","dummyHashCache","getDummyHash","password","hoursFromNow","hours","Date","now","daysFromNow","days","recordRefreshOutcome","outcome","loadTokenMemberships","userId","workspaces","memberships","listMembershipsForTokenClaims","catch","error","signAccessToken","user","refreshSessionId","id","name","secret","expiresIn","AUTH_JWT_EXPIRES_IN","isWithinRotationGrace","refreshToken","rotatedAt","graceMs","AUTH_REFRESH_ROTATION_GRACE_SECONDS","getTime","createRefreshSession","session","sessionId","hashedToken","expiresAt","AUTH_REFRESH_TOKEN_EXPIRES_IN_DAYS","createSessionTokens","crypto","randomUUID","token","adminRole","passwordResetLink","rawToken","CLIENT_BASE_URL","invitationErrorFromKnownError","method","contractMethod","methods","code","invitationErrorMessage","parse","existing","signup","canResumeVerification","status","emailVerifiedAt","hashedPassword","hash","emailChallenge","purpose","continuation","idempotencyKey","sourceIp","signedUp","viaInvitation","signupWithInvitation","preflightInvitationAcceptance","invitationToken","membership","acceptError","acceptInvitation","workspaceId","verified","login","hasPassword","ok","createSessionForUser","IMPERSONATION_MAX_TTL_SECONDS","impersonationTtlSeconds","configuredSeconds","ttlSeconds","Math","min","TypeError","createImpersonatedSessionToken","AUTH_IMPERSONATION_ENABLED","targetUserId","undefined","impersonatorId","claims","exp","refreshAccessToken","currentHashedToken","current","nextRefreshToken","rotated","nextHashedToken","latestUser","latest","confirmEmailVerification","challengeId","verifiedUser","resendEmailVerification","nextResendAvailableAt","requestPasswordReset","sendEmail","resetLink","expiresInHours","confirmPasswordReset","consumed","existingUser","newPassword","changePassword","currentPassword","currentRefreshSession","exceptRefreshTokenId","logout","getCurrentUser"],"mappings":"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"}
@@ -0,0 +1,75 @@
1
+ export type AgentClientKind = 'registered' | 'cimd';
2
+ export interface AgentClient {
3
+ id: string;
4
+ clientId: string;
5
+ name: string;
6
+ redirectUris: string[];
7
+ kind: AgentClientKind;
8
+ lastSeenAt: Date | null;
9
+ unreferencedAt: Date | null;
10
+ createdAt: Date;
11
+ updatedAt: Date;
12
+ }
13
+ export interface AgentAuthorizationRequest {
14
+ id: string;
15
+ clientId: string;
16
+ redirectUri: string;
17
+ resource: string;
18
+ scopes: string[];
19
+ codeChallenge: string;
20
+ state: string | null;
21
+ expiresAt: Date;
22
+ consumedAt: Date | null;
23
+ createdAt: Date;
24
+ updatedAt: Date;
25
+ }
26
+ export interface AgentGrant {
27
+ id: string;
28
+ userId: string;
29
+ workspaceId: string;
30
+ clientId: string;
31
+ scopes: string[];
32
+ lastUsedAt: Date | null;
33
+ revokedAt: Date | null;
34
+ terminalAt: Date | null;
35
+ createdAt: Date;
36
+ updatedAt: Date;
37
+ }
38
+ export interface AgentAuthorizationCode {
39
+ id: string;
40
+ grantId: string;
41
+ hashedCode: string;
42
+ codeChallenge: string;
43
+ redirectUri: string;
44
+ resource: string;
45
+ expiresAt: Date;
46
+ consumedAt: Date | null;
47
+ createdAt: Date;
48
+ updatedAt: Date;
49
+ }
50
+ export interface AgentRefreshToken {
51
+ id: string;
52
+ grantId: string;
53
+ hashedToken: string;
54
+ expiresAt: Date;
55
+ rotatedAt: Date | null;
56
+ revokedAt: Date | null;
57
+ lastUsedAt: Date | null;
58
+ createdAt: Date;
59
+ updatedAt: Date;
60
+ }
61
+ export interface AgentPersonalAccessToken {
62
+ id: string;
63
+ userId: string;
64
+ workspaceId: string;
65
+ hashedToken: string;
66
+ prefix: string;
67
+ name: string;
68
+ scopes: string[];
69
+ expiresAt: Date;
70
+ lastUsedAt: Date | null;
71
+ revokedAt: Date | null;
72
+ createdAt: Date;
73
+ updatedAt: Date;
74
+ }
75
+ //# sourceMappingURL=agent-access.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-access.d.ts","sourceRoot":"","sources":["../../../src/core/entities/agent-access.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GAAG,YAAY,GAAG,MAAM,CAAC;AAEpD,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,IAAI,EAAE,eAAe,CAAC;IACtB,UAAU,EAAE,IAAI,GAAG,IAAI,CAAC;IACxB,cAAc,EAAE,IAAI,GAAG,IAAI,CAAC;IAC5B,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,yBAAyB;IACxC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,SAAS,EAAE,IAAI,CAAC;IAChB,UAAU,EAAE,IAAI,GAAG,IAAI,CAAC;IACxB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,UAAU,EAAE,IAAI,GAAG,IAAI,CAAC;IACxB,SAAS,EAAE,IAAI,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,IAAI,GAAG,IAAI,CAAC;IACxB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,IAAI,CAAC;IAChB,UAAU,EAAE,IAAI,GAAG,IAAI,CAAC;IACxB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,GAAG,IAAI,CAAC;IACvB,SAAS,EAAE,IAAI,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,IAAI,GAAG,IAAI,CAAC;IACxB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,wBAAwB;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,EAAE,IAAI,CAAC;IAChB,UAAU,EAAE,IAAI,GAAG,IAAI,CAAC;IACxB,SAAS,EAAE,IAAI,GAAG,IAAI,CAAC;IACvB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB"}
@@ -0,0 +1,3 @@
1
+ export { };
2
+
3
+ //# sourceMappingURL=agent-access.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/core/entities/agent-access.ts"],"sourcesContent":["export type AgentClientKind = 'registered' | 'cimd';\n\nexport interface AgentClient {\n id: string;\n clientId: string;\n name: string;\n redirectUris: string[];\n kind: AgentClientKind;\n lastSeenAt: Date | null;\n unreferencedAt: Date | null;\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface AgentAuthorizationRequest {\n id: string;\n clientId: string;\n redirectUri: string;\n resource: string;\n scopes: string[];\n codeChallenge: string;\n state: string | null;\n expiresAt: Date;\n consumedAt: Date | null;\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface AgentGrant {\n id: string;\n userId: string;\n workspaceId: string;\n clientId: string;\n scopes: string[];\n lastUsedAt: Date | null;\n revokedAt: Date | null;\n terminalAt: Date | null;\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface AgentAuthorizationCode {\n id: string;\n grantId: string;\n hashedCode: string;\n codeChallenge: string;\n redirectUri: string;\n resource: string;\n expiresAt: Date;\n consumedAt: Date | null;\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface AgentRefreshToken {\n id: string;\n grantId: string;\n hashedToken: string;\n expiresAt: Date;\n rotatedAt: Date | null;\n revokedAt: Date | null;\n lastUsedAt: Date | null;\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface AgentPersonalAccessToken {\n id: string;\n userId: string;\n workspaceId: string;\n hashedToken: string;\n prefix: string;\n name: string;\n scopes: string[];\n expiresAt: Date;\n lastUsedAt: Date | null;\n revokedAt: Date | null;\n createdAt: Date;\n updatedAt: Date;\n}\n"],"names":[],"mappings":"AAkEA,WAaC"}
@@ -39,6 +39,9 @@ export declare class AdminRoleRequiredError extends Error {
39
39
  readonly minimumRole: import('@shipfox/api-auth-dto').AdminRole;
40
40
  constructor(minimumRole: import('@shipfox/api-auth-dto').AdminRole);
41
41
  }
42
+ export declare class InvalidAdministratorUserDirectoryFilterError extends Error {
43
+ constructor(message: string);
44
+ }
42
45
  export declare class LastAdminOwnerError extends Error {
43
46
  constructor();
44
47
  }
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,YAAY,CAAC;AAE1D,qBAAa,sBAAuB,SAAQ,KAAK;IAC/C,YAAY,WAAW,EAAE,MAAM,EAG9B;CACF;AAED,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,YAAY,EAAE,EAAE,MAAM,EAGrB;CACF;AAED,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,YAAY,EAAE,EAAE,MAAM,EAGrB;CACF;AAED,qBAAa,gCAAiC,SAAQ,KAAK;IACzD,cAGC;CACF;AAED,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,YAAY,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAG9C;CACF;AAED,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,YAAY,SAAS,EAAE,MAAM,EAG5B;CACF;AAED,qBAAa,eAAgB,SAAQ,KAAK;IACxC,YAAY,KAAK,EAAE,MAAM,EAGxB;CACF;AAED,qBAAa,qBAAsB,SAAQ,KAAK;IAG5C,QAAQ,CAAC,MAAM,CAAC,EAAE,yBAAyB;IAF7C,YACE,OAAO,EAAE,MAAM,EACN,MAAM,CAAC,EAAE,yBAAyB,YAAA,EAI5C;CACF;AAED,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,cAGC;CACF;AAED,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,cAGC;CACF;AAED,qBAAa,8BAA+B,SAAQ,KAAK;IACvD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,SAAkB,KAAK,EAAE,OAAO,CAAC;IAEjC,YAAY,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAK7C;CACF;AAED,qBAAa,sBAAuB,SAAQ,KAAK;IAC/C,QAAQ,CAAC,WAAW,EAAE,OAAO,uBAAuB,EAAE,SAAS,CAAC;IAEhE,YAAY,WAAW,EAAE,OAAO,uBAAuB,EAAE,SAAS,EAIjE;CACF;AAED,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,cAGC;CACF;AAED,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,cAGC;CACF;AAED,qBAAa,4BAA6B,SAAQ,KAAK;IACrD,cAGC;CACF;AAED,qBAAa,yBAA0B,SAAQ,KAAK;IAClD,cAGC;CACF;AAED,qBAAa,+BAAgC,SAAQ,KAAK;IACxD,cAGC;CACF;AAED,qBAAa,6BAA8B,SAAQ,KAAK;IACtD,cAGC;CACF;AAED,qBAAa,0BAA2B,SAAQ,KAAK;IACnD,cAGC;CACF;AAED,qBAAa,0BAA2B,SAAQ,KAAK;IACnD,cAGC;CACF;AAED,qBAAa,mCAAoC,SAAQ,KAAK;IAC5D,cAGC;CACF;AAED,qBAAa,iCAAkC,SAAQ,KAAK;IAC1D,cAGC;CACF;AAED,qBAAa,yBAA0B,SAAQ,KAAK;IAClD,cAGC;CACF;AAED,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,YAAY,MAAM,CAAC,EAAE,MAAM,EAG1B;CACF;AAED,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,cAGC;CACF;AAED,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,cAGC;CACF;AAED,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,YAAY,WAAW,EAAE,MAAM,EAG9B;CACF;AAED,qBAAa,eAAgB,SAAQ,KAAK;IACxC,YAAY,WAAW,EAAE,MAAM,EAG9B;CACF;AAED,qBAAa,4BAA6B,SAAQ,KAAK;IACrD,cAGC;CACF;AAED,qBAAa,yBAA0B,SAAQ,KAAK;IAClD,YAAY,KAAK,EAAE,MAAM,EAGxB;CACF"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,YAAY,CAAC;AAE1D,qBAAa,sBAAuB,SAAQ,KAAK;IAC/C,YAAY,WAAW,EAAE,MAAM,EAG9B;CACF;AAED,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,YAAY,EAAE,EAAE,MAAM,EAGrB;CACF;AAED,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,YAAY,EAAE,EAAE,MAAM,EAGrB;CACF;AAED,qBAAa,gCAAiC,SAAQ,KAAK;IACzD,cAGC;CACF;AAED,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,YAAY,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAG9C;CACF;AAED,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,YAAY,SAAS,EAAE,MAAM,EAG5B;CACF;AAED,qBAAa,eAAgB,SAAQ,KAAK;IACxC,YAAY,KAAK,EAAE,MAAM,EAGxB;CACF;AAED,qBAAa,qBAAsB,SAAQ,KAAK;IAG5C,QAAQ,CAAC,MAAM,CAAC,EAAE,yBAAyB;IAF7C,YACE,OAAO,EAAE,MAAM,EACN,MAAM,CAAC,EAAE,yBAAyB,YAAA,EAI5C;CACF;AAED,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,cAGC;CACF;AAED,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,cAGC;CACF;AAED,qBAAa,8BAA+B,SAAQ,KAAK;IACvD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,SAAkB,KAAK,EAAE,OAAO,CAAC;IAEjC,YAAY,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAK7C;CACF;AAED,qBAAa,sBAAuB,SAAQ,KAAK;IAC/C,QAAQ,CAAC,WAAW,EAAE,OAAO,uBAAuB,EAAE,SAAS,CAAC;IAEhE,YAAY,WAAW,EAAE,OAAO,uBAAuB,EAAE,SAAS,EAIjE;CACF;AAED,qBAAa,4CAA6C,SAAQ,KAAK;IACrE,YAAY,OAAO,EAAE,MAAM,EAG1B;CACF;AAED,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,cAGC;CACF;AAED,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,cAGC;CACF;AAED,qBAAa,4BAA6B,SAAQ,KAAK;IACrD,cAGC;CACF;AAED,qBAAa,yBAA0B,SAAQ,KAAK;IAClD,cAGC;CACF;AAED,qBAAa,+BAAgC,SAAQ,KAAK;IACxD,cAGC;CACF;AAED,qBAAa,6BAA8B,SAAQ,KAAK;IACtD,cAGC;CACF;AAED,qBAAa,0BAA2B,SAAQ,KAAK;IACnD,cAGC;CACF;AAED,qBAAa,0BAA2B,SAAQ,KAAK;IACnD,cAGC;CACF;AAED,qBAAa,mCAAoC,SAAQ,KAAK;IAC5D,cAGC;CACF;AAED,qBAAa,iCAAkC,SAAQ,KAAK;IAC1D,cAGC;CACF;AAED,qBAAa,yBAA0B,SAAQ,KAAK;IAClD,cAGC;CACF;AAED,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,YAAY,MAAM,CAAC,EAAE,MAAM,EAG1B;CACF;AAED,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,cAGC;CACF;AAED,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,cAGC;CACF;AAED,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,YAAY,WAAW,EAAE,MAAM,EAG9B;CACF;AAED,qBAAa,eAAgB,SAAQ,KAAK;IACxC,YAAY,WAAW,EAAE,MAAM,EAG9B;CACF;AAED,qBAAa,4BAA6B,SAAQ,KAAK;IACrD,cAGC;CACF;AAED,qBAAa,yBAA0B,SAAQ,KAAK;IAClD,YAAY,KAAK,EAAE,MAAM,EAGxB;CACF"}
@@ -73,6 +73,12 @@ export class AdminRoleRequiredError extends Error {
73
73
  this.minimumRole = minimumRole;
74
74
  }
75
75
  }
76
+ export class InvalidAdministratorUserDirectoryFilterError extends Error {
77
+ constructor(message){
78
+ super(message);
79
+ this.name = 'InvalidAdministratorUserDirectoryFilterError';
80
+ }
81
+ }
76
82
  export class LastAdminOwnerError extends Error {
77
83
  constructor(){
78
84
  super('Cannot remove the final active administrator owner');
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/errors.ts"],"sourcesContent":["import type {SignupDenialMessageFormat} from './ports.js';\n\nexport class WorkspaceNotFoundError extends Error {\n constructor(workspaceId: string) {\n super(`Workspace not found: ${workspaceId}`);\n this.name = 'WorkspaceNotFoundError';\n }\n}\n\nexport class ApiKeyNotFoundError extends Error {\n constructor(id: string) {\n super(`API key not found: ${id}`);\n this.name = 'ApiKeyNotFoundError';\n }\n}\n\nexport class InvitationNotFoundError extends Error {\n constructor(id: string) {\n super(`Invitation not found: ${id}`);\n this.name = 'InvitationNotFoundError';\n }\n}\n\nexport class InvitationWorkspaceMismatchError extends Error {\n constructor() {\n super('Invitation does not belong to this workspace');\n this.name = 'InvitationWorkspaceMismatchError';\n }\n}\n\nexport class MembershipNotFoundError extends Error {\n constructor(userId: string, workspaceId: string) {\n super(`Membership not found for user ${userId} in workspace ${workspaceId}`);\n this.name = 'MembershipNotFoundError';\n }\n}\n\nexport class UserNotFoundError extends Error {\n constructor(idOrEmail: string) {\n super(`User not found: ${idOrEmail}`);\n this.name = 'UserNotFoundError';\n }\n}\n\nexport class EmailTakenError extends Error {\n constructor(email: string) {\n super(`Email already registered: ${email}`);\n this.name = 'EmailTakenError';\n }\n}\n\nexport class SignupNotAllowedError extends Error {\n constructor(\n message: string,\n readonly format?: SignupDenialMessageFormat,\n ) {\n super(message);\n this.name = 'SignupNotAllowedError';\n }\n}\n\nexport class InvalidCredentialsError extends Error {\n constructor() {\n super('Invalid credentials');\n this.name = 'InvalidCredentialsError';\n }\n}\n\nexport class EmailNotVerifiedError extends Error {\n constructor() {\n super('Email not verified');\n this.name = 'EmailNotVerifiedError';\n }\n}\n\nexport class AuthDependencyUnavailableError extends Error {\n readonly dependency: string;\n override readonly cause: unknown;\n\n constructor(dependency: string, cause: unknown) {\n super(`Authentication dependency unavailable: ${dependency}`);\n this.name = 'AuthDependencyUnavailableError';\n this.dependency = dependency;\n this.cause = cause;\n }\n}\n\nexport class AdminRoleRequiredError extends Error {\n readonly minimumRole: import('@shipfox/api-auth-dto').AdminRole;\n\n constructor(minimumRole: import('@shipfox/api-auth-dto').AdminRole) {\n super(`Administrator role required: ${minimumRole}`);\n this.name = 'AdminRoleRequiredError';\n this.minimumRole = minimumRole;\n }\n}\n\nexport class LastAdminOwnerError extends Error {\n constructor() {\n super('Cannot remove the final active administrator owner');\n this.name = 'LastAdminOwnerError';\n }\n}\n\nexport class AdminGrantNotFoundError extends Error {\n constructor() {\n super('Administrator grant not found');\n this.name = 'AdminGrantNotFoundError';\n }\n}\n\nexport class AdminGrantAlreadyExistsError extends Error {\n constructor() {\n super('Administrator grant already exists');\n this.name = 'AdminGrantAlreadyExistsError';\n }\n}\n\nexport class AdminBootstrapClosedError extends Error {\n constructor() {\n super('First administrator owner has already been created');\n this.name = 'AdminBootstrapClosedError';\n }\n}\n\nexport class InvalidAdminBootstrapTokenError extends Error {\n constructor() {\n super('Bootstrap token is invalid');\n this.name = 'InvalidAdminBootstrapTokenError';\n }\n}\n\nexport class AdminIdempotencyKeyReuseError extends Error {\n constructor() {\n super('Idempotency key was already used for a different command');\n this.name = 'AdminIdempotencyKeyReuseError';\n }\n}\n\nexport class ImpersonationDisabledError extends Error {\n constructor() {\n super('Impersonation is disabled');\n this.name = 'ImpersonationDisabledError';\n }\n}\n\nexport class CannotImpersonateSelfError extends Error {\n constructor() {\n super('Cannot impersonate yourself');\n this.name = 'CannotImpersonateSelfError';\n }\n}\n\nexport class CannotImpersonateAdministratorError extends Error {\n constructor() {\n super('Cannot impersonate an administrator');\n this.name = 'CannotImpersonateAdministratorError';\n }\n}\n\nexport class ImpersonationTargetNotActiveError extends Error {\n constructor() {\n super('Impersonation target is not active or verified');\n this.name = 'ImpersonationTargetNotActiveError';\n }\n}\n\nexport class ImpersonationExpiredError extends Error {\n constructor() {\n super('Impersonation session has expired');\n this.name = 'ImpersonationExpiredError';\n }\n}\n\nexport class TokenInvalidError extends Error {\n constructor(reason?: string) {\n super(reason ? `Invalid token: ${reason}` : 'Invalid token');\n this.name = 'TokenInvalidError';\n }\n}\n\nexport class TokenExpiredError extends Error {\n constructor() {\n super('Token has expired');\n this.name = 'TokenExpiredError';\n }\n}\n\nexport class TokenAlreadyUsedError extends Error {\n constructor() {\n super('Token has already been used');\n this.name = 'TokenAlreadyUsedError';\n }\n}\n\nexport class MembershipRequiredError extends Error {\n constructor(workspaceId: string) {\n super(`Membership required for workspace: ${workspaceId}`);\n this.name = 'MembershipRequiredError';\n }\n}\n\nexport class LastMemberError extends Error {\n constructor(workspaceId: string) {\n super(`Cannot remove the last member of workspace: ${workspaceId}`);\n this.name = 'LastMemberError';\n }\n}\n\nexport class InvitationEmailMismatchError extends Error {\n constructor() {\n super('Invitation email does not match authenticated user');\n this.name = 'InvitationEmailMismatchError';\n }\n}\n\nexport class OpenInvitationExistsError extends Error {\n constructor(email: string) {\n super(`An open invitation already exists for: ${email}`);\n this.name = 'OpenInvitationExistsError';\n }\n}\n"],"names":["WorkspaceNotFoundError","Error","workspaceId","name","ApiKeyNotFoundError","id","InvitationNotFoundError","InvitationWorkspaceMismatchError","MembershipNotFoundError","userId","UserNotFoundError","idOrEmail","EmailTakenError","email","SignupNotAllowedError","message","format","InvalidCredentialsError","EmailNotVerifiedError","AuthDependencyUnavailableError","dependency","cause","AdminRoleRequiredError","minimumRole","LastAdminOwnerError","AdminGrantNotFoundError","AdminGrantAlreadyExistsError","AdminBootstrapClosedError","InvalidAdminBootstrapTokenError","AdminIdempotencyKeyReuseError","ImpersonationDisabledError","CannotImpersonateSelfError","CannotImpersonateAdministratorError","ImpersonationTargetNotActiveError","ImpersonationExpiredError","TokenInvalidError","reason","TokenExpiredError","TokenAlreadyUsedError","MembershipRequiredError","LastMemberError","InvitationEmailMismatchError","OpenInvitationExistsError"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/core/errors.ts"],"sourcesContent":["import type {SignupDenialMessageFormat} from './ports.js';\n\nexport class WorkspaceNotFoundError extends Error {\n constructor(workspaceId: string) {\n super(`Workspace not found: ${workspaceId}`);\n this.name = 'WorkspaceNotFoundError';\n }\n}\n\nexport class ApiKeyNotFoundError extends Error {\n constructor(id: string) {\n super(`API key not found: ${id}`);\n this.name = 'ApiKeyNotFoundError';\n }\n}\n\nexport class InvitationNotFoundError extends Error {\n constructor(id: string) {\n super(`Invitation not found: ${id}`);\n this.name = 'InvitationNotFoundError';\n }\n}\n\nexport class InvitationWorkspaceMismatchError extends Error {\n constructor() {\n super('Invitation does not belong to this workspace');\n this.name = 'InvitationWorkspaceMismatchError';\n }\n}\n\nexport class MembershipNotFoundError extends Error {\n constructor(userId: string, workspaceId: string) {\n super(`Membership not found for user ${userId} in workspace ${workspaceId}`);\n this.name = 'MembershipNotFoundError';\n }\n}\n\nexport class UserNotFoundError extends Error {\n constructor(idOrEmail: string) {\n super(`User not found: ${idOrEmail}`);\n this.name = 'UserNotFoundError';\n }\n}\n\nexport class EmailTakenError extends Error {\n constructor(email: string) {\n super(`Email already registered: ${email}`);\n this.name = 'EmailTakenError';\n }\n}\n\nexport class SignupNotAllowedError extends Error {\n constructor(\n message: string,\n readonly format?: SignupDenialMessageFormat,\n ) {\n super(message);\n this.name = 'SignupNotAllowedError';\n }\n}\n\nexport class InvalidCredentialsError extends Error {\n constructor() {\n super('Invalid credentials');\n this.name = 'InvalidCredentialsError';\n }\n}\n\nexport class EmailNotVerifiedError extends Error {\n constructor() {\n super('Email not verified');\n this.name = 'EmailNotVerifiedError';\n }\n}\n\nexport class AuthDependencyUnavailableError extends Error {\n readonly dependency: string;\n override readonly cause: unknown;\n\n constructor(dependency: string, cause: unknown) {\n super(`Authentication dependency unavailable: ${dependency}`);\n this.name = 'AuthDependencyUnavailableError';\n this.dependency = dependency;\n this.cause = cause;\n }\n}\n\nexport class AdminRoleRequiredError extends Error {\n readonly minimumRole: import('@shipfox/api-auth-dto').AdminRole;\n\n constructor(minimumRole: import('@shipfox/api-auth-dto').AdminRole) {\n super(`Administrator role required: ${minimumRole}`);\n this.name = 'AdminRoleRequiredError';\n this.minimumRole = minimumRole;\n }\n}\n\nexport class InvalidAdministratorUserDirectoryFilterError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'InvalidAdministratorUserDirectoryFilterError';\n }\n}\n\nexport class LastAdminOwnerError extends Error {\n constructor() {\n super('Cannot remove the final active administrator owner');\n this.name = 'LastAdminOwnerError';\n }\n}\n\nexport class AdminGrantNotFoundError extends Error {\n constructor() {\n super('Administrator grant not found');\n this.name = 'AdminGrantNotFoundError';\n }\n}\n\nexport class AdminGrantAlreadyExistsError extends Error {\n constructor() {\n super('Administrator grant already exists');\n this.name = 'AdminGrantAlreadyExistsError';\n }\n}\n\nexport class AdminBootstrapClosedError extends Error {\n constructor() {\n super('First administrator owner has already been created');\n this.name = 'AdminBootstrapClosedError';\n }\n}\n\nexport class InvalidAdminBootstrapTokenError extends Error {\n constructor() {\n super('Bootstrap token is invalid');\n this.name = 'InvalidAdminBootstrapTokenError';\n }\n}\n\nexport class AdminIdempotencyKeyReuseError extends Error {\n constructor() {\n super('Idempotency key was already used for a different command');\n this.name = 'AdminIdempotencyKeyReuseError';\n }\n}\n\nexport class ImpersonationDisabledError extends Error {\n constructor() {\n super('Impersonation is disabled');\n this.name = 'ImpersonationDisabledError';\n }\n}\n\nexport class CannotImpersonateSelfError extends Error {\n constructor() {\n super('Cannot impersonate yourself');\n this.name = 'CannotImpersonateSelfError';\n }\n}\n\nexport class CannotImpersonateAdministratorError extends Error {\n constructor() {\n super('Cannot impersonate an administrator');\n this.name = 'CannotImpersonateAdministratorError';\n }\n}\n\nexport class ImpersonationTargetNotActiveError extends Error {\n constructor() {\n super('Impersonation target is not active or verified');\n this.name = 'ImpersonationTargetNotActiveError';\n }\n}\n\nexport class ImpersonationExpiredError extends Error {\n constructor() {\n super('Impersonation session has expired');\n this.name = 'ImpersonationExpiredError';\n }\n}\n\nexport class TokenInvalidError extends Error {\n constructor(reason?: string) {\n super(reason ? `Invalid token: ${reason}` : 'Invalid token');\n this.name = 'TokenInvalidError';\n }\n}\n\nexport class TokenExpiredError extends Error {\n constructor() {\n super('Token has expired');\n this.name = 'TokenExpiredError';\n }\n}\n\nexport class TokenAlreadyUsedError extends Error {\n constructor() {\n super('Token has already been used');\n this.name = 'TokenAlreadyUsedError';\n }\n}\n\nexport class MembershipRequiredError extends Error {\n constructor(workspaceId: string) {\n super(`Membership required for workspace: ${workspaceId}`);\n this.name = 'MembershipRequiredError';\n }\n}\n\nexport class LastMemberError extends Error {\n constructor(workspaceId: string) {\n super(`Cannot remove the last member of workspace: ${workspaceId}`);\n this.name = 'LastMemberError';\n }\n}\n\nexport class InvitationEmailMismatchError extends Error {\n constructor() {\n super('Invitation email does not match authenticated user');\n this.name = 'InvitationEmailMismatchError';\n }\n}\n\nexport class OpenInvitationExistsError extends Error {\n constructor(email: string) {\n super(`An open invitation already exists for: ${email}`);\n this.name = 'OpenInvitationExistsError';\n }\n}\n"],"names":["WorkspaceNotFoundError","Error","workspaceId","name","ApiKeyNotFoundError","id","InvitationNotFoundError","InvitationWorkspaceMismatchError","MembershipNotFoundError","userId","UserNotFoundError","idOrEmail","EmailTakenError","email","SignupNotAllowedError","message","format","InvalidCredentialsError","EmailNotVerifiedError","AuthDependencyUnavailableError","dependency","cause","AdminRoleRequiredError","minimumRole","InvalidAdministratorUserDirectoryFilterError","LastAdminOwnerError","AdminGrantNotFoundError","AdminGrantAlreadyExistsError","AdminBootstrapClosedError","InvalidAdminBootstrapTokenError","AdminIdempotencyKeyReuseError","ImpersonationDisabledError","CannotImpersonateSelfError","CannotImpersonateAdministratorError","ImpersonationTargetNotActiveError","ImpersonationExpiredError","TokenInvalidError","reason","TokenExpiredError","TokenAlreadyUsedError","MembershipRequiredError","LastMemberError","InvitationEmailMismatchError","OpenInvitationExistsError"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"runner-session-token.d.ts","sourceRoot":"","sources":["../../src/core/runner-session-token.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,wBAAwB,EAE9B,MAAM,uBAAuB,CAAC;AAO/B,MAAM,MAAM,6BAA6B,GAAG,IAAI,CAAC,wBAAwB,EAAE,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC,CAAC;AAElG,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,6BAA6B,GACpC,OAAO,CAAC,MAAM,CAAC,CAejB;AAED,oFAAoF;AACpF,wBAAsB,wBAAwB,CAC5C,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,wBAAwB,GAAG,IAAI,CAAC,CAc1C"}
1
+ {"version":3,"file":"runner-session-token.d.ts","sourceRoot":"","sources":["../../src/core/runner-session-token.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,wBAAwB,EAE9B,MAAM,uBAAuB,CAAC;AAO/B,MAAM,MAAM,6BAA6B,GAAG,IAAI,CAAC,wBAAwB,EAAE,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC,CAAC;AAElG,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,6BAA6B,GACpC,OAAO,CAAC,MAAM,CAAC,CAgBjB;AAED,oFAAoF;AACpF,wBAAsB,wBAAwB,CAC5C,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,wBAAwB,GAAG,IAAI,CAAC,CAc1C"}
@@ -10,7 +10,8 @@ export async function issueRunnerSessionToken(claims) {
10
10
  workspaceId: claims.workspaceId,
11
11
  scope: claims.scope,
12
12
  labels: claims.labels,
13
- maxClaims: claims.maxClaims
13
+ maxClaims: claims.maxClaims,
14
+ lifecycleCapabilities: claims.lifecycleCapabilities
14
15
  },
15
16
  secret: runnerSessionTokenKey(),
16
17
  expiresIn: config.AUTH_RUNNER_SESSION_TOKEN_EXPIRES_IN,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/runner-session-token.ts"],"sourcesContent":["import {\n RUNNER_SESSION_TOKEN_AUDIENCE,\n type RunnerSessionTokenClaims,\n runnerSessionTokenClaimsSchema,\n} from '@shipfox/api-auth-dto';\nimport {runnerSessionTokenKey} from '@shipfox/node-auth-root-key';\nimport {signHs256, verifyHs256} from '@shipfox/node-jwt';\nimport {config} from '#config.js';\nimport {recordTokenIssued, recordTokenVerified} from '#metrics/index.js';\n\n// `aud`, `iat` and `exp` are set by the codec (jose); callers supply the business ids only.\nexport type IssueRunnerSessionTokenParams = Omit<RunnerSessionTokenClaims, 'aud' | 'iat' | 'exp'>;\n\nexport async function issueRunnerSessionToken(\n claims: IssueRunnerSessionTokenParams,\n): Promise<string> {\n const token = await signHs256({\n payload: {\n runnerSessionId: claims.runnerSessionId,\n workspaceId: claims.workspaceId,\n scope: claims.scope,\n labels: claims.labels,\n maxClaims: claims.maxClaims,\n },\n secret: runnerSessionTokenKey(),\n expiresIn: config.AUTH_RUNNER_SESSION_TOKEN_EXPIRES_IN,\n audience: RUNNER_SESSION_TOKEN_AUDIENCE,\n });\n recordTokenIssued('runner_session');\n return token;\n}\n\n/** Returns the claims on success, or `null` for any invalid input. Never throws. */\nexport async function verifyRunnerSessionToken(\n token: string,\n): Promise<RunnerSessionTokenClaims | null> {\n try {\n const claims = await verifyHs256({\n token,\n secret: runnerSessionTokenKey(),\n schema: runnerSessionTokenClaimsSchema,\n audience: RUNNER_SESSION_TOKEN_AUDIENCE,\n });\n recordTokenVerified('runner_session', 'ok');\n return claims;\n } catch {\n recordTokenVerified('runner_session', 'rejected');\n return null;\n }\n}\n"],"names":["RUNNER_SESSION_TOKEN_AUDIENCE","runnerSessionTokenClaimsSchema","runnerSessionTokenKey","signHs256","verifyHs256","config","recordTokenIssued","recordTokenVerified","issueRunnerSessionToken","claims","token","payload","runnerSessionId","workspaceId","scope","labels","maxClaims","secret","expiresIn","AUTH_RUNNER_SESSION_TOKEN_EXPIRES_IN","audience","verifyRunnerSessionToken","schema"],"mappings":"AAAA,SACEA,6BAA6B,EAE7BC,8BAA8B,QACzB,wBAAwB;AAC/B,SAAQC,qBAAqB,QAAO,8BAA8B;AAClE,SAAQC,SAAS,EAAEC,WAAW,QAAO,oBAAoB;AACzD,SAAQC,MAAM,QAAO,aAAa;AAClC,SAAQC,iBAAiB,EAAEC,mBAAmB,QAAO,oBAAoB;AAKzE,OAAO,eAAeC,wBACpBC,MAAqC;IAErC,MAAMC,QAAQ,MAAMP,UAAU;QAC5BQ,SAAS;YACPC,iBAAiBH,OAAOG,eAAe;YACvCC,aAAaJ,OAAOI,WAAW;YAC/BC,OAAOL,OAAOK,KAAK;YACnBC,QAAQN,OAAOM,MAAM;YACrBC,WAAWP,OAAOO,SAAS;QAC7B;QACAC,QAAQf;QACRgB,WAAWb,OAAOc,oCAAoC;QACtDC,UAAUpB;IACZ;IACAM,kBAAkB;IAClB,OAAOI;AACT;AAEA,kFAAkF,GAClF,OAAO,eAAeW,yBACpBX,KAAa;IAEb,IAAI;QACF,MAAMD,SAAS,MAAML,YAAY;YAC/BM;YACAO,QAAQf;YACRoB,QAAQrB;YACRmB,UAAUpB;QACZ;QACAO,oBAAoB,kBAAkB;QACtC,OAAOE;IACT,EAAE,OAAM;QACNF,oBAAoB,kBAAkB;QACtC,OAAO;IACT;AACF"}
1
+ {"version":3,"sources":["../../src/core/runner-session-token.ts"],"sourcesContent":["import {\n RUNNER_SESSION_TOKEN_AUDIENCE,\n type RunnerSessionTokenClaims,\n runnerSessionTokenClaimsSchema,\n} from '@shipfox/api-auth-dto';\nimport {runnerSessionTokenKey} from '@shipfox/node-auth-root-key';\nimport {signHs256, verifyHs256} from '@shipfox/node-jwt';\nimport {config} from '#config.js';\nimport {recordTokenIssued, recordTokenVerified} from '#metrics/index.js';\n\n// `aud`, `iat` and `exp` are set by the codec (jose); callers supply the business ids only.\nexport type IssueRunnerSessionTokenParams = Omit<RunnerSessionTokenClaims, 'aud' | 'iat' | 'exp'>;\n\nexport async function issueRunnerSessionToken(\n claims: IssueRunnerSessionTokenParams,\n): Promise<string> {\n const token = await signHs256({\n payload: {\n runnerSessionId: claims.runnerSessionId,\n workspaceId: claims.workspaceId,\n scope: claims.scope,\n labels: claims.labels,\n maxClaims: claims.maxClaims,\n lifecycleCapabilities: claims.lifecycleCapabilities,\n },\n secret: runnerSessionTokenKey(),\n expiresIn: config.AUTH_RUNNER_SESSION_TOKEN_EXPIRES_IN,\n audience: RUNNER_SESSION_TOKEN_AUDIENCE,\n });\n recordTokenIssued('runner_session');\n return token;\n}\n\n/** Returns the claims on success, or `null` for any invalid input. Never throws. */\nexport async function verifyRunnerSessionToken(\n token: string,\n): Promise<RunnerSessionTokenClaims | null> {\n try {\n const claims = await verifyHs256({\n token,\n secret: runnerSessionTokenKey(),\n schema: runnerSessionTokenClaimsSchema,\n audience: RUNNER_SESSION_TOKEN_AUDIENCE,\n });\n recordTokenVerified('runner_session', 'ok');\n return claims;\n } catch {\n recordTokenVerified('runner_session', 'rejected');\n return null;\n }\n}\n"],"names":["RUNNER_SESSION_TOKEN_AUDIENCE","runnerSessionTokenClaimsSchema","runnerSessionTokenKey","signHs256","verifyHs256","config","recordTokenIssued","recordTokenVerified","issueRunnerSessionToken","claims","token","payload","runnerSessionId","workspaceId","scope","labels","maxClaims","lifecycleCapabilities","secret","expiresIn","AUTH_RUNNER_SESSION_TOKEN_EXPIRES_IN","audience","verifyRunnerSessionToken","schema"],"mappings":"AAAA,SACEA,6BAA6B,EAE7BC,8BAA8B,QACzB,wBAAwB;AAC/B,SAAQC,qBAAqB,QAAO,8BAA8B;AAClE,SAAQC,SAAS,EAAEC,WAAW,QAAO,oBAAoB;AACzD,SAAQC,MAAM,QAAO,aAAa;AAClC,SAAQC,iBAAiB,EAAEC,mBAAmB,QAAO,oBAAoB;AAKzE,OAAO,eAAeC,wBACpBC,MAAqC;IAErC,MAAMC,QAAQ,MAAMP,UAAU;QAC5BQ,SAAS;YACPC,iBAAiBH,OAAOG,eAAe;YACvCC,aAAaJ,OAAOI,WAAW;YAC/BC,OAAOL,OAAOK,KAAK;YACnBC,QAAQN,OAAOM,MAAM;YACrBC,WAAWP,OAAOO,SAAS;YAC3BC,uBAAuBR,OAAOQ,qBAAqB;QACrD;QACAC,QAAQhB;QACRiB,WAAWd,OAAOe,oCAAoC;QACtDC,UAAUrB;IACZ;IACAM,kBAAkB;IAClB,OAAOI;AACT;AAEA,kFAAkF,GAClF,OAAO,eAAeY,yBACpBZ,KAAa;IAEb,IAAI;QACF,MAAMD,SAAS,MAAML,YAAY;YAC/BM;YACAQ,QAAQhB;YACRqB,QAAQtB;YACRoB,UAAUrB;QACZ;QACAO,oBAAoB,kBAAkB;QACtC,OAAOE;IACT,EAAE,OAAM;QACNF,oBAAoB,kBAAkB;QACtC,OAAO;IACT;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"admin-user-moderation.d.ts","sourceRoot":"","sources":["../../src/db/admin-user-moderation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,uBAAuB,CAAC;AACrD,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,yBAAyB,CAAC;AAKvE,OAAO,EAKL,KAAK,EAAE,EAER,MAAM,oBAAoB,CAAC;AAY5B,UAAU,2BAA2B;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,yBAAyB,EAAE,MAAM,CAAC;IAClC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,KAAK,EAAE,yBAAyB,CAAC;CAClC;AA2ED,wBAAsB,0BAA0B,CAAC,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAiB5F;AAkBD,iBAAe,4BAA4B,CACzC,MAAM,EAAE,2BAA2B,GAAG;IACpC,SAAS,EAAE,SAAS,GAAG,YAAY,GAAG,iBAAiB,CAAC;CACzD;;QA1FG,EAAE;QACF,KAAK;QACL,IAAI;QACJ,eAAe;QACf,MAAM;QACN,SAAS;QACT,SAAS;;;;GA2Jd;AAED,MAAM,MAAM,oBAAoB,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,4BAA4B,CAAC,CAAC,CAAC;AAE5F,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,2BAA2B,GAClC,OAAO,CAAC,oBAAoB,CAAC,CAE/B;AAED,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,2BAA2B,GAClC,OAAO,CAAC,oBAAoB,CAAC,CAE/B;AAED,wBAAsB,2BAA2B,CAC/C,MAAM,EAAE,2BAA2B,GAClC,OAAO,CAAC,oBAAoB,CAAC,CAE/B"}
1
+ {"version":3,"file":"admin-user-moderation.d.ts","sourceRoot":"","sources":["../../src/db/admin-user-moderation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,uBAAuB,CAAC;AACrD,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,yBAAyB,CAAC;AAKvE,OAAO,EAKL,KAAK,EAAE,EAER,MAAM,oBAAoB,CAAC;AAY5B,UAAU,2BAA2B;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,yBAAyB,EAAE,MAAM,CAAC;IAClC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,KAAK,EAAE,yBAAyB,CAAC;CAClC;AA2ED,wBAAsB,0BAA0B,CAAC,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAiB5F;AAsED,iBAAe,4BAA4B,CACzC,MAAM,EAAE,2BAA2B,GAAG;IACpC,SAAS,EAAE,SAAS,GAAG,YAAY,GAAG,iBAAiB,CAAC;CACzD;;QA9IG,EAAE;QACF,KAAK;QACL,IAAI;QACJ,eAAe;QACf,MAAM;QACN,SAAS;QACT,SAAS;;;;GAmKd;AAED,MAAM,MAAM,oBAAoB,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,4BAA4B,CAAC,CAAC,CAAC;AAE5F,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,2BAA2B,GAClC,OAAO,CAAC,oBAAoB,CAAC,CAE/B;AAED,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,2BAA2B,GAClC,OAAO,CAAC,oBAAoB,CAAC,CAE/B;AAED,wBAAsB,2BAA2B,CAC/C,MAAM,EAAE,2BAA2B,GAClC,OAAO,CAAC,oBAAoB,CAAC,CAE/B"}
@@ -89,6 +89,35 @@ async function revokeActiveSessions(tx, userId) {
89
89
  });
90
90
  return new Set(revoked.map(({ sessionId })=>sessionId)).size;
91
91
  }
92
+ async function suspendUser(tx, user) {
93
+ if (user.status !== 'active') return;
94
+ const activeOwners = await tx.select({
95
+ id: adminGrants.id
96
+ }).from(adminGrants).innerJoin(users, eq(adminGrants.userId, users.id)).where(and(eq(adminGrants.role, 'admin-owner'), isNull(adminGrants.revokedAt), eq(users.status, 'active'))).limit(2);
97
+ const targetIsActiveOwner = await tx.select({
98
+ id: adminGrants.id
99
+ }).from(adminGrants).where(and(eq(adminGrants.userId, user.id), eq(adminGrants.role, 'admin-owner'), isNull(adminGrants.revokedAt))).limit(1);
100
+ if (targetIsActiveOwner.length > 0 && activeOwners.length <= 1) throw new LastAdminOwnerError();
101
+ await tx.update(users).set({
102
+ status: 'suspended',
103
+ updatedAt: sql`now()`
104
+ }).where(eq(users.id, user.id));
105
+ }
106
+ async function applyModerationOperation(tx, user, operation) {
107
+ if (operation === 'suspend') {
108
+ await suspendUser(tx, user);
109
+ return await revokeActiveSessions(tx, user.id);
110
+ }
111
+ if (operation === 'reactivate' && user.status === 'suspended') {
112
+ await tx.update(users).set({
113
+ status: 'active',
114
+ updatedAt: sql`now()`
115
+ }).where(eq(users.id, user.id));
116
+ return 0;
117
+ }
118
+ if (operation === 'revoke-sessions') return await revokeActiveSessions(tx, user.id);
119
+ return 0;
120
+ }
92
121
  async function executeUserModerationCommand(params) {
93
122
  return await db().transaction(async (tx)=>{
94
123
  await lockAdminCommand(tx, params);
@@ -101,32 +130,7 @@ async function executeUserModerationCommand(params) {
101
130
  await lockUserSessionMutations(tx, params.userId);
102
131
  const user = await readTargetUserForUpdate(tx, params.userId);
103
132
  await requireActiveAdminOperator(tx, params.actorId);
104
- let sessionsRevoked = 0;
105
- if (params.operation === 'suspend') {
106
- if (user.status === 'active') {
107
- const activeOwners = await tx.select({
108
- id: adminGrants.id
109
- }).from(adminGrants).innerJoin(users, eq(adminGrants.userId, users.id)).where(and(eq(adminGrants.role, 'admin-owner'), isNull(adminGrants.revokedAt), eq(users.status, 'active'))).limit(2);
110
- const targetIsActiveOwner = await tx.select({
111
- id: adminGrants.id
112
- }).from(adminGrants).where(and(eq(adminGrants.userId, user.id), eq(adminGrants.role, 'admin-owner'), isNull(adminGrants.revokedAt))).limit(1);
113
- if (targetIsActiveOwner.length > 0 && activeOwners.length <= 1) {
114
- throw new LastAdminOwnerError();
115
- }
116
- await tx.update(users).set({
117
- status: 'suspended',
118
- updatedAt: sql`now()`
119
- }).where(eq(users.id, user.id));
120
- }
121
- sessionsRevoked = await revokeActiveSessions(tx, user.id);
122
- } else if (params.operation === 'reactivate' && user.status === 'suspended') {
123
- await tx.update(users).set({
124
- status: 'active',
125
- updatedAt: sql`now()`
126
- }).where(eq(users.id, user.id));
127
- } else if (params.operation === 'revoke-sessions') {
128
- sessionsRevoked = await revokeActiveSessions(tx, user.id);
129
- }
133
+ const sessionsRevoked = await applyModerationOperation(tx, user, params.operation);
130
134
  const summary = await findUserSummary(tx, user.id);
131
135
  const result = {
132
136
  user: summary,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/db/admin-user-moderation.ts"],"sourcesContent":["import type {AdminRole} from '@shipfox/api-auth-dto';\nimport type {AdministrationActionEvent} from '@shipfox/api-common-dto';\nimport {and, eq, gt, isNull, sql} from 'drizzle-orm';\nimport {hasMinimumAdminRole, highestAdminRole} from '#core/admin-role-model.js';\nimport type {AdministratorUserSummary} from '#core/entities/administrator-read-model.js';\nimport {AdminRoleRequiredError, LastAdminOwnerError, UserNotFoundError} from '#core/errors.js';\nimport {\n findAdminCommandResult,\n lockAdminCommand,\n lockAdminOwnerGrants,\n storeAdminCommandResult,\n type Tx,\n writeAdminAction,\n} from './admin-command.js';\nimport {findAdministratorUserSummary} from './admin-user-summary.js';\nimport {db} from './db.js';\nimport {lockUserSessionMutations} from './refresh-tokens.js';\nimport type {\n StoredAdministratorUserSummary,\n StoredAdminUserModerationResult,\n} from './schema/admin-command-results.js';\nimport {adminGrants} from './schema/admin-grants.js';\nimport {refreshTokens} from './schema/refresh-tokens.js';\nimport {users} from './schema/users.js';\n\ninterface UserModerationCommandParams {\n actorId: string;\n userId: string;\n idempotencyKeyFingerprint: string;\n requestFingerprint: string;\n event: AdministrationActionEvent;\n}\n\nfunction toStoredUserSummary(user: AdministratorUserSummary): StoredAdministratorUserSummary {\n return {\n id: user.id,\n email: user.email,\n name: user.name,\n emailVerifiedAt: user.emailVerifiedAt?.toISOString() ?? null,\n status: user.status,\n createdAt: user.createdAt.toISOString(),\n adminRole: user.adminRole,\n };\n}\n\nasync function findUserSummary(tx: Tx, userId: string): Promise<StoredAdministratorUserSummary> {\n const user = await findAdministratorUserSummary(tx, {id: userId});\n if (!user) throw new Error('User summary query returned no rows');\n return toStoredUserSummary(user);\n}\n\nfunction fromStoredResult(result: StoredAdminUserModerationResult) {\n return {\n user: {\n id: result.user.id,\n email: result.user.email,\n name: result.user.name,\n emailVerifiedAt: result.user.emailVerifiedAt ? new Date(result.user.emailVerifiedAt) : null,\n status: result.user.status,\n createdAt: new Date(result.user.createdAt),\n adminRole: result.user.adminRole,\n },\n correlationId: result.correlationId,\n sessionsRevoked: result.sessionsRevoked,\n };\n}\n\nasync function findCommandResult(\n tx: Tx,\n params: Pick<\n UserModerationCommandParams,\n 'actorId' | 'idempotencyKeyFingerprint' | 'requestFingerprint'\n > & {command: string},\n) {\n const result = await findAdminCommandResult(tx, params);\n if (!result) return undefined;\n if (!('userModeration' in result.result)) {\n throw new Error('Administrator command result has an unexpected shape');\n }\n return fromStoredResult(result.result.userModeration);\n}\n\nasync function storeCommandResult(\n tx: Tx,\n params: UserModerationCommandParams,\n result: StoredAdminUserModerationResult,\n): Promise<void> {\n await storeAdminCommandResult(\n tx,\n {...params, command: params.event.command},\n {userModeration: result},\n );\n}\n\nasync function readTargetUserForUpdate(tx: Tx, userId: string) {\n const rows = await tx\n .select({id: users.id, status: users.status})\n .from(users)\n .where(eq(users.id, userId))\n .limit(1)\n .for('update');\n const user = rows[0];\n if (!user || user.status === 'deleted') throw new UserNotFoundError(userId);\n return user;\n}\n\nexport async function requireActiveAdminOperator(tx: Tx, actorId: string): Promise<AdminRole> {\n const actorRows = await tx\n .select({status: users.status})\n .from(users)\n .where(eq(users.id, actorId))\n .limit(1);\n const actor = actorRows[0];\n const grants = await tx\n .select({role: adminGrants.role})\n .from(adminGrants)\n .where(and(eq(adminGrants.userId, actorId), isNull(adminGrants.revokedAt)));\n const role = highestAdminRole(grants.map(({role}) => role));\n\n if (actor?.status !== 'active' || !role || !hasMinimumAdminRole(role, 'admin-operator')) {\n throw new AdminRoleRequiredError('admin-operator');\n }\n return role;\n}\n\nasync function revokeActiveSessions(tx: Tx, userId: string): Promise<number> {\n const revoked = await tx\n .update(refreshTokens)\n .set({revokedAt: sql`now()`, updatedAt: sql`now()`})\n .where(\n and(\n eq(refreshTokens.userId, userId),\n isNull(refreshTokens.revokedAt),\n gt(refreshTokens.expiresAt, sql`now()`),\n ),\n )\n .returning({sessionId: refreshTokens.sessionId});\n\n return new Set(revoked.map(({sessionId}) => sessionId)).size;\n}\n\nasync function executeUserModerationCommand(\n params: UserModerationCommandParams & {\n operation: 'suspend' | 'reactivate' | 'revoke-sessions';\n },\n) {\n return await db().transaction(async (tx) => {\n await lockAdminCommand(tx, params);\n if (params.operation === 'suspend') await lockAdminOwnerGrants(tx);\n\n const existing = await findCommandResult(tx, {\n ...params,\n command: params.event.command,\n });\n if (existing) return existing;\n\n await lockUserSessionMutations(tx, params.userId);\n const user = await readTargetUserForUpdate(tx, params.userId);\n await requireActiveAdminOperator(tx, params.actorId);\n let sessionsRevoked = 0;\n\n if (params.operation === 'suspend') {\n if (user.status === 'active') {\n const activeOwners = await tx\n .select({id: adminGrants.id})\n .from(adminGrants)\n .innerJoin(users, eq(adminGrants.userId, users.id))\n .where(\n and(\n eq(adminGrants.role, 'admin-owner'),\n isNull(adminGrants.revokedAt),\n eq(users.status, 'active'),\n ),\n )\n .limit(2);\n const targetIsActiveOwner = await tx\n .select({id: adminGrants.id})\n .from(adminGrants)\n .where(\n and(\n eq(adminGrants.userId, user.id),\n eq(adminGrants.role, 'admin-owner'),\n isNull(adminGrants.revokedAt),\n ),\n )\n .limit(1);\n if (targetIsActiveOwner.length > 0 && activeOwners.length <= 1) {\n throw new LastAdminOwnerError();\n }\n\n await tx\n .update(users)\n .set({status: 'suspended', updatedAt: sql`now()`})\n .where(eq(users.id, user.id));\n }\n sessionsRevoked = await revokeActiveSessions(tx, user.id);\n } else if (params.operation === 'reactivate' && user.status === 'suspended') {\n await tx\n .update(users)\n .set({status: 'active', updatedAt: sql`now()`})\n .where(eq(users.id, user.id));\n } else if (params.operation === 'revoke-sessions') {\n sessionsRevoked = await revokeActiveSessions(tx, user.id);\n }\n\n const summary = await findUserSummary(tx, user.id);\n const result: StoredAdminUserModerationResult = {\n user: summary,\n correlationId: params.event.correlationId,\n sessionsRevoked,\n };\n await writeAdminAction(tx, params.event);\n await storeCommandResult(tx, params, result);\n return fromStoredResult(result);\n });\n}\n\nexport type UserModerationResult = Awaited<ReturnType<typeof executeUserModerationCommand>>;\n\nexport async function suspendUserWithAudit(\n params: UserModerationCommandParams,\n): Promise<UserModerationResult> {\n return await executeUserModerationCommand({...params, operation: 'suspend'});\n}\n\nexport async function reactivateUserWithAudit(\n params: UserModerationCommandParams,\n): Promise<UserModerationResult> {\n return await executeUserModerationCommand({...params, operation: 'reactivate'});\n}\n\nexport async function revokeUserSessionsWithAudit(\n params: UserModerationCommandParams,\n): Promise<UserModerationResult> {\n return await executeUserModerationCommand({...params, operation: 'revoke-sessions'});\n}\n"],"names":["and","eq","gt","isNull","sql","hasMinimumAdminRole","highestAdminRole","AdminRoleRequiredError","LastAdminOwnerError","UserNotFoundError","findAdminCommandResult","lockAdminCommand","lockAdminOwnerGrants","storeAdminCommandResult","writeAdminAction","findAdministratorUserSummary","db","lockUserSessionMutations","adminGrants","refreshTokens","users","toStoredUserSummary","user","id","email","name","emailVerifiedAt","toISOString","status","createdAt","adminRole","findUserSummary","tx","userId","Error","fromStoredResult","result","Date","correlationId","sessionsRevoked","findCommandResult","params","undefined","userModeration","storeCommandResult","command","event","readTargetUserForUpdate","rows","select","from","where","limit","for","requireActiveAdminOperator","actorId","actorRows","actor","grants","role","revokedAt","map","revokeActiveSessions","revoked","update","set","updatedAt","expiresAt","returning","sessionId","Set","size","executeUserModerationCommand","transaction","operation","existing","activeOwners","innerJoin","targetIsActiveOwner","length","summary","suspendUserWithAudit","reactivateUserWithAudit","revokeUserSessionsWithAudit"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/db/admin-user-moderation.ts"],"sourcesContent":["import type {AdminRole} from '@shipfox/api-auth-dto';\nimport type {AdministrationActionEvent} from '@shipfox/api-common-dto';\nimport {and, eq, gt, isNull, sql} from 'drizzle-orm';\nimport {hasMinimumAdminRole, highestAdminRole} from '#core/admin-role-model.js';\nimport type {AdministratorUserSummary} from '#core/entities/administrator-read-model.js';\nimport {AdminRoleRequiredError, LastAdminOwnerError, UserNotFoundError} from '#core/errors.js';\nimport {\n findAdminCommandResult,\n lockAdminCommand,\n lockAdminOwnerGrants,\n storeAdminCommandResult,\n type Tx,\n writeAdminAction,\n} from './admin-command.js';\nimport {findAdministratorUserSummary} from './admin-user-summary.js';\nimport {db} from './db.js';\nimport {lockUserSessionMutations} from './refresh-tokens.js';\nimport type {\n StoredAdministratorUserSummary,\n StoredAdminUserModerationResult,\n} from './schema/admin-command-results.js';\nimport {adminGrants} from './schema/admin-grants.js';\nimport {refreshTokens} from './schema/refresh-tokens.js';\nimport {users} from './schema/users.js';\n\ninterface UserModerationCommandParams {\n actorId: string;\n userId: string;\n idempotencyKeyFingerprint: string;\n requestFingerprint: string;\n event: AdministrationActionEvent;\n}\n\nfunction toStoredUserSummary(user: AdministratorUserSummary): StoredAdministratorUserSummary {\n return {\n id: user.id,\n email: user.email,\n name: user.name,\n emailVerifiedAt: user.emailVerifiedAt?.toISOString() ?? null,\n status: user.status,\n createdAt: user.createdAt.toISOString(),\n adminRole: user.adminRole,\n };\n}\n\nasync function findUserSummary(tx: Tx, userId: string): Promise<StoredAdministratorUserSummary> {\n const user = await findAdministratorUserSummary(tx, {id: userId});\n if (!user) throw new Error('User summary query returned no rows');\n return toStoredUserSummary(user);\n}\n\nfunction fromStoredResult(result: StoredAdminUserModerationResult) {\n return {\n user: {\n id: result.user.id,\n email: result.user.email,\n name: result.user.name,\n emailVerifiedAt: result.user.emailVerifiedAt ? new Date(result.user.emailVerifiedAt) : null,\n status: result.user.status,\n createdAt: new Date(result.user.createdAt),\n adminRole: result.user.adminRole,\n },\n correlationId: result.correlationId,\n sessionsRevoked: result.sessionsRevoked,\n };\n}\n\nasync function findCommandResult(\n tx: Tx,\n params: Pick<\n UserModerationCommandParams,\n 'actorId' | 'idempotencyKeyFingerprint' | 'requestFingerprint'\n > & {command: string},\n) {\n const result = await findAdminCommandResult(tx, params);\n if (!result) return undefined;\n if (!('userModeration' in result.result)) {\n throw new Error('Administrator command result has an unexpected shape');\n }\n return fromStoredResult(result.result.userModeration);\n}\n\nasync function storeCommandResult(\n tx: Tx,\n params: UserModerationCommandParams,\n result: StoredAdminUserModerationResult,\n): Promise<void> {\n await storeAdminCommandResult(\n tx,\n {...params, command: params.event.command},\n {userModeration: result},\n );\n}\n\nasync function readTargetUserForUpdate(tx: Tx, userId: string) {\n const rows = await tx\n .select({id: users.id, status: users.status})\n .from(users)\n .where(eq(users.id, userId))\n .limit(1)\n .for('update');\n const user = rows[0];\n if (!user || user.status === 'deleted') throw new UserNotFoundError(userId);\n return user;\n}\n\nexport async function requireActiveAdminOperator(tx: Tx, actorId: string): Promise<AdminRole> {\n const actorRows = await tx\n .select({status: users.status})\n .from(users)\n .where(eq(users.id, actorId))\n .limit(1);\n const actor = actorRows[0];\n const grants = await tx\n .select({role: adminGrants.role})\n .from(adminGrants)\n .where(and(eq(adminGrants.userId, actorId), isNull(adminGrants.revokedAt)));\n const role = highestAdminRole(grants.map(({role}) => role));\n\n if (actor?.status !== 'active' || !role || !hasMinimumAdminRole(role, 'admin-operator')) {\n throw new AdminRoleRequiredError('admin-operator');\n }\n return role;\n}\n\nasync function revokeActiveSessions(tx: Tx, userId: string): Promise<number> {\n const revoked = await tx\n .update(refreshTokens)\n .set({revokedAt: sql`now()`, updatedAt: sql`now()`})\n .where(\n and(\n eq(refreshTokens.userId, userId),\n isNull(refreshTokens.revokedAt),\n gt(refreshTokens.expiresAt, sql`now()`),\n ),\n )\n .returning({sessionId: refreshTokens.sessionId});\n\n return new Set(revoked.map(({sessionId}) => sessionId)).size;\n}\n\nasync function suspendUser(tx: Tx, user: Awaited<ReturnType<typeof readTargetUserForUpdate>>) {\n if (user.status !== 'active') return;\n const activeOwners = await tx\n .select({id: adminGrants.id})\n .from(adminGrants)\n .innerJoin(users, eq(adminGrants.userId, users.id))\n .where(\n and(\n eq(adminGrants.role, 'admin-owner'),\n isNull(adminGrants.revokedAt),\n eq(users.status, 'active'),\n ),\n )\n .limit(2);\n const targetIsActiveOwner = await tx\n .select({id: adminGrants.id})\n .from(adminGrants)\n .where(\n and(\n eq(adminGrants.userId, user.id),\n eq(adminGrants.role, 'admin-owner'),\n isNull(adminGrants.revokedAt),\n ),\n )\n .limit(1);\n if (targetIsActiveOwner.length > 0 && activeOwners.length <= 1) throw new LastAdminOwnerError();\n await tx\n .update(users)\n .set({status: 'suspended', updatedAt: sql`now()`})\n .where(eq(users.id, user.id));\n}\n\nasync function applyModerationOperation(\n tx: Tx,\n user: Awaited<ReturnType<typeof readTargetUserForUpdate>>,\n operation: 'suspend' | 'reactivate' | 'revoke-sessions',\n): Promise<number> {\n if (operation === 'suspend') {\n await suspendUser(tx, user);\n return await revokeActiveSessions(tx, user.id);\n }\n if (operation === 'reactivate' && user.status === 'suspended') {\n await tx\n .update(users)\n .set({status: 'active', updatedAt: sql`now()`})\n .where(eq(users.id, user.id));\n return 0;\n }\n if (operation === 'revoke-sessions') return await revokeActiveSessions(tx, user.id);\n return 0;\n}\n\nasync function executeUserModerationCommand(\n params: UserModerationCommandParams & {\n operation: 'suspend' | 'reactivate' | 'revoke-sessions';\n },\n) {\n return await db().transaction(async (tx) => {\n await lockAdminCommand(tx, params);\n if (params.operation === 'suspend') await lockAdminOwnerGrants(tx);\n\n const existing = await findCommandResult(tx, {\n ...params,\n command: params.event.command,\n });\n if (existing) return existing;\n\n await lockUserSessionMutations(tx, params.userId);\n const user = await readTargetUserForUpdate(tx, params.userId);\n await requireActiveAdminOperator(tx, params.actorId);\n const sessionsRevoked = await applyModerationOperation(tx, user, params.operation);\n\n const summary = await findUserSummary(tx, user.id);\n const result: StoredAdminUserModerationResult = {\n user: summary,\n correlationId: params.event.correlationId,\n sessionsRevoked,\n };\n await writeAdminAction(tx, params.event);\n await storeCommandResult(tx, params, result);\n return fromStoredResult(result);\n });\n}\n\nexport type UserModerationResult = Awaited<ReturnType<typeof executeUserModerationCommand>>;\n\nexport async function suspendUserWithAudit(\n params: UserModerationCommandParams,\n): Promise<UserModerationResult> {\n return await executeUserModerationCommand({...params, operation: 'suspend'});\n}\n\nexport async function reactivateUserWithAudit(\n params: UserModerationCommandParams,\n): Promise<UserModerationResult> {\n return await executeUserModerationCommand({...params, operation: 'reactivate'});\n}\n\nexport async function revokeUserSessionsWithAudit(\n params: UserModerationCommandParams,\n): Promise<UserModerationResult> {\n return await executeUserModerationCommand({...params, operation: 'revoke-sessions'});\n}\n"],"names":["and","eq","gt","isNull","sql","hasMinimumAdminRole","highestAdminRole","AdminRoleRequiredError","LastAdminOwnerError","UserNotFoundError","findAdminCommandResult","lockAdminCommand","lockAdminOwnerGrants","storeAdminCommandResult","writeAdminAction","findAdministratorUserSummary","db","lockUserSessionMutations","adminGrants","refreshTokens","users","toStoredUserSummary","user","id","email","name","emailVerifiedAt","toISOString","status","createdAt","adminRole","findUserSummary","tx","userId","Error","fromStoredResult","result","Date","correlationId","sessionsRevoked","findCommandResult","params","undefined","userModeration","storeCommandResult","command","event","readTargetUserForUpdate","rows","select","from","where","limit","for","requireActiveAdminOperator","actorId","actorRows","actor","grants","role","revokedAt","map","revokeActiveSessions","revoked","update","set","updatedAt","expiresAt","returning","sessionId","Set","size","suspendUser","activeOwners","innerJoin","targetIsActiveOwner","length","applyModerationOperation","operation","executeUserModerationCommand","transaction","existing","summary","suspendUserWithAudit","reactivateUserWithAudit","revokeUserSessionsWithAudit"],"mappings":"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"}