@shipfox/api-auth 18.0.0 → 20.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 (151) hide show
  1. package/.turbo/turbo-build.log +17 -2
  2. package/CHANGELOG.md +47 -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/cimd.d.ts +39 -0
  12. package/dist/core/cimd.d.ts.map +1 -0
  13. package/dist/core/cimd.js +392 -0
  14. package/dist/core/cimd.js.map +1 -0
  15. package/dist/core/entities/agent-access.d.ts +75 -0
  16. package/dist/core/entities/agent-access.d.ts.map +1 -0
  17. package/dist/core/entities/agent-access.js +3 -0
  18. package/dist/core/entities/agent-access.js.map +1 -0
  19. package/dist/core/errors.d.ts +17 -0
  20. package/dist/core/errors.d.ts.map +1 -1
  21. package/dist/core/errors.js +32 -0
  22. package/dist/core/errors.js.map +1 -1
  23. package/dist/core/oauth-client-resolver.d.ts +54 -0
  24. package/dist/core/oauth-client-resolver.d.ts.map +1 -0
  25. package/dist/core/oauth-client-resolver.js +154 -0
  26. package/dist/core/oauth-client-resolver.js.map +1 -0
  27. package/dist/core/oauth-client.d.ts +39 -0
  28. package/dist/core/oauth-client.d.ts.map +1 -0
  29. package/dist/core/oauth-client.js +236 -0
  30. package/dist/core/oauth-client.js.map +1 -0
  31. package/dist/db/admin-user-moderation.d.ts.map +1 -1
  32. package/dist/db/admin-user-moderation.js +30 -26
  33. package/dist/db/admin-user-moderation.js.map +1 -1
  34. package/dist/db/admin-user-summary.d.ts +22 -0
  35. package/dist/db/admin-user-summary.d.ts.map +1 -1
  36. package/dist/db/admin-user-summary.js +75 -1
  37. package/dist/db/admin-user-summary.js.map +1 -1
  38. package/dist/db/admin-users.d.ts +11 -0
  39. package/dist/db/admin-users.d.ts.map +1 -1
  40. package/dist/db/admin-users.js +4 -1
  41. package/dist/db/admin-users.js.map +1 -1
  42. package/dist/db/agent-access-retention.d.ts +10 -0
  43. package/dist/db/agent-access-retention.d.ts.map +1 -0
  44. package/dist/db/agent-access-retention.js +11 -0
  45. package/dist/db/agent-access-retention.js.map +1 -0
  46. package/dist/db/agent-access.d.ts +255 -0
  47. package/dist/db/agent-access.d.ts.map +1 -0
  48. package/dist/db/agent-access.js +705 -0
  49. package/dist/db/agent-access.js.map +1 -0
  50. package/dist/db/db.d.ts +2604 -238
  51. package/dist/db/db.d.ts.map +1 -1
  52. package/dist/db/db.js +7 -0
  53. package/dist/db/db.js.map +1 -1
  54. package/dist/db/schema/agent-access.d.ts +1204 -0
  55. package/dist/db/schema/agent-access.d.ts.map +1 -0
  56. package/dist/db/schema/agent-access.js +267 -0
  57. package/dist/db/schema/agent-access.js.map +1 -0
  58. package/dist/db/schema/users.d.ts.map +1 -1
  59. package/dist/db/schema/users.js +8 -3
  60. package/dist/db/schema/users.js.map +1 -1
  61. package/dist/index.d.ts +11 -2
  62. package/dist/index.d.ts.map +1 -1
  63. package/dist/index.js +26 -2
  64. package/dist/index.js.map +1 -1
  65. package/dist/metrics/instance.d.ts +1 -1
  66. package/dist/metrics/instance.d.ts.map +1 -1
  67. package/dist/metrics/instance.js.map +1 -1
  68. package/dist/presentation/routes/administration.d.ts.map +1 -1
  69. package/dist/presentation/routes/administration.js +123 -20
  70. package/dist/presentation/routes/administration.js.map +1 -1
  71. package/dist/presentation/routes/email-verification/verify-email-resend.d.ts.map +1 -1
  72. package/dist/presentation/routes/email-verification/verify-email-resend.js +6 -1
  73. package/dist/presentation/routes/email-verification/verify-email-resend.js.map +1 -1
  74. package/dist/presentation/routes/oauth.d.ts +9 -0
  75. package/dist/presentation/routes/oauth.d.ts.map +1 -0
  76. package/dist/presentation/routes/oauth.js +150 -0
  77. package/dist/presentation/routes/oauth.js.map +1 -0
  78. package/dist/presentation/routes/rate-limit.d.ts.map +1 -1
  79. package/dist/presentation/routes/rate-limit.js +34 -0
  80. package/dist/presentation/routes/rate-limit.js.map +1 -1
  81. package/dist/presentation/routes/registration/signup.d.ts.map +1 -1
  82. package/dist/presentation/routes/registration/signup.js +59 -50
  83. package/dist/presentation/routes/registration/signup.js.map +1 -1
  84. package/dist/temporal/activities/agent-access-retention.d.ts +13 -0
  85. package/dist/temporal/activities/agent-access-retention.d.ts.map +1 -0
  86. package/dist/temporal/activities/agent-access-retention.js +58 -0
  87. package/dist/temporal/activities/agent-access-retention.js.map +1 -0
  88. package/dist/temporal/activities/index.d.ts +5 -0
  89. package/dist/temporal/activities/index.d.ts.map +1 -0
  90. package/dist/temporal/activities/index.js +8 -0
  91. package/dist/temporal/activities/index.js.map +1 -0
  92. package/dist/temporal/constants.d.ts +2 -0
  93. package/dist/temporal/constants.d.ts.map +1 -0
  94. package/dist/temporal/constants.js +3 -0
  95. package/dist/temporal/constants.js.map +1 -0
  96. package/dist/temporal/workflows/agent-access-retention-cron.d.ts +2 -0
  97. package/dist/temporal/workflows/agent-access-retention-cron.d.ts.map +1 -0
  98. package/dist/temporal/workflows/agent-access-retention-cron.js +22 -0
  99. package/dist/temporal/workflows/agent-access-retention-cron.js.map +1 -0
  100. package/dist/temporal/workflows/index.bundle.js +25227 -0
  101. package/dist/temporal/workflows/index.bundle.meta.json +1 -0
  102. package/dist/temporal/workflows/index.d.ts +2 -0
  103. package/dist/temporal/workflows/index.d.ts.map +1 -0
  104. package/dist/temporal/workflows/index.js +3 -0
  105. package/dist/temporal/workflows/index.js.map +1 -0
  106. package/dist/tsconfig.test.tsbuildinfo +1 -1
  107. package/drizzle/0003_brainy_luckman.sql +6 -0
  108. package/drizzle/0004_yummy_runaways.sql +110 -0
  109. package/drizzle/meta/0003_snapshot.json +844 -0
  110. package/drizzle/meta/0004_snapshot.json +1751 -0
  111. package/drizzle/meta/_journal.json +14 -0
  112. package/package.json +11 -8
  113. package/src/core/admin-role.test.ts +70 -0
  114. package/src/core/administration.ts +78 -1
  115. package/src/core/auth.ts +3 -5
  116. package/src/core/cimd.test.ts +213 -0
  117. package/src/core/cimd.ts +532 -0
  118. package/src/core/entities/agent-access.ts +80 -0
  119. package/src/core/errors.ts +48 -0
  120. package/src/core/oauth-client-resolver.test.ts +198 -0
  121. package/src/core/oauth-client-resolver.ts +253 -0
  122. package/src/core/oauth-client.test.ts +157 -0
  123. package/src/core/oauth-client.ts +331 -0
  124. package/src/db/admin-user-moderation.ts +53 -45
  125. package/src/db/admin-user-summary.test.ts +228 -0
  126. package/src/db/admin-user-summary.ts +143 -1
  127. package/src/db/admin-users.ts +21 -1
  128. package/src/db/agent-access-retention.ts +10 -0
  129. package/src/db/agent-access.test.ts +1064 -0
  130. package/src/db/agent-access.ts +1491 -0
  131. package/src/db/db.ts +14 -0
  132. package/src/db/schema/agent-access.ts +273 -0
  133. package/src/db/schema/users.ts +9 -3
  134. package/src/index.test.ts +27 -0
  135. package/src/index.ts +82 -0
  136. package/src/metrics/instance.ts +6 -1
  137. package/src/presentation/routes/administration.test.ts +359 -2
  138. package/src/presentation/routes/administration.ts +142 -20
  139. package/src/presentation/routes/email-verification/verify-email-resend.ts +7 -1
  140. package/src/presentation/routes/oauth.test.ts +133 -0
  141. package/src/presentation/routes/oauth.ts +144 -0
  142. package/src/presentation/routes/rate-limit.ts +16 -0
  143. package/src/presentation/routes/registration/signup.ts +52 -41
  144. package/src/temporal/activities/agent-access-retention.ts +76 -0
  145. package/src/temporal/activities/index.ts +5 -0
  146. package/src/temporal/constants.ts +1 -0
  147. package/src/temporal/workflows/agent-access-retention-cron.ts +23 -0
  148. package/src/temporal/workflows/index.ts +1 -0
  149. package/test/globalSetup.ts +1 -1
  150. package/test/routes.ts +3 -1
  151. package/tsconfig.build.tsbuildinfo +1 -1
@@ -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\nexport class InvalidOAuthClientMetadataError extends Error {\n constructor() {\n super('OAuth client metadata is invalid');\n this.name = 'InvalidOAuthClientMetadataError';\n }\n}\n\nexport class OAuthRedirectUriNotRegisteredError extends Error {\n constructor() {\n super('OAuth redirect URI is not registered');\n this.name = 'OAuthRedirectUriNotRegisteredError';\n }\n}\n\nexport class OAuthMetadataFetchError extends Error {\n readonly reason:\n | 'invalid-url'\n | 'private-address'\n | 'dns-failed'\n | 'connection-failed'\n | 'redirected'\n | 'timeout'\n | 'response-too-large'\n | 'invalid-response';\n override readonly cause: unknown;\n\n constructor(reason: OAuthMetadataFetchError['reason'], cause?: unknown) {\n super('OAuth client metadata could not be fetched');\n this.name = 'OAuthMetadataFetchError';\n this.reason = reason;\n this.cause = cause;\n }\n}\n\nexport class InvalidOAuthConfigurationError extends Error {\n constructor() {\n super('OAuth public URL configuration is invalid');\n this.name = 'InvalidOAuthConfigurationError';\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","InvalidOAuthClientMetadataError","OAuthRedirectUriNotRegisteredError","OAuthMetadataFetchError","InvalidOAuthConfigurationError"],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ import { type FetchedCimdMetadata } from './cimd.js';
2
+ import type { AgentClient } from './entities/agent-access.js';
3
+ import { type OAuthGrantType, type ValidatedOAuthClientMetadata } from './oauth-client.js';
4
+ export interface ResolvedOAuthClient {
5
+ client: AgentClient;
6
+ metadata: ValidatedOAuthClientMetadata;
7
+ kind: AgentClient['kind'];
8
+ }
9
+ export interface ResolveOAuthClientParams {
10
+ clientId: string;
11
+ requestIp?: string | undefined;
12
+ redirectUri?: string | undefined;
13
+ tokenEndpointAuthMethod?: string | undefined;
14
+ }
15
+ type FetchMetadata = (clientId: string) => Promise<FetchedCimdMetadata>;
16
+ type FindClient = (params: {
17
+ clientId: string;
18
+ }) => Promise<AgentClient | undefined>;
19
+ type UpsertCimdClient = (params: {
20
+ clientId: string;
21
+ name: string;
22
+ redirectUris: string[];
23
+ }) => Promise<AgentClient>;
24
+ type CheckCimdRateLimit = (ip: string) => Promise<void>;
25
+ export interface OAuthClientResolver {
26
+ resolve(params: ResolveOAuthClientParams): Promise<ResolvedOAuthClient>;
27
+ clearCache(): void;
28
+ }
29
+ export interface OAuthClientResolverOptions {
30
+ fetchMetadata?: FetchMetadata;
31
+ findClient?: FindClient;
32
+ upsertCimdClient?: UpsertCimdClient;
33
+ checkCimdRateLimit?: CheckCimdRateLimit;
34
+ now?: () => Date;
35
+ }
36
+ /** Bounds process-wide CIMD metadata retained between authorization requests. */
37
+ export declare const OAUTH_CIMD_CACHE_MAX_ENTRIES = 1024;
38
+ export declare function createOAuthClientResolver(options?: OAuthClientResolverOptions): OAuthClientResolver;
39
+ export interface RegisterOAuthClientParams {
40
+ clientName: string;
41
+ redirectUris: string[];
42
+ grantTypes?: OAuthGrantType[] | undefined;
43
+ responseTypes?: 'code'[] | undefined;
44
+ tokenEndpointAuthMethod?: 'none' | undefined;
45
+ scope?: string | undefined;
46
+ }
47
+ export interface RegisteredOAuthClient {
48
+ client: AgentClient;
49
+ metadata: ValidatedOAuthClientMetadata;
50
+ }
51
+ export declare function registerOAuthClient(input: RegisterOAuthClientParams): Promise<RegisteredOAuthClient>;
52
+ export declare function resolveOAuthClient(params: ResolveOAuthClientParams, resolver?: OAuthClientResolver): Promise<ResolvedOAuthClient>;
53
+ export {};
54
+ //# sourceMappingURL=oauth-client-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth-client-resolver.d.ts","sourceRoot":"","sources":["../../src/core/oauth-client-resolver.ts"],"names":[],"mappings":"AAKA,OAAO,EAAC,KAAK,mBAAmB,EAAwB,MAAM,WAAW,CAAC;AAC1E,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,4BAA4B,CAAC;AAE5D,OAAO,EAKL,KAAK,cAAc,EACnB,KAAK,4BAA4B,EAGlC,MAAM,mBAAmB,CAAC;AAG3B,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,WAAW,CAAC;IACpB,QAAQ,EAAE,4BAA4B,CAAC;IACvC,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CAC3B;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,uBAAuB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC9C;AAED,KAAK,aAAa,GAAG,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,mBAAmB,CAAC,CAAC;AACxE,KAAK,UAAU,GAAG,CAAC,MAAM,EAAE;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAC,KAAK,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,CAAC;AACnF,KAAK,gBAAgB,GAAG,CAAC,MAAM,EAAE;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;AAC3B,KAAK,kBAAkB,GAAG,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAOxD,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,MAAM,EAAE,wBAAwB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACxE,UAAU,IAAI,IAAI,CAAC;CACpB;AAED,MAAM,WAAW,0BAA0B;IACzC,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IACxC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB;AAED,iFAAiF;AACjF,eAAO,MAAM,4BAA4B,OAAO,CAAC;AA+DjD,wBAAgB,yBAAyB,CACvC,OAAO,GAAE,0BAA+B,GACvC,mBAAmB,CA2ErB;AAED,MAAM,WAAW,yBAAyB;IACxC,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,UAAU,CAAC,EAAE,cAAc,EAAE,GAAG,SAAS,CAAC;IAC1C,aAAa,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IACrC,uBAAuB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7C,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5B;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,WAAW,CAAC;IACpB,QAAQ,EAAE,4BAA4B,CAAC;CACxC;AAED,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,yBAAyB,GAC/B,OAAO,CAAC,qBAAqB,CAAC,CAwBhC;AAID,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,wBAAwB,EAChC,QAAQ,GAAE,mBAAgD,GACzD,OAAO,CAAC,mBAAmB,CAAC,CAE9B"}
@@ -0,0 +1,154 @@
1
+ import { createAgentClient, findAgentClientByClientId, upsertCimdAgentClient } from '#db/agent-access.js';
2
+ import { fetchClientIdMetadata } from './cimd.js';
3
+ import { InvalidOAuthClientMetadataError } from './errors.js';
4
+ import { assertOAuthClientMetadataMatchesRequest, metadataForAgentClient, OAUTH_CIMD_CACHE_MAX_AGE_SECONDS, OAUTH_CLIENT_ID_MAX_BYTES, validateOAuthClientId, validateOAuthDynamicClientRegistration } from './oauth-client.js';
5
+ import { checkAuthRateLimit } from './rate-limit.js';
6
+ /** Bounds process-wide CIMD metadata retained between authorization requests. */ export const OAUTH_CIMD_CACHE_MAX_ENTRIES = 1024;
7
+ function assertClientIdBounded(clientId) {
8
+ if (clientId.length === 0 || clientId.trim() !== clientId || Buffer.byteLength(clientId, 'utf8') > OAUTH_CLIENT_ID_MAX_BYTES) {
9
+ throw new InvalidOAuthClientMetadataError();
10
+ }
11
+ }
12
+ function normalizedCacheAge(seconds) {
13
+ if (!Number.isFinite(seconds) || seconds <= 0) return 0;
14
+ return Math.min(Math.floor(seconds), OAUTH_CIMD_CACHE_MAX_AGE_SECONDS);
15
+ }
16
+ function pruneExpiredCache(cache, nowMs) {
17
+ for (const [clientId, cached] of cache){
18
+ if (cached.expiresAt <= nowMs) cache.delete(clientId);
19
+ }
20
+ }
21
+ function cacheCimdClient(cache, clientId, cached, nowMs) {
22
+ pruneExpiredCache(cache, nowMs);
23
+ if (!cache.has(clientId) && cache.size >= OAUTH_CIMD_CACHE_MAX_ENTRIES) {
24
+ const oldest = cache.keys().next();
25
+ if (!oldest.done) cache.delete(oldest.value);
26
+ }
27
+ cache.set(clientId, cached);
28
+ }
29
+ function registeredMetadata(client) {
30
+ return {
31
+ client,
32
+ metadata: metadataForAgentClient(client),
33
+ kind: client.kind
34
+ };
35
+ }
36
+ function assertResolvedClientMatchesRequest(resolved, params) {
37
+ assertOAuthClientMetadataMatchesRequest({
38
+ metadata: resolved.metadata,
39
+ redirectUri: params.redirectUri,
40
+ tokenEndpointAuthMethod: params.tokenEndpointAuthMethod
41
+ });
42
+ }
43
+ async function resolveCachedClient(params, cached, checkCimdRateLimit) {
44
+ if (params.requestIp === undefined) throw new InvalidOAuthClientMetadataError();
45
+ await checkCimdRateLimit(params.requestIp);
46
+ assertResolvedClientMatchesRequest(cached.resolved, params);
47
+ return cached.resolved;
48
+ }
49
+ export function createOAuthClientResolver(options = {}) {
50
+ const cache = new Map();
51
+ const now = options.now ?? (()=>new Date());
52
+ const findClient = options.findClient ?? findAgentClientByClientId;
53
+ const fetchMetadata = options.fetchMetadata ?? ((clientId)=>fetchClientIdMetadata(clientId));
54
+ const upsertCimdClient = options.upsertCimdClient ?? upsertCimdAgentClient;
55
+ const checkCimdRateLimit = options.checkCimdRateLimit ?? (async (requestIp)=>{
56
+ await checkAuthRateLimit({
57
+ action: 'oauth-cimd',
58
+ scope: 'ip',
59
+ identifier: requestIp,
60
+ limit: 30,
61
+ windowSeconds: 60 * 60
62
+ });
63
+ });
64
+ const resolve = async (params)=>{
65
+ assertClientIdBounded(params.clientId);
66
+ const cached = cache.get(params.clientId);
67
+ if (cached) {
68
+ if (cached.expiresAt > now().getTime()) {
69
+ return await resolveCachedClient(params, cached, checkCimdRateLimit);
70
+ }
71
+ cache.delete(params.clientId);
72
+ }
73
+ const existing = await findClient({
74
+ clientId: params.clientId
75
+ });
76
+ if (existing?.kind === 'registered') {
77
+ const resolved = registeredMetadata(existing);
78
+ assertResolvedClientMatchesRequest(resolved, params);
79
+ return resolved;
80
+ }
81
+ if (params.requestIp === undefined) throw new InvalidOAuthClientMetadataError();
82
+ await checkCimdRateLimit(params.requestIp);
83
+ validateOAuthClientId(params.clientId);
84
+ const fetched = await fetchMetadata(params.clientId);
85
+ if (fetched.metadata.clientId !== params.clientId) {
86
+ throw new InvalidOAuthClientMetadataError();
87
+ }
88
+ assertOAuthClientMetadataMatchesRequest({
89
+ metadata: fetched.metadata,
90
+ redirectUri: params.redirectUri,
91
+ tokenEndpointAuthMethod: params.tokenEndpointAuthMethod
92
+ });
93
+ const client = await upsertCimdClient({
94
+ clientId: fetched.metadata.clientId,
95
+ name: fetched.metadata.clientName,
96
+ redirectUris: fetched.metadata.redirectUris
97
+ });
98
+ const resolved = {
99
+ client,
100
+ metadata: fetched.metadata,
101
+ kind: 'cimd'
102
+ };
103
+ const cacheAge = normalizedCacheAge(fetched.cacheMaxAgeSeconds);
104
+ if (cacheAge > 0) {
105
+ const nowMs = now().getTime();
106
+ cacheCimdClient(cache, params.clientId, {
107
+ expiresAt: nowMs + cacheAge * 1000,
108
+ resolved
109
+ }, nowMs);
110
+ }
111
+ return resolved;
112
+ };
113
+ return {
114
+ resolve,
115
+ clearCache: ()=>cache.clear()
116
+ };
117
+ }
118
+ export async function registerOAuthClient(input) {
119
+ const validated = validateOAuthDynamicClientRegistration({
120
+ client_name: input.clientName,
121
+ redirect_uris: input.redirectUris,
122
+ ...input.grantTypes !== undefined ? {
123
+ grant_types: input.grantTypes
124
+ } : {},
125
+ ...input.responseTypes !== undefined ? {
126
+ response_types: input.responseTypes
127
+ } : {},
128
+ ...input.tokenEndpointAuthMethod !== undefined ? {
129
+ token_endpoint_auth_method: input.tokenEndpointAuthMethod
130
+ } : {},
131
+ ...input.scope !== undefined ? {
132
+ scope: input.scope
133
+ } : {}
134
+ });
135
+ const client = await createAgentClient({
136
+ clientId: `client_${crypto.randomUUID()}`,
137
+ name: validated.clientName,
138
+ redirectUris: validated.redirectUris,
139
+ kind: 'registered'
140
+ });
141
+ return {
142
+ client,
143
+ metadata: {
144
+ clientId: client.clientId,
145
+ ...validated
146
+ }
147
+ };
148
+ }
149
+ const defaultOAuthClientResolver = createOAuthClientResolver();
150
+ export async function resolveOAuthClient(params, resolver = defaultOAuthClientResolver) {
151
+ return await resolver.resolve(params);
152
+ }
153
+
154
+ //# sourceMappingURL=oauth-client-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/oauth-client-resolver.ts"],"sourcesContent":["import {\n createAgentClient,\n findAgentClientByClientId,\n upsertCimdAgentClient,\n} from '#db/agent-access.js';\nimport {type FetchedCimdMetadata, fetchClientIdMetadata} from './cimd.js';\nimport type {AgentClient} from './entities/agent-access.js';\nimport {InvalidOAuthClientMetadataError} from './errors.js';\nimport {\n assertOAuthClientMetadataMatchesRequest,\n metadataForAgentClient,\n OAUTH_CIMD_CACHE_MAX_AGE_SECONDS,\n OAUTH_CLIENT_ID_MAX_BYTES,\n type OAuthGrantType,\n type ValidatedOAuthClientMetadata,\n validateOAuthClientId,\n validateOAuthDynamicClientRegistration,\n} from './oauth-client.js';\nimport {checkAuthRateLimit} from './rate-limit.js';\n\nexport interface ResolvedOAuthClient {\n client: AgentClient;\n metadata: ValidatedOAuthClientMetadata;\n kind: AgentClient['kind'];\n}\n\nexport interface ResolveOAuthClientParams {\n clientId: string;\n requestIp?: string | undefined;\n redirectUri?: string | undefined;\n tokenEndpointAuthMethod?: string | undefined;\n}\n\ntype FetchMetadata = (clientId: string) => Promise<FetchedCimdMetadata>;\ntype FindClient = (params: {clientId: string}) => Promise<AgentClient | undefined>;\ntype UpsertCimdClient = (params: {\n clientId: string;\n name: string;\n redirectUris: string[];\n}) => Promise<AgentClient>;\ntype CheckCimdRateLimit = (ip: string) => Promise<void>;\n\ninterface CachedCimdClient {\n expiresAt: number;\n resolved: ResolvedOAuthClient;\n}\n\nexport interface OAuthClientResolver {\n resolve(params: ResolveOAuthClientParams): Promise<ResolvedOAuthClient>;\n clearCache(): void;\n}\n\nexport interface OAuthClientResolverOptions {\n fetchMetadata?: FetchMetadata;\n findClient?: FindClient;\n upsertCimdClient?: UpsertCimdClient;\n checkCimdRateLimit?: CheckCimdRateLimit;\n now?: () => Date;\n}\n\n/** Bounds process-wide CIMD metadata retained between authorization requests. */\nexport const OAUTH_CIMD_CACHE_MAX_ENTRIES = 1024;\n\nfunction assertClientIdBounded(clientId: string): void {\n if (\n clientId.length === 0 ||\n clientId.trim() !== clientId ||\n Buffer.byteLength(clientId, 'utf8') > OAUTH_CLIENT_ID_MAX_BYTES\n ) {\n throw new InvalidOAuthClientMetadataError();\n }\n}\n\nfunction normalizedCacheAge(seconds: number): number {\n if (!Number.isFinite(seconds) || seconds <= 0) return 0;\n return Math.min(Math.floor(seconds), OAUTH_CIMD_CACHE_MAX_AGE_SECONDS);\n}\n\nfunction pruneExpiredCache(cache: Map<string, CachedCimdClient>, nowMs: number): void {\n for (const [clientId, cached] of cache) {\n if (cached.expiresAt <= nowMs) cache.delete(clientId);\n }\n}\n\nfunction cacheCimdClient(\n cache: Map<string, CachedCimdClient>,\n clientId: string,\n cached: CachedCimdClient,\n nowMs: number,\n): void {\n pruneExpiredCache(cache, nowMs);\n if (!cache.has(clientId) && cache.size >= OAUTH_CIMD_CACHE_MAX_ENTRIES) {\n const oldest = cache.keys().next();\n if (!oldest.done) cache.delete(oldest.value);\n }\n cache.set(clientId, cached);\n}\n\nfunction registeredMetadata(client: AgentClient): ResolvedOAuthClient {\n return {client, metadata: metadataForAgentClient(client), kind: client.kind};\n}\n\nfunction assertResolvedClientMatchesRequest(\n resolved: ResolvedOAuthClient,\n params: ResolveOAuthClientParams,\n): void {\n assertOAuthClientMetadataMatchesRequest({\n metadata: resolved.metadata,\n redirectUri: params.redirectUri,\n tokenEndpointAuthMethod: params.tokenEndpointAuthMethod,\n });\n}\n\nasync function resolveCachedClient(\n params: ResolveOAuthClientParams,\n cached: CachedCimdClient,\n checkCimdRateLimit: CheckCimdRateLimit,\n): Promise<ResolvedOAuthClient> {\n if (params.requestIp === undefined) throw new InvalidOAuthClientMetadataError();\n await checkCimdRateLimit(params.requestIp);\n assertResolvedClientMatchesRequest(cached.resolved, params);\n return cached.resolved;\n}\n\nexport function createOAuthClientResolver(\n options: OAuthClientResolverOptions = {},\n): OAuthClientResolver {\n const cache = new Map<string, CachedCimdClient>();\n const now = options.now ?? (() => new Date());\n const findClient = options.findClient ?? findAgentClientByClientId;\n const fetchMetadata = options.fetchMetadata ?? ((clientId) => fetchClientIdMetadata(clientId));\n const upsertCimdClient = options.upsertCimdClient ?? upsertCimdAgentClient;\n const checkCimdRateLimit =\n options.checkCimdRateLimit ??\n (async (requestIp: string) => {\n await checkAuthRateLimit({\n action: 'oauth-cimd',\n scope: 'ip',\n identifier: requestIp,\n limit: 30,\n windowSeconds: 60 * 60,\n });\n });\n\n const resolve = async (params: ResolveOAuthClientParams): Promise<ResolvedOAuthClient> => {\n assertClientIdBounded(params.clientId);\n\n const cached = cache.get(params.clientId);\n if (cached) {\n if (cached.expiresAt > now().getTime()) {\n return await resolveCachedClient(params, cached, checkCimdRateLimit);\n }\n cache.delete(params.clientId);\n }\n\n const existing = await findClient({clientId: params.clientId});\n if (existing?.kind === 'registered') {\n const resolved = registeredMetadata(existing);\n assertResolvedClientMatchesRequest(resolved, params);\n return resolved;\n }\n\n if (params.requestIp === undefined) throw new InvalidOAuthClientMetadataError();\n await checkCimdRateLimit(params.requestIp);\n validateOAuthClientId(params.clientId);\n const fetched = await fetchMetadata(params.clientId);\n if (fetched.metadata.clientId !== params.clientId) {\n throw new InvalidOAuthClientMetadataError();\n }\n assertOAuthClientMetadataMatchesRequest({\n metadata: fetched.metadata,\n redirectUri: params.redirectUri,\n tokenEndpointAuthMethod: params.tokenEndpointAuthMethod,\n });\n const client = await upsertCimdClient({\n clientId: fetched.metadata.clientId,\n name: fetched.metadata.clientName,\n redirectUris: fetched.metadata.redirectUris,\n });\n const resolved: ResolvedOAuthClient = {\n client,\n metadata: fetched.metadata,\n kind: 'cimd',\n };\n const cacheAge = normalizedCacheAge(fetched.cacheMaxAgeSeconds);\n if (cacheAge > 0) {\n const nowMs = now().getTime();\n cacheCimdClient(\n cache,\n params.clientId,\n {expiresAt: nowMs + cacheAge * 1000, resolved},\n nowMs,\n );\n }\n return resolved;\n };\n\n return {\n resolve,\n clearCache: () => cache.clear(),\n };\n}\n\nexport interface RegisterOAuthClientParams {\n clientName: string;\n redirectUris: string[];\n grantTypes?: OAuthGrantType[] | undefined;\n responseTypes?: 'code'[] | undefined;\n tokenEndpointAuthMethod?: 'none' | undefined;\n scope?: string | undefined;\n}\n\nexport interface RegisteredOAuthClient {\n client: AgentClient;\n metadata: ValidatedOAuthClientMetadata;\n}\n\nexport async function registerOAuthClient(\n input: RegisterOAuthClientParams,\n): Promise<RegisteredOAuthClient> {\n const validated = validateOAuthDynamicClientRegistration({\n client_name: input.clientName,\n redirect_uris: input.redirectUris,\n ...(input.grantTypes !== undefined ? {grant_types: input.grantTypes} : {}),\n ...(input.responseTypes !== undefined ? {response_types: input.responseTypes} : {}),\n ...(input.tokenEndpointAuthMethod !== undefined\n ? {token_endpoint_auth_method: input.tokenEndpointAuthMethod}\n : {}),\n ...(input.scope !== undefined ? {scope: input.scope} : {}),\n });\n const client = await createAgentClient({\n clientId: `client_${crypto.randomUUID()}`,\n name: validated.clientName,\n redirectUris: validated.redirectUris,\n kind: 'registered',\n });\n return {\n client,\n metadata: {\n clientId: client.clientId,\n ...validated,\n },\n };\n}\n\nconst defaultOAuthClientResolver = createOAuthClientResolver();\n\nexport async function resolveOAuthClient(\n params: ResolveOAuthClientParams,\n resolver: OAuthClientResolver = defaultOAuthClientResolver,\n): Promise<ResolvedOAuthClient> {\n return await resolver.resolve(params);\n}\n"],"names":["createAgentClient","findAgentClientByClientId","upsertCimdAgentClient","fetchClientIdMetadata","InvalidOAuthClientMetadataError","assertOAuthClientMetadataMatchesRequest","metadataForAgentClient","OAUTH_CIMD_CACHE_MAX_AGE_SECONDS","OAUTH_CLIENT_ID_MAX_BYTES","validateOAuthClientId","validateOAuthDynamicClientRegistration","checkAuthRateLimit","OAUTH_CIMD_CACHE_MAX_ENTRIES","assertClientIdBounded","clientId","length","trim","Buffer","byteLength","normalizedCacheAge","seconds","Number","isFinite","Math","min","floor","pruneExpiredCache","cache","nowMs","cached","expiresAt","delete","cacheCimdClient","has","size","oldest","keys","next","done","value","set","registeredMetadata","client","metadata","kind","assertResolvedClientMatchesRequest","resolved","params","redirectUri","tokenEndpointAuthMethod","resolveCachedClient","checkCimdRateLimit","requestIp","undefined","createOAuthClientResolver","options","Map","now","Date","findClient","fetchMetadata","upsertCimdClient","action","scope","identifier","limit","windowSeconds","resolve","get","getTime","existing","fetched","name","clientName","redirectUris","cacheAge","cacheMaxAgeSeconds","clearCache","clear","registerOAuthClient","input","validated","client_name","redirect_uris","grantTypes","grant_types","responseTypes","response_types","token_endpoint_auth_method","crypto","randomUUID","defaultOAuthClientResolver","resolveOAuthClient","resolver"],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import { OAUTH_READ_SCOPE, type OAuthClientMetadataDocumentDto, type OAuthDynamicClientRegistrationRequestDto } from '@shipfox/api-auth-dto';
2
+ import type { AgentClient } from './entities/agent-access.js';
3
+ export declare const OAUTH_CLIENT_ID_MAX_BYTES = 2048;
4
+ export declare const OAUTH_CLIENT_NAME_MAX_BYTES = 256;
5
+ export declare const OAUTH_REDIRECT_URI_MAX_BYTES = 2048;
6
+ export declare const OAUTH_REDIRECT_URI_MAX_COUNT = 10;
7
+ export declare const OAUTH_CIMD_CACHE_MAX_AGE_SECONDS: number;
8
+ export type OAuthGrantType = 'authorization_code' | 'refresh_token';
9
+ export interface ValidatedOAuthClientMetadata {
10
+ clientId: string;
11
+ clientName: string;
12
+ redirectUris: string[];
13
+ grantTypes: OAuthGrantType[];
14
+ responseTypes: ['code'];
15
+ tokenEndpointAuthMethod: 'none';
16
+ scope: typeof OAUTH_READ_SCOPE;
17
+ }
18
+ /** Validates an OAuth redirect URI without making network assumptions. */
19
+ export declare function validateOAuthRedirectUri(value: string): URL;
20
+ export declare function isOAuthLoopbackRedirectUri(value: string): boolean;
21
+ /**
22
+ * Redirect URI comparison is byte-for-byte for public HTTPS clients. Only a
23
+ * loopback HTTP client's port may vary, as required by native-app OAuth.
24
+ */
25
+ export declare function oauthRedirectUriMatches(registered: string, requested: string): boolean;
26
+ export declare function assertOAuthRedirectUriRegistered(redirectUris: readonly string[], requested: string): void;
27
+ /** Client ID Metadata Documents must be HTTPS URLs with a non-root path. */
28
+ export declare function validateOAuthClientId(value: string): URL;
29
+ /** Validates the public origin injected into the dormant route factory. */
30
+ export declare function validateOAuthPublicOrigin(value: string): string;
31
+ export declare function validateOAuthDynamicClientRegistration(input: OAuthDynamicClientRegistrationRequestDto): Omit<ValidatedOAuthClientMetadata, 'clientId'>;
32
+ export declare function validateOAuthClientMetadataDocument(input: OAuthClientMetadataDocumentDto, fetchedUrl: string): ValidatedOAuthClientMetadata;
33
+ export declare function metadataForAgentClient(client: AgentClient): ValidatedOAuthClientMetadata;
34
+ export declare function assertOAuthClientMetadataMatchesRequest(params: {
35
+ metadata: ValidatedOAuthClientMetadata;
36
+ redirectUri?: string | undefined;
37
+ tokenEndpointAuthMethod?: string | undefined;
38
+ }): void;
39
+ //# sourceMappingURL=oauth-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth-client.d.ts","sourceRoot":"","sources":["../../src/core/oauth-client.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,KAAK,8BAA8B,EACnC,KAAK,wCAAwC,EAG9C,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,4BAA4B,CAAC;AAkB5D,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAC9C,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAC/C,eAAO,MAAM,4BAA4B,OAAO,CAAC;AACjD,eAAO,MAAM,4BAA4B,KAAK,CAAC;AAC/C,eAAO,MAAM,gCAAgC,QAAU,CAAC;AAExD,MAAM,MAAM,cAAc,GAAG,oBAAoB,GAAG,eAAe,CAAC;AAEpE,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,UAAU,EAAE,cAAc,EAAE,CAAC;IAC7B,aAAa,EAAE,CAAC,MAAM,CAAC,CAAC;IACxB,uBAAuB,EAAE,MAAM,CAAC;IAChC,KAAK,EAAE,OAAO,gBAAgB,CAAC;CAChC;AAuCD,0EAA0E;AAC1E,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,CAkB3D;AAED,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAOjE;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAkBtF;AAyBD,wBAAgB,gCAAgC,CAC9C,YAAY,EAAE,SAAS,MAAM,EAAE,EAC/B,SAAS,EAAE,MAAM,GAChB,IAAI,CAKN;AAED,4EAA4E;AAC5E,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,CAexD;AAED,2EAA2E;AAC3E,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAuB/D;AAoDD,wBAAgB,sCAAsC,CACpD,KAAK,EAAE,wCAAwC,GAC9C,IAAI,CAAC,4BAA4B,EAAE,UAAU,CAAC,CAoBhD;AAED,wBAAgB,mCAAmC,CACjD,KAAK,EAAE,8BAA8B,EACrC,UAAU,EAAE,MAAM,GACjB,4BAA4B,CAgB9B;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,WAAW,GAAG,4BAA4B,CAaxF;AAED,wBAAgB,uCAAuC,CAAC,MAAM,EAAE;IAC9D,QAAQ,EAAE,4BAA4B,CAAC;IACvC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,uBAAuB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC9C,GAAG,IAAI,CAKP"}
@@ -0,0 +1,236 @@
1
+ import { OAUTH_READ_SCOPE, oauthClientMetadataDocumentSchema, oauthDynamicClientRegistrationRequestSchema } from '@shipfox/api-auth-dto';
2
+ import { InvalidOAuthClientMetadataError, InvalidOAuthConfigurationError, OAuthRedirectUriNotRegisteredError } from './errors.js';
3
+ const URI_SUFFIX_PATTERN = /[/?#]/u;
4
+ const PORT_PATTERN = /^\d+$/u;
5
+ const BRACKETED_PORT_PATTERN = /^:\d+$/u;
6
+ function hasControlCharacter(value) {
7
+ return [
8
+ ...value
9
+ ].some((character)=>{
10
+ const codePoint = character.codePointAt(0) ?? 0;
11
+ return codePoint < 0x20 || codePoint === 0x7f;
12
+ });
13
+ }
14
+ export const OAUTH_CLIENT_ID_MAX_BYTES = 2048;
15
+ export const OAUTH_CLIENT_NAME_MAX_BYTES = 256;
16
+ export const OAUTH_REDIRECT_URI_MAX_BYTES = 2048;
17
+ export const OAUTH_REDIRECT_URI_MAX_COUNT = 10;
18
+ export const OAUTH_CIMD_CACHE_MAX_AGE_SECONDS = 15 * 60;
19
+ function hasByteLengthAtMost(value, maxBytes) {
20
+ return Buffer.byteLength(value, 'utf8') <= maxBytes;
21
+ }
22
+ function rejectInvalidMetadata() {
23
+ throw new InvalidOAuthClientMetadataError();
24
+ }
25
+ function rejectInvalidConfiguration() {
26
+ throw new InvalidOAuthConfigurationError();
27
+ }
28
+ function parseUrl(value) {
29
+ try {
30
+ return new URL(value);
31
+ } catch {
32
+ return rejectInvalidMetadata();
33
+ }
34
+ }
35
+ function isLoopbackHostname(hostname) {
36
+ const normalized = hostname.toLowerCase().replace(/^\[|\]$/gu, '');
37
+ return normalized === 'localhost' || normalized === '127.0.0.1' || normalized === '::1';
38
+ }
39
+ function validateUrlShape(url) {
40
+ if (url.username || url.password || url.hash || url.hostname.length === 0 || hasControlCharacter(url.href)) {
41
+ rejectInvalidMetadata();
42
+ }
43
+ }
44
+ /** Validates an OAuth redirect URI without making network assumptions. */ export function validateOAuthRedirectUri(value) {
45
+ if (!hasByteLengthAtMost(value, OAUTH_REDIRECT_URI_MAX_BYTES) || value.length === 0 || value.trim() !== value || hasControlCharacter(value)) {
46
+ return rejectInvalidMetadata();
47
+ }
48
+ const url = parseUrl(value);
49
+ validateUrlShape(url);
50
+ if (url.protocol === 'https:') return url;
51
+ if (url.protocol !== 'http:' || !isLoopbackHostname(url.hostname)) {
52
+ return rejectInvalidMetadata();
53
+ }
54
+ return url;
55
+ }
56
+ export function isOAuthLoopbackRedirectUri(value) {
57
+ try {
58
+ const url = validateOAuthRedirectUri(value);
59
+ return url.protocol === 'http:' && isLoopbackHostname(url.hostname);
60
+ } catch {
61
+ return false;
62
+ }
63
+ }
64
+ /**
65
+ * Redirect URI comparison is byte-for-byte for public HTTPS clients. Only a
66
+ * loopback HTTP client's port may vary, as required by native-app OAuth.
67
+ */ export function oauthRedirectUriMatches(registered, requested) {
68
+ try {
69
+ const registeredUrl = validateOAuthRedirectUri(registered);
70
+ const requestedUrl = validateOAuthRedirectUri(requested);
71
+ if (registered === requested) return true;
72
+ if (registeredUrl.protocol !== 'http:' || requestedUrl.protocol !== 'http:' || !isLoopbackHostname(registeredUrl.hostname) || !isLoopbackHostname(requestedUrl.hostname)) {
73
+ return false;
74
+ }
75
+ return withoutLoopbackPort(registered) === withoutLoopbackPort(requested);
76
+ } catch {
77
+ return false;
78
+ }
79
+ }
80
+ function withoutLoopbackPort(value) {
81
+ const authorityStart = value.indexOf('//') + 2;
82
+ const suffixOffset = value.slice(authorityStart).search(URI_SUFFIX_PATTERN);
83
+ const authorityEnd = suffixOffset === -1 ? value.length : authorityStart + suffixOffset;
84
+ const authority = value.slice(authorityStart, authorityEnd);
85
+ let authorityWithoutPort = authority;
86
+ if (authority.startsWith('[')) {
87
+ const closingBracket = authority.indexOf(']');
88
+ const port = closingBracket === -1 ? '' : authority.slice(closingBracket + 1);
89
+ if (closingBracket !== -1 && BRACKETED_PORT_PATTERN.test(port)) {
90
+ authorityWithoutPort = authority.slice(0, closingBracket + 1);
91
+ }
92
+ } else {
93
+ const colon = authority.lastIndexOf(':');
94
+ if (colon !== -1 && PORT_PATTERN.test(authority.slice(colon + 1))) {
95
+ authorityWithoutPort = authority.slice(0, colon);
96
+ }
97
+ }
98
+ return `${value.slice(0, authorityStart)}${authorityWithoutPort}${value.slice(authorityEnd)}`;
99
+ }
100
+ export function assertOAuthRedirectUriRegistered(redirectUris, requested) {
101
+ validateOAuthRedirectUri(requested);
102
+ if (!redirectUris.some((registered)=>oauthRedirectUriMatches(registered, requested))) {
103
+ throw new OAuthRedirectUriNotRegisteredError();
104
+ }
105
+ }
106
+ /** Client ID Metadata Documents must be HTTPS URLs with a non-root path. */ export function validateOAuthClientId(value) {
107
+ if (!hasByteLengthAtMost(value, OAUTH_CLIENT_ID_MAX_BYTES) || value.trim() !== value || hasControlCharacter(value)) {
108
+ return rejectInvalidMetadata();
109
+ }
110
+ const url = parseUrl(value);
111
+ validateUrlShape(url);
112
+ if (url.protocol !== 'https:' || url.pathname === '/' || url.pathname.length === 0) {
113
+ return rejectInvalidMetadata();
114
+ }
115
+ return url;
116
+ }
117
+ /** Validates the public origin injected into the dormant route factory. */ export function validateOAuthPublicOrigin(value) {
118
+ let url;
119
+ try {
120
+ url = new URL(value);
121
+ } catch {
122
+ return rejectInvalidConfiguration();
123
+ }
124
+ if (url.protocol !== 'https:' && !(url.protocol === 'http:' && isLoopbackHostname(url.hostname)) || value.trim() !== value || hasControlCharacter(value) || url.username || url.password || url.pathname !== '/' || url.search || url.hash || url.hostname.length === 0) {
125
+ return rejectInvalidConfiguration();
126
+ }
127
+ return url.origin;
128
+ }
129
+ function validateByteBoundedString(value, maxBytes) {
130
+ if (!hasByteLengthAtMost(value, maxBytes) || value.length === 0 || hasControlCharacter(value)) {
131
+ rejectInvalidMetadata();
132
+ }
133
+ }
134
+ function validateGrantTypes(value) {
135
+ const grantTypes = [
136
+ ...value ?? [
137
+ 'authorization_code'
138
+ ]
139
+ ];
140
+ if (grantTypes.length === 0 || grantTypes.length > 2 || new Set(grantTypes).size !== grantTypes.length || !grantTypes.includes('authorization_code') || grantTypes.some((grantType)=>grantType !== 'authorization_code' && grantType !== 'refresh_token')) {
141
+ rejectInvalidMetadata();
142
+ }
143
+ return grantTypes;
144
+ }
145
+ function validateResponseTypes(value) {
146
+ const responseTypes = [
147
+ ...value ?? [
148
+ 'code'
149
+ ]
150
+ ];
151
+ if (responseTypes.length !== 1 || responseTypes[0] !== 'code') rejectInvalidMetadata();
152
+ return [
153
+ 'code'
154
+ ];
155
+ }
156
+ function validateScope(value) {
157
+ if (value !== undefined && value !== OAUTH_READ_SCOPE) rejectInvalidMetadata();
158
+ return OAUTH_READ_SCOPE;
159
+ }
160
+ function validateRedirectUris(redirectUris) {
161
+ if (redirectUris.length === 0 || redirectUris.length > OAUTH_REDIRECT_URI_MAX_COUNT || new Set(redirectUris).size !== redirectUris.length) {
162
+ rejectInvalidMetadata();
163
+ }
164
+ for (const redirectUri of redirectUris)validateOAuthRedirectUri(redirectUri);
165
+ return [
166
+ ...redirectUris
167
+ ];
168
+ }
169
+ function validateTokenEndpointAuthMethod(value) {
170
+ if (value !== undefined && value !== 'none') rejectInvalidMetadata();
171
+ return 'none';
172
+ }
173
+ export function validateOAuthDynamicClientRegistration(input) {
174
+ const parsed = oauthDynamicClientRegistrationRequestSchema.safeParse(input);
175
+ if (!parsed.success) rejectInvalidMetadata();
176
+ const value = parsed.data;
177
+ validateByteBoundedString(value.client_name, OAUTH_CLIENT_NAME_MAX_BYTES);
178
+ const grantTypes = validateGrantTypes(value.grant_types);
179
+ // Registered rows do not persist grant types yet. Keep the registration
180
+ // response and later resolution consistent until refresh-token support lands.
181
+ if (grantTypes.length !== 1 || grantTypes[0] !== 'authorization_code') {
182
+ rejectInvalidMetadata();
183
+ }
184
+ return {
185
+ clientName: value.client_name,
186
+ redirectUris: validateRedirectUris(value.redirect_uris),
187
+ grantTypes,
188
+ responseTypes: validateResponseTypes(value.response_types),
189
+ tokenEndpointAuthMethod: validateTokenEndpointAuthMethod(value.token_endpoint_auth_method),
190
+ scope: validateScope(value.scope)
191
+ };
192
+ }
193
+ export function validateOAuthClientMetadataDocument(input, fetchedUrl) {
194
+ const parsed = oauthClientMetadataDocumentSchema.safeParse(input);
195
+ if (!parsed.success || parsed.data.client_id !== fetchedUrl) rejectInvalidMetadata();
196
+ validateOAuthClientId(fetchedUrl);
197
+ const value = parsed.data;
198
+ validateByteBoundedString(value.client_name, OAUTH_CLIENT_NAME_MAX_BYTES);
199
+ return {
200
+ clientId: fetchedUrl,
201
+ clientName: value.client_name,
202
+ redirectUris: validateRedirectUris(value.redirect_uris),
203
+ grantTypes: validateGrantTypes(value.grant_types),
204
+ responseTypes: validateResponseTypes(value.response_types),
205
+ tokenEndpointAuthMethod: validateTokenEndpointAuthMethod(value.token_endpoint_auth_method),
206
+ scope: validateScope(value.scope)
207
+ };
208
+ }
209
+ export function metadataForAgentClient(client) {
210
+ validateByteBoundedString(client.clientId, OAUTH_CLIENT_ID_MAX_BYTES);
211
+ validateByteBoundedString(client.name, OAUTH_CLIENT_NAME_MAX_BYTES);
212
+ validateRedirectUris(client.redirectUris);
213
+ return {
214
+ clientId: client.clientId,
215
+ clientName: client.name,
216
+ redirectUris: [
217
+ ...client.redirectUris
218
+ ],
219
+ grantTypes: [
220
+ 'authorization_code'
221
+ ],
222
+ responseTypes: [
223
+ 'code'
224
+ ],
225
+ tokenEndpointAuthMethod: 'none',
226
+ scope: OAUTH_READ_SCOPE
227
+ };
228
+ }
229
+ export function assertOAuthClientMetadataMatchesRequest(params) {
230
+ if (params.redirectUri !== undefined) {
231
+ assertOAuthRedirectUriRegistered(params.metadata.redirectUris, params.redirectUri);
232
+ }
233
+ validateTokenEndpointAuthMethod(params.tokenEndpointAuthMethod);
234
+ }
235
+
236
+ //# sourceMappingURL=oauth-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/oauth-client.ts"],"sourcesContent":["import {\n OAUTH_READ_SCOPE,\n type OAuthClientMetadataDocumentDto,\n type OAuthDynamicClientRegistrationRequestDto,\n oauthClientMetadataDocumentSchema,\n oauthDynamicClientRegistrationRequestSchema,\n} from '@shipfox/api-auth-dto';\nimport type {AgentClient} from './entities/agent-access.js';\nimport {\n InvalidOAuthClientMetadataError,\n InvalidOAuthConfigurationError,\n OAuthRedirectUriNotRegisteredError,\n} from './errors.js';\n\nconst URI_SUFFIX_PATTERN = /[/?#]/u;\nconst PORT_PATTERN = /^\\d+$/u;\nconst BRACKETED_PORT_PATTERN = /^:\\d+$/u;\n\nfunction hasControlCharacter(value: string): boolean {\n return [...value].some((character) => {\n const codePoint = character.codePointAt(0) ?? 0;\n return codePoint < 0x20 || codePoint === 0x7f;\n });\n}\n\nexport const OAUTH_CLIENT_ID_MAX_BYTES = 2048;\nexport const OAUTH_CLIENT_NAME_MAX_BYTES = 256;\nexport const OAUTH_REDIRECT_URI_MAX_BYTES = 2048;\nexport const OAUTH_REDIRECT_URI_MAX_COUNT = 10;\nexport const OAUTH_CIMD_CACHE_MAX_AGE_SECONDS = 15 * 60;\n\nexport type OAuthGrantType = 'authorization_code' | 'refresh_token';\n\nexport interface ValidatedOAuthClientMetadata {\n clientId: string;\n clientName: string;\n redirectUris: string[];\n grantTypes: OAuthGrantType[];\n responseTypes: ['code'];\n tokenEndpointAuthMethod: 'none';\n scope: typeof OAUTH_READ_SCOPE;\n}\n\nfunction hasByteLengthAtMost(value: string, maxBytes: number): boolean {\n return Buffer.byteLength(value, 'utf8') <= maxBytes;\n}\n\nfunction rejectInvalidMetadata(): never {\n throw new InvalidOAuthClientMetadataError();\n}\n\nfunction rejectInvalidConfiguration(): never {\n throw new InvalidOAuthConfigurationError();\n}\n\nfunction parseUrl(value: string): URL {\n try {\n return new URL(value);\n } catch {\n return rejectInvalidMetadata();\n }\n}\n\nfunction isLoopbackHostname(hostname: string): boolean {\n const normalized = hostname.toLowerCase().replace(/^\\[|\\]$/gu, '');\n return normalized === 'localhost' || normalized === '127.0.0.1' || normalized === '::1';\n}\n\nfunction validateUrlShape(url: URL): void {\n if (\n url.username ||\n url.password ||\n url.hash ||\n url.hostname.length === 0 ||\n hasControlCharacter(url.href)\n ) {\n rejectInvalidMetadata();\n }\n}\n\n/** Validates an OAuth redirect URI without making network assumptions. */\nexport function validateOAuthRedirectUri(value: string): URL {\n if (\n !hasByteLengthAtMost(value, OAUTH_REDIRECT_URI_MAX_BYTES) ||\n value.length === 0 ||\n value.trim() !== value ||\n hasControlCharacter(value)\n ) {\n return rejectInvalidMetadata();\n }\n\n const url = parseUrl(value);\n validateUrlShape(url);\n\n if (url.protocol === 'https:') return url;\n if (url.protocol !== 'http:' || !isLoopbackHostname(url.hostname)) {\n return rejectInvalidMetadata();\n }\n return url;\n}\n\nexport function isOAuthLoopbackRedirectUri(value: string): boolean {\n try {\n const url = validateOAuthRedirectUri(value);\n return url.protocol === 'http:' && isLoopbackHostname(url.hostname);\n } catch {\n return false;\n }\n}\n\n/**\n * Redirect URI comparison is byte-for-byte for public HTTPS clients. Only a\n * loopback HTTP client's port may vary, as required by native-app OAuth.\n */\nexport function oauthRedirectUriMatches(registered: string, requested: string): boolean {\n try {\n const registeredUrl = validateOAuthRedirectUri(registered);\n const requestedUrl = validateOAuthRedirectUri(requested);\n if (registered === requested) return true;\n if (\n registeredUrl.protocol !== 'http:' ||\n requestedUrl.protocol !== 'http:' ||\n !isLoopbackHostname(registeredUrl.hostname) ||\n !isLoopbackHostname(requestedUrl.hostname)\n ) {\n return false;\n }\n\n return withoutLoopbackPort(registered) === withoutLoopbackPort(requested);\n } catch {\n return false;\n }\n}\n\nfunction withoutLoopbackPort(value: string): string {\n const authorityStart = value.indexOf('//') + 2;\n const suffixOffset = value.slice(authorityStart).search(URI_SUFFIX_PATTERN);\n const authorityEnd = suffixOffset === -1 ? value.length : authorityStart + suffixOffset;\n const authority = value.slice(authorityStart, authorityEnd);\n\n let authorityWithoutPort = authority;\n if (authority.startsWith('[')) {\n const closingBracket = authority.indexOf(']');\n const port = closingBracket === -1 ? '' : authority.slice(closingBracket + 1);\n if (closingBracket !== -1 && BRACKETED_PORT_PATTERN.test(port)) {\n authorityWithoutPort = authority.slice(0, closingBracket + 1);\n }\n } else {\n const colon = authority.lastIndexOf(':');\n if (colon !== -1 && PORT_PATTERN.test(authority.slice(colon + 1))) {\n authorityWithoutPort = authority.slice(0, colon);\n }\n }\n\n return `${value.slice(0, authorityStart)}${authorityWithoutPort}${value.slice(authorityEnd)}`;\n}\n\nexport function assertOAuthRedirectUriRegistered(\n redirectUris: readonly string[],\n requested: string,\n): void {\n validateOAuthRedirectUri(requested);\n if (!redirectUris.some((registered) => oauthRedirectUriMatches(registered, requested))) {\n throw new OAuthRedirectUriNotRegisteredError();\n }\n}\n\n/** Client ID Metadata Documents must be HTTPS URLs with a non-root path. */\nexport function validateOAuthClientId(value: string): URL {\n if (\n !hasByteLengthAtMost(value, OAUTH_CLIENT_ID_MAX_BYTES) ||\n value.trim() !== value ||\n hasControlCharacter(value)\n ) {\n return rejectInvalidMetadata();\n }\n\n const url = parseUrl(value);\n validateUrlShape(url);\n if (url.protocol !== 'https:' || url.pathname === '/' || url.pathname.length === 0) {\n return rejectInvalidMetadata();\n }\n return url;\n}\n\n/** Validates the public origin injected into the dormant route factory. */\nexport function validateOAuthPublicOrigin(value: string): string {\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n return rejectInvalidConfiguration();\n }\n\n if (\n (url.protocol !== 'https:' &&\n !(url.protocol === 'http:' && isLoopbackHostname(url.hostname))) ||\n value.trim() !== value ||\n hasControlCharacter(value) ||\n url.username ||\n url.password ||\n url.pathname !== '/' ||\n url.search ||\n url.hash ||\n url.hostname.length === 0\n ) {\n return rejectInvalidConfiguration();\n }\n return url.origin;\n}\n\nfunction validateByteBoundedString(value: string, maxBytes: number): void {\n if (!hasByteLengthAtMost(value, maxBytes) || value.length === 0 || hasControlCharacter(value)) {\n rejectInvalidMetadata();\n }\n}\n\nfunction validateGrantTypes(value: readonly OAuthGrantType[] | undefined): OAuthGrantType[] {\n const grantTypes = [...(value ?? ['authorization_code'])];\n if (\n grantTypes.length === 0 ||\n grantTypes.length > 2 ||\n new Set(grantTypes).size !== grantTypes.length ||\n !grantTypes.includes('authorization_code') ||\n grantTypes.some(\n (grantType) => grantType !== 'authorization_code' && grantType !== 'refresh_token',\n )\n ) {\n rejectInvalidMetadata();\n }\n return grantTypes;\n}\n\nfunction validateResponseTypes(value: readonly 'code'[] | undefined): ['code'] {\n const responseTypes = [...(value ?? ['code'])];\n if (responseTypes.length !== 1 || responseTypes[0] !== 'code') rejectInvalidMetadata();\n return ['code'];\n}\n\nfunction validateScope(value: string | undefined): typeof OAUTH_READ_SCOPE {\n if (value !== undefined && value !== OAUTH_READ_SCOPE) rejectInvalidMetadata();\n return OAUTH_READ_SCOPE;\n}\n\nfunction validateRedirectUris(redirectUris: readonly string[]): string[] {\n if (\n redirectUris.length === 0 ||\n redirectUris.length > OAUTH_REDIRECT_URI_MAX_COUNT ||\n new Set(redirectUris).size !== redirectUris.length\n ) {\n rejectInvalidMetadata();\n }\n for (const redirectUri of redirectUris) validateOAuthRedirectUri(redirectUri);\n return [...redirectUris];\n}\n\nfunction validateTokenEndpointAuthMethod(value: string | undefined): 'none' {\n if (value !== undefined && value !== 'none') rejectInvalidMetadata();\n return 'none';\n}\n\nexport function validateOAuthDynamicClientRegistration(\n input: OAuthDynamicClientRegistrationRequestDto,\n): Omit<ValidatedOAuthClientMetadata, 'clientId'> {\n const parsed = oauthDynamicClientRegistrationRequestSchema.safeParse(input);\n if (!parsed.success) rejectInvalidMetadata();\n\n const value = parsed.data;\n validateByteBoundedString(value.client_name, OAUTH_CLIENT_NAME_MAX_BYTES);\n const grantTypes = validateGrantTypes(value.grant_types);\n // Registered rows do not persist grant types yet. Keep the registration\n // response and later resolution consistent until refresh-token support lands.\n if (grantTypes.length !== 1 || grantTypes[0] !== 'authorization_code') {\n rejectInvalidMetadata();\n }\n return {\n clientName: value.client_name,\n redirectUris: validateRedirectUris(value.redirect_uris),\n grantTypes,\n responseTypes: validateResponseTypes(value.response_types),\n tokenEndpointAuthMethod: validateTokenEndpointAuthMethod(value.token_endpoint_auth_method),\n scope: validateScope(value.scope),\n };\n}\n\nexport function validateOAuthClientMetadataDocument(\n input: OAuthClientMetadataDocumentDto,\n fetchedUrl: string,\n): ValidatedOAuthClientMetadata {\n const parsed = oauthClientMetadataDocumentSchema.safeParse(input);\n if (!parsed.success || parsed.data.client_id !== fetchedUrl) rejectInvalidMetadata();\n\n validateOAuthClientId(fetchedUrl);\n const value = parsed.data;\n validateByteBoundedString(value.client_name, OAUTH_CLIENT_NAME_MAX_BYTES);\n return {\n clientId: fetchedUrl,\n clientName: value.client_name,\n redirectUris: validateRedirectUris(value.redirect_uris),\n grantTypes: validateGrantTypes(value.grant_types),\n responseTypes: validateResponseTypes(value.response_types),\n tokenEndpointAuthMethod: validateTokenEndpointAuthMethod(value.token_endpoint_auth_method),\n scope: validateScope(value.scope),\n };\n}\n\nexport function metadataForAgentClient(client: AgentClient): ValidatedOAuthClientMetadata {\n validateByteBoundedString(client.clientId, OAUTH_CLIENT_ID_MAX_BYTES);\n validateByteBoundedString(client.name, OAUTH_CLIENT_NAME_MAX_BYTES);\n validateRedirectUris(client.redirectUris);\n return {\n clientId: client.clientId,\n clientName: client.name,\n redirectUris: [...client.redirectUris],\n grantTypes: ['authorization_code'],\n responseTypes: ['code'],\n tokenEndpointAuthMethod: 'none',\n scope: OAUTH_READ_SCOPE,\n };\n}\n\nexport function assertOAuthClientMetadataMatchesRequest(params: {\n metadata: ValidatedOAuthClientMetadata;\n redirectUri?: string | undefined;\n tokenEndpointAuthMethod?: string | undefined;\n}): void {\n if (params.redirectUri !== undefined) {\n assertOAuthRedirectUriRegistered(params.metadata.redirectUris, params.redirectUri);\n }\n validateTokenEndpointAuthMethod(params.tokenEndpointAuthMethod);\n}\n"],"names":["OAUTH_READ_SCOPE","oauthClientMetadataDocumentSchema","oauthDynamicClientRegistrationRequestSchema","InvalidOAuthClientMetadataError","InvalidOAuthConfigurationError","OAuthRedirectUriNotRegisteredError","URI_SUFFIX_PATTERN","PORT_PATTERN","BRACKETED_PORT_PATTERN","hasControlCharacter","value","some","character","codePoint","codePointAt","OAUTH_CLIENT_ID_MAX_BYTES","OAUTH_CLIENT_NAME_MAX_BYTES","OAUTH_REDIRECT_URI_MAX_BYTES","OAUTH_REDIRECT_URI_MAX_COUNT","OAUTH_CIMD_CACHE_MAX_AGE_SECONDS","hasByteLengthAtMost","maxBytes","Buffer","byteLength","rejectInvalidMetadata","rejectInvalidConfiguration","parseUrl","URL","isLoopbackHostname","hostname","normalized","toLowerCase","replace","validateUrlShape","url","username","password","hash","length","href","validateOAuthRedirectUri","trim","protocol","isOAuthLoopbackRedirectUri","oauthRedirectUriMatches","registered","requested","registeredUrl","requestedUrl","withoutLoopbackPort","authorityStart","indexOf","suffixOffset","slice","search","authorityEnd","authority","authorityWithoutPort","startsWith","closingBracket","port","test","colon","lastIndexOf","assertOAuthRedirectUriRegistered","redirectUris","validateOAuthClientId","pathname","validateOAuthPublicOrigin","origin","validateByteBoundedString","validateGrantTypes","grantTypes","Set","size","includes","grantType","validateResponseTypes","responseTypes","validateScope","undefined","validateRedirectUris","redirectUri","validateTokenEndpointAuthMethod","validateOAuthDynamicClientRegistration","input","parsed","safeParse","success","data","client_name","grant_types","clientName","redirect_uris","response_types","tokenEndpointAuthMethod","token_endpoint_auth_method","scope","validateOAuthClientMetadataDocument","fetchedUrl","client_id","clientId","metadataForAgentClient","client","name","assertOAuthClientMetadataMatchesRequest","params","metadata"],"mappings":"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"}
@@ -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"}