@authup/server-core 1.0.0-beta.60 → 1.0.0-beta.62

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 (172) hide show
  1. package/dist/adapters/database/domains/client-scope/entity.d.ts.map +1 -1
  2. package/dist/adapters/database/domains/client-scope/entity.mjs +16 -8
  3. package/dist/adapters/database/domains/client-scope/entity.mjs.map +1 -1
  4. package/dist/adapters/database/domains/identity-provider-account/entity.d.ts.map +1 -1
  5. package/dist/adapters/database/domains/identity-provider-account/entity.mjs +1 -1
  6. package/dist/adapters/database/domains/identity-provider-account/entity.mjs.map +1 -1
  7. package/dist/adapters/database/migrations/mysql/1786631686318-SortIndexesAndAccountUniqueness.d.ts +50 -0
  8. package/dist/adapters/database/migrations/mysql/1786631686318-SortIndexesAndAccountUniqueness.d.ts.map +1 -0
  9. package/dist/adapters/database/migrations/mysql/1786631686318-SortIndexesAndAccountUniqueness.mjs +86 -0
  10. package/dist/adapters/database/migrations/mysql/1786631686318-SortIndexesAndAccountUniqueness.mjs.map +1 -0
  11. package/dist/adapters/database/migrations/postgres/1786631686318-SortIndexesAndAccountUniqueness.d.ts +42 -0
  12. package/dist/adapters/database/migrations/postgres/1786631686318-SortIndexesAndAccountUniqueness.d.ts.map +1 -0
  13. package/dist/adapters/database/migrations/postgres/1786631686318-SortIndexesAndAccountUniqueness.mjs +80 -0
  14. package/dist/adapters/database/migrations/postgres/1786631686318-SortIndexesAndAccountUniqueness.mjs.map +1 -0
  15. package/dist/adapters/http/adapters/oauth2/grant-types/utils/credentials.mjs +1 -1
  16. package/dist/adapters/http/adapters/oauth2/grant-types/utils/guess.mjs +1 -1
  17. package/dist/adapters/http/controllers/entities/client/module.mjs +2 -2
  18. package/dist/adapters/http/controllers/entities/identity-provider/module.d.ts +12 -8
  19. package/dist/adapters/http/controllers/entities/identity-provider/module.d.ts.map +1 -1
  20. package/dist/adapters/http/controllers/entities/identity-provider/module.mjs +137 -88
  21. package/dist/adapters/http/controllers/entities/identity-provider/module.mjs.map +1 -1
  22. package/dist/adapters/http/controllers/entities/identity-provider/types.d.ts +7 -5
  23. package/dist/adapters/http/controllers/entities/identity-provider/types.d.ts.map +1 -1
  24. package/dist/adapters/http/controllers/entities/realm/module.mjs +2 -2
  25. package/dist/adapters/http/controllers/entities/session/module.mjs +2 -2
  26. package/dist/adapters/http/controllers/entities/user/module.mjs +2 -2
  27. package/dist/adapters/http/controllers/entities/user-authenticator/module.mjs +2 -2
  28. package/dist/adapters/http/controllers/workflows/authorize/module.d.ts.map +1 -1
  29. package/dist/adapters/http/controllers/workflows/authorize/module.mjs +9 -5
  30. package/dist/adapters/http/controllers/workflows/authorize/module.mjs.map +1 -1
  31. package/dist/adapters/http/controllers/workflows/token/utils/extract.mjs +2 -1
  32. package/dist/adapters/http/controllers/workflows/token/utils/extract.mjs.map +1 -1
  33. package/dist/adapters/http/middleware/built-in/assets.mjs +1 -1
  34. package/dist/adapters/http/request/helpers/param-id.d.ts.map +1 -1
  35. package/dist/adapters/http/request/helpers/param-id.mjs +2 -1
  36. package/dist/adapters/http/request/helpers/param-id.mjs.map +1 -1
  37. package/dist/adapters/http/ui/account-console/module.mjs +1 -1
  38. package/dist/adapters/http/ui/auth-console/http-client.mjs +1 -1
  39. package/dist/adapters/http/ui/auth-console/module.mjs +1 -1
  40. package/dist/adapters/http/ui/console-packages/module.d.ts +1 -1
  41. package/dist/adapters/http/ui/console-packages/module.mjs +2 -2
  42. package/dist/adapters/http/ui/console-packages/module.mjs.map +1 -1
  43. package/dist/adapters/http/ui/shared/html.d.ts.map +1 -1
  44. package/dist/adapters/http/ui/shared/html.mjs +1 -0
  45. package/dist/adapters/http/ui/shared/html.mjs.map +1 -1
  46. package/dist/app/index.mjs +3 -1
  47. package/dist/app/modules/config/read/env.mjs +1 -1
  48. package/dist/app/modules/database/repositories/event/repository.d.ts +2 -2
  49. package/dist/app/modules/database/repositories/event/repository.d.ts.map +1 -1
  50. package/dist/app/modules/database/repositories/event/repository.mjs +23 -6
  51. package/dist/app/modules/database/repositories/event/repository.mjs.map +1 -1
  52. package/dist/app/modules/database/repositories/identity-provider-account/repository.d.ts.map +1 -1
  53. package/dist/app/modules/database/repositories/identity-provider-account/repository.mjs +9 -1
  54. package/dist/app/modules/database/repositories/identity-provider-account/repository.mjs.map +1 -1
  55. package/dist/app/modules/http/modules/controller.d.ts.map +1 -1
  56. package/dist/app/modules/http/modules/controller.mjs +18 -6
  57. package/dist/app/modules/http/modules/controller.mjs.map +1 -1
  58. package/dist/app/modules/identity/constants.d.ts +2 -1
  59. package/dist/app/modules/identity/constants.d.ts.map +1 -1
  60. package/dist/app/modules/identity/constants.mjs +1 -0
  61. package/dist/app/modules/identity/constants.mjs.map +1 -1
  62. package/dist/app/modules/identity/index.mjs +3 -1
  63. package/dist/app/modules/identity/module.d.ts.map +1 -1
  64. package/dist/app/modules/identity/module.mjs +13 -3
  65. package/dist/app/modules/identity/module.mjs.map +1 -1
  66. package/dist/app/modules/identity/repositories/constants.d.ts +4 -0
  67. package/dist/app/modules/identity/repositories/constants.d.ts.map +1 -0
  68. package/dist/app/modules/identity/repositories/constants.mjs +12 -0
  69. package/dist/app/modules/identity/repositories/constants.mjs.map +1 -0
  70. package/dist/app/modules/identity/repositories/index.d.ts +1 -0
  71. package/dist/app/modules/identity/repositories/index.d.ts.map +1 -1
  72. package/dist/app/modules/identity/repositories/index.mjs +3 -1
  73. package/dist/app/modules/identity/repositories/provider/index.d.ts +1 -0
  74. package/dist/app/modules/identity/repositories/provider/index.d.ts.map +1 -1
  75. package/dist/app/modules/identity/repositories/provider/index.mjs +2 -1
  76. package/dist/app/modules/identity/repositories/provider/link.d.ts +9 -0
  77. package/dist/app/modules/identity/repositories/provider/link.d.ts.map +1 -0
  78. package/dist/app/modules/identity/repositories/provider/link.mjs +34 -0
  79. package/dist/app/modules/identity/repositories/provider/link.mjs.map +1 -0
  80. package/dist/app/modules/identity/repositories/user.d.ts +1 -0
  81. package/dist/app/modules/identity/repositories/user.d.ts.map +1 -1
  82. package/dist/app/modules/identity/repositories/user.mjs +3 -0
  83. package/dist/app/modules/identity/repositories/user.mjs.map +1 -1
  84. package/dist/app/modules/index.mjs +3 -1
  85. package/dist/app/modules/oauth2/repositories/authorize/state/repository.d.ts +1 -2
  86. package/dist/app/modules/oauth2/repositories/authorize/state/repository.d.ts.map +1 -1
  87. package/dist/app/modules/oauth2/repositories/authorize/state/repository.mjs +7 -16
  88. package/dist/app/modules/oauth2/repositories/authorize/state/repository.mjs.map +1 -1
  89. package/dist/app/modules/runtime/module.mjs +2 -2
  90. package/dist/cli/commands/migration.mjs +1 -1
  91. package/dist/core/entities/client-scope/schema.d.ts.map +1 -1
  92. package/dist/core/entities/client-scope/schema.mjs +16 -1
  93. package/dist/core/entities/client-scope/schema.mjs.map +1 -1
  94. package/dist/core/entities/event/constants.d.ts +9 -0
  95. package/dist/core/entities/event/constants.d.ts.map +1 -1
  96. package/dist/core/entities/event/constants.mjs +9 -1
  97. package/dist/core/entities/event/constants.mjs.map +1 -1
  98. package/dist/core/entities/event/index.mjs +2 -2
  99. package/dist/core/entities/event/types.d.ts +11 -2
  100. package/dist/core/entities/event/types.d.ts.map +1 -1
  101. package/dist/core/entities/identity-provider-account/types.d.ts +6 -0
  102. package/dist/core/entities/identity-provider-account/types.d.ts.map +1 -1
  103. package/dist/core/entities/identity-provider-account/types.mjs.map +1 -1
  104. package/dist/core/entities/index.mjs +2 -2
  105. package/dist/core/identity/entities/user/types.d.ts +1 -0
  106. package/dist/core/identity/entities/user/types.d.ts.map +1 -1
  107. package/dist/core/identity/index.mjs +12 -11
  108. package/dist/core/identity/provider/account/constants.d.ts +10 -0
  109. package/dist/core/identity/provider/account/constants.d.ts.map +1 -0
  110. package/dist/core/identity/provider/account/constants.mjs +16 -0
  111. package/dist/core/identity/provider/account/constants.mjs.map +1 -0
  112. package/dist/core/identity/provider/account/index.d.ts +1 -0
  113. package/dist/core/identity/provider/account/index.d.ts.map +1 -1
  114. package/dist/core/identity/provider/account/index.mjs +2 -1
  115. package/dist/core/identity/provider/account/module.d.ts +3 -0
  116. package/dist/core/identity/provider/account/module.d.ts.map +1 -1
  117. package/dist/core/identity/provider/account/module.mjs +54 -27
  118. package/dist/core/identity/provider/account/module.mjs.map +1 -1
  119. package/dist/core/identity/provider/account/types.d.ts +42 -0
  120. package/dist/core/identity/provider/account/types.d.ts.map +1 -1
  121. package/dist/core/identity/provider/authentication/index.mjs +1 -1
  122. package/dist/core/identity/provider/authentication/protocols/oauth2/module.d.ts +18 -0
  123. package/dist/core/identity/provider/authentication/protocols/oauth2/module.d.ts.map +1 -1
  124. package/dist/core/identity/provider/authentication/protocols/oauth2/module.mjs +55 -2
  125. package/dist/core/identity/provider/authentication/protocols/oauth2/module.mjs.map +1 -1
  126. package/dist/core/identity/provider/authentication/protocols/oauth2/types.d.ts +2 -0
  127. package/dist/core/identity/provider/authentication/protocols/oauth2/types.d.ts.map +1 -1
  128. package/dist/core/identity/provider/authentication/protocols/open-id/module.d.ts +0 -3
  129. package/dist/core/identity/provider/authentication/protocols/open-id/module.d.ts.map +1 -1
  130. package/dist/core/identity/provider/authentication/protocols/open-id/module.mjs +0 -17
  131. package/dist/core/identity/provider/authentication/protocols/open-id/module.mjs.map +1 -1
  132. package/dist/core/identity/provider/index.mjs +12 -11
  133. package/dist/core/index.mjs +15 -12
  134. package/dist/core/oauth2/authorization/code-request/verifier/module.d.ts.map +1 -1
  135. package/dist/core/oauth2/authorization/code-request/verifier/module.mjs +9 -2
  136. package/dist/core/oauth2/authorization/code-request/verifier/module.mjs.map +1 -1
  137. package/dist/core/oauth2/authorization/state/module.mjs +1 -2
  138. package/dist/core/oauth2/authorization/state/module.mjs.map +1 -1
  139. package/dist/core/oauth2/authorization/state/types.d.ts +5 -2
  140. package/dist/core/oauth2/authorization/state/types.d.ts.map +1 -1
  141. package/dist/core/oauth2/end-session/service.d.ts.map +1 -1
  142. package/dist/core/oauth2/end-session/service.mjs +1 -0
  143. package/dist/core/oauth2/end-session/service.mjs.map +1 -1
  144. package/dist/core/oauth2/federated-login/index.d.ts +3 -0
  145. package/dist/core/oauth2/federated-login/index.d.ts.map +1 -0
  146. package/dist/core/oauth2/federated-login/index.mjs +6 -0
  147. package/dist/core/oauth2/federated-login/module.d.ts +20 -0
  148. package/dist/core/oauth2/federated-login/module.d.ts.map +1 -0
  149. package/dist/core/oauth2/federated-login/module.mjs +137 -0
  150. package/dist/core/oauth2/federated-login/module.mjs.map +1 -0
  151. package/dist/core/oauth2/federated-login/types.d.ts +138 -0
  152. package/dist/core/oauth2/federated-login/types.d.ts.map +1 -0
  153. package/dist/core/oauth2/federated-login/types.mjs +31 -0
  154. package/dist/core/oauth2/federated-login/types.mjs.map +1 -0
  155. package/dist/core/oauth2/index.d.ts +1 -0
  156. package/dist/core/oauth2/index.d.ts.map +1 -1
  157. package/dist/core/oauth2/index.mjs +4 -1
  158. package/dist/core/provisioning/entities/policy/validator.d.ts.map +1 -1
  159. package/dist/core/provisioning/entities/policy/validator.mjs +2 -1
  160. package/dist/core/provisioning/entities/policy/validator.mjs.map +1 -1
  161. package/dist/core/provisioning/entities/realm/wildcard-validator.d.ts.map +1 -1
  162. package/dist/core/provisioning/entities/realm/wildcard-validator.mjs +2 -1
  163. package/dist/core/provisioning/entities/realm/wildcard-validator.mjs.map +1 -1
  164. package/dist/core/security/policy/evaluator.mjs.map +1 -1
  165. package/dist/index.mjs +3 -1
  166. package/dist/node_modules/@ebec/core/dist/index.mjs +130 -0
  167. package/dist/node_modules/@ebec/core/dist/index.mjs.map +1 -0
  168. package/dist/swagger.json +146 -18
  169. package/dist/utils/error.d.ts.map +1 -1
  170. package/dist/utils/error.mjs +5 -6
  171. package/dist/utils/error.mjs.map +1 -1
  172. package/package.json +28 -28
@@ -1 +1 @@
1
- {"version":3,"file":"types.mjs","names":["IDENTITY_PROVIDER_ACCOUNT_FILTER_KEYS"],"sources":["../../../../src/core/entities/identity-provider-account/types.ts"],"sourcesContent":["/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport type { IdentityProviderAccount } from '@authup/core-kit';\nimport type { IQuery } from '@rapiq/core';\nimport type { ActorContext, EntityRepositoryFindManyResult } from '@authup/server-kit';\nimport type { DeepPartial } from 'typeorm';\nimport type { IdentityProviderIdentity } from '../../identity/provider/types.ts';\n\n/**\n * Filter keys an identity-provider-account list query may target.\n */\nexport const IDENTITY_PROVIDER_ACCOUNT_FILTER_KEYS = [\n 'id',\n 'userId',\n 'providerId',\n 'userRealmId',\n 'providerUserId',\n] as const;\n\nexport type IdentityProviderAccountFindManyOptions = {\n /**\n * Force the result to a single user (self-service). Applied as a\n * mandatory WHERE that a rapiq query filter cannot override.\n */\n userId?: string,\n /**\n * Restrict the result to a single owner realm (the nested\n * `/realms/:realmId/identity-provider-accounts` mount). The owner\n * realm of a row is `userRealmId`.\n */\n realmId?: string,\n};\n\n/**\n * Unified port: the entity CRUD surface (management API) plus the\n * account-manager methods the federated login and link flows consume.\n * One adapter implements both concerns over the same table.\n */\nexport interface IIdentityProviderAccountRepository {\n findMany(query: IQuery, options?: IdentityProviderAccountFindManyOptions): Promise<EntityRepositoryFindManyResult<IdentityProviderAccount>>;\n\n findOneById(id: string): Promise<IdentityProviderAccount | null>;\n\n remove(entity: IdentityProviderAccount): Promise<void>;\n\n /**\n * Atomically remove the account, refusing (returning `false`) when it\n * is the user's LAST linked account and `userHasOtherLogin` is false —\n * the unlink lockout guard. The count and the delete run in one\n * transaction with the user's account rows locked (on drivers that\n * support row locks), so two concurrent unlinks for the same user\n * cannot both pass the guard and strand a password-less user with no\n * login. Fires the entity subscriber like `remove`. Returns `true`\n * when the row was removed.\n */\n removeGuarded(entity: IdentityProviderAccount, userHasOtherLogin: boolean): Promise<boolean>;\n\n /**\n * Find identity account with user relation (federated login + link flow).\n */\n findOneByProviderIdentity(identity: IdentityProviderIdentity): Promise<IdentityProviderAccount | null>;\n\n save(entity: DeepPartial<IdentityProviderAccount>): Promise<IdentityProviderAccount>;\n}\n\nexport type IdentityProviderAccountServiceReadOptions = {\n realmId?: string,\n};\n\nexport interface IIdentityProviderAccountService {\n /**\n * List linked accounts. An actor without\n * `IDENTITY_PROVIDER_ACCOUNT_READ` is scoped to its own rows.\n */\n getMany(\n query: Record<string, any>,\n actor: ActorContext,\n options?: IdentityProviderAccountServiceReadOptions\n ): Promise<EntityRepositoryFindManyResult<IdentityProviderAccount>>;\n\n /**\n * Read a single linked account. Own rows need no permission.\n */\n getOne(id: string, actor: ActorContext, options?: IdentityProviderAccountServiceReadOptions): Promise<IdentityProviderAccount>;\n\n /**\n * Unlink. Own rows need no permission. Refuses for EVERY caller when\n * the row is the user's last linked account and the user has no\n * password (lockout guard).\n */\n delete(id: string, actor: ActorContext, options?: IdentityProviderAccountServiceReadOptions): Promise<IdentityProviderAccount>;\n}\n"],"mappings":";;;;;;GAgBA,MAAaA,wCAAwC;CACjD;CACA;CACA;CACA;CACA;AACH"}
1
+ {"version":3,"file":"types.mjs","names":["IDENTITY_PROVIDER_ACCOUNT_FILTER_KEYS"],"sources":["../../../../src/core/entities/identity-provider-account/types.ts"],"sourcesContent":["/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport type { IdentityProviderAccount } from '@authup/core-kit';\nimport type { IQuery } from '@rapiq/core';\nimport type { ActorContext, EntityRepositoryFindManyResult } from '@authup/server-kit';\nimport type { DeepPartial } from 'typeorm';\nimport type { IdentityProviderIdentity } from '../../identity/provider/types.ts';\n\n/**\n * Filter keys an identity-provider-account list query may target.\n */\nexport const IDENTITY_PROVIDER_ACCOUNT_FILTER_KEYS = [\n 'id',\n 'userId',\n 'providerId',\n 'userRealmId',\n 'providerUserId',\n] as const;\n\nexport type IdentityProviderAccountFindManyOptions = {\n /**\n * Force the result to a single user (self-service). Applied as a\n * mandatory WHERE that a rapiq query filter cannot override.\n */\n userId?: string,\n /**\n * Restrict the result to a single owner realm (the nested\n * `/realms/:realmId/identity-provider-accounts` mount). The owner\n * realm of a row is `userRealmId`.\n */\n realmId?: string,\n};\n\n/**\n * Unified port: the entity CRUD surface (management API) plus the\n * account-manager methods the federated login and link flows consume.\n * One adapter implements both concerns over the same table.\n */\nexport interface IIdentityProviderAccountRepository {\n findMany(query: IQuery, options?: IdentityProviderAccountFindManyOptions): Promise<EntityRepositoryFindManyResult<IdentityProviderAccount>>;\n\n findOneById(id: string): Promise<IdentityProviderAccount | null>;\n\n remove(entity: IdentityProviderAccount): Promise<void>;\n\n /**\n * Atomically remove the account, refusing (returning `false`) when it\n * is the user's LAST linked account and `userHasOtherLogin` is false —\n * the unlink lockout guard. The count and the delete run in one\n * transaction with the user's account rows locked (on drivers that\n * support row locks), so two concurrent unlinks for the same user\n * cannot both pass the guard and strand a password-less user with no\n * login. Fires the entity subscriber like `remove`. Returns `true`\n * when the row was removed.\n */\n removeGuarded(entity: IdentityProviderAccount, userHasOtherLogin: boolean): Promise<boolean>;\n\n /**\n * Find identity account with user relation (federated login + link flow).\n */\n findOneByProviderIdentity(identity: IdentityProviderIdentity): Promise<IdentityProviderAccount | null>;\n\n /**\n * Throws `EntityConflictError` (`@authup/errors`) when the row violates\n * EITHER unique index, (providerUserId, providerId) or (providerId,\n * userId): the driver does not tell them apart, so the caller\n * classifies by re-reading the identity.\n */\n save(entity: DeepPartial<IdentityProviderAccount>): Promise<IdentityProviderAccount>;\n}\n\nexport type IdentityProviderAccountServiceReadOptions = {\n realmId?: string,\n};\n\nexport interface IIdentityProviderAccountService {\n /**\n * List linked accounts. An actor without\n * `IDENTITY_PROVIDER_ACCOUNT_READ` is scoped to its own rows.\n */\n getMany(\n query: Record<string, any>,\n actor: ActorContext,\n options?: IdentityProviderAccountServiceReadOptions\n ): Promise<EntityRepositoryFindManyResult<IdentityProviderAccount>>;\n\n /**\n * Read a single linked account. Own rows need no permission.\n */\n getOne(id: string, actor: ActorContext, options?: IdentityProviderAccountServiceReadOptions): Promise<IdentityProviderAccount>;\n\n /**\n * Unlink. Own rows need no permission. Refuses for EVERY caller when\n * the row is the user's last linked account and the user has no\n * password (lockout guard).\n */\n delete(id: string, actor: ActorContext, options?: IdentityProviderAccountServiceReadOptions): Promise<IdentityProviderAccount>;\n}\n"],"mappings":";;;;;;GAgBA,MAAaA,wCAAwC;CACjD;CACA;CACA;CACA;CACA;AACH"}
@@ -2,7 +2,7 @@ import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
4
  import { CONSENT_FILTER_KEYS, CONSENT_SCOPE_MAX_LENGTH } from "./consent/types.mjs";
5
- import { EVENT_ACTOR_NAME_MAX_LENGTH, EVENT_LOG_RETENTION_DAYS_DEFAULT } from "./event/constants.mjs";
5
+ import { EVENT_ACTOR_NAME_MAX_LENGTH, EVENT_LOG_RETENTION_DAYS_DEFAULT, EVENT_RETENTION_SWEEP_BATCH_SIZE } from "./event/constants.mjs";
6
6
  import { CLIENT_READ_PERMISSIONS } from "./client/constants.mjs";
7
7
  import { clientSchema } from "./client/schema.mjs";
8
8
  import { clientPermissionSchema } from "./client-permission/schema.mjs";
@@ -91,4 +91,4 @@ import { UserRoleService } from "./user-role/service.mjs";
91
91
  import "./user-role/index.mjs";
92
92
  import { IdentityProviderRoleMappingService } from "./identity-provider-role-mapping/service.mjs";
93
93
  import "./identity-provider-role-mapping/index.mjs";
94
- export { CLIENT_READ_PERMISSIONS, CONSENT_FILTER_KEYS, CONSENT_SCOPE_MAX_LENGTH, ClientPermissionService, ClientRoleService, ClientScopeService, ClientService, ConsentService, EVENT_ACTOR_NAME_MAX_LENGTH, EVENT_DIFF_SECRET_KEY_REGEX, EVENT_DIFF_VALUE_MAX_LENGTH, EVENT_LOG_RETENTION_DAYS_DEFAULT, EntityEventHandler, EventService, IDENTITY_PROVIDER_ACCOUNT_FILTER_KEYS, IDENTITY_PROVIDER_ACCOUNT_UNLINK_BLOCKED_ERROR_INSTANCE, IdentityProviderAccountService, IdentityProviderAccountUnlinkBlockedError, IdentityProviderRoleMappingService, KeyService, PermissionPolicyService, PermissionService, PolicyService, RealmService, RoleAttributeService, RolePermissionService, RoleService, SESSION_TOKEN_FILTER_KEYS, SYSTEM_CLIENT_DEFINITIONS, SYSTEM_CLIENT_SCOPE_NAMES, ScopeService, SessionService, SessionTokenService, SystemClientProvisioner, TrustAnchorService, USER_AUTHENTICATOR_ATTEMPT_CACHE_PREFIX, USER_AUTHENTICATOR_ATTEMPT_LOCK_FACTOR, USER_AUTHENTICATOR_ATTEMPT_LOCK_MAX, USER_AUTHENTICATOR_ATTEMPT_WINDOW, USER_AUTHENTICATOR_EMAIL_CODE_CACHE_PREFIX, USER_AUTHENTICATOR_EMAIL_CODE_EXPIRES_IN_MINUTES, USER_AUTHENTICATOR_EMAIL_CODE_LENGTH, USER_AUTHENTICATOR_EMAIL_SEND_CACHE_PREFIX, USER_AUTHENTICATOR_EMAIL_SEND_COOLDOWN, USER_AUTHENTICATOR_FILTER_KEYS, USER_AUTHENTICATOR_RECOVERY_CODE_COUNT, USER_AUTHENTICATOR_THROTTLE_CACHE_PREFIX, USER_AUTHENTICATOR_TOTP_ALGORITHM, USER_AUTHENTICATOR_TOTP_DIGITS, USER_AUTHENTICATOR_TOTP_PERIOD, USER_AUTHENTICATOR_VERIFY_LOCK_CACHE_PREFIX, USER_AUTHENTICATOR_VERIFY_LOCK_RENEW_INTERVAL, USER_AUTHENTICATOR_VERIFY_LOCK_TTL, USER_AUTHENTICATOR_WEBAUTHN_AUTH_CACHE_PREFIX, USER_AUTHENTICATOR_WEBAUTHN_CHALLENGE_WINDOW, USER_AUTHENTICATOR_WEBAUTHN_REG_CACHE_PREFIX, UserAttributeService, UserAuthenticatorService, UserPermissionService, UserRoleService, UserService, buildEntityDiff, buildSystemClientAttributes, buildWebauthnAuthenticationOptions, buildWebauthnRegistrationOptions, clientPermissionSchema, clientRoleSchema, clientSchema, clientScopeSchema, consentSchema, eventSchema, generateNumericCode, generateRecoveryCode, guessUserAuthenticatorKindByResponse, identityProviderAccountSchema, identityProviderRoleMappingSchema, identityProviderSchema, isIdentityProviderAccountUnlinkBlockedError, keySchema, permissionPolicySchema, permissionSchema, policySchema, realmSchema, roleAttributeSchema, rolePermissionSchema, roleSchema, sanitizeEventData, scopeSchema, sessionSchema, sessionTokenSchema, trustAnchorSchema, userAttributeSchema, userAuthenticatorSchema, userPermissionSchema, userRoleSchema, userSchema, verifyWebauthnAuthentication, verifyWebauthnRegistration };
94
+ export { CLIENT_READ_PERMISSIONS, CONSENT_FILTER_KEYS, CONSENT_SCOPE_MAX_LENGTH, ClientPermissionService, ClientRoleService, ClientScopeService, ClientService, ConsentService, EVENT_ACTOR_NAME_MAX_LENGTH, EVENT_DIFF_SECRET_KEY_REGEX, EVENT_DIFF_VALUE_MAX_LENGTH, EVENT_LOG_RETENTION_DAYS_DEFAULT, EVENT_RETENTION_SWEEP_BATCH_SIZE, EntityEventHandler, EventService, IDENTITY_PROVIDER_ACCOUNT_FILTER_KEYS, IDENTITY_PROVIDER_ACCOUNT_UNLINK_BLOCKED_ERROR_INSTANCE, IdentityProviderAccountService, IdentityProviderAccountUnlinkBlockedError, IdentityProviderRoleMappingService, KeyService, PermissionPolicyService, PermissionService, PolicyService, RealmService, RoleAttributeService, RolePermissionService, RoleService, SESSION_TOKEN_FILTER_KEYS, SYSTEM_CLIENT_DEFINITIONS, SYSTEM_CLIENT_SCOPE_NAMES, ScopeService, SessionService, SessionTokenService, SystemClientProvisioner, TrustAnchorService, USER_AUTHENTICATOR_ATTEMPT_CACHE_PREFIX, USER_AUTHENTICATOR_ATTEMPT_LOCK_FACTOR, USER_AUTHENTICATOR_ATTEMPT_LOCK_MAX, USER_AUTHENTICATOR_ATTEMPT_WINDOW, USER_AUTHENTICATOR_EMAIL_CODE_CACHE_PREFIX, USER_AUTHENTICATOR_EMAIL_CODE_EXPIRES_IN_MINUTES, USER_AUTHENTICATOR_EMAIL_CODE_LENGTH, USER_AUTHENTICATOR_EMAIL_SEND_CACHE_PREFIX, USER_AUTHENTICATOR_EMAIL_SEND_COOLDOWN, USER_AUTHENTICATOR_FILTER_KEYS, USER_AUTHENTICATOR_RECOVERY_CODE_COUNT, USER_AUTHENTICATOR_THROTTLE_CACHE_PREFIX, USER_AUTHENTICATOR_TOTP_ALGORITHM, USER_AUTHENTICATOR_TOTP_DIGITS, USER_AUTHENTICATOR_TOTP_PERIOD, USER_AUTHENTICATOR_VERIFY_LOCK_CACHE_PREFIX, USER_AUTHENTICATOR_VERIFY_LOCK_RENEW_INTERVAL, USER_AUTHENTICATOR_VERIFY_LOCK_TTL, USER_AUTHENTICATOR_WEBAUTHN_AUTH_CACHE_PREFIX, USER_AUTHENTICATOR_WEBAUTHN_CHALLENGE_WINDOW, USER_AUTHENTICATOR_WEBAUTHN_REG_CACHE_PREFIX, UserAttributeService, UserAuthenticatorService, UserPermissionService, UserRoleService, UserService, buildEntityDiff, buildSystemClientAttributes, buildWebauthnAuthenticationOptions, buildWebauthnRegistrationOptions, clientPermissionSchema, clientRoleSchema, clientSchema, clientScopeSchema, consentSchema, eventSchema, generateNumericCode, generateRecoveryCode, guessUserAuthenticatorKindByResponse, identityProviderAccountSchema, identityProviderRoleMappingSchema, identityProviderSchema, isIdentityProviderAccountUnlinkBlockedError, keySchema, permissionPolicySchema, permissionSchema, policySchema, realmSchema, roleAttributeSchema, rolePermissionSchema, roleSchema, sanitizeEventData, scopeSchema, sessionSchema, sessionTokenSchema, trustAnchorSchema, userAttributeSchema, userAuthenticatorSchema, userPermissionSchema, userRoleSchema, userSchema, verifyWebauthnAuthentication, verifyWebauthnRegistration };
@@ -8,5 +8,6 @@ export interface IUserIdentityRepository {
8
8
  savePermissions(user: User, items: IdentityProviderMapperElement[]): Promise<void>;
9
9
  saveRoles(user: User, items: IdentityProviderMapperElement[]): Promise<void>;
10
10
  saveOneWithEA(user: Partial<User>, extraAttributes: Record<string, any>): Promise<User>;
11
+ remove(user: User): Promise<void>;
11
12
  }
12
13
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/core/identity/entities/user/types.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,yBAAyB,CAAC;AAE7E,MAAM,WAAW,uBAAuB;IACpC,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IAC9C,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IAClE,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IAC1E,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IAE5D,eAAe,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,6BAA6B,EAAE,GAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACpF,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,6BAA6B,EAAE,GAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9E,aAAa,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,EAAE,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5F"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/core/identity/entities/user/types.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,yBAAyB,CAAC;AAE7E,MAAM,WAAW,uBAAuB;IACpC,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IAC9C,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IAClE,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IAC1E,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IAE5D,eAAe,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,6BAA6B,EAAE,GAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACpF,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,6BAA6B,EAAE,GAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9E,aAAa,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,EAAE,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzF,MAAM,CAAC,IAAI,EAAE,IAAI,GAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACtC"}
@@ -3,6 +3,16 @@ import "node:url";
3
3
  import.meta.url;
4
4
  import { applyJunctionCreateGrant, buildJunctionUpdateData } from "./permission/junction-grant.mjs";
5
5
  import { toIdentityPolicyData } from "./permission/identity-policy-data.mjs";
6
+ import { IdentityProviderLdapAuthenticator } from "./provider/authentication/protocols/ldap/module.mjs";
7
+ import { IdentityProviderLdapCollectionAuthenticator } from "./provider/authentication/protocols/ldap/collection.mjs";
8
+ import { IdentityProviderOAuth2Authenticator } from "./provider/authentication/protocols/oauth2/module.mjs";
9
+ import { IdentityProviderOpenIDAuthenticator } from "./provider/authentication/protocols/open-id/module.mjs";
10
+ import { IdentityProviderFacebookAuthenticator } from "./provider/authentication/presets/facebook.mjs";
11
+ import { IdentityProviderGithubAuthenticator } from "./provider/authentication/presets/github.mjs";
12
+ import { IdentityProviderGoogleAuthenticator } from "./provider/authentication/presets/google.mjs";
13
+ import { IdentityProviderInstagramAuthenticator } from "./provider/authentication/presets/instagram.mjs";
14
+ import { IdentityProviderPaypalAuthenticator } from "./provider/authentication/presets/paypal.mjs";
15
+ import { createIdentityProviderOAuth2Authenticator } from "./provider/authentication/factory.mjs";
6
16
  import { PASSWORD_RESET_EXPIRES_IN_MINUTES } from "./password-recovery/constants.mjs";
7
17
  import { PasswordRecoveryService } from "./password-recovery/service.mjs";
8
18
  import "./password-recovery/index.mjs";
@@ -11,6 +21,7 @@ import { IdentityPermissionProvider } from "./permission/module.mjs";
11
21
  import "./permission/index.mjs";
12
22
  import { PolicyCheckerService } from "./policy/checker/service.mjs";
13
23
  import "./policy/index.mjs";
24
+ import { IDENTITY_PROVIDER_ACCOUNT_LINK_TTL } from "./provider/account/constants.mjs";
14
25
  import { IDENTITY_PROVIDER_ACCOUNT_ALREADY_LINKED_ERROR_INSTANCE, IdentityProviderAccountAlreadyLinkedError, isIdentityProviderAccountAlreadyLinkedError } from "./provider/account/error.mjs";
15
26
  import { IdentityProviderMapperOperation } from "./provider/mapper/constants.mjs";
16
27
  import { IdentityProviderAccountBaseMapper } from "./provider/mapper/base.mjs";
@@ -18,21 +29,11 @@ import { IdentityProviderAttributeMapper } from "./provider/mapper/attributes/mo
18
29
  import { IdentityProviderRoleMapper } from "./provider/mapper/role/module.mjs";
19
30
  import { IdentityProviderPermissionMapper } from "./provider/mapper/permission/module.mjs";
20
31
  import { IdentityProviderAccountManager } from "./provider/account/module.mjs";
21
- import { IdentityProviderLdapAuthenticator } from "./provider/authentication/protocols/ldap/module.mjs";
22
- import { IdentityProviderLdapCollectionAuthenticator } from "./provider/authentication/protocols/ldap/collection.mjs";
23
- import { IdentityProviderOAuth2Authenticator } from "./provider/authentication/protocols/oauth2/module.mjs";
24
- import { IdentityProviderOpenIDAuthenticator } from "./provider/authentication/protocols/open-id/module.mjs";
25
- import { IdentityProviderFacebookAuthenticator } from "./provider/authentication/presets/facebook.mjs";
26
- import { IdentityProviderGithubAuthenticator } from "./provider/authentication/presets/github.mjs";
27
- import { IdentityProviderGoogleAuthenticator } from "./provider/authentication/presets/google.mjs";
28
- import { IdentityProviderInstagramAuthenticator } from "./provider/authentication/presets/instagram.mjs";
29
- import { IdentityProviderPaypalAuthenticator } from "./provider/authentication/presets/paypal.mjs";
30
32
  import { IDENTITY_PROVIDER_LDAP_COLLECTION_AUTHENTICATOR_TOKEN } from "./provider/authentication/constants.mjs";
31
- import { createIdentityProviderOAuth2Authenticator } from "./provider/authentication/factory.mjs";
32
33
  import "./provider/index.mjs";
33
34
  import { RegistrationService } from "./registration/service.mjs";
34
35
  import "./registration/index.mjs";
35
36
  import { IdentityResolver } from "./resolver/module.mjs";
36
37
  import "./resolver/index.mjs";
37
38
  import { IdentityRoleProvider } from "./role/module.mjs";
38
- export { IDENTITY_PROVIDER_ACCOUNT_ALREADY_LINKED_ERROR_INSTANCE, IDENTITY_PROVIDER_LDAP_COLLECTION_AUTHENTICATOR_TOKEN, IdentityPermissionProvider, IdentityProviderAccountAlreadyLinkedError, IdentityProviderAccountBaseMapper, IdentityProviderAccountManager, IdentityProviderAttributeMapper, IdentityProviderFacebookAuthenticator, IdentityProviderGithubAuthenticator, IdentityProviderGoogleAuthenticator, IdentityProviderInstagramAuthenticator, IdentityProviderLdapAuthenticator, IdentityProviderLdapCollectionAuthenticator, IdentityProviderMapperOperation, IdentityProviderOAuth2Authenticator, IdentityProviderOpenIDAuthenticator, IdentityProviderPaypalAuthenticator, IdentityProviderPermissionMapper, IdentityProviderRoleMapper, IdentityResolver, IdentityRoleProvider, PASSWORD_RESET_EXPIRES_IN_MINUTES, PasswordRecoveryService, PermissionCheckerService, PolicyCheckerService, RegistrationService, applyJunctionCreateGrant, buildJunctionUpdateData, createIdentityProviderOAuth2Authenticator, isIdentityProviderAccountAlreadyLinkedError, toIdentityPolicyData };
39
+ export { IDENTITY_PROVIDER_ACCOUNT_ALREADY_LINKED_ERROR_INSTANCE, IDENTITY_PROVIDER_ACCOUNT_LINK_TTL, IDENTITY_PROVIDER_LDAP_COLLECTION_AUTHENTICATOR_TOKEN, IdentityPermissionProvider, IdentityProviderAccountAlreadyLinkedError, IdentityProviderAccountBaseMapper, IdentityProviderAccountManager, IdentityProviderAttributeMapper, IdentityProviderFacebookAuthenticator, IdentityProviderGithubAuthenticator, IdentityProviderGoogleAuthenticator, IdentityProviderInstagramAuthenticator, IdentityProviderLdapAuthenticator, IdentityProviderLdapCollectionAuthenticator, IdentityProviderMapperOperation, IdentityProviderOAuth2Authenticator, IdentityProviderOpenIDAuthenticator, IdentityProviderPaypalAuthenticator, IdentityProviderPermissionMapper, IdentityProviderRoleMapper, IdentityResolver, IdentityRoleProvider, PASSWORD_RESET_EXPIRES_IN_MINUTES, PasswordRecoveryService, PermissionCheckerService, PolicyCheckerService, RegistrationService, applyJunctionCreateGrant, buildJunctionUpdateData, createIdentityProviderOAuth2Authenticator, isIdentityProviderAccountAlreadyLinkedError, toIdentityPolicyData };
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Lifetime of a pending account link (issue #3439).
3
+ *
4
+ * The handle is redeemed by the very next page load, so it needs to cover a
5
+ * redirect and a bootstrap, not a user's attention span. The authorization
6
+ * state's 30 minutes would leave a redeemable credential-binding handle
7
+ * lying in browser history far longer than the flow that produced it.
8
+ */
9
+ export declare const IDENTITY_PROVIDER_ACCOUNT_LINK_TTL: number;
10
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../../src/core/identity/provider/account/constants.ts"],"names":[],"mappings":"AAOA;;;;;;;GAOG;AACH,eAAO,MAAM,kCAAkC,QAAgB,CAAC"}
@@ -0,0 +1,16 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ //#region src/core/identity/provider/account/constants.ts
5
+ /**
6
+ * Lifetime of a pending account link (issue #3439).
7
+ *
8
+ * The handle is redeemed by the very next page load, so it needs to cover a
9
+ * redirect and a bootstrap, not a user's attention span. The authorization
10
+ * state's 30 minutes would leave a redeemable credential-binding handle
11
+ * lying in browser history far longer than the flow that produced it.
12
+ */ const IDENTITY_PROVIDER_ACCOUNT_LINK_TTL = 3e5;
13
+ //#endregion
14
+ export { IDENTITY_PROVIDER_ACCOUNT_LINK_TTL };
15
+
16
+ //# sourceMappingURL=constants.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.mjs","names":["IDENTITY_PROVIDER_ACCOUNT_LINK_TTL"],"sources":["../../../../../src/core/identity/provider/account/constants.ts"],"sourcesContent":["/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\n/**\n * Lifetime of a pending account link (issue #3439).\n *\n * The handle is redeemed by the very next page load, so it needs to cover a\n * redirect and a bootstrap, not a user's attention span. The authorization\n * state's 30 minutes would leave a redeemable credential-binding handle\n * lying in browser history far longer than the flow that produced it.\n */\nexport const IDENTITY_PROVIDER_ACCOUNT_LINK_TTL = 1000 * 60 * 5;\n"],"mappings":";;;;;;;;;;;GAeA,MAAaA,qCAAqC"}
@@ -1,3 +1,4 @@
1
+ export * from './constants.ts';
1
2
  export * from './error.ts';
2
3
  export * from './module.ts';
3
4
  export * from './types.ts';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/core/identity/provider/account/index.ts"],"names":[],"mappings":"AAOA,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/core/identity/provider/account/index.ts"],"names":[],"mappings":"AAOA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC"}
@@ -1,6 +1,7 @@
1
1
  import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
+ import { IDENTITY_PROVIDER_ACCOUNT_LINK_TTL } from "./constants.mjs";
4
5
  import { IDENTITY_PROVIDER_ACCOUNT_ALREADY_LINKED_ERROR_INSTANCE, IdentityProviderAccountAlreadyLinkedError, isIdentityProviderAccountAlreadyLinkedError } from "./error.mjs";
5
6
  import { IdentityProviderAccountManager } from "./module.mjs";
6
- export { IDENTITY_PROVIDER_ACCOUNT_ALREADY_LINKED_ERROR_INSTANCE, IdentityProviderAccountAlreadyLinkedError, IdentityProviderAccountManager, isIdentityProviderAccountAlreadyLinkedError };
7
+ export { IDENTITY_PROVIDER_ACCOUNT_ALREADY_LINKED_ERROR_INSTANCE, IDENTITY_PROVIDER_ACCOUNT_LINK_TTL, IdentityProviderAccountAlreadyLinkedError, IdentityProviderAccountManager, isIdentityProviderAccountAlreadyLinkedError };
@@ -1,5 +1,6 @@
1
1
  import type { IdentityProviderAccount, User } from '@authup/core-kit';
2
2
  import { UserValidator } from '@authup/core-kit';
3
+ import type { Logger } from '@authup/server-kit';
3
4
  import type { IUserIdentityRepository } from '../../entities/index.ts';
4
5
  import type { IIdentityProviderMapper } from '../mapper/index.ts';
5
6
  import type { IdentityProviderIdentity } from '../types.ts';
@@ -11,8 +12,10 @@ export declare class IdentityProviderAccountManager implements IIdentityProvider
11
12
  protected repository: IIdentityProviderAccountRepository;
12
13
  protected userRepository: IUserIdentityRepository;
13
14
  protected userValidator: UserValidator;
15
+ protected logger?: Logger;
14
16
  constructor(ctx: IdentityProviderAccountManagerContext);
15
17
  save(identity: IdentityProviderIdentity): Promise<IdentityProviderAccount>;
18
+ protected update(identity: IdentityProviderIdentity, account: IdentityProviderAccount): Promise<IdentityProviderAccount>;
16
19
  link(identity: IdentityProviderIdentity, userId: string): Promise<IdentityProviderAccount>;
17
20
  protected pickCandidate(identity: IdentityProviderIdentity, key: keyof User, maxLength: number): string | null;
18
21
  saveUser(identity: IdentityProviderIdentity, user?: User): Promise<User>;
@@ -1 +1 @@
1
- {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../../../../src/core/identity/provider/account/module.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,uBAAuB,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EACH,aAAa,EAGhB,MAAM,kBAAkB,CAAC;AAI1B,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAEvE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAElE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAE5D,OAAO,KAAK,EAAE,+BAA+B,EAAE,kCAAkC,EAAE,qCAAqC,EAAE,MAAM,YAAY,CAAC;AAE7I,qBAAa,8BAA+B,YAAW,+BAA+B;IAClF,SAAS,CAAC,gBAAgB,EAAG,uBAAuB,CAAC;IAErD,SAAS,CAAC,gBAAgB,EAAG,uBAAuB,CAAC;IAErD,SAAS,CAAC,UAAU,EAAG,uBAAuB,CAAC;IAE/C,SAAS,CAAC,UAAU,EAAG,kCAAkC,CAAC;IAE1D,SAAS,CAAC,cAAc,EAAE,uBAAuB,CAAC;IAElD,SAAS,CAAC,aAAa,EAAG,aAAa,CAAC;gBAE5B,GAAG,EAAE,qCAAqC;IAUhD,IAAI,CAAC,QAAQ,EAAE,wBAAwB,GAAG,OAAO,CAAC,uBAAuB,CAAC;IAmC1E,IAAI,CAAC,QAAQ,EAAE,wBAAwB,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,uBAAuB,CAAC;IAuChG,SAAS,CAAC,aAAa,CACnB,QAAQ,EAAE,wBAAwB,EAClC,GAAG,EAAE,MAAM,IAAI,EACf,SAAS,EAAE,MAAM,GAClB,MAAM,GAAG,IAAI;IAWV,QAAQ,CACV,QAAQ,EAAE,wBAAwB,EAClC,IAAI,CAAC,EAAE,IAAI,GACZ,OAAO,CAAC,IAAI,CAAC;IA0FV,kBAAkB,CACpB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,QAAQ,EAAE,wBAAwB,EAClC,QAAQ,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IA+ElB,eAAe,CACjB,QAAQ,EAAE,wBAAwB,EAClC,IAAI,EAAE,IAAI;IAOR,SAAS,CACX,QAAQ,EAAE,wBAAwB,EAClC,IAAI,EAAE,IAAI;CAMjB"}
1
+ {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../../../../src/core/identity/provider/account/module.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,uBAAuB,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EACH,aAAa,EAGhB,MAAM,kBAAkB,CAAC;AAI1B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAEjD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAEvE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAElE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAE5D,OAAO,KAAK,EAAE,+BAA+B,EAAE,kCAAkC,EAAE,qCAAqC,EAAE,MAAM,YAAY,CAAC;AAE7I,qBAAa,8BAA+B,YAAW,+BAA+B;IAClF,SAAS,CAAC,gBAAgB,EAAG,uBAAuB,CAAC;IAErD,SAAS,CAAC,gBAAgB,EAAG,uBAAuB,CAAC;IAErD,SAAS,CAAC,UAAU,EAAG,uBAAuB,CAAC;IAE/C,SAAS,CAAC,UAAU,EAAG,kCAAkC,CAAC;IAE1D,SAAS,CAAC,cAAc,EAAE,uBAAuB,CAAC;IAElD,SAAS,CAAC,aAAa,EAAG,aAAa,CAAC;IAExC,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;gBAEd,GAAG,EAAE,qCAAqC;IAWhD,IAAI,CAAC,QAAQ,EAAE,wBAAwB,GAAG,OAAO,CAAC,uBAAuB,CAAC;cAsDhE,MAAM,CAClB,QAAQ,EAAE,wBAAwB,EAClC,OAAO,EAAE,uBAAuB,GACjC,OAAO,CAAC,uBAAuB,CAAC;IAa7B,IAAI,CAAC,QAAQ,EAAE,wBAAwB,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,uBAAuB,CAAC;IA6DhG,SAAS,CAAC,aAAa,CACnB,QAAQ,EAAE,wBAAwB,EAClC,GAAG,EAAE,MAAM,IAAI,EACf,SAAS,EAAE,MAAM,GAClB,MAAM,GAAG,IAAI;IAWV,QAAQ,CACV,QAAQ,EAAE,wBAAwB,EAClC,IAAI,CAAC,EAAE,IAAI,GACZ,OAAO,CAAC,IAAI,CAAC;IA0FV,kBAAkB,CACpB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,QAAQ,EAAE,wBAAwB,EAClC,QAAQ,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IA+ElB,eAAe,CACjB,QAAQ,EAAE,wBAAwB,EAClC,IAAI,EAAE,IAAI;IAOR,SAAS,CACX,QAAQ,EAAE,wBAAwB,EAClC,IAAI,EAAE,IAAI;CAMjB"}
@@ -2,12 +2,14 @@ import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
4
  import { IdentityProviderAccountAlreadyLinkedError } from "./error.mjs";
5
+ import { describeError } from "../../../../utils/error.mjs";
6
+ import "../../../../utils/index.mjs";
5
7
  import { IdentityProviderIdentityOperation } from "../constants.mjs";
6
8
  import { IdentityProviderMapperOperation } from "../mapper/constants.mjs";
7
9
  import "../mapper/index.mjs";
8
10
  import { ValidatorGroup, createNanoID, extendObject } from "@authup/kit";
9
11
  import { UserValidator, buildUserFakeEmail, isUserFakeEmail } from "@authup/core-kit";
10
- import { ValidationError } from "@authup/errors";
12
+ import { ValidationError, isEntityConflictError } from "@authup/errors";
11
13
  import { isValidupError, stringifyPath } from "validup";
12
14
  //#region src/core/identity/provider/account/module.ts
13
15
  var IdentityProviderAccountManager = class {
@@ -17,35 +19,50 @@ var IdentityProviderAccountManager = class {
17
19
  repository;
18
20
  userRepository;
19
21
  userValidator;
22
+ logger;
20
23
  constructor(ctx) {
21
24
  this.attributesMapper = ctx.attributeMapper;
22
25
  this.permissionMapper = ctx.permissionMapper;
23
26
  this.roleMapper = ctx.roleMapper;
24
27
  this.repository = ctx.repository;
25
28
  this.userRepository = ctx.userRepository;
29
+ this.logger = ctx.logger;
26
30
  this.userValidator = new UserValidator();
27
31
  }
28
32
  async save(identity) {
29
33
  let account = await this.repository.findOneByProviderIdentity(identity);
30
- if (account) {
31
- identity.operation = IdentityProviderIdentityOperation.UPDATE;
32
- account.user = await this.saveUser(identity, account.user);
33
- await this.repository.save(account);
34
- await this.saveRoles(identity, account.user);
35
- await this.savePermissions(identity, account.user);
36
- return account;
37
- }
34
+ if (account) return this.update(identity, account);
38
35
  identity.operation = IdentityProviderIdentityOperation.CREATE;
39
36
  const user = await this.saveUser(identity);
40
- account = await this.repository.save({
41
- providerId: identity.provider.id,
42
- providerUserId: identity.id,
43
- providerUserName: user.name,
44
- providerRealmId: identity.provider.realmId,
45
- user,
46
- userId: user.id,
47
- userRealmId: user.realmId
48
- });
37
+ try {
38
+ account = await this.repository.save({
39
+ providerId: identity.provider.id,
40
+ providerUserId: identity.id,
41
+ providerUserName: user.name,
42
+ providerRealmId: identity.provider.realmId,
43
+ user,
44
+ userId: user.id,
45
+ userRealmId: user.realmId
46
+ });
47
+ } catch (e) {
48
+ if (!isEntityConflictError(e)) throw e;
49
+ const raced = await this.repository.findOneByProviderIdentity(identity);
50
+ if (!raced) throw e;
51
+ try {
52
+ await this.userRepository.remove(user);
53
+ } catch (removeError) {
54
+ this.logger?.warn(describeError(removeError, `The user provisioned for the external identity "${identity.id}" could not be removed after losing a concurrent first login.`));
55
+ }
56
+ return this.update(identity, raced);
57
+ }
58
+ await this.saveRoles(identity, account.user);
59
+ await this.savePermissions(identity, account.user);
60
+ return account;
61
+ }
62
+ async update(identity, account) {
63
+ identity.operation = IdentityProviderIdentityOperation.UPDATE;
64
+ account.user = await this.saveUser(identity, account.user);
65
+ await this.repository.save(account);
49
66
  await this.saveRoles(identity, account.user);
50
67
  await this.savePermissions(identity, account.user);
51
68
  return account;
@@ -63,15 +80,25 @@ var IdentityProviderAccountManager = class {
63
80
  existing.providerUserEmail = providerUserEmail ?? existing.providerUserEmail;
64
81
  return this.repository.save(existing);
65
82
  }
66
- return this.repository.save({
67
- providerId: identity.provider.id,
68
- providerUserId: identity.id,
69
- providerUserName,
70
- providerUserEmail: providerUserEmail ?? void 0,
71
- providerRealmId: identity.provider.realmId,
72
- userId,
73
- userRealmId: user.realmId ?? null
74
- });
83
+ try {
84
+ return await this.repository.save({
85
+ providerId: identity.provider.id,
86
+ providerUserId: identity.id,
87
+ providerUserName,
88
+ providerUserEmail: providerUserEmail ?? void 0,
89
+ providerRealmId: identity.provider.realmId,
90
+ userId,
91
+ userRealmId: user.realmId ?? null
92
+ });
93
+ } catch (e) {
94
+ if (!isEntityConflictError(e)) throw e;
95
+ const raced = await this.repository.findOneByProviderIdentity(identity);
96
+ if (raced) {
97
+ if (raced.userId === userId) return raced;
98
+ throw new IdentityProviderAccountAlreadyLinkedError();
99
+ }
100
+ throw new ValidationError("The user is already linked to this identity provider through another account.");
101
+ }
75
102
  }
76
103
  pickCandidate(identity, key, maxLength) {
77
104
  const candidates = identity.attributeCandidates?.[key] || [];
@@ -1 +1 @@
1
- {"version":3,"file":"module.mjs","names":["UserValidator","buildUserFakeEmail","isUserFakeEmail","ValidatorGroup","createNanoID","extendObject","ValidationError","isValidupError","stringifyPath","IdentityProviderIdentityOperation","IdentityProviderMapperOperation","IdentityProviderAccountAlreadyLinkedError","IdentityProviderAccountManager","attributesMapper","permissionMapper","roleMapper","repository","userRepository","userValidator","ctx","attributeMapper","save","identity","account","findOneByProviderIdentity","operation","UPDATE","user","saveUser","saveRoles","savePermissions","CREATE","providerId","provider","id","providerUserId","providerUserName","name","providerRealmId","realmId","userId","userRealmId","link","findOneById","pickCandidate","slice","providerUserEmail","existing","undefined","key","maxLength","candidates","attributeCandidates","candidate","length","attributes","execute","entity","attribute","value","active","nameLocked","clientId","attributesSelf","validateAttributes","Error","attributesSelfKeys","Object","keys","attributesExtra","entityKeys","entityKey","index","indexOf","output","trim","toLowerCase","attempts","Math","max","saveOneWithEA","names","shift","run","group","email","e","retry","i","issues","child","pathNormalized","path","Array","isArray","first","rest","push","entities"],"sources":["../../../../../src/core/identity/provider/account/module.ts"],"sourcesContent":["/*\n * Copyright (c) 2025.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport type { IdentityProviderAccount, User } from '@authup/core-kit';\nimport {\n UserValidator,\n buildUserFakeEmail,\n isUserFakeEmail,\n} from '@authup/core-kit';\nimport { ValidatorGroup, createNanoID, extendObject } from '@authup/kit';\nimport { ValidationError } from '@authup/errors';\nimport { isValidupError, stringifyPath } from 'validup';\nimport type { IUserIdentityRepository } from '../../entities/index.ts';\nimport { IdentityProviderIdentityOperation } from '../constants.ts';\nimport type { IIdentityProviderMapper } from '../mapper/index.ts';\nimport { IdentityProviderMapperOperation } from '../mapper/index.ts';\nimport type { IdentityProviderIdentity } from '../types.ts';\nimport { IdentityProviderAccountAlreadyLinkedError } from './error.ts';\nimport type { IIdentityProviderAccountManager, IIdentityProviderAccountRepository, IdentityProviderAccountManagerContext } from './types.ts';\n\nexport class IdentityProviderAccountManager implements IIdentityProviderAccountManager {\n protected attributesMapper : IIdentityProviderMapper;\n\n protected permissionMapper : IIdentityProviderMapper;\n\n protected roleMapper : IIdentityProviderMapper;\n\n protected repository : IIdentityProviderAccountRepository;\n\n protected userRepository: IUserIdentityRepository;\n\n protected userValidator : UserValidator;\n\n constructor(ctx: IdentityProviderAccountManagerContext) {\n this.attributesMapper = ctx.attributeMapper;\n this.permissionMapper = ctx.permissionMapper;\n this.roleMapper = ctx.roleMapper;\n this.repository = ctx.repository;\n this.userRepository = ctx.userRepository;\n\n this.userValidator = new UserValidator();\n }\n\n async save(identity: IdentityProviderIdentity): Promise<IdentityProviderAccount> {\n let account = await this.repository.findOneByProviderIdentity(identity);\n if (account) {\n identity.operation = IdentityProviderIdentityOperation.UPDATE;\n\n account.user = await this.saveUser(identity, account.user);\n\n await this.repository.save(account);\n\n await this.saveRoles(identity, account.user);\n await this.savePermissions(identity, account.user);\n\n return account;\n }\n\n identity.operation = IdentityProviderIdentityOperation.CREATE;\n\n const user = await this.saveUser(identity);\n\n account = await this.repository.save({\n providerId: identity.provider.id,\n providerUserId: identity.id,\n providerUserName: user.name, // todo: parse identity.name\n providerRealmId: identity.provider.realmId,\n user,\n userId: user.id,\n userRealmId: user.realmId,\n });\n\n await this.saveRoles(identity, account.user);\n await this.savePermissions(identity, account.user);\n\n return account;\n }\n\n async link(identity: IdentityProviderIdentity, userId: string): Promise<IdentityProviderAccount> {\n const user = await this.userRepository.findOneById(userId);\n if (!user) {\n throw new ValidationError('The linking user does not exist.');\n }\n\n if (\n identity.provider.realmId &&\n user.realmId !== identity.provider.realmId\n ) {\n throw new ValidationError('The provider and user realm do not match.');\n }\n\n const providerUserName = this.pickCandidate(identity, 'name', 256) ?? identity.id.slice(0, 256);\n const providerUserEmail = this.pickCandidate(identity, 'email', 512);\n\n const existing = await this.repository.findOneByProviderIdentity(identity);\n if (existing) {\n if (existing.userId !== userId) {\n throw new IdentityProviderAccountAlreadyLinkedError();\n }\n\n existing.providerUserName = providerUserName ?? existing.providerUserName;\n existing.providerUserEmail = providerUserEmail ?? existing.providerUserEmail;\n\n return this.repository.save(existing);\n }\n\n return this.repository.save({\n providerId: identity.provider.id,\n providerUserId: identity.id,\n providerUserName,\n providerUserEmail: providerUserEmail ?? undefined,\n providerRealmId: identity.provider.realmId,\n userId,\n userRealmId: user.realmId ?? null,\n });\n }\n\n protected pickCandidate(\n identity: IdentityProviderIdentity,\n key: keyof User,\n maxLength: number,\n ): string | null {\n const candidates = identity.attributeCandidates?.[key] || [];\n for (const candidate of candidates) {\n if (typeof candidate === 'string' && candidate.length > 0) {\n return candidate.slice(0, maxLength);\n }\n }\n\n return null;\n }\n\n async saveUser(\n identity: IdentityProviderIdentity,\n user?: User,\n ): Promise<User> {\n const attributes = await this.attributesMapper.execute(\n identity,\n );\n\n const entity : Record<string, any> = {};\n for (const attribute of attributes) {\n if (\n attribute.key &&\n attribute.operation === IdentityProviderMapperOperation.CREATE\n ) {\n // attribute value might be object, array, ...\n entity[attribute.key] = attribute.value;\n }\n }\n\n if (!user) {\n entity.realmId = identity.provider.realmId;\n entity.active = true;\n entity.nameLocked = true;\n entity.clientId = identity.clientId || null;\n }\n\n const attributesSelf = await this.validateAttributes(entity, identity, 10);\n if (!attributesSelf) {\n // todo: better error name\n throw new Error('Identity provider attributes could not be validated.');\n }\n const attributesSelfKeys = Object.keys(attributesSelf);\n\n const attributesExtra : Record<string, any> = {};\n\n const entityKeys = Object.keys(entity);\n for (const entityKey of entityKeys) {\n const index = attributesSelfKeys.indexOf(entityKey);\n if (index !== -1) {\n continue;\n }\n\n attributesExtra[entityKey] = entity[entityKey];\n }\n\n let output : User;\n if (user) {\n output = extendObject(user, attributesSelf);\n } else {\n output = attributesSelf;\n }\n\n if (typeof output.name === 'string') {\n output.name = output.name.trim().toLowerCase();\n }\n\n let attempts = Math.max((identity.attributeCandidates?.name?.length || 0) + 1, 10);\n while (attempts > 0) {\n try {\n // todo: we also need to remove existing ones via idp login flow ( but not other attributes!)\n return await this.userRepository.saveOneWithEA(output, attributesExtra);\n } catch {\n const names = identity.attributeCandidates?.name || [];\n if (names.length > 0) {\n while (names.length > 0) {\n output.name = `${names.shift()}`.trim().toLowerCase();\n\n try {\n await this.userValidator.run(output, {\n group: identity.operation === IdentityProviderIdentityOperation.CREATE ?\n ValidatorGroup.CREATE :\n ValidatorGroup.UPDATE,\n });\n break;\n } catch {\n // todo: do nothing.\n }\n }\n } else {\n output.name = createNanoID('0123456789abcdefghijklmnopqrstuvwxyz-_', 10);\n }\n\n if (isUserFakeEmail(output.email)) {\n output.email = buildUserFakeEmail(output.name);\n }\n\n attempts -= 1;\n }\n }\n\n throw new Error('The user account could not be created due a conflict error.');\n }\n\n async validateAttributes(\n entity: Record<string, any>,\n identity: IdentityProviderIdentity,\n attempts: number,\n ) : Promise<User | null> {\n attempts--;\n if (attempts <= 0) {\n return null;\n }\n\n try {\n return await this.userValidator.run(entity, {\n group: identity.operation === IdentityProviderIdentityOperation.CREATE ?\n ValidatorGroup.CREATE :\n ValidatorGroup.UPDATE,\n });\n } catch (e: any) {\n if (!isValidupError(e)) {\n return null;\n }\n\n let retry = false;\n for (let i = 0; i < e.issues.length; i++) {\n const child = e.issues[i];\n\n const pathNormalized = stringifyPath(child.path);\n\n if (\n Array.isArray(entity[pathNormalized]) &&\n entity[pathNormalized].length > 0\n ) {\n const [first, ...rest] = entity[pathNormalized];\n entity[pathNormalized] = first;\n\n if (rest.length > 0) {\n if (!identity.attributeCandidates) {\n identity.attributeCandidates = {};\n }\n\n if (!identity.attributeCandidates[pathNormalized]) {\n identity.attributeCandidates[pathNormalized] = [];\n }\n\n identity.attributeCandidates[pathNormalized]!.push(...rest);\n }\n\n retry = true;\n break;\n }\n\n if (\n identity.attributeCandidates &&\n identity.attributeCandidates[pathNormalized] &&\n identity.attributeCandidates[pathNormalized]!.length > 0\n ) {\n entity[pathNormalized] = identity.attributeCandidates[pathNormalized]!.shift();\n retry = true;\n break;\n }\n\n if (\n entity.name &&\n pathNormalized === 'email'\n ) {\n entity[pathNormalized] = buildUserFakeEmail(entity.name);\n retry = true;\n break;\n }\n\n if (entity[pathNormalized]) {\n entity[pathNormalized] = null;\n break;\n }\n }\n\n if (retry) {\n return this.validateAttributes(entity, identity, attempts);\n }\n\n return null;\n }\n }\n\n async savePermissions(\n identity: IdentityProviderIdentity,\n user: User,\n ) {\n const entities = await this.permissionMapper.execute(identity);\n\n await this.userRepository.savePermissions(user, entities);\n }\n\n async saveRoles(\n identity: IdentityProviderIdentity,\n user: User,\n ) {\n const entities = await this.roleMapper.execute(identity);\n\n await this.userRepository.saveRoles(user, entities);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"module.mjs","names":["UserValidator","buildUserFakeEmail","isUserFakeEmail","ValidatorGroup","createNanoID","extendObject","ValidationError","isEntityConflictError","isValidupError","stringifyPath","describeError","IdentityProviderIdentityOperation","IdentityProviderMapperOperation","IdentityProviderAccountAlreadyLinkedError","IdentityProviderAccountManager","attributesMapper","permissionMapper","roleMapper","repository","userRepository","userValidator","logger","ctx","attributeMapper","save","identity","account","findOneByProviderIdentity","update","operation","CREATE","user","saveUser","providerId","provider","id","providerUserId","providerUserName","name","providerRealmId","realmId","userId","userRealmId","e","raced","remove","removeError","warn","saveRoles","savePermissions","UPDATE","link","findOneById","pickCandidate","slice","providerUserEmail","existing","undefined","key","maxLength","candidates","attributeCandidates","candidate","length","attributes","execute","entity","attribute","value","active","nameLocked","clientId","attributesSelf","validateAttributes","Error","attributesSelfKeys","Object","keys","attributesExtra","entityKeys","entityKey","index","indexOf","output","trim","toLowerCase","attempts","Math","max","saveOneWithEA","names","shift","run","group","email","retry","i","issues","child","pathNormalized","path","Array","isArray","first","rest","push","entities"],"sources":["../../../../../src/core/identity/provider/account/module.ts"],"sourcesContent":["/*\n * Copyright (c) 2025.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport type { IdentityProviderAccount, User } from '@authup/core-kit';\nimport {\n UserValidator,\n buildUserFakeEmail,\n isUserFakeEmail,\n} from '@authup/core-kit';\nimport { ValidatorGroup, createNanoID, extendObject } from '@authup/kit';\nimport { ValidationError, isEntityConflictError } from '@authup/errors';\nimport { isValidupError, stringifyPath } from 'validup';\nimport type { Logger } from '@authup/server-kit';\nimport { describeError } from '../../../../utils/index.ts';\nimport type { IUserIdentityRepository } from '../../entities/index.ts';\nimport { IdentityProviderIdentityOperation } from '../constants.ts';\nimport type { IIdentityProviderMapper } from '../mapper/index.ts';\nimport { IdentityProviderMapperOperation } from '../mapper/index.ts';\nimport type { IdentityProviderIdentity } from '../types.ts';\nimport { IdentityProviderAccountAlreadyLinkedError } from './error.ts';\nimport type { IIdentityProviderAccountManager, IIdentityProviderAccountRepository, IdentityProviderAccountManagerContext } from './types.ts';\n\nexport class IdentityProviderAccountManager implements IIdentityProviderAccountManager {\n protected attributesMapper : IIdentityProviderMapper;\n\n protected permissionMapper : IIdentityProviderMapper;\n\n protected roleMapper : IIdentityProviderMapper;\n\n protected repository : IIdentityProviderAccountRepository;\n\n protected userRepository: IUserIdentityRepository;\n\n protected userValidator : UserValidator;\n\n protected logger?: Logger;\n\n constructor(ctx: IdentityProviderAccountManagerContext) {\n this.attributesMapper = ctx.attributeMapper;\n this.permissionMapper = ctx.permissionMapper;\n this.roleMapper = ctx.roleMapper;\n this.repository = ctx.repository;\n this.userRepository = ctx.userRepository;\n this.logger = ctx.logger;\n\n this.userValidator = new UserValidator();\n }\n\n async save(identity: IdentityProviderIdentity): Promise<IdentityProviderAccount> {\n let account = await this.repository.findOneByProviderIdentity(identity);\n if (account) {\n return this.update(identity, account);\n }\n\n identity.operation = IdentityProviderIdentityOperation.CREATE;\n\n const user = await this.saveUser(identity);\n\n try {\n account = await this.repository.save({\n providerId: identity.provider.id,\n providerUserId: identity.id,\n providerUserName: user.name, // todo: parse identity.name\n providerRealmId: identity.provider.realmId,\n user,\n userId: user.id,\n userRealmId: user.realmId,\n });\n } catch (e) {\n if (!isEntityConflictError(e)) {\n throw e;\n }\n\n // the loser of two concurrent first logins: the identity is linked\n // now, so continue with the winner's account and drop the user\n // this request provisioned moments ago (unreferenced; best effort,\n // a leftover row must not fail the login).\n const raced = await this.repository.findOneByProviderIdentity(identity);\n if (!raced) {\n throw e;\n }\n\n try {\n await this.userRepository.remove(user);\n } catch (removeError) {\n // The row is unreferenced, so it costs nothing but a squatted\n // user name. Left unreported it would be invisible forever.\n this.logger?.warn(describeError(\n removeError,\n `The user provisioned for the external identity \"${identity.id}\" could not be removed after losing a concurrent first login.`,\n ));\n }\n\n return this.update(identity, raced);\n }\n\n await this.saveRoles(identity, account.user);\n await this.savePermissions(identity, account.user);\n\n return account;\n }\n\n protected async update(\n identity: IdentityProviderIdentity,\n account: IdentityProviderAccount,\n ): Promise<IdentityProviderAccount> {\n identity.operation = IdentityProviderIdentityOperation.UPDATE;\n\n account.user = await this.saveUser(identity, account.user);\n\n await this.repository.save(account);\n\n await this.saveRoles(identity, account.user);\n await this.savePermissions(identity, account.user);\n\n return account;\n }\n\n async link(identity: IdentityProviderIdentity, userId: string): Promise<IdentityProviderAccount> {\n const user = await this.userRepository.findOneById(userId);\n if (!user) {\n throw new ValidationError('The linking user does not exist.');\n }\n\n if (\n identity.provider.realmId &&\n user.realmId !== identity.provider.realmId\n ) {\n throw new ValidationError('The provider and user realm do not match.');\n }\n\n const providerUserName = this.pickCandidate(identity, 'name', 256) ?? identity.id.slice(0, 256);\n const providerUserEmail = this.pickCandidate(identity, 'email', 512);\n\n const existing = await this.repository.findOneByProviderIdentity(identity);\n if (existing) {\n if (existing.userId !== userId) {\n throw new IdentityProviderAccountAlreadyLinkedError();\n }\n\n existing.providerUserName = providerUserName ?? existing.providerUserName;\n existing.providerUserEmail = providerUserEmail ?? existing.providerUserEmail;\n\n return this.repository.save(existing);\n }\n\n try {\n return await this.repository.save({\n providerId: identity.provider.id,\n providerUserId: identity.id,\n providerUserName,\n providerUserEmail: providerUserEmail ?? undefined,\n providerRealmId: identity.provider.realmId,\n userId,\n userRealmId: user.realmId ?? null,\n });\n } catch (e) {\n if (!isEntityConflictError(e)) {\n throw e;\n }\n\n // one of the two unique indexes fired; the re-read tells which:\n // a row for the identity means (providerUserId, providerId), a\n // race with the SAME user (two Connect tabs) being a no-op; no\n // row means (providerId, userId), the user already holds a link\n // at this provider through another external account.\n const raced = await this.repository.findOneByProviderIdentity(identity);\n if (raced) {\n if (raced.userId === userId) {\n return raced;\n }\n\n throw new IdentityProviderAccountAlreadyLinkedError();\n }\n\n throw new ValidationError('The user is already linked to this identity provider through another account.');\n }\n }\n\n protected pickCandidate(\n identity: IdentityProviderIdentity,\n key: keyof User,\n maxLength: number,\n ): string | null {\n const candidates = identity.attributeCandidates?.[key] || [];\n for (const candidate of candidates) {\n if (typeof candidate === 'string' && candidate.length > 0) {\n return candidate.slice(0, maxLength);\n }\n }\n\n return null;\n }\n\n async saveUser(\n identity: IdentityProviderIdentity,\n user?: User,\n ): Promise<User> {\n const attributes = await this.attributesMapper.execute(\n identity,\n );\n\n const entity : Record<string, any> = {};\n for (const attribute of attributes) {\n if (\n attribute.key &&\n attribute.operation === IdentityProviderMapperOperation.CREATE\n ) {\n // attribute value might be object, array, ...\n entity[attribute.key] = attribute.value;\n }\n }\n\n if (!user) {\n entity.realmId = identity.provider.realmId;\n entity.active = true;\n entity.nameLocked = true;\n entity.clientId = identity.clientId || null;\n }\n\n const attributesSelf = await this.validateAttributes(entity, identity, 10);\n if (!attributesSelf) {\n // todo: better error name\n throw new Error('Identity provider attributes could not be validated.');\n }\n const attributesSelfKeys = Object.keys(attributesSelf);\n\n const attributesExtra : Record<string, any> = {};\n\n const entityKeys = Object.keys(entity);\n for (const entityKey of entityKeys) {\n const index = attributesSelfKeys.indexOf(entityKey);\n if (index !== -1) {\n continue;\n }\n\n attributesExtra[entityKey] = entity[entityKey];\n }\n\n let output : User;\n if (user) {\n output = extendObject(user, attributesSelf);\n } else {\n output = attributesSelf;\n }\n\n if (typeof output.name === 'string') {\n output.name = output.name.trim().toLowerCase();\n }\n\n let attempts = Math.max((identity.attributeCandidates?.name?.length || 0) + 1, 10);\n while (attempts > 0) {\n try {\n // todo: we also need to remove existing ones via idp login flow ( but not other attributes!)\n return await this.userRepository.saveOneWithEA(output, attributesExtra);\n } catch {\n const names = identity.attributeCandidates?.name || [];\n if (names.length > 0) {\n while (names.length > 0) {\n output.name = `${names.shift()}`.trim().toLowerCase();\n\n try {\n await this.userValidator.run(output, {\n group: identity.operation === IdentityProviderIdentityOperation.CREATE ?\n ValidatorGroup.CREATE :\n ValidatorGroup.UPDATE,\n });\n break;\n } catch {\n // todo: do nothing.\n }\n }\n } else {\n output.name = createNanoID('0123456789abcdefghijklmnopqrstuvwxyz-_', 10);\n }\n\n if (isUserFakeEmail(output.email)) {\n output.email = buildUserFakeEmail(output.name);\n }\n\n attempts -= 1;\n }\n }\n\n throw new Error('The user account could not be created due a conflict error.');\n }\n\n async validateAttributes(\n entity: Record<string, any>,\n identity: IdentityProviderIdentity,\n attempts: number,\n ) : Promise<User | null> {\n attempts--;\n if (attempts <= 0) {\n return null;\n }\n\n try {\n return await this.userValidator.run(entity, {\n group: identity.operation === IdentityProviderIdentityOperation.CREATE ?\n ValidatorGroup.CREATE :\n ValidatorGroup.UPDATE,\n });\n } catch (e: any) {\n if (!isValidupError(e)) {\n return null;\n }\n\n let retry = false;\n for (let i = 0; i < e.issues.length; i++) {\n const child = e.issues[i];\n\n const pathNormalized = stringifyPath(child.path);\n\n if (\n Array.isArray(entity[pathNormalized]) &&\n entity[pathNormalized].length > 0\n ) {\n const [first, ...rest] = entity[pathNormalized];\n entity[pathNormalized] = first;\n\n if (rest.length > 0) {\n if (!identity.attributeCandidates) {\n identity.attributeCandidates = {};\n }\n\n if (!identity.attributeCandidates[pathNormalized]) {\n identity.attributeCandidates[pathNormalized] = [];\n }\n\n identity.attributeCandidates[pathNormalized]!.push(...rest);\n }\n\n retry = true;\n break;\n }\n\n if (\n identity.attributeCandidates &&\n identity.attributeCandidates[pathNormalized] &&\n identity.attributeCandidates[pathNormalized]!.length > 0\n ) {\n entity[pathNormalized] = identity.attributeCandidates[pathNormalized]!.shift();\n retry = true;\n break;\n }\n\n if (\n entity.name &&\n pathNormalized === 'email'\n ) {\n entity[pathNormalized] = buildUserFakeEmail(entity.name);\n retry = true;\n break;\n }\n\n if (entity[pathNormalized]) {\n entity[pathNormalized] = null;\n break;\n }\n }\n\n if (retry) {\n return this.validateAttributes(entity, identity, attempts);\n }\n\n return null;\n }\n }\n\n async savePermissions(\n identity: IdentityProviderIdentity,\n user: User,\n ) {\n const entities = await this.permissionMapper.execute(identity);\n\n await this.userRepository.savePermissions(user, entities);\n }\n\n async saveRoles(\n identity: IdentityProviderIdentity,\n user: User,\n ) {\n const entities = await this.roleMapper.execute(identity);\n\n await this.userRepository.saveRoles(user, entities);\n }\n}\n"],"mappings":"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"}
@@ -1,13 +1,55 @@
1
1
  import type { IdentityProviderAccount } from '@authup/core-kit';
2
+ import type { Logger } from '@authup/server-kit';
2
3
  import type { IUserIdentityRepository } from '../../entities/index.ts';
3
4
  import type { IIdentityProviderMapper } from '../mapper/index.ts';
4
5
  import type { IdentityProviderIdentity } from '../types.ts';
5
6
  import type { IIdentityProviderAccountRepository } from '../../../entities/identity-provider-account/types.ts';
6
7
  export type { IIdentityProviderAccountRepository } from '../../../entities/identity-provider-account/types.ts';
8
+ /**
9
+ * A resolved external identity awaiting a bearer-authenticated confirmation
10
+ * (issue #3439). The callback that resolved it is unauthenticated, so it may
11
+ * not perform the credential binding itself; it stashes this projection under
12
+ * a one-time handle and the account console redeems it with its bearer.
13
+ *
14
+ * Deliberately four scalars rather than the `IdentityProviderIdentity` it was
15
+ * projected from: that object carries the full provider entity (including the
16
+ * EA-loaded `clientSecret`) and the raw external token payload, neither of
17
+ * which belongs in a cache. They are also exactly what `link()` reads.
18
+ */
19
+ export type IdentityProviderAccountLink = {
20
+ providerId: string;
21
+ /**
22
+ * The user the link-request was minted for. The confirm endpoint
23
+ * requires it to equal the AUTHENTICATED user, which is what stops an
24
+ * attacker-minted handle from binding a victim's external identity to
25
+ * the attacker's account.
26
+ */
27
+ userId: string;
28
+ providerUserId: string;
29
+ providerUserName?: string | null;
30
+ providerUserEmail?: string | null;
31
+ };
32
+ export interface IIdentityProviderAccountLinkStore {
33
+ /**
34
+ * @returns the one-time handle
35
+ */
36
+ save(data: IdentityProviderAccountLink): Promise<string>;
37
+ /**
38
+ * Reads and DROPS the stash. Single use: a handle that reaches a log or
39
+ * a browser history entry must not be redeemable twice.
40
+ */
41
+ consume(handle: string): Promise<IdentityProviderAccountLink | null>;
42
+ }
7
43
  export type IdentityProviderAccountManagerContext = {
8
44
  attributeMapper: IIdentityProviderMapper;
9
45
  permissionMapper: IIdentityProviderMapper;
10
46
  roleMapper: IIdentityProviderMapper;
47
+ /**
48
+ * Reports the compensating actions `save()` takes when it loses a
49
+ * concurrent first login. They must not fail the login, so they are
50
+ * swallowed. Without a logger they are also invisible.
51
+ */
52
+ logger?: Logger;
11
53
  repository: IIdentityProviderAccountRepository;
12
54
  userRepository: IUserIdentityRepository;
13
55
  };
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/core/identity/provider/account/types.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AACvE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAClE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAG5D,OAAO,KAAK,EAAE,kCAAkC,EAAE,MAAM,sDAAsD,CAAC;AAE/G,YAAY,EAAE,kCAAkC,EAAE,MAAM,sDAAsD,CAAC;AAE/G,MAAM,MAAM,qCAAqC,GAAG;IAChD,eAAe,EAAE,uBAAuB,CAAC;IACzC,gBAAgB,EAAE,uBAAuB,CAAC;IAC1C,UAAU,EAAE,uBAAuB,CAAC;IAEpC,UAAU,EAAE,kCAAkC,CAAC;IAC/C,cAAc,EAAE,uBAAuB,CAAA;CAC1C,CAAC;AAEF,MAAM,WAAW,+BAA+B;IAC5C;;;;OAIG;IACH,IAAI,CAAC,QAAQ,EAAE,wBAAwB,GAAI,OAAO,CAAC,uBAAuB,CAAC,CAAC;IAE5E;;;;;;;;OAQG;IACH,IAAI,CAAC,QAAQ,EAAE,wBAAwB,EAAE,MAAM,EAAE,MAAM,GAAI,OAAO,CAAC,uBAAuB,CAAC,CAAC;CAC/F"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/core/identity/provider/account/types.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AACvE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAClE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAG5D,OAAO,KAAK,EAAE,kCAAkC,EAAE,MAAM,sDAAsD,CAAC;AAE/G,YAAY,EAAE,kCAAkC,EAAE,MAAM,sDAAsD,CAAC;AAE/G;;;;;;;;;;GAUG;AACH,MAAM,MAAM,2BAA2B,GAAG;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;OAKG;IACH,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACrC,CAAC;AAEF,MAAM,WAAW,iCAAiC;IAC9C;;OAEG;IACH,IAAI,CAAC,IAAI,EAAE,2BAA2B,GAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAE1D;;;OAGG;IACH,OAAO,CAAC,MAAM,EAAE,MAAM,GAAI,OAAO,CAAC,2BAA2B,GAAG,IAAI,CAAC,CAAC;CACzE;AAED,MAAM,MAAM,qCAAqC,GAAG;IAChD,eAAe,EAAE,uBAAuB,CAAC;IACzC,gBAAgB,EAAE,uBAAuB,CAAC;IAC1C,UAAU,EAAE,uBAAuB,CAAC;IACpC;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,UAAU,EAAE,kCAAkC,CAAC;IAC/C,cAAc,EAAE,uBAAuB,CAAA;CAC1C,CAAC;AAEF,MAAM,WAAW,+BAA+B;IAC5C;;;;OAIG;IACH,IAAI,CAAC,QAAQ,EAAE,wBAAwB,GAAI,OAAO,CAAC,uBAAuB,CAAC,CAAC;IAE5E;;;;;;;;OAQG;IACH,IAAI,CAAC,QAAQ,EAAE,wBAAwB,EAAE,MAAM,EAAE,MAAM,GAAI,OAAO,CAAC,uBAAuB,CAAC,CAAC;CAC/F"}
@@ -12,6 +12,6 @@ import { IdentityProviderGoogleAuthenticator } from "./presets/google.mjs";
12
12
  import { IdentityProviderInstagramAuthenticator } from "./presets/instagram.mjs";
13
13
  import { IdentityProviderPaypalAuthenticator } from "./presets/paypal.mjs";
14
14
  import "./presets/index.mjs";
15
- import { IDENTITY_PROVIDER_LDAP_COLLECTION_AUTHENTICATOR_TOKEN } from "./constants.mjs";
16
15
  import { createIdentityProviderOAuth2Authenticator } from "./factory.mjs";
16
+ import { IDENTITY_PROVIDER_LDAP_COLLECTION_AUTHENTICATOR_TOKEN } from "./constants.mjs";
17
17
  export { IDENTITY_PROVIDER_LDAP_COLLECTION_AUTHENTICATOR_TOKEN, IdentityProviderFacebookAuthenticator, IdentityProviderGithubAuthenticator, IdentityProviderGoogleAuthenticator, IdentityProviderInstagramAuthenticator, IdentityProviderLdapAuthenticator, IdentityProviderLdapCollectionAuthenticator, IdentityProviderOAuth2Authenticator, IdentityProviderOpenIDAuthenticator, IdentityProviderPaypalAuthenticator, createIdentityProviderOAuth2Authenticator };
@@ -1,5 +1,7 @@
1
1
  import type { OAuth2IdentityProvider, OpenIDIdentityProvider, User } from '@authup/core-kit';
2
2
  import type { Result } from '@authup/kit';
3
+ import type { JWTClaims } from '@authup/specs';
4
+ import type { Logger } from '@authup/server-kit';
3
5
  import type { AuthorizeParameters, TokenGrantResponse } from '@hapic/oauth2';
4
6
  import { OAuth2Client } from '@hapic/oauth2';
5
7
  import type { IIdentityProviderAccountManager } from '../../../account/index.ts';
@@ -10,11 +12,27 @@ export declare class IdentityProviderOAuth2Authenticator implements IOAuth2Authe
10
12
  protected options: IdentityProviderOAuth2AuthenticatorOptions;
11
13
  protected accountManager: IIdentityProviderAccountManager;
12
14
  protected provider: OAuth2IdentityProvider | OpenIDIdentityProvider;
15
+ protected logger?: Logger;
13
16
  constructor(ctx: IdentityProviderOAuth2AuthenticatorContext);
14
17
  buildRedirectURL(parameters?: Partial<AuthorizeParameters>): string;
15
18
  resolveIdentity(params: OAuth2AuthorizationCodeGrantPayload): Promise<IdentityProviderIdentity>;
16
19
  authenticate(params: OAuth2AuthorizationCodeGrantPayload): Promise<User>;
17
20
  safeAuthenticate(params: OAuth2AuthorizationCodeGrantPayload): Promise<Result<User>>;
21
+ /**
22
+ * The claims an external identity is described by, richest source last.
23
+ *
24
+ * The access token is opaque by contract (OIDC Core §2), so it is only
25
+ * the floor: authup's own carries `sub`, `kind` and `realm_name` and no
26
+ * username at all, which is why a federated user used to be provisioned
27
+ * under the remote subject UUID.
28
+ */
29
+ protected resolveClaims(input: TokenGrantResponse, payload: JWTClaims): Promise<JWTClaims>;
30
+ /**
31
+ * hapic passes no `signal` to `fetch`, so an endpoint that accepts the
32
+ * connection and never answers would hold the login for undici's 300s
33
+ * headers timeout. Enrichment must not outlast the request it enriches.
34
+ */
35
+ protected withTimeout<T>(promise: Promise<T>): Promise<T>;
18
36
  protected buildIdentityWithTokenGrantResponse(input: TokenGrantResponse): Promise<IdentityProviderIdentity>;
19
37
  }
20
38
  //# sourceMappingURL=module.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/identity/provider/authentication/protocols/oauth2/module.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAG7F,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,OAAO,KAAK,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAC7E,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,2BAA2B,CAAC;AACjF,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,KAAK,EACR,oBAAoB,EACpB,0CAA0C,EAC1C,0CAA0C,EAC1C,mCAAmC,EACtC,MAAM,YAAY,CAAC;AAEpB,qBAAa,mCAAoC,YAAW,oBAAoB,CAAC,IAAI,CAAC;IAClF,SAAS,CAAC,MAAM,EAAG,YAAY,CAAC;IAEhC,SAAS,CAAC,OAAO,EAAG,0CAA0C,CAAC;IAE/D,SAAS,CAAC,cAAc,EAAE,+BAA+B,CAAC;IAE1D,SAAS,CAAC,QAAQ,EAAG,sBAAsB,GAAG,sBAAsB,CAAC;gBAIzD,GAAG,EAAE,0CAA0C;IAoB3D,gBAAgB,CAAC,UAAU,GAAE,OAAO,CAAC,mBAAmB,CAAM,GAAI,MAAM;IAYlE,eAAe,CAAC,MAAM,EAAE,mCAAmC,GAAG,OAAO,CAAC,wBAAwB,CAAC;IAW/F,YAAY,CAAC,MAAM,EAAE,mCAAmC,GAAG,OAAO,CAAC,IAAI,CAAC;IAQxE,gBAAgB,CAAC,MAAM,EAAE,mCAAmC,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;cAiB1E,mCAAmC,CAAC,KAAK,EAAE,kBAAkB,GAAI,OAAO,CAAC,wBAAwB,CAAC;CAiBrH"}
1
+ {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/identity/provider/authentication/protocols/oauth2/module.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAG7F,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAEjD,OAAO,KAAK,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAC7E,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,2BAA2B,CAAC;AACjF,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,KAAK,EACR,oBAAoB,EACpB,0CAA0C,EAC1C,0CAA0C,EAC1C,mCAAmC,EACtC,MAAM,YAAY,CAAC;AAQpB,qBAAa,mCAAoC,YAAW,oBAAoB,CAAC,IAAI,CAAC;IAClF,SAAS,CAAC,MAAM,EAAG,YAAY,CAAC;IAEhC,SAAS,CAAC,OAAO,EAAG,0CAA0C,CAAC;IAE/D,SAAS,CAAC,cAAc,EAAE,+BAA+B,CAAC;IAE1D,SAAS,CAAC,QAAQ,EAAG,sBAAsB,GAAG,sBAAsB,CAAC;IAErE,SAAS,CAAC,MAAM,CAAC,EAAG,MAAM,CAAC;gBAIf,GAAG,EAAE,0CAA0C;IAqB3D,gBAAgB,CAAC,UAAU,GAAE,OAAO,CAAC,mBAAmB,CAAM,GAAI,MAAM;IAYlE,eAAe,CAAC,MAAM,EAAE,mCAAmC,GAAG,OAAO,CAAC,wBAAwB,CAAC;IAW/F,YAAY,CAAC,MAAM,EAAE,mCAAmC,GAAG,OAAO,CAAC,IAAI,CAAC;IAQxE,gBAAgB,CAAC,MAAM,EAAE,mCAAmC,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAiB1F;;;;;;;OAOG;cACa,aAAa,CAAC,KAAK,EAAE,kBAAkB,EAAE,OAAO,EAAE,SAAS,GAAI,OAAO,CAAC,SAAS,CAAC;IAyDjG;;;;OAIG;IACH,SAAS,CAAC,WAAW,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAI,OAAO,CAAC,CAAC,CAAC;cAY1C,mCAAmC,CAAC,KAAK,EAAE,kBAAkB,GAAI,OAAO,CAAC,wBAAwB,CAAC;CA2BrH"}