@authup/server-core 1.0.0-beta.61 → 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 (133) 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 +3 -5
  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 +54 -82
  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 +6 -4
  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/modules/config/read/env.mjs +1 -1
  47. package/dist/app/modules/database/repositories/event/repository.d.ts +2 -2
  48. package/dist/app/modules/database/repositories/event/repository.d.ts.map +1 -1
  49. package/dist/app/modules/database/repositories/event/repository.mjs +23 -6
  50. package/dist/app/modules/database/repositories/event/repository.mjs.map +1 -1
  51. package/dist/app/modules/database/repositories/identity-provider-account/repository.d.ts.map +1 -1
  52. package/dist/app/modules/database/repositories/identity-provider-account/repository.mjs +9 -1
  53. package/dist/app/modules/database/repositories/identity-provider-account/repository.mjs.map +1 -1
  54. package/dist/app/modules/http/modules/controller.d.ts.map +1 -1
  55. package/dist/app/modules/http/modules/controller.mjs +16 -5
  56. package/dist/app/modules/http/modules/controller.mjs.map +1 -1
  57. package/dist/app/modules/identity/module.d.ts.map +1 -1
  58. package/dist/app/modules/identity/module.mjs +6 -3
  59. package/dist/app/modules/identity/module.mjs.map +1 -1
  60. package/dist/app/modules/identity/repositories/user.d.ts +1 -0
  61. package/dist/app/modules/identity/repositories/user.d.ts.map +1 -1
  62. package/dist/app/modules/identity/repositories/user.mjs +3 -0
  63. package/dist/app/modules/identity/repositories/user.mjs.map +1 -1
  64. package/dist/app/modules/oauth2/repositories/authorize/state/repository.d.ts +1 -2
  65. package/dist/app/modules/oauth2/repositories/authorize/state/repository.d.ts.map +1 -1
  66. package/dist/app/modules/oauth2/repositories/authorize/state/repository.mjs +7 -16
  67. package/dist/app/modules/oauth2/repositories/authorize/state/repository.mjs.map +1 -1
  68. package/dist/app/modules/runtime/module.mjs +2 -2
  69. package/dist/cli/commands/migration.mjs +1 -1
  70. package/dist/core/entities/client-scope/schema.d.ts.map +1 -1
  71. package/dist/core/entities/client-scope/schema.mjs +16 -1
  72. package/dist/core/entities/client-scope/schema.mjs.map +1 -1
  73. package/dist/core/entities/event/constants.d.ts +9 -0
  74. package/dist/core/entities/event/constants.d.ts.map +1 -1
  75. package/dist/core/entities/event/constants.mjs +9 -1
  76. package/dist/core/entities/event/constants.mjs.map +1 -1
  77. package/dist/core/entities/event/index.mjs +2 -2
  78. package/dist/core/entities/event/types.d.ts +11 -2
  79. package/dist/core/entities/event/types.d.ts.map +1 -1
  80. package/dist/core/entities/identity-provider-account/types.d.ts +6 -0
  81. package/dist/core/entities/identity-provider-account/types.d.ts.map +1 -1
  82. package/dist/core/entities/identity-provider-account/types.mjs.map +1 -1
  83. package/dist/core/entities/index.mjs +2 -2
  84. package/dist/core/identity/entities/user/types.d.ts +1 -0
  85. package/dist/core/identity/entities/user/types.d.ts.map +1 -1
  86. package/dist/core/identity/index.mjs +10 -10
  87. package/dist/core/identity/provider/account/module.d.ts +3 -0
  88. package/dist/core/identity/provider/account/module.d.ts.map +1 -1
  89. package/dist/core/identity/provider/account/module.mjs +54 -27
  90. package/dist/core/identity/provider/account/module.mjs.map +1 -1
  91. package/dist/core/identity/provider/account/types.d.ts +7 -0
  92. package/dist/core/identity/provider/account/types.d.ts.map +1 -1
  93. package/dist/core/identity/provider/authentication/index.mjs +1 -1
  94. package/dist/core/identity/provider/index.mjs +10 -10
  95. package/dist/core/index.mjs +14 -12
  96. package/dist/core/oauth2/authorization/code-request/verifier/module.d.ts.map +1 -1
  97. package/dist/core/oauth2/authorization/code-request/verifier/module.mjs +7 -1
  98. package/dist/core/oauth2/authorization/code-request/verifier/module.mjs.map +1 -1
  99. package/dist/core/oauth2/authorization/state/module.mjs +1 -2
  100. package/dist/core/oauth2/authorization/state/module.mjs.map +1 -1
  101. package/dist/core/oauth2/authorization/state/types.d.ts +5 -2
  102. package/dist/core/oauth2/authorization/state/types.d.ts.map +1 -1
  103. package/dist/core/oauth2/end-session/service.d.ts.map +1 -1
  104. package/dist/core/oauth2/end-session/service.mjs +1 -0
  105. package/dist/core/oauth2/end-session/service.mjs.map +1 -1
  106. package/dist/core/oauth2/federated-login/index.d.ts +3 -0
  107. package/dist/core/oauth2/federated-login/index.d.ts.map +1 -0
  108. package/dist/core/oauth2/federated-login/index.mjs +6 -0
  109. package/dist/core/oauth2/federated-login/module.d.ts +20 -0
  110. package/dist/core/oauth2/federated-login/module.d.ts.map +1 -0
  111. package/dist/core/oauth2/federated-login/module.mjs +137 -0
  112. package/dist/core/oauth2/federated-login/module.mjs.map +1 -0
  113. package/dist/core/oauth2/federated-login/types.d.ts +138 -0
  114. package/dist/core/oauth2/federated-login/types.d.ts.map +1 -0
  115. package/dist/core/oauth2/federated-login/types.mjs +31 -0
  116. package/dist/core/oauth2/federated-login/types.mjs.map +1 -0
  117. package/dist/core/oauth2/index.d.ts +1 -0
  118. package/dist/core/oauth2/index.d.ts.map +1 -1
  119. package/dist/core/oauth2/index.mjs +4 -1
  120. package/dist/core/provisioning/entities/policy/validator.d.ts.map +1 -1
  121. package/dist/core/provisioning/entities/policy/validator.mjs +2 -1
  122. package/dist/core/provisioning/entities/policy/validator.mjs.map +1 -1
  123. package/dist/core/provisioning/entities/realm/wildcard-validator.d.ts.map +1 -1
  124. package/dist/core/provisioning/entities/realm/wildcard-validator.mjs +2 -1
  125. package/dist/core/provisioning/entities/realm/wildcard-validator.mjs.map +1 -1
  126. package/dist/core/security/policy/evaluator.mjs.map +1 -1
  127. package/dist/node_modules/@ebec/core/dist/index.mjs +130 -0
  128. package/dist/node_modules/@ebec/core/dist/index.mjs.map +1 -0
  129. package/dist/swagger.json +30 -2
  130. package/dist/utils/error.d.ts.map +1 -1
  131. package/dist/utils/error.mjs +5 -6
  132. package/dist/utils/error.mjs.map +1 -1
  133. package/package.json +28 -28
@@ -1 +1 @@
1
- {"version":3,"file":"repository.mjs","names":["createNanoID","buildCacheKey","CacheOAuth2Prefix","OAuth2AuthorizationStateRepository","cache","findOneById","key","id","prefix","AUTHORIZATION_CODE","payload","get","remove","drop","insert","data","state","set","ttl"],"sources":["../../../../../../../src/app/modules/oauth2/repositories/authorize/state/repository.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 { createNanoID } from '@authup/kit';\nimport type { ICache } from '@authup/server-kit';\nimport { buildCacheKey } from '@authup/server-kit';\nimport type { IOAuth2AuthorizeStateRepository, OAuth2AuthorizationState } from '../../../../../../core/index.ts';\nimport { CacheOAuth2Prefix } from '../../constants.ts';\n\nexport class OAuth2AuthorizationStateRepository implements IOAuth2AuthorizeStateRepository {\n protected cache : ICache;\n\n constructor(cache: ICache) {\n this.cache = cache;\n }\n\n async findOneById(key: string): Promise<OAuth2AuthorizationState | null> {\n const id = buildCacheKey({\n prefix: CacheOAuth2Prefix.AUTHORIZATION_CODE,\n key, \n });\n const payload = await this.cache.get<OAuth2AuthorizationState>(id);\n if (payload) {\n return payload;\n }\n\n return null;\n }\n\n async remove(key: string): Promise<void> {\n await this.cache.drop(\n buildCacheKey({\n prefix: CacheOAuth2Prefix.AUTHORIZATION_CODE,\n key, \n }),\n );\n }\n\n async insert(data: OAuth2AuthorizationState): Promise<string> {\n const state = createNanoID();\n\n await this.cache.set(\n buildCacheKey({\n prefix: CacheOAuth2Prefix.AUTHORIZATION_CODE,\n key: state, \n }),\n data,\n { ttl: 1000 * 60 * 30 }, // 30 min\n );\n\n return state;\n }\n}\n"],"mappings":";;;;;;;AAaA,IAAaG,qCAAb,MAAaA;CACCC;CAEV,YAAYA,OAAe;EACvB,KAAKA,QAAQA;CACjB;CAEA,MAAMC,YAAYC,KAAuD;EACrE,MAAMC,KAAKN,cAAc;GACrBO,QAAQN,kBAAkBO;GAC1BH;EACJ,CAAA;EACA,MAAMI,UAAU,MAAM,KAAKN,MAAMO,IAA8BJ,EAAAA;EAC/D,IAAIG,SACA,OAAOA;EAGX,OAAO;CACX;CAEA,MAAME,OAAON,KAA4B;EACrC,MAAM,KAAKF,MAAMS,KACbZ,cAAc;GACVO,QAAQN,kBAAkBO;GAC1BH;EACJ,CAAA,CAAA;CAER;CAEA,MAAMQ,OAAOC,MAAiD;EAC1D,MAAMC,QAAQhB,aAAAA;EAEd,MAAM,KAAKI,MAAMa,IACbhB,cAAc;GACVO,QAAQN,kBAAkBO;GAC1BH,KAAKU;EACT,CAAA,GACAD,MACA,EAAEG,KAAK,KAAe,CAAA;EAG1B,OAAOF;CACX;AACJ"}
1
+ {"version":3,"file":"repository.mjs","names":["createNanoID","buildCacheKey","CacheOAuth2Prefix","OAuth2AuthorizationStateRepository","cache","insert","data","state","set","prefix","AUTHORIZATION_STATE","key","ttl","popOneById","pop"],"sources":["../../../../../../../src/app/modules/oauth2/repositories/authorize/state/repository.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 { createNanoID } from '@authup/kit';\nimport type { ICache } from '@authup/server-kit';\nimport { buildCacheKey } from '@authup/server-kit';\nimport type { IOAuth2AuthorizeStateRepository, OAuth2AuthorizationState } from '../../../../../../core/index.ts';\nimport { CacheOAuth2Prefix } from '../../constants.ts';\n\nexport class OAuth2AuthorizationStateRepository implements IOAuth2AuthorizeStateRepository {\n protected cache : ICache;\n\n constructor(cache: ICache) {\n this.cache = cache;\n }\n\n async insert(data: OAuth2AuthorizationState): Promise<string> {\n const state = createNanoID();\n\n await this.cache.set(\n buildCacheKey({\n prefix: CacheOAuth2Prefix.AUTHORIZATION_STATE,\n key: state,\n }),\n data,\n { ttl: 1000 * 60 * 30 }, // 30 min\n );\n\n return state;\n }\n\n async popOneById(key: string): Promise<OAuth2AuthorizationState | null> {\n return this.cache.pop<OAuth2AuthorizationState>(buildCacheKey({\n prefix: CacheOAuth2Prefix.AUTHORIZATION_STATE,\n key,\n }));\n }\n}\n"],"mappings":";;;;;;;AAaA,IAAaG,qCAAb,MAAaA;CACCC;CAEV,YAAYA,OAAe;EACvB,KAAKA,QAAQA;CACjB;CAEA,MAAMC,OAAOC,MAAiD;EAC1D,MAAMC,QAAQP,aAAAA;EAEd,MAAM,KAAKI,MAAMI,IACbP,cAAc;GACVQ,QAAQP,kBAAkBQ;GAC1BC,KAAKJ;EACT,CAAA,GACAD,MACA,EAAEM,KAAK,KAAe,CAAA;EAG1B,OAAOL;CACX;CAEA,MAAMM,WAAWF,KAAuD;EACpE,OAAO,KAAKP,MAAMU,IAA8Bb,cAAc;GAC1DQ,QAAQP,kBAAkBQ;GAC1BC;EACJ,CAAA,CAAA;CACJ;AACJ"}
@@ -1,13 +1,13 @@
1
1
  import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
+ import { resolveURL } from "../../../utils/url.mjs";
5
+ import "../../../utils/index.mjs";
4
6
  import { ModuleName } from "../constants.mjs";
5
7
  import { ConfigInjectionKey } from "../config/constants.mjs";
6
8
  import "../config/index.mjs";
7
9
  import { LoggerInjectionKey } from "../logger/constants.mjs";
8
10
  import "../logger/index.mjs";
9
- import { resolveURL } from "../../../utils/url.mjs";
10
- import "../../../utils/index.mjs";
11
11
  //#region src/app/modules/runtime/module.ts
12
12
  var RuntimeModule = class {
13
13
  name;
@@ -11,9 +11,9 @@ import { ApplicationBuilder } from "../../app/builder.mjs";
11
11
  import "../../app/index.mjs";
12
12
  import { createCLIConfigModule } from "../config.mjs";
13
13
  import { DataSource } from "typeorm";
14
+ import { checkDatabase, createDatabase, dropDatabase, generateMigration, transformFilePath } from "typeorm-extension";
14
15
  import path from "node:path";
15
16
  import process from "node:process";
16
- import { checkDatabase, createDatabase, dropDatabase, generateMigration, transformFilePath } from "typeorm-extension";
17
17
  import { defineCommand } from "citty";
18
18
  //#region src/cli/commands/migration.ts
19
19
  var MigrationOperation = /*#__PURE__*/ function(MigrationOperation) {
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../../src/core/entities/client-scope/schema.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAMpD,eAAO,MAAM,iBAAiB,gDA2B5B,CAAC"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../../src/core/entities/client-scope/schema.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAMpD,eAAO,MAAM,iBAAiB,gDA8C5B,CAAC"}
@@ -17,7 +17,9 @@ const clientScopeSchema = defineSchema({
17
17
  ["scopeId"],
18
18
  ["default"],
19
19
  ["clientRealmId"],
20
- ["scopeRealmId"]
20
+ ["scopeRealmId"],
21
+ ["createdAt"],
22
+ ["updatedAt"]
21
23
  ],
22
24
  fields: { default: [
23
25
  "id",
@@ -44,6 +46,19 @@ const clientScopeSchema = defineSchema({
44
46
  allowed: ["client", "scope"],
45
47
  validate: createRelationsReadGate(schemaMapping)
46
48
  },
49
+ sorts: {
50
+ allowed: [
51
+ "id",
52
+ "default",
53
+ "clientId",
54
+ "clientRealmId",
55
+ "scopeId",
56
+ "scopeRealmId",
57
+ "createdAt",
58
+ "updatedAt"
59
+ ],
60
+ indexed: true
61
+ },
47
62
  pagination: { maxLimit: 50 },
48
63
  schemaMapping
49
64
  });
@@ -1 +1 @@
1
- {"version":3,"file":"schema.mjs","names":["defineSchema","EntityType","createRelationsReadGate","schemaMapping","client","CLIENT","scope","SCOPE","clientScopeSchema","name","CLIENT_SCOPE","indexes","fields","default","filters","allowed","indexed","relations","validate","pagination","maxLimit"],"sources":["../../../../src/core/entities/client-scope/schema.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 { defineSchema } from '@rapiq/core';\nimport type { ClientScope } from '@authup/core-kit';\nimport { EntityType } from '@authup/core-kit';\nimport { createRelationsReadGate } from '../../query/relations.ts';\n\nconst schemaMapping = { client: EntityType.CLIENT, scope: EntityType.SCOPE };\n\nexport const clientScopeSchema = defineSchema<ClientScope>({\n name: EntityType.CLIENT_SCOPE,\n indexes: [\n ['id'],\n ['clientId', 'scopeId'],\n ['scopeId'],\n ['default'],\n ['clientRealmId'],\n ['scopeRealmId'],\n ],\n // see user-role schema — explicit root projection for include= decodes\n fields: {\n default: [\n 'id',\n 'default',\n 'clientId',\n 'clientRealmId',\n 'scopeId',\n 'scopeRealmId',\n 'createdAt',\n 'updatedAt',\n ],\n },\n filters: { allowed: ['clientId', 'scopeId', 'default', 'id', 'clientRealmId', 'scopeRealmId'], indexed: true },\n relations: { allowed: ['client', 'scope'], validate: createRelationsReadGate(schemaMapping) },\n pagination: { maxLimit: 50 },\n schemaMapping,\n});\n"],"mappings":";;;;;;;AAYA,MAAMG,gBAAgB;CAAEC,QAAQH,WAAWI;CAAQC,OAAOL,WAAWM;AAAM;AAE3E,MAAaC,oBAAoBR,aAA0B;CACvDS,MAAMR,WAAWS;CACjBC,SAAS;EACL,CAAC,IAAK;EACN,CAAC,YAAY,SAAU;EACvB,CAAC,SAAU;EACX,CAAC,SAAU;EACX,CAAC,eAAgB;EACjB,CAAC,cAAe;CACnB;CAEDC,QAAQ,EACJC,SAAS;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACH,EACL;CACAC,SAAS;EAAEC,SAAS;GAAC;GAAY;GAAW;GAAW;GAAM;GAAiB;EAAe;EAAEC,SAAS;CAAK;CAC7GC,WAAW;EAAEF,SAAS,CAAC,UAAU,OAAQ;EAAEG,UAAUhB,wBAAwBC,aAAAA;CAAe;CAC5FgB,YAAY,EAAEC,UAAU,GAAG;CAC3BjB;AACJ,CAAA"}
1
+ {"version":3,"file":"schema.mjs","names":["defineSchema","EntityType","createRelationsReadGate","schemaMapping","client","CLIENT","scope","SCOPE","clientScopeSchema","name","CLIENT_SCOPE","indexes","fields","default","filters","allowed","indexed","relations","validate","sorts","pagination","maxLimit"],"sources":["../../../../src/core/entities/client-scope/schema.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 { defineSchema } from '@rapiq/core';\nimport type { ClientScope } from '@authup/core-kit';\nimport { EntityType } from '@authup/core-kit';\nimport { createRelationsReadGate } from '../../query/relations.ts';\n\nconst schemaMapping = { client: EntityType.CLIENT, scope: EntityType.SCOPE };\n\nexport const clientScopeSchema = defineSchema<ClientScope>({\n name: EntityType.CLIENT_SCOPE,\n indexes: [\n ['id'],\n ['clientId', 'scopeId'],\n ['scopeId'],\n ['default'],\n ['clientRealmId'],\n ['scopeRealmId'],\n ['createdAt'],\n ['updatedAt'],\n ],\n // see user-role schema — explicit root projection for include= decodes\n fields: {\n default: [\n 'id',\n 'default',\n 'clientId',\n 'clientRealmId',\n 'scopeId',\n 'scopeRealmId',\n 'createdAt',\n 'updatedAt',\n ],\n },\n filters: { allowed: ['clientId', 'scopeId', 'default', 'id', 'clientRealmId', 'scopeRealmId'], indexed: true },\n relations: { allowed: ['client', 'scope'], validate: createRelationsReadGate(schemaMapping) },\n // wider than the junction siblings' id/createdAt/updatedAt on purpose:\n // every column here is already indexed and already sorts today through\n // the missing-allow-list fallback, so a narrow list would demote the\n // rest from working-and-sorted to silently unsorted (#3441)\n sorts: {\n allowed: [\n 'id',\n 'default',\n 'clientId',\n 'clientRealmId',\n 'scopeId',\n 'scopeRealmId',\n 'createdAt',\n 'updatedAt',\n ],\n indexed: true,\n },\n pagination: { maxLimit: 50 },\n schemaMapping,\n});\n"],"mappings":";;;;;;;AAYA,MAAMG,gBAAgB;CAAEC,QAAQH,WAAWI;CAAQC,OAAOL,WAAWM;AAAM;AAE3E,MAAaC,oBAAoBR,aAA0B;CACvDS,MAAMR,WAAWS;CACjBC,SAAS;EACL,CAAC,IAAK;EACN,CAAC,YAAY,SAAU;EACvB,CAAC,SAAU;EACX,CAAC,SAAU;EACX,CAAC,eAAgB;EACjB,CAAC,cAAe;EAChB,CAAC,WAAY;EACb,CAAC,WAAY;CAChB;CAEDC,QAAQ,EACJC,SAAS;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACH,EACL;CACAC,SAAS;EAAEC,SAAS;GAAC;GAAY;GAAW;GAAW;GAAM;GAAiB;EAAe;EAAEC,SAAS;CAAK;CAC7GC,WAAW;EAAEF,SAAS,CAAC,UAAU,OAAQ;EAAEG,UAAUhB,wBAAwBC,aAAAA;CAAe;CAK5FgB,OAAO;EACHJ,SAAS;GACL;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACH;EACDC,SAAS;CACb;CACAI,YAAY,EAAEC,UAAU,GAAG;CAC3BlB;AACJ,CAAA"}
@@ -4,6 +4,15 @@
4
4
  * EVENT_LOG_RETENTION_DAYS, 0 = keep forever).
5
5
  */
6
6
  export declare const EVENT_LOG_RETENTION_DAYS_DEFAULT = 90;
7
+ /**
8
+ * Rows removed per statement by the retention sweep. The sweep runs every
9
+ * minute on every replica, and the first one after a retention change (or the
10
+ * day a full retention window first matures) can match millions of rows. A
11
+ * single unbounded DELETE would then be one long transaction, issued
12
+ * concurrently by every replica. Batching keeps each statement bounded; the
13
+ * sweep still drains by looping.
14
+ */
15
+ export declare const EVENT_RETENTION_SWEEP_BATCH_SIZE = 1000;
7
16
  /**
8
17
  * Column bound for the denormalized actor name. Shared by the write boundary
9
18
  * (EventService.record truncates to it) and every reader that matches stored
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../src/core/entities/event/constants.ts"],"names":[],"mappings":"AAOA;;;;GAIG;AACH,eAAO,MAAM,gCAAgC,KAAK,CAAC;AAEnD;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B,MAAM,CAAC"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../src/core/entities/event/constants.ts"],"names":[],"mappings":"AAOA;;;;GAIG;AACH,eAAO,MAAM,gCAAgC,KAAK,CAAC;AAEnD;;;;;;;GAOG;AACH,eAAO,MAAM,gCAAgC,OAAO,CAAC;AAErD;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B,MAAM,CAAC"}
@@ -8,12 +8,20 @@ import.meta.url;
8
8
  * EVENT_LOG_RETENTION_DAYS, 0 = keep forever).
9
9
  */ const EVENT_LOG_RETENTION_DAYS_DEFAULT = 90;
10
10
  /**
11
+ * Rows removed per statement by the retention sweep. The sweep runs every
12
+ * minute on every replica, and the first one after a retention change (or the
13
+ * day a full retention window first matures) can match millions of rows. A
14
+ * single unbounded DELETE would then be one long transaction, issued
15
+ * concurrently by every replica. Batching keeps each statement bounded; the
16
+ * sweep still drains by looping.
17
+ */ const EVENT_RETENTION_SWEEP_BATCH_SIZE = 1e3;
18
+ /**
11
19
  * Column bound for the denormalized actor name. Shared by the write boundary
12
20
  * (EventService.record truncates to it) and every reader that matches stored
13
21
  * rows by actor name (the login throttle) so the two cannot drift — an
14
22
  * untruncated lookup key never matches its own truncated rows.
15
23
  */ const EVENT_ACTOR_NAME_MAX_LENGTH = 128;
16
24
  //#endregion
17
- export { EVENT_ACTOR_NAME_MAX_LENGTH, EVENT_LOG_RETENTION_DAYS_DEFAULT };
25
+ export { EVENT_ACTOR_NAME_MAX_LENGTH, EVENT_LOG_RETENTION_DAYS_DEFAULT, EVENT_RETENTION_SWEEP_BATCH_SIZE };
18
26
 
19
27
  //# sourceMappingURL=constants.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.mjs","names":["EVENT_LOG_RETENTION_DAYS_DEFAULT","EVENT_ACTOR_NAME_MAX_LENGTH"],"sources":["../../../../src/core/entities/event/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 * Default retention for persisted security events in days (hosted-provider\n * posture, Okta-parity; raise via eventLogRetentionDays /\n * EVENT_LOG_RETENTION_DAYS, 0 = keep forever).\n */\nexport const EVENT_LOG_RETENTION_DAYS_DEFAULT = 90;\n\n/**\n * Column bound for the denormalized actor name. Shared by the write boundary\n * (EventService.record truncates to it) and every reader that matches stored\n * rows by actor name (the login throttle) so the two cannot drift — an\n * untruncated lookup key never matches its own truncated rows.\n */\nexport const EVENT_ACTOR_NAME_MAX_LENGTH = 128;\n"],"mappings":";;;;;;;;GAYA,MAAaA,mCAAmC;;;;;;GAQhD,MAAaC,8BAA8B"}
1
+ {"version":3,"file":"constants.mjs","names":["EVENT_LOG_RETENTION_DAYS_DEFAULT","EVENT_RETENTION_SWEEP_BATCH_SIZE","EVENT_ACTOR_NAME_MAX_LENGTH"],"sources":["../../../../src/core/entities/event/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 * Default retention for persisted security events in days (hosted-provider\n * posture, Okta-parity; raise via eventLogRetentionDays /\n * EVENT_LOG_RETENTION_DAYS, 0 = keep forever).\n */\nexport const EVENT_LOG_RETENTION_DAYS_DEFAULT = 90;\n\n/**\n * Rows removed per statement by the retention sweep. The sweep runs every\n * minute on every replica, and the first one after a retention change (or the\n * day a full retention window first matures) can match millions of rows. A\n * single unbounded DELETE would then be one long transaction, issued\n * concurrently by every replica. Batching keeps each statement bounded; the\n * sweep still drains by looping.\n */\nexport const EVENT_RETENTION_SWEEP_BATCH_SIZE = 1000;\n\n/**\n * Column bound for the denormalized actor name. Shared by the write boundary\n * (EventService.record truncates to it) and every reader that matches stored\n * rows by actor name (the login throttle) so the two cannot drift — an\n * untruncated lookup key never matches its own truncated rows.\n */\nexport const EVENT_ACTOR_NAME_MAX_LENGTH = 128;\n"],"mappings":";;;;;;;;GAYA,MAAaA,mCAAmC;;;;;;;;GAUhD,MAAaC,mCAAmC;;;;;;GAQhD,MAAaC,8BAA8B"}
@@ -1,10 +1,10 @@
1
1
  import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
- import { EVENT_ACTOR_NAME_MAX_LENGTH, EVENT_LOG_RETENTION_DAYS_DEFAULT } from "./constants.mjs";
4
+ import { EVENT_ACTOR_NAME_MAX_LENGTH, EVENT_LOG_RETENTION_DAYS_DEFAULT, EVENT_RETENTION_SWEEP_BATCH_SIZE } from "./constants.mjs";
5
5
  import { eventSchema } from "./schema.mjs";
6
6
  import { EVENT_DIFF_SECRET_KEY_REGEX, EVENT_DIFF_VALUE_MAX_LENGTH, buildEntityDiff } from "./diff.mjs";
7
7
  import { EntityEventHandler } from "./entity-event-handler.mjs";
8
8
  import { sanitizeEventData } from "./sanitize.mjs";
9
9
  import { EventService } from "./service.mjs";
10
- export { EVENT_ACTOR_NAME_MAX_LENGTH, EVENT_DIFF_SECRET_KEY_REGEX, EVENT_DIFF_VALUE_MAX_LENGTH, EVENT_LOG_RETENTION_DAYS_DEFAULT, EntityEventHandler, EventService, buildEntityDiff, eventSchema, sanitizeEventData };
10
+ export { 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, buildEntityDiff, eventSchema, sanitizeEventData };
@@ -42,10 +42,19 @@ export interface IEventRepository {
42
42
  /**
43
43
  * Retention sweep: drop every expiring row whose expiresAt lies before
44
44
  * the given instant (non-expiring rows are kept forever). Returns the
45
- * number of removed rows.
45
+ * number of removed rows. Removal is batched (see
46
+ * EVENT_RETENTION_SWEEP_BATCH_SIZE).
46
47
  */
47
- deleteExpired(now: string): Promise<number>;
48
+ deleteExpired(now: string, options?: EventDeleteExpiredOptions): Promise<number>;
48
49
  }
50
+ export type EventDeleteExpiredOptions = {
51
+ /**
52
+ * Rows removed per statement. Defaults to
53
+ * EVENT_RETENTION_SWEEP_BATCH_SIZE, which anything that is not a positive
54
+ * safe integer also falls back to.
55
+ */
56
+ batchSize?: number;
57
+ };
49
58
  export type EventRecordInput = {
50
59
  scope: `${EventScope}`;
51
60
  name: `${EventName}`;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/core/entities/event/types.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACR,KAAK,EACL,SAAS,EACT,UAAU,EACV,YAAY,EACf,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,YAAY,EAAE,8BAA8B,EAAE,MAAM,oBAAoB,CAAC;AACvF,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,MAAM,UAAU,GAAG;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAC9B,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,QAAQ,EAAE,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IAC/B;;;OAGG;IACH,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,UAAU,CAAC,EAAE,mBAAmB,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACjC,IAAI,EAAE,GAAG,SAAS,EAAE,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,WAAW,gBAAgB;IAC7B,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC;IAEpC,IAAI,CAAC,MAAM,EAAE,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IAEpC,QAAQ,CACJ,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,oBAAoB,GAC/B,OAAO,CAAC,8BAA8B,CAAC,KAAK,CAAC,CAAC,CAAC;IAElD,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;IAE/C,WAAW,CAAC,MAAM,EAAE,sBAAsB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAE7D;;;;OAIG;IACH,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC/C;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC3B,KAAK,EAAE,GAAG,UAAU,EAAE,CAAC;IACvB,IAAI,EAAE,GAAG,SAAS,EAAE,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,CAAC,EAAE,GAAG,YAAY,EAAE,GAAG,IAAI,CAAC;IACrC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC;IAClC;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAC9B;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAC9B,SAAS,EAAE,GAAG,YAAY,EAAE,GAAG,IAAI,CAAC;IACpC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;CACnC,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACpC;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,MAAM,WAAW,aAAa;IAC1B;;;OAGG;IACH,MAAM,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE/C;;;;OAIG;IACH,OAAO,CACH,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC1B,KAAK,EAAE,YAAY,EACnB,OAAO,CAAC,EAAE,uBAAuB,GAClC,OAAO,CAAC,8BAA8B,CAAC,KAAK,CAAC,CAAC,CAAC;IAElD;;OAEG;IACH,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,uBAAuB,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;CAC9F"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/core/entities/event/types.ts"],"names":[],"mappings":"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"}
@@ -44,6 +44,12 @@ export interface IIdentityProviderAccountRepository {
44
44
  * Find identity account with user relation (federated login + link flow).
45
45
  */
46
46
  findOneByProviderIdentity(identity: IdentityProviderIdentity): Promise<IdentityProviderAccount | null>;
47
+ /**
48
+ * Throws `EntityConflictError` (`@authup/errors`) when the row violates
49
+ * EITHER unique index, (providerUserId, providerId) or (providerId,
50
+ * userId): the driver does not tell them apart, so the caller
51
+ * classifies by re-reading the identity.
52
+ */
47
53
  save(entity: DeepPartial<IdentityProviderAccount>): Promise<IdentityProviderAccount>;
48
54
  }
49
55
  export type IdentityProviderAccountServiceReadOptions = {
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/core/entities/identity-provider-account/types.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,YAAY,EAAE,8BAA8B,EAAE,MAAM,oBAAoB,CAAC;AACvF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AAEjF;;GAEG;AACH,eAAO,MAAM,qCAAqC,0EAMxC,CAAC;AAEX,MAAM,MAAM,sCAAsC,GAAG;IACjD;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;;GAIG;AACH,MAAM,WAAW,kCAAkC;IAC/C,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,sCAAsC,GAAG,OAAO,CAAC,8BAA8B,CAAC,uBAAuB,CAAC,CAAC,CAAC;IAE5I,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,uBAAuB,GAAG,IAAI,CAAC,CAAC;IAEjE,MAAM,CAAC,MAAM,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvD;;;;;;;;;OASG;IACH,aAAa,CAAC,MAAM,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAE7F;;OAEG;IACH,yBAAyB,CAAC,QAAQ,EAAE,wBAAwB,GAAG,OAAO,CAAC,uBAAuB,GAAG,IAAI,CAAC,CAAC;IAEvG,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,uBAAuB,CAAC,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAAC;CACxF;AAED,MAAM,MAAM,yCAAyC,GAAG;IACpD,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,WAAW,+BAA+B;IAC5C;;;OAGG;IACH,OAAO,CACH,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC1B,KAAK,EAAE,YAAY,EACnB,OAAO,CAAC,EAAE,yCAAyC,GACpD,OAAO,CAAC,8BAA8B,CAAC,uBAAuB,CAAC,CAAC,CAAC;IAEpE;;OAEG;IACH,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,yCAAyC,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAAC;IAE/H;;;;OAIG;IACH,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,yCAAyC,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAAC;CAClI"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/core/entities/identity-provider-account/types.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,YAAY,EAAE,8BAA8B,EAAE,MAAM,oBAAoB,CAAC;AACvF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AAEjF;;GAEG;AACH,eAAO,MAAM,qCAAqC,0EAMxC,CAAC;AAEX,MAAM,MAAM,sCAAsC,GAAG;IACjD;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;;GAIG;AACH,MAAM,WAAW,kCAAkC;IAC/C,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,sCAAsC,GAAG,OAAO,CAAC,8BAA8B,CAAC,uBAAuB,CAAC,CAAC,CAAC;IAE5I,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,uBAAuB,GAAG,IAAI,CAAC,CAAC;IAEjE,MAAM,CAAC,MAAM,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvD;;;;;;;;;OASG;IACH,aAAa,CAAC,MAAM,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAE7F;;OAEG;IACH,yBAAyB,CAAC,QAAQ,EAAE,wBAAwB,GAAG,OAAO,CAAC,uBAAuB,GAAG,IAAI,CAAC,CAAC;IAEvG;;;;;OAKG;IACH,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,uBAAuB,CAAC,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAAC;CACxF;AAED,MAAM,MAAM,yCAAyC,GAAG;IACpD,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,WAAW,+BAA+B;IAC5C;;;OAGG;IACH,OAAO,CACH,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC1B,KAAK,EAAE,YAAY,EACnB,OAAO,CAAC,EAAE,yCAAyC,GACpD,OAAO,CAAC,8BAA8B,CAAC,uBAAuB,CAAC,CAAC,CAAC;IAEpE;;OAEG;IACH,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,yCAAyC,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAAC;IAE/H;;;;OAIG;IACH,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,yCAAyC,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAAC;CAClI"}
@@ -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";
@@ -19,17 +29,7 @@ import { IdentityProviderAttributeMapper } from "./provider/mapper/attributes/mo
19
29
  import { IdentityProviderRoleMapper } from "./provider/mapper/role/module.mjs";
20
30
  import { IdentityProviderPermissionMapper } from "./provider/mapper/permission/module.mjs";
21
31
  import { IdentityProviderAccountManager } from "./provider/account/module.mjs";
22
- import { IdentityProviderLdapAuthenticator } from "./provider/authentication/protocols/ldap/module.mjs";
23
- import { IdentityProviderLdapCollectionAuthenticator } from "./provider/authentication/protocols/ldap/collection.mjs";
24
- import { IdentityProviderOAuth2Authenticator } from "./provider/authentication/protocols/oauth2/module.mjs";
25
- import { IdentityProviderOpenIDAuthenticator } from "./provider/authentication/protocols/open-id/module.mjs";
26
- import { IdentityProviderFacebookAuthenticator } from "./provider/authentication/presets/facebook.mjs";
27
- import { IdentityProviderGithubAuthenticator } from "./provider/authentication/presets/github.mjs";
28
- import { IdentityProviderGoogleAuthenticator } from "./provider/authentication/presets/google.mjs";
29
- import { IdentityProviderInstagramAuthenticator } from "./provider/authentication/presets/instagram.mjs";
30
- import { IdentityProviderPaypalAuthenticator } from "./provider/authentication/presets/paypal.mjs";
31
32
  import { IDENTITY_PROVIDER_LDAP_COLLECTION_AUTHENTICATOR_TOKEN } from "./provider/authentication/constants.mjs";
32
- import { createIdentityProviderOAuth2Authenticator } from "./provider/authentication/factory.mjs";
33
33
  import "./provider/index.mjs";
34
34
  import { RegistrationService } from "./registration/service.mjs";
35
35
  import "./registration/index.mjs";
@@ -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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"}