@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
@@ -6,15 +6,21 @@ import { extractTokenPayload } from "@authup/server-kit";
6
6
  import { ValidationError } from "@authup/errors";
7
7
  import { OAuth2Client } from "@hapic/oauth2";
8
8
  //#region src/core/identity/provider/authentication/protocols/oauth2/module.ts
9
+ /**
10
+ * Bound on the optional userinfo enrichment. Short by design: the caller is
11
+ * a browser sitting on a redirect, and the login proceeds without it.
12
+ */ const USERINFO_TIMEOUT = 5e3;
9
13
  var IdentityProviderOAuth2Authenticator = class {
10
14
  client;
11
15
  options;
12
16
  accountManager;
13
17
  provider;
18
+ logger;
14
19
  constructor(ctx) {
15
20
  this.options = ctx.options;
16
21
  this.accountManager = ctx.accountManager;
17
22
  this.provider = ctx.provider;
23
+ this.logger = ctx.logger;
18
24
  this.client = new OAuth2Client({ options: {
19
25
  clientId: ctx.provider.clientId,
20
26
  clientSecret: ctx.provider.clientSecret,
@@ -55,13 +61,60 @@ var IdentityProviderOAuth2Authenticator = class {
55
61
  };
56
62
  }
57
63
  }
64
+ /**
65
+ * The claims an external identity is described by, richest source last.
66
+ *
67
+ * The access token is opaque by contract (OIDC Core §2), so it is only
68
+ * the floor: authup's own carries `sub`, `kind` and `realm_name` and no
69
+ * username at all, which is why a federated user used to be provisioned
70
+ * under the remote subject UUID.
71
+ */ async resolveClaims(input, payload) {
72
+ let claims = payload;
73
+ if (typeof input.id_token === "string") try {
74
+ claims = {
75
+ ...claims,
76
+ ...extractTokenPayload(input.id_token)
77
+ };
78
+ } catch (e) {
79
+ this.logger?.warn(`The identity provider (${this.provider.id}) id_token could not be read: ${e.message}`);
80
+ }
81
+ if (this.provider.userInfoUrl) try {
82
+ const userInfo = await this.withTimeout(this.client.userInfo.get({
83
+ type: "Bearer",
84
+ token: input.access_token
85
+ }));
86
+ if (typeof userInfo.sub === "string" && typeof payload.sub === "string" && userInfo.sub !== payload.sub) this.logger?.warn(`The identity provider (${this.provider.id}) userinfo subject does not match the token subject.`);
87
+ else claims = {
88
+ ...claims,
89
+ ...userInfo
90
+ };
91
+ } catch (e) {
92
+ this.logger?.warn(`The identity provider (${this.provider.id}) userinfo request failed: ${e.message}`);
93
+ }
94
+ return claims;
95
+ }
96
+ /**
97
+ * hapic passes no `signal` to `fetch`, so an endpoint that accepts the
98
+ * connection and never answers would hold the login for undici's 300s
99
+ * headers timeout. Enrichment must not outlast the request it enriches.
100
+ */ withTimeout(promise) {
101
+ return Promise.race([promise, new Promise((_resolve, reject) => {
102
+ setTimeout(() => reject(/* @__PURE__ */ new Error(`the request exceeded ${USERINFO_TIMEOUT}ms`)), USERINFO_TIMEOUT).unref();
103
+ })]);
104
+ }
58
105
  async buildIdentityWithTokenGrantResponse(input) {
59
106
  const payload = extractTokenPayload(input.access_token);
107
+ const claims = await this.resolveClaims(input, payload);
60
108
  return {
61
109
  id: payload.sub,
62
110
  attributeCandidates: {
63
- name: [payload.sub],
64
- email: [payload.email]
111
+ name: [
112
+ claims.preferred_username,
113
+ claims.nickname,
114
+ claims.name,
115
+ claims.sub
116
+ ],
117
+ email: [claims.email]
65
118
  },
66
119
  data: payload,
67
120
  provider: this.provider
@@ -1 +1 @@
1
- {"version":3,"file":"module.mjs","names":["buildIdentityProviderAuthorizeCallbackPath","ValidationError","extractTokenPayload","OAuth2Client","IdentityProviderOAuth2Authenticator","client","options","accountManager","provider","ctx","clientId","clientSecret","redirectUri","baseURL","id","scope","undefined","authorizationEndpoint","authorizeUrl","tokenEndpoint","tokenUrl","userinfoEndpoint","userInfoUrl","buildRedirectURL","parameters","authorize","buildURL","resolveIdentity","params","token","createWithAuthorizationCode","identity","buildIdentityWithTokenGrantResponse","authenticate","account","save","user","safeAuthenticate","data","success","e","error","input","payload","access_token","sub","attributeCandidates","name","email"],"sources":["../../../../../../../src/core/identity/provider/authentication/protocols/oauth2/module.ts"],"sourcesContent":["/*\n * Copyright (c) 2023-2023.\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 { OAuth2IdentityProvider, OpenIDIdentityProvider, User } from '@authup/core-kit';\nimport { buildIdentityProviderAuthorizeCallbackPath } from '@authup/core-kit';\nimport { ValidationError } from '@authup/errors';\nimport type { Result } from '@authup/kit';\nimport { extractTokenPayload } from '@authup/server-kit';\nimport type { AuthorizeParameters, TokenGrantResponse } from '@hapic/oauth2';\nimport { OAuth2Client } from '@hapic/oauth2';\nimport type { IIdentityProviderAccountManager } from '../../../account/index.ts';\nimport type { IdentityProviderIdentity } from '../../../types.ts';\nimport type {\n IOAuth2Authenticator,\n IdentityProviderOAuth2AuthenticatorContext,\n IdentityProviderOAuth2AuthenticatorOptions,\n OAuth2AuthorizationCodeGrantPayload,\n} from './types.ts';\n\nexport class IdentityProviderOAuth2Authenticator implements IOAuth2Authenticator<User> {\n protected client : OAuth2Client;\n\n protected options : IdentityProviderOAuth2AuthenticatorOptions;\n\n protected accountManager: IIdentityProviderAccountManager;\n\n protected provider : OAuth2IdentityProvider | OpenIDIdentityProvider;\n\n //----------------------------------------------------------------------\n\n constructor(ctx: IdentityProviderOAuth2AuthenticatorContext) {\n this.options = ctx.options;\n this.accountManager = ctx.accountManager;\n this.provider = ctx.provider;\n\n this.client = new OAuth2Client({\n options: {\n clientId: ctx.provider.clientId,\n clientSecret: ctx.provider.clientSecret,\n redirectUri: `${ctx.options.baseURL}${buildIdentityProviderAuthorizeCallbackPath(ctx.provider.id)}`,\n scope: ctx.provider.scope || undefined,\n authorizationEndpoint: ctx.provider.authorizeUrl,\n tokenEndpoint: ctx.provider.tokenUrl,\n userinfoEndpoint: ctx.provider.userInfoUrl || undefined,\n },\n });\n }\n\n //----------------------------------------------------------------------\n\n buildRedirectURL(parameters: Partial<AuthorizeParameters> = {}) : string {\n try {\n return this.client.authorize.buildURL(parameters);\n } catch {\n throw new ValidationError(\n 'The identity provider is misconfigured and has an invalid or missing authorize URL.',\n );\n }\n }\n\n //----------------------------------------------------------------------\n\n async resolveIdentity(params: OAuth2AuthorizationCodeGrantPayload): Promise<IdentityProviderIdentity> {\n const token = await this.client.token.createWithAuthorizationCode(params);\n\n const identity = await this.buildIdentityWithTokenGrantResponse(token);\n if (this.options.clientId) {\n identity.clientId = this.options.clientId;\n }\n\n return identity;\n }\n\n async authenticate(params: OAuth2AuthorizationCodeGrantPayload): Promise<User> {\n const identity = await this.resolveIdentity(params);\n\n const account = await this.accountManager.save(identity);\n\n return account.user;\n }\n\n async safeAuthenticate(params: OAuth2AuthorizationCodeGrantPayload): Promise<Result<User>> {\n try {\n const data = await this.authenticate(params);\n return {\n success: true,\n data, \n };\n } catch (e) {\n return {\n success: false,\n error: e as Error, \n };\n }\n }\n\n //----------------------------------------------------------------------\n\n protected async buildIdentityWithTokenGrantResponse(input: TokenGrantResponse) : Promise<IdentityProviderIdentity> {\n const payload = extractTokenPayload(input.access_token);\n\n return {\n id: payload.sub!,\n attributeCandidates: {\n name: [\n payload.sub,\n ],\n email: [\n payload.email,\n ],\n },\n data: payload,\n provider: this.provider,\n };\n }\n}\n"],"mappings":";;;;;;;;AAuBA,IAAaI,sCAAb,MAAaA;CACCC;CAEAC;CAEAC;CAEAC;CAIV,YAAYC,KAAiD;EACzD,KAAKH,UAAUG,IAAIH;EACnB,KAAKC,iBAAiBE,IAAIF;EAC1B,KAAKC,WAAWC,IAAID;EAEpB,KAAKH,SAAS,IAAIF,aAAa,EAC3BG,SAAS;GACLI,UAAUD,IAAID,SAASE;GACvBC,cAAcF,IAAID,SAASG;GAC3BC,aAAa,GAAGH,IAAIH,QAAQO,UAAUb,2CAA2CS,IAAID,SAASM,EAAE;GAChGC,OAAON,IAAID,SAASO,SAASC,KAAAA;GAC7BC,uBAAuBR,IAAID,SAASU;GACpCC,eAAeV,IAAID,SAASY;GAC5BC,kBAAkBZ,IAAID,SAASc,eAAeN,KAAAA;EAClD,EACJ,CAAA;CACJ;CAIAO,iBAAiBC,aAA2C,CAAC,GAAY;EACrE,IAAI;GACA,OAAO,KAAKnB,OAAOoB,UAAUC,SAASF,UAAAA;EAC1C,QAAQ;GACJ,MAAM,IAAIvB,gBACN,qFAAA;EAER;CACJ;CAIA,MAAM0B,gBAAgBC,QAAgF;EAClG,MAAMC,QAAQ,MAAM,KAAKxB,OAAOwB,MAAMC,4BAA4BF,MAAAA;EAElE,MAAMG,WAAW,MAAM,KAAKC,oCAAoCH,KAAAA;EAChE,IAAI,KAAKvB,QAAQI,UACbqB,SAASrB,WAAW,KAAKJ,QAAQI;EAGrC,OAAOqB;CACX;CAEA,MAAME,aAAaL,QAA4D;EAC3E,MAAMG,WAAW,MAAM,KAAKJ,gBAAgBC,MAAAA;EAI5C,QAAOM,MAFe,KAAK3B,eAAe4B,KAAKJ,QAAAA,EAAAA,CAEhCK;CACnB;CAEA,MAAMC,iBAAiBT,QAAoE;EACvF,IAAI;GAEA,OAAO;IACHW,SAAS;IACTD,MAAAA,MAHe,KAAKL,aAAaL,MAAAA;GAIrC;EACJ,SAASY,GAAG;GACR,OAAO;IACHD,SAAS;IACTE,OAAOD;GACX;EACJ;CACJ;CAIA,MAAgBR,oCAAoCU,OAA+D;EAC/G,MAAMC,UAAUzC,oBAAoBwC,MAAME,YAAY;EAEtD,OAAO;GACH9B,IAAI6B,QAAQE;GACZC,qBAAqB;IACjBC,MAAM,CACFJ,QAAQE,GACX;IACDG,OAAO,CACHL,QAAQK,KACX;GACL;GACAV,MAAMK;GACNnC,UAAU,KAAKA;EACnB;CACJ;AACJ"}
1
+ {"version":3,"file":"module.mjs","names":["buildIdentityProviderAuthorizeCallbackPath","ValidationError","extractTokenPayload","OAuth2Client","USERINFO_TIMEOUT","IdentityProviderOAuth2Authenticator","client","options","accountManager","provider","logger","ctx","clientId","clientSecret","redirectUri","baseURL","id","scope","undefined","authorizationEndpoint","authorizeUrl","tokenEndpoint","tokenUrl","userinfoEndpoint","userInfoUrl","buildRedirectURL","parameters","authorize","buildURL","resolveIdentity","params","token","createWithAuthorizationCode","identity","buildIdentityWithTokenGrantResponse","authenticate","account","save","user","safeAuthenticate","data","success","e","error","resolveClaims","input","payload","claims","id_token","warn","message","userInfo","withTimeout","get","type","access_token","sub","promise","Promise","race","_resolve","reject","setTimeout","Error","unref","attributeCandidates","name","preferred_username","nickname","email"],"sources":["../../../../../../../src/core/identity/provider/authentication/protocols/oauth2/module.ts"],"sourcesContent":["/*\n * Copyright (c) 2023-2023.\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 { OAuth2IdentityProvider, OpenIDIdentityProvider, User } from '@authup/core-kit';\nimport { buildIdentityProviderAuthorizeCallbackPath } from '@authup/core-kit';\nimport { ValidationError } from '@authup/errors';\nimport type { Result } from '@authup/kit';\nimport type { JWTClaims } from '@authup/specs';\nimport type { Logger } from '@authup/server-kit';\nimport { extractTokenPayload } from '@authup/server-kit';\nimport type { AuthorizeParameters, TokenGrantResponse } from '@hapic/oauth2';\nimport { OAuth2Client } from '@hapic/oauth2';\nimport type { IIdentityProviderAccountManager } from '../../../account/index.ts';\nimport type { IdentityProviderIdentity } from '../../../types.ts';\nimport type {\n IOAuth2Authenticator,\n IdentityProviderOAuth2AuthenticatorContext,\n IdentityProviderOAuth2AuthenticatorOptions,\n OAuth2AuthorizationCodeGrantPayload,\n} from './types.ts';\n\n/**\n * Bound on the optional userinfo enrichment. Short by design: the caller is\n * a browser sitting on a redirect, and the login proceeds without it.\n */\nconst USERINFO_TIMEOUT = 5000;\n\nexport class IdentityProviderOAuth2Authenticator implements IOAuth2Authenticator<User> {\n protected client : OAuth2Client;\n\n protected options : IdentityProviderOAuth2AuthenticatorOptions;\n\n protected accountManager: IIdentityProviderAccountManager;\n\n protected provider : OAuth2IdentityProvider | OpenIDIdentityProvider;\n\n protected logger? : Logger;\n\n //----------------------------------------------------------------------\n\n constructor(ctx: IdentityProviderOAuth2AuthenticatorContext) {\n this.options = ctx.options;\n this.accountManager = ctx.accountManager;\n this.provider = ctx.provider;\n this.logger = ctx.logger;\n\n this.client = new OAuth2Client({\n options: {\n clientId: ctx.provider.clientId,\n clientSecret: ctx.provider.clientSecret,\n redirectUri: `${ctx.options.baseURL}${buildIdentityProviderAuthorizeCallbackPath(ctx.provider.id)}`,\n scope: ctx.provider.scope || undefined,\n authorizationEndpoint: ctx.provider.authorizeUrl,\n tokenEndpoint: ctx.provider.tokenUrl,\n userinfoEndpoint: ctx.provider.userInfoUrl || undefined,\n },\n });\n }\n\n //----------------------------------------------------------------------\n\n buildRedirectURL(parameters: Partial<AuthorizeParameters> = {}) : string {\n try {\n return this.client.authorize.buildURL(parameters);\n } catch {\n throw new ValidationError(\n 'The identity provider is misconfigured and has an invalid or missing authorize URL.',\n );\n }\n }\n\n //----------------------------------------------------------------------\n\n async resolveIdentity(params: OAuth2AuthorizationCodeGrantPayload): Promise<IdentityProviderIdentity> {\n const token = await this.client.token.createWithAuthorizationCode(params);\n\n const identity = await this.buildIdentityWithTokenGrantResponse(token);\n if (this.options.clientId) {\n identity.clientId = this.options.clientId;\n }\n\n return identity;\n }\n\n async authenticate(params: OAuth2AuthorizationCodeGrantPayload): Promise<User> {\n const identity = await this.resolveIdentity(params);\n\n const account = await this.accountManager.save(identity);\n\n return account.user;\n }\n\n async safeAuthenticate(params: OAuth2AuthorizationCodeGrantPayload): Promise<Result<User>> {\n try {\n const data = await this.authenticate(params);\n return {\n success: true,\n data, \n };\n } catch (e) {\n return {\n success: false,\n error: e as Error, \n };\n }\n }\n\n //----------------------------------------------------------------------\n\n /**\n * The claims an external identity is described by, richest source last.\n *\n * The access token is opaque by contract (OIDC Core §2), so it is only\n * the floor: authup's own carries `sub`, `kind` and `realm_name` and no\n * username at all, which is why a federated user used to be provisioned\n * under the remote subject UUID.\n */\n protected async resolveClaims(input: TokenGrantResponse, payload: JWTClaims) : Promise<JWTClaims> {\n let claims = payload;\n\n if (typeof input.id_token === 'string') {\n try {\n claims = {\n ...claims,\n ...extractTokenPayload(input.id_token),\n };\n } catch (e) {\n // an encrypted (five-segment JWE) id_token is not decodable\n // here, and was ignored outright before it was read at all.\n // Logged rather than swallowed silently: every other reason\n // to land here leaves the user provisioned under the remote\n // subject, which is the defect this method exists to fix.\n this.logger?.warn(\n `The identity provider (${this.provider.id}) id_token could not be read: ${(e as Error).message}`,\n );\n }\n }\n\n if (this.provider.userInfoUrl) {\n // the guard is load-bearing: the client carries no baseURL, so\n // hapic's `/userinfo` default would be a relative fetch URL\n try {\n const userInfo = await this.withTimeout(this.client.userInfo.get({\n type: 'Bearer',\n token: input.access_token,\n }));\n\n // OIDC Core 5.3.2: a userinfo response whose `sub` does not\n // match the token's MUST NOT be used. Without this a\n // mis-routed response (a multi-tenant gateway, a token\n // mix-up) would name and, worse, EMAIL the local user after\n // somebody else.\n if (\n typeof userInfo.sub === 'string' &&\n typeof payload.sub === 'string' &&\n userInfo.sub !== payload.sub\n ) {\n this.logger?.warn(\n `The identity provider (${this.provider.id}) userinfo subject does not match the token subject.`,\n );\n } else {\n claims = { ...claims, ...userInfo };\n }\n } catch (e) {\n // enrichment, never a login blocker\n this.logger?.warn(\n `The identity provider (${this.provider.id}) userinfo request failed: ${(e as Error).message}`,\n );\n }\n }\n\n return claims;\n }\n\n /**\n * hapic passes no `signal` to `fetch`, so an endpoint that accepts the\n * connection and never answers would hold the login for undici's 300s\n * headers timeout. Enrichment must not outlast the request it enriches.\n */\n protected withTimeout<T>(promise: Promise<T>) : Promise<T> {\n return Promise.race([\n promise,\n new Promise<T>((_resolve, reject) => {\n setTimeout(\n () => reject(new Error(`the request exceeded ${USERINFO_TIMEOUT}ms`)),\n USERINFO_TIMEOUT,\n ).unref();\n }),\n ]);\n }\n\n protected async buildIdentityWithTokenGrantResponse(input: TokenGrantResponse) : Promise<IdentityProviderIdentity> {\n const payload = extractTokenPayload(input.access_token);\n const claims = await this.resolveClaims(input, payload);\n\n return {\n // the account key: sourcing it from a richer claim set would\n // orphan every existing auth_identity_provider_accounts row\n id: payload.sub!,\n attributeCandidates: {\n name: [\n // keycloak/authentik put the username here, authup its\n // (nullable) display name; a candidate failing name\n // validation shifts to the next one\n claims.preferred_username,\n claims.nickname,\n // authup's own id_token: the real user name\n claims.name,\n claims.sub,\n ],\n email: [\n claims.email,\n ],\n },\n data: payload,\n provider: this.provider,\n };\n }\n}\n"],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import type { OAuth2IdentityProvider, OpenIDIdentityProvider } from '@authup/core-kit';
2
2
  import type { ObjectLiteral, Result } from '@authup/kit';
3
+ import type { Logger } from '@authup/server-kit';
3
4
  import type { AuthorizeParameters } from '@hapic/oauth2';
4
5
  import type { IIdentityProviderAccountManager } from '../../../account/index.ts';
5
6
  import type { IdentityProviderIdentity } from '../../../types.ts';
@@ -26,5 +27,6 @@ export type IdentityProviderOAuth2AuthenticatorContext = {
26
27
  options: IdentityProviderOAuth2AuthenticatorOptions;
27
28
  accountManager: IIdentityProviderAccountManager;
28
29
  provider: OAuth2IdentityProvider | OpenIDIdentityProvider;
30
+ logger?: Logger;
29
31
  };
30
32
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/identity/provider/authentication/protocols/oauth2/types.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AACvF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,2BAA2B,CAAC;AACjF,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAElE,MAAM,MAAM,mCAAmC,GAAG;IAC9C,IAAI,EAAE,MAAM,CAAA;IACZ,aAAa,CAAC,EAAE,MAAM,CAAA;CACzB,CAAC;AAEF,MAAM,WAAW,oBAAoB,CAAC,CAAC,SAAS,aAAa,GAAG,aAAa;IACzE,YAAY,CAAC,MAAM,EAAE,mCAAmC,GAAI,OAAO,CAAC,CAAC,CAAC,CAAC;IAEvE,gBAAgB,CAAC,MAAM,EAAE,mCAAmC,GAAI,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IAEnF;;;;OAIG;IACH,eAAe,CAAC,MAAM,EAAE,mCAAmC,GAAI,OAAO,CAAC,wBAAwB,CAAC,CAAC;IAEjG,gBAAgB,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC,mBAAmB,CAAC,GAAG,MAAM,CAAC;CACvE;AAED,MAAM,MAAM,0CAA0C,GAAG;IACrD,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAA;CACpB,CAAC;AAEF,MAAM,MAAM,0CAA0C,GAAG;IACrD,OAAO,EAAE,0CAA0C,CAAC;IACpD,cAAc,EAAE,+BAA+B,CAAA;IAC/C,QAAQ,EAAE,sBAAsB,GAAG,sBAAsB,CAAC;CAC7D,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/identity/provider/authentication/protocols/oauth2/types.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AACvF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,2BAA2B,CAAC;AACjF,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAElE,MAAM,MAAM,mCAAmC,GAAG;IAC9C,IAAI,EAAE,MAAM,CAAA;IACZ,aAAa,CAAC,EAAE,MAAM,CAAA;CACzB,CAAC;AAEF,MAAM,WAAW,oBAAoB,CAAC,CAAC,SAAS,aAAa,GAAG,aAAa;IACzE,YAAY,CAAC,MAAM,EAAE,mCAAmC,GAAI,OAAO,CAAC,CAAC,CAAC,CAAC;IAEvE,gBAAgB,CAAC,MAAM,EAAE,mCAAmC,GAAI,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IAEnF;;;;OAIG;IACH,eAAe,CAAC,MAAM,EAAE,mCAAmC,GAAI,OAAO,CAAC,wBAAwB,CAAC,CAAC;IAEjG,gBAAgB,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC,mBAAmB,CAAC,GAAG,MAAM,CAAC;CACvE;AAED,MAAM,MAAM,0CAA0C,GAAG;IACrD,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAA;CACpB,CAAC;AAEF,MAAM,MAAM,0CAA0C,GAAG;IACrD,OAAO,EAAE,0CAA0C,CAAC;IACpD,cAAc,EAAE,+BAA+B,CAAA;IAC/C,QAAQ,EAAE,sBAAsB,GAAG,sBAAsB,CAAC;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC"}
@@ -1,9 +1,6 @@
1
- import type { TokenGrantResponse } from '@hapic/oauth2';
2
- import type { IdentityProviderIdentity } from '../../../types.ts';
3
1
  import type { IdentityProviderOAuth2AuthenticatorContext } from '../oauth2/index.ts';
4
2
  import { IdentityProviderOAuth2Authenticator } from '../oauth2/index.ts';
5
3
  export declare class IdentityProviderOpenIDAuthenticator extends IdentityProviderOAuth2Authenticator {
6
4
  constructor(ctx: IdentityProviderOAuth2AuthenticatorContext);
7
- protected buildIdentityWithTokenGrantResponse(input: TokenGrantResponse): Promise<IdentityProviderIdentity>;
8
5
  }
9
6
  //# sourceMappingURL=module.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/identity/provider/authentication/protocols/open-id/module.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,KAAK,EAAE,0CAA0C,EAAE,MAAM,oBAAoB,CAAC;AACrF,OAAO,EAAE,mCAAmC,EAAE,MAAM,oBAAoB,CAAC;AAEzE,qBAAa,mCAAoC,SAAQ,mCAAmC;gBAC5E,GAAG,EAAE,0CAA0C;cAU3C,mCAAmC,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,wBAAwB,CAAC;CAmBpH"}
1
+ {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/identity/provider/authentication/protocols/open-id/module.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,0CAA0C,EAAE,MAAM,oBAAoB,CAAC;AACrF,OAAO,EAAE,mCAAmC,EAAE,MAAM,oBAAoB,CAAC;AAEzE,qBAAa,mCAAoC,SAAQ,mCAAmC;gBAC5E,GAAG,EAAE,0CAA0C;CAS9D"}
@@ -3,7 +3,6 @@ import "node:url";
3
3
  import.meta.url;
4
4
  import { IdentityProviderOAuth2Authenticator } from "../oauth2/module.mjs";
5
5
  import "../oauth2/index.mjs";
6
- import { extractTokenPayload } from "@authup/server-kit";
7
6
  import { mergeOAuth2Scopes } from "@authup/specs";
8
7
  //#region src/core/identity/provider/authentication/protocols/open-id/module.ts
9
8
  var IdentityProviderOpenIDAuthenticator = class extends IdentityProviderOAuth2Authenticator {
@@ -11,22 +10,6 @@ var IdentityProviderOpenIDAuthenticator = class extends IdentityProviderOAuth2Au
11
10
  ctx.provider.scope = mergeOAuth2Scopes("openid", ctx.provider.scope || "openid profile email");
12
11
  super(ctx);
13
12
  }
14
- async buildIdentityWithTokenGrantResponse(input) {
15
- const payload = extractTokenPayload(input.access_token);
16
- return {
17
- id: payload.sub,
18
- attributeCandidates: {
19
- name: [
20
- payload.preferred_username,
21
- payload.nickname,
22
- payload.sub
23
- ],
24
- email: [payload.email]
25
- },
26
- data: payload,
27
- provider: this.provider
28
- };
29
- }
30
13
  };
31
14
  //#endregion
32
15
  export { IdentityProviderOpenIDAuthenticator };
@@ -1 +1 @@
1
- {"version":3,"file":"module.mjs","names":["extractTokenPayload","mergeOAuth2Scopes","IdentityProviderOAuth2Authenticator","IdentityProviderOpenIDAuthenticator","ctx","provider","scope","buildIdentityWithTokenGrantResponse","input","payload","access_token","id","sub","attributeCandidates","name","preferred_username","nickname","email","data"],"sources":["../../../../../../../src/core/identity/provider/authentication/protocols/open-id/module.ts"],"sourcesContent":["/*\n * Copyright (c) 2023-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 { extractTokenPayload } from '@authup/server-kit';\nimport { mergeOAuth2Scopes } from '@authup/specs';\nimport type { TokenGrantResponse } from '@hapic/oauth2';\nimport type { IdentityProviderIdentity } from '../../../types.ts';\nimport type { IdentityProviderOAuth2AuthenticatorContext } from '../oauth2/index.ts';\nimport { IdentityProviderOAuth2Authenticator } from '../oauth2/index.ts';\n\nexport class IdentityProviderOpenIDAuthenticator extends IdentityProviderOAuth2Authenticator {\n constructor(ctx: IdentityProviderOAuth2AuthenticatorContext) {\n // OIDC requires the openid scope (Core §3.1.2.1)\n ctx.provider.scope = mergeOAuth2Scopes(\n 'openid',\n ctx.provider.scope || 'openid profile email',\n );\n\n super(ctx);\n }\n\n protected async buildIdentityWithTokenGrantResponse(input: TokenGrantResponse): Promise<IdentityProviderIdentity> {\n const payload = extractTokenPayload(input.access_token);\n\n return {\n id: payload.sub!,\n attributeCandidates: {\n name: [\n payload.preferred_username,\n payload.nickname,\n payload.sub,\n ],\n email: [\n payload.email,\n ],\n },\n data: payload,\n provider: this.provider,\n };\n }\n}\n"],"mappings":";;;;;;;;AAcA,IAAaG,sCAAb,cAAyDD,oCAAAA;CACrD,YAAYE,KAAiD;EAEzDA,IAAIC,SAASC,QAAQL,kBACjB,UACAG,IAAIC,SAASC,SAAS,sBAAA;EAG1B,MAAMF,GAAAA;CACV;CAEA,MAAgBG,oCAAoCC,OAA8D;EAC9G,MAAMC,UAAUT,oBAAoBQ,MAAME,YAAY;EAEtD,OAAO;GACHC,IAAIF,QAAQG;GACZC,qBAAqB;IACjBC,MAAM;KACFL,QAAQM;KACRN,QAAQO;KACRP,QAAQG;IACX;IACDK,OAAO,CACHR,QAAQQ,KACX;GACL;GACAC,MAAMT;GACNJ,UAAU,KAAKA;EACnB;CACJ;AACJ"}
1
+ {"version":3,"file":"module.mjs","names":["mergeOAuth2Scopes","IdentityProviderOAuth2Authenticator","IdentityProviderOpenIDAuthenticator","ctx","provider","scope"],"sources":["../../../../../../../src/core/identity/provider/authentication/protocols/open-id/module.ts"],"sourcesContent":["/*\n * Copyright (c) 2023-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 { mergeOAuth2Scopes } from '@authup/specs';\nimport type { IdentityProviderOAuth2AuthenticatorContext } from '../oauth2/index.ts';\nimport { IdentityProviderOAuth2Authenticator } from '../oauth2/index.ts';\n\nexport class IdentityProviderOpenIDAuthenticator extends IdentityProviderOAuth2Authenticator {\n constructor(ctx: IdentityProviderOAuth2AuthenticatorContext) {\n // OIDC requires the openid scope (Core §3.1.2.1)\n ctx.provider.scope = mergeOAuth2Scopes(\n 'openid',\n ctx.provider.scope || 'openid profile email',\n );\n\n super(ctx);\n }\n}\n"],"mappings":";;;;;;;AAWA,IAAaE,sCAAb,cAAyDD,oCAAAA;CACrD,YAAYE,KAAiD;EAEzDA,IAAIC,SAASC,QAAQL,kBACjB,UACAG,IAAIC,SAASC,SAAS,sBAAA;EAG1B,MAAMF,GAAAA;CACV;AACJ"}
@@ -1,15 +1,6 @@
1
1
  import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
- import { IDENTITY_PROVIDER_ACCOUNT_ALREADY_LINKED_ERROR_INSTANCE, IdentityProviderAccountAlreadyLinkedError, isIdentityProviderAccountAlreadyLinkedError } from "./account/error.mjs";
5
- import { IdentityProviderMapperOperation } from "./mapper/constants.mjs";
6
- import { IdentityProviderAccountBaseMapper } from "./mapper/base.mjs";
7
- import { IdentityProviderAttributeMapper } from "./mapper/attributes/module.mjs";
8
- import { IdentityProviderRoleMapper } from "./mapper/role/module.mjs";
9
- import { IdentityProviderPermissionMapper } from "./mapper/permission/module.mjs";
10
- import "./mapper/index.mjs";
11
- import { IdentityProviderAccountManager } from "./account/module.mjs";
12
- import "./account/index.mjs";
13
4
  import { IdentityProviderLdapAuthenticator } from "./authentication/protocols/ldap/module.mjs";
14
5
  import { IdentityProviderLdapCollectionAuthenticator } from "./authentication/protocols/ldap/collection.mjs";
15
6
  import { IdentityProviderOAuth2Authenticator } from "./authentication/protocols/oauth2/module.mjs";
@@ -19,7 +10,17 @@ import { IdentityProviderGithubAuthenticator } from "./authentication/presets/gi
19
10
  import { IdentityProviderGoogleAuthenticator } from "./authentication/presets/google.mjs";
20
11
  import { IdentityProviderInstagramAuthenticator } from "./authentication/presets/instagram.mjs";
21
12
  import { IdentityProviderPaypalAuthenticator } from "./authentication/presets/paypal.mjs";
22
- import { IDENTITY_PROVIDER_LDAP_COLLECTION_AUTHENTICATOR_TOKEN } from "./authentication/constants.mjs";
23
13
  import { createIdentityProviderOAuth2Authenticator } from "./authentication/factory.mjs";
14
+ import { IDENTITY_PROVIDER_ACCOUNT_LINK_TTL } from "./account/constants.mjs";
15
+ import { IDENTITY_PROVIDER_ACCOUNT_ALREADY_LINKED_ERROR_INSTANCE, IdentityProviderAccountAlreadyLinkedError, isIdentityProviderAccountAlreadyLinkedError } from "./account/error.mjs";
16
+ import { IdentityProviderMapperOperation } from "./mapper/constants.mjs";
17
+ import { IdentityProviderAccountBaseMapper } from "./mapper/base.mjs";
18
+ import { IdentityProviderAttributeMapper } from "./mapper/attributes/module.mjs";
19
+ import { IdentityProviderRoleMapper } from "./mapper/role/module.mjs";
20
+ import { IdentityProviderPermissionMapper } from "./mapper/permission/module.mjs";
21
+ import "./mapper/index.mjs";
22
+ import { IdentityProviderAccountManager } from "./account/module.mjs";
23
+ import "./account/index.mjs";
24
+ import { IDENTITY_PROVIDER_LDAP_COLLECTION_AUTHENTICATOR_TOKEN } from "./authentication/constants.mjs";
24
25
  import "./authentication/index.mjs";
25
- export { IDENTITY_PROVIDER_ACCOUNT_ALREADY_LINKED_ERROR_INSTANCE, IDENTITY_PROVIDER_LDAP_COLLECTION_AUTHENTICATOR_TOKEN, IdentityProviderAccountAlreadyLinkedError, IdentityProviderAccountBaseMapper, IdentityProviderAccountManager, IdentityProviderAttributeMapper, IdentityProviderFacebookAuthenticator, IdentityProviderGithubAuthenticator, IdentityProviderGoogleAuthenticator, IdentityProviderInstagramAuthenticator, IdentityProviderLdapAuthenticator, IdentityProviderLdapCollectionAuthenticator, IdentityProviderMapperOperation, IdentityProviderOAuth2Authenticator, IdentityProviderOpenIDAuthenticator, IdentityProviderPaypalAuthenticator, IdentityProviderPermissionMapper, IdentityProviderRoleMapper, createIdentityProviderOAuth2Authenticator, isIdentityProviderAccountAlreadyLinkedError };
26
+ export { IDENTITY_PROVIDER_ACCOUNT_ALREADY_LINKED_ERROR_INSTANCE, IDENTITY_PROVIDER_ACCOUNT_LINK_TTL, IDENTITY_PROVIDER_LDAP_COLLECTION_AUTHENTICATOR_TOKEN, IdentityProviderAccountAlreadyLinkedError, IdentityProviderAccountBaseMapper, IdentityProviderAccountManager, IdentityProviderAttributeMapper, IdentityProviderFacebookAuthenticator, IdentityProviderGithubAuthenticator, IdentityProviderGoogleAuthenticator, IdentityProviderInstagramAuthenticator, IdentityProviderLdapAuthenticator, IdentityProviderLdapCollectionAuthenticator, IdentityProviderMapperOperation, IdentityProviderOAuth2Authenticator, IdentityProviderOpenIDAuthenticator, IdentityProviderPaypalAuthenticator, IdentityProviderPermissionMapper, IdentityProviderRoleMapper, createIdentityProviderOAuth2Authenticator, isIdentityProviderAccountAlreadyLinkedError };
@@ -7,7 +7,7 @@ import { UserCredentialsService } from "./authentication/credential/entities/use
7
7
  import { BaseCredentialsAuthenticator } from "./authentication/base.mjs";
8
8
  import { ClientAuthenticator } from "./authentication/entities/client/module.mjs";
9
9
  import { UserAuthenticator } from "./authentication/entities/user/module.mjs";
10
- import { EVENT_ACTOR_NAME_MAX_LENGTH, EVENT_LOG_RETENTION_DAYS_DEFAULT } from "./entities/event/constants.mjs";
10
+ import { EVENT_ACTOR_NAME_MAX_LENGTH, EVENT_LOG_RETENTION_DAYS_DEFAULT, EVENT_RETENTION_SWEEP_BATCH_SIZE } from "./entities/event/constants.mjs";
11
11
  import { LoginThrottleService } from "./authentication/login-throttle/service.mjs";
12
12
  import { CredentialsAuthenticator } from "./authentication/module.mjs";
13
13
  import { SessionManager } from "./authentication/session/module.mjs";
@@ -113,6 +113,18 @@ import { toIdentityPolicyData } from "./identity/permission/identity-policy-data
113
113
  import { OAuth2Authorization } from "./oauth2/authorization/module.mjs";
114
114
  import { OAuth2EndSessionService } from "./oauth2/end-session/service.mjs";
115
115
  import { OAuth2EndSessionRequestValidator } from "./oauth2/end-session/validator.mjs";
116
+ import { IdentityProviderLdapAuthenticator } from "./identity/provider/authentication/protocols/ldap/module.mjs";
117
+ import { IdentityProviderLdapCollectionAuthenticator } from "./identity/provider/authentication/protocols/ldap/collection.mjs";
118
+ import { IdentityProviderOAuth2Authenticator } from "./identity/provider/authentication/protocols/oauth2/module.mjs";
119
+ import { IdentityProviderOpenIDAuthenticator } from "./identity/provider/authentication/protocols/open-id/module.mjs";
120
+ import { IdentityProviderFacebookAuthenticator } from "./identity/provider/authentication/presets/facebook.mjs";
121
+ import { IdentityProviderGithubAuthenticator } from "./identity/provider/authentication/presets/github.mjs";
122
+ import { IdentityProviderGoogleAuthenticator } from "./identity/provider/authentication/presets/google.mjs";
123
+ import { IdentityProviderInstagramAuthenticator } from "./identity/provider/authentication/presets/instagram.mjs";
124
+ import { IdentityProviderPaypalAuthenticator } from "./identity/provider/authentication/presets/paypal.mjs";
125
+ import { createIdentityProviderOAuth2Authenticator } from "./identity/provider/authentication/factory.mjs";
126
+ import { OAuth2FederatedLoginRefusal } from "./oauth2/federated-login/types.mjs";
127
+ import { OAuth2FederatedLoginService } from "./oauth2/federated-login/module.mjs";
116
128
  import { buildOAuth2BearerTokenResponse } from "./oauth2/response/bearer.mjs";
117
129
  import { OAuth2AuthorizeGrant } from "./oauth2/grant-types/authorize.mjs";
118
130
  import { ClientCredentialsGrant } from "./oauth2/grant-types/client-credentials.mjs";
@@ -136,6 +148,7 @@ import { PasswordRecoveryService } from "./identity/password-recovery/service.mj
136
148
  import { PermissionCheckerService } from "./identity/permission/checker/service.mjs";
137
149
  import { IdentityPermissionProvider } from "./identity/permission/module.mjs";
138
150
  import { PolicyCheckerService } from "./identity/policy/checker/service.mjs";
151
+ import { IDENTITY_PROVIDER_ACCOUNT_LINK_TTL } from "./identity/provider/account/constants.mjs";
139
152
  import { IDENTITY_PROVIDER_ACCOUNT_ALREADY_LINKED_ERROR_INSTANCE, IdentityProviderAccountAlreadyLinkedError, isIdentityProviderAccountAlreadyLinkedError } from "./identity/provider/account/error.mjs";
140
153
  import { IdentityProviderMapperOperation } from "./identity/provider/mapper/constants.mjs";
141
154
  import { IdentityProviderAccountBaseMapper } from "./identity/provider/mapper/base.mjs";
@@ -143,17 +156,7 @@ import { IdentityProviderAttributeMapper } from "./identity/provider/mapper/attr
143
156
  import { IdentityProviderRoleMapper } from "./identity/provider/mapper/role/module.mjs";
144
157
  import { IdentityProviderPermissionMapper } from "./identity/provider/mapper/permission/module.mjs";
145
158
  import { IdentityProviderAccountManager } from "./identity/provider/account/module.mjs";
146
- import { IdentityProviderLdapAuthenticator } from "./identity/provider/authentication/protocols/ldap/module.mjs";
147
- import { IdentityProviderLdapCollectionAuthenticator } from "./identity/provider/authentication/protocols/ldap/collection.mjs";
148
- import { IdentityProviderOAuth2Authenticator } from "./identity/provider/authentication/protocols/oauth2/module.mjs";
149
- import { IdentityProviderOpenIDAuthenticator } from "./identity/provider/authentication/protocols/open-id/module.mjs";
150
- import { IdentityProviderFacebookAuthenticator } from "./identity/provider/authentication/presets/facebook.mjs";
151
- import { IdentityProviderGithubAuthenticator } from "./identity/provider/authentication/presets/github.mjs";
152
- import { IdentityProviderGoogleAuthenticator } from "./identity/provider/authentication/presets/google.mjs";
153
- import { IdentityProviderInstagramAuthenticator } from "./identity/provider/authentication/presets/instagram.mjs";
154
- import { IdentityProviderPaypalAuthenticator } from "./identity/provider/authentication/presets/paypal.mjs";
155
159
  import { IDENTITY_PROVIDER_LDAP_COLLECTION_AUTHENTICATOR_TOKEN } from "./identity/provider/authentication/constants.mjs";
156
- import { createIdentityProviderOAuth2Authenticator } from "./identity/provider/authentication/factory.mjs";
157
160
  import { RegistrationService } from "./identity/registration/service.mjs";
158
161
  import { IdentityResolver } from "./identity/resolver/module.mjs";
159
162
  import { IdentityRoleProvider } from "./identity/role/module.mjs";
@@ -187,4 +190,4 @@ import { UserProvisioningSynchronizer } from "./provisioning/synchronizer/user/m
187
190
  import { WildcardRealmProvisioner, expandWildcardRealmEntry, extractWildcardRealmEntry } from "./provisioning/wildcard/module.mjs";
188
191
  import "./provisioning/index.mjs";
189
192
  import "./security/index.mjs";
190
- export { BaseCredentialsAuthenticator, CLIENT_READ_PERMISSIONS, CONSENT_FILTER_KEYS, CONSENT_SCOPE_MAX_LENGTH, ClientAuthenticator, ClientCertificateValidator, ClientCredentialsGrant, ClientCredentialsService, ClientPermissionService, ClientProvisioningSynchronizer, ClientProvisioningValidator, ClientRoleService, ClientScopeService, ClientService, ConsentService, CredentialsAuthenticator, EVENT_ACTOR_NAME_MAX_LENGTH, EVENT_DIFF_SECRET_KEY_REGEX, EVENT_DIFF_VALUE_MAX_LENGTH, EVENT_LOG_RETENTION_DAYS_DEFAULT, EntityEventHandler, EventService, GraphProvisioningSynchronizer, IDENTITY_PROVIDER_ACCOUNT_ALREADY_LINKED_ERROR_INSTANCE, IDENTITY_PROVIDER_ACCOUNT_FILTER_KEYS, IDENTITY_PROVIDER_ACCOUNT_UNLINK_BLOCKED_ERROR_INSTANCE, IDENTITY_PROVIDER_LDAP_COLLECTION_AUTHENTICATOR_TOKEN, IdentityGrantType, IdentityPermissionProvider, IdentityProviderAccountAlreadyLinkedError, IdentityProviderAccountBaseMapper, IdentityProviderAccountManager, IdentityProviderAccountService, IdentityProviderAccountUnlinkBlockedError, IdentityProviderAttributeMapper, IdentityProviderFacebookAuthenticator, IdentityProviderGithubAuthenticator, IdentityProviderGoogleAuthenticator, IdentityProviderInstagramAuthenticator, IdentityProviderLdapAuthenticator, IdentityProviderLdapCollectionAuthenticator, IdentityProviderMapperOperation, IdentityProviderOAuth2Authenticator, IdentityProviderOpenIDAuthenticator, IdentityProviderPaypalAuthenticator, IdentityProviderPermissionMapper, IdentityProviderRoleMapper, IdentityProviderRoleMappingService, IdentityResolver, IdentityRoleProvider, KEY_CERTIFICATE_ERROR_INSTANCE, KeyCertificateError, KeyProvisioner, KeyService, LoginThrottleService, MAIL_TEMPLATE_REGISTRY, MailTemplateName, MailTemplateRenderer, NoopAuthFlowMetrics, OAuth2AccessPolicyEvaluator, OAuth2AccessTokenIssuer, OAuth2Authorization, OAuth2AuthorizationCodeIssuer, OAuth2AuthorizationCodeRequestValidator, OAuth2AuthorizationCodeRequestVerifier, OAuth2AuthorizationCodeVerifier, OAuth2AuthorizationStateManager, OAuth2AuthorizeGrant, OAuth2ClientAuthenticator, OAuth2EndSessionRequestValidator, OAuth2EndSessionService, OAuth2MfaLoginService, OAuth2MfaTokenIssuer, OAuth2OpenIDClaimsBuilder, OAuth2OpenIDTokenIssuer, OAuth2RefreshTokenGrant, OAuth2RefreshTokenIssuer, OAuth2ScopeAttributesResolver, OAuth2TokenRevoker, OAuth2TokenSigner, OAuth2TokenVerifier, PASSWORD_RESET_EXPIRES_IN_MINUTES, PasswordGrantType, PasswordRecoveryService, PermissionBindingPolicyEvaluator, PermissionCheckerService, PermissionPolicyService, PermissionProvisioningRelationsValidator, PermissionProvisioningSynchronizer, PermissionProvisioningValidator, PermissionService, PolicyCheckerService, PolicyEngine, PolicyProvisioningSynchronizer, PolicyProvisioningValidator, PolicyService, ProvisioningEntityResolver, ProvisioningEntityStrategyType, ProvisioningJunctionSynchronizer, ProvisioningStrategyValidator, REALM_CIPHER_BLOB_ERROR_INSTANCE, REALM_CIPHER_BLOB_VERSION, REALM_WILDCARD_NAME, RECORD_QUERY_PARAMETERS, RealmCipher, RealmCipherBlobError, RealmProvisioningSynchronizer, RealmProvisioningValidator, RealmService, RealmWildcardProvisioningValidator, RegistrationService, RoleAttributeService, RolePermissionService, RoleProvisioningSynchronizer, RoleProvisioningValidator, RoleService, RootProvisioningValidator, SESSION_FILTER_KEYS, SESSION_TOKEN_FILTER_KEYS, SESSION_TOKEN_RELATION_MISSING_ERROR_INSTANCE, SYSTEM_CLIENT_DEFINITIONS, SYSTEM_CLIENT_SCOPE_NAMES, ScopeProvisioningSynchronizer, ScopeProvisioningValidator, ScopeService, SessionManager, SessionService, SessionTokenRelationMissingError, 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, UserAuthenticator, UserAuthenticatorService, UserCredentialsService, UserPermissionService, UserProvisioningSynchronizer, UserProvisioningValidator, UserRoleService, UserService, WRAPPED_KEY_MATERIAL_PREFIX, WildcardRealmProvisioner, appendQueryConditions, applyJunctionCreateGrant, assertCertificateMatchesKey, assertClientCertificateEvidenceValidForBinding, assertClientGrantAllowed, base64URLEncode, buildCertificateJwkFields, buildClientCertificateThumbprint, buildEntityDiff, buildJunctionUpdateData, buildOAuth2BearerTokenResponse, buildOAuth2CodeChallenge, buildOAuth2TokenHash, buildProvisioningEntityKey, buildSystemClientAttributes, buildWebauthnAuthenticationOptions, buildWebauthnRegistrationOptions, buildX5c, buildX5tS256, clientPermissionSchema, clientRoleSchema, clientSchema, clientScopeSchema, consentSchema, createIdentityProviderOAuth2Authenticator, createProvisioningEntitiesValidator, createRelationsReadGate, decodeQuery, defineMailTemplate, deriveAmrAcr, describeQuerySchema, eventSchema, expandWildcardRealmEntry, extractWildcardRealmEntry, generateNumericCode, generateOAuth2CodeVerifier, generateRecoveryCode, guessUserAuthenticatorKindByResponse, identityProviderAccountSchema, identityProviderRoleMappingSchema, identityProviderSchema, isIdentityProviderAccountAlreadyLinkedError, isIdentityProviderAccountUnlinkBlockedError, isRealmCipherBlobError, isSessionTokenRelationMissingError, isWrappedKeyMaterial, keySchema, mergeProvisioningEntities, mergeProvisioningEntity, normalizeEntityProvisioningStrategy, parseCertificateChain, parseClientCertificateChain, permissionPolicySchema, permissionSchema, policySchema, queryCodec, realmSchema, roleAttributeSchema, rolePermissionSchema, roleSchema, sanitizeEventData, schemaRegistry, schemas, scopeSchema, sessionSchema, sessionTokenSchema, toIdentityPolicyData, trustAnchorSchema, unwrapKeyMaterial, userAttributeSchema, userAuthenticatorSchema, userPermissionSchema, userRoleSchema, userSchema, verifyWebauthnAuthentication, verifyWebauthnRegistration, wrapKeyMaterial };
193
+ export { BaseCredentialsAuthenticator, CLIENT_READ_PERMISSIONS, CONSENT_FILTER_KEYS, CONSENT_SCOPE_MAX_LENGTH, ClientAuthenticator, ClientCertificateValidator, ClientCredentialsGrant, ClientCredentialsService, ClientPermissionService, ClientProvisioningSynchronizer, ClientProvisioningValidator, ClientRoleService, ClientScopeService, ClientService, ConsentService, CredentialsAuthenticator, 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, GraphProvisioningSynchronizer, IDENTITY_PROVIDER_ACCOUNT_ALREADY_LINKED_ERROR_INSTANCE, IDENTITY_PROVIDER_ACCOUNT_FILTER_KEYS, IDENTITY_PROVIDER_ACCOUNT_LINK_TTL, IDENTITY_PROVIDER_ACCOUNT_UNLINK_BLOCKED_ERROR_INSTANCE, IDENTITY_PROVIDER_LDAP_COLLECTION_AUTHENTICATOR_TOKEN, IdentityGrantType, IdentityPermissionProvider, IdentityProviderAccountAlreadyLinkedError, IdentityProviderAccountBaseMapper, IdentityProviderAccountManager, IdentityProviderAccountService, IdentityProviderAccountUnlinkBlockedError, IdentityProviderAttributeMapper, IdentityProviderFacebookAuthenticator, IdentityProviderGithubAuthenticator, IdentityProviderGoogleAuthenticator, IdentityProviderInstagramAuthenticator, IdentityProviderLdapAuthenticator, IdentityProviderLdapCollectionAuthenticator, IdentityProviderMapperOperation, IdentityProviderOAuth2Authenticator, IdentityProviderOpenIDAuthenticator, IdentityProviderPaypalAuthenticator, IdentityProviderPermissionMapper, IdentityProviderRoleMapper, IdentityProviderRoleMappingService, IdentityResolver, IdentityRoleProvider, KEY_CERTIFICATE_ERROR_INSTANCE, KeyCertificateError, KeyProvisioner, KeyService, LoginThrottleService, MAIL_TEMPLATE_REGISTRY, MailTemplateName, MailTemplateRenderer, NoopAuthFlowMetrics, OAuth2AccessPolicyEvaluator, OAuth2AccessTokenIssuer, OAuth2Authorization, OAuth2AuthorizationCodeIssuer, OAuth2AuthorizationCodeRequestValidator, OAuth2AuthorizationCodeRequestVerifier, OAuth2AuthorizationCodeVerifier, OAuth2AuthorizationStateManager, OAuth2AuthorizeGrant, OAuth2ClientAuthenticator, OAuth2EndSessionRequestValidator, OAuth2EndSessionService, OAuth2FederatedLoginRefusal, OAuth2FederatedLoginService, OAuth2MfaLoginService, OAuth2MfaTokenIssuer, OAuth2OpenIDClaimsBuilder, OAuth2OpenIDTokenIssuer, OAuth2RefreshTokenGrant, OAuth2RefreshTokenIssuer, OAuth2ScopeAttributesResolver, OAuth2TokenRevoker, OAuth2TokenSigner, OAuth2TokenVerifier, PASSWORD_RESET_EXPIRES_IN_MINUTES, PasswordGrantType, PasswordRecoveryService, PermissionBindingPolicyEvaluator, PermissionCheckerService, PermissionPolicyService, PermissionProvisioningRelationsValidator, PermissionProvisioningSynchronizer, PermissionProvisioningValidator, PermissionService, PolicyCheckerService, PolicyEngine, PolicyProvisioningSynchronizer, PolicyProvisioningValidator, PolicyService, ProvisioningEntityResolver, ProvisioningEntityStrategyType, ProvisioningJunctionSynchronizer, ProvisioningStrategyValidator, REALM_CIPHER_BLOB_ERROR_INSTANCE, REALM_CIPHER_BLOB_VERSION, REALM_WILDCARD_NAME, RECORD_QUERY_PARAMETERS, RealmCipher, RealmCipherBlobError, RealmProvisioningSynchronizer, RealmProvisioningValidator, RealmService, RealmWildcardProvisioningValidator, RegistrationService, RoleAttributeService, RolePermissionService, RoleProvisioningSynchronizer, RoleProvisioningValidator, RoleService, RootProvisioningValidator, SESSION_FILTER_KEYS, SESSION_TOKEN_FILTER_KEYS, SESSION_TOKEN_RELATION_MISSING_ERROR_INSTANCE, SYSTEM_CLIENT_DEFINITIONS, SYSTEM_CLIENT_SCOPE_NAMES, ScopeProvisioningSynchronizer, ScopeProvisioningValidator, ScopeService, SessionManager, SessionService, SessionTokenRelationMissingError, 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, UserAuthenticator, UserAuthenticatorService, UserCredentialsService, UserPermissionService, UserProvisioningSynchronizer, UserProvisioningValidator, UserRoleService, UserService, WRAPPED_KEY_MATERIAL_PREFIX, WildcardRealmProvisioner, appendQueryConditions, applyJunctionCreateGrant, assertCertificateMatchesKey, assertClientCertificateEvidenceValidForBinding, assertClientGrantAllowed, base64URLEncode, buildCertificateJwkFields, buildClientCertificateThumbprint, buildEntityDiff, buildJunctionUpdateData, buildOAuth2BearerTokenResponse, buildOAuth2CodeChallenge, buildOAuth2TokenHash, buildProvisioningEntityKey, buildSystemClientAttributes, buildWebauthnAuthenticationOptions, buildWebauthnRegistrationOptions, buildX5c, buildX5tS256, clientPermissionSchema, clientRoleSchema, clientSchema, clientScopeSchema, consentSchema, createIdentityProviderOAuth2Authenticator, createProvisioningEntitiesValidator, createRelationsReadGate, decodeQuery, defineMailTemplate, deriveAmrAcr, describeQuerySchema, eventSchema, expandWildcardRealmEntry, extractWildcardRealmEntry, generateNumericCode, generateOAuth2CodeVerifier, generateRecoveryCode, guessUserAuthenticatorKindByResponse, identityProviderAccountSchema, identityProviderRoleMappingSchema, identityProviderSchema, isIdentityProviderAccountAlreadyLinkedError, isIdentityProviderAccountUnlinkBlockedError, isRealmCipherBlobError, isSessionTokenRelationMissingError, isWrappedKeyMaterial, keySchema, mergeProvisioningEntities, mergeProvisioningEntity, normalizeEntityProvisioningStrategy, parseCertificateChain, parseClientCertificateChain, permissionPolicySchema, permissionSchema, policySchema, queryCodec, realmSchema, roleAttributeSchema, rolePermissionSchema, roleSchema, sanitizeEventData, schemaRegistry, schemas, scopeSchema, sessionSchema, sessionTokenSchema, toIdentityPolicyData, trustAnchorSchema, unwrapKeyMaterial, userAttributeSchema, userAuthenticatorSchema, userPermissionSchema, userRoleSchema, userSchema, verifyWebauthnAuthentication, verifyWebauthnRegistration, wrapKeyMaterial };
@@ -1 +1 @@
1
- {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../../../../../src/core/oauth2/authorization/code-request/verifier/module.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,kBAAkB,CAAC;AAWvE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AAExE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACtE,OAAO,KAAK,EACR,uCAAuC,EACvC,gDAAgD,EAChD,6CAA6C,EAChD,MAAM,YAAY,CAAC;AAEpB,qBAAa,sCAAuC,YAAW,uCAAuC;IAClG,SAAS,CAAC,gBAAgB,EAAE,uBAAuB,CAAC;IAEpD,SAAS,CAAC,eAAe,EAAE,sBAAsB,CAAC;gBAEtC,GAAG,EAAE,6CAA6C;IAK9D;;;OAGG;IACG,MAAM,CACR,IAAI,EAAE,8BAA8B,GACpC,OAAO,CAAC,gDAAgD,CAAC;CA6FhE"}
1
+ {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../../../../../src/core/oauth2/authorization/code-request/verifier/module.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,kBAAkB,CAAC;AAWvE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AAExE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACtE,OAAO,KAAK,EACR,uCAAuC,EACvC,gDAAgD,EAChD,6CAA6C,EAChD,MAAM,YAAY,CAAC;AAEpB,qBAAa,sCAAuC,YAAW,uCAAuC;IAClG,SAAS,CAAC,gBAAgB,EAAE,uBAAuB,CAAC;IAEpD,SAAS,CAAC,eAAe,EAAE,sBAAsB,CAAC;gBAEtC,GAAG,EAAE,6CAA6C;IAK9D;;;OAGG;IACG,MAAM,CACR,IAAI,EAAE,8BAA8B,GACpC,OAAO,CAAC,gDAAgD,CAAC;CA0HhE"}
@@ -3,7 +3,7 @@ import "node:url";
3
3
  import.meta.url;
4
4
  import { assertClientGrantAllowed } from "../../../client/grant-type.mjs";
5
5
  import "../../../client/index.mjs";
6
- import { isSimpleURLMatch, isUUID } from "@authup/kit";
6
+ import { isSafeRedirectURLScheme, isSimpleURLMatch, isUUID } from "@authup/kit";
7
7
  import { ScopeName, isClientPublic } from "@authup/core-kit";
8
8
  import { OAuth2ClientError, OAuth2GrantError, OAuth2RequestError, OAuth2ScopeError, OAuth2TokenGrant, hasOAuth2Scopes } from "@authup/specs";
9
9
  //#region src/core/oauth2/authorization/code-request/verifier/module.ts
@@ -34,10 +34,17 @@ var OAuth2AuthorizationCodeRequestVerifier = class {
34
34
  if (data.scope) {
35
35
  if (!hasOAuth2Scopes(scopeNames, data.scope) && !hasOAuth2Scopes(data.scope, ScopeName.GLOBAL)) throw OAuth2ScopeError.insufficient();
36
36
  } else data.scope = scopeNames.join(" ");
37
- const redirectUriVerified = !!data.redirect_uri;
37
+ let redirectUriVerified = false;
38
38
  if (data.redirect_uri) {
39
+ let url;
40
+ try {
41
+ url = new URL(data.redirect_uri);
42
+ } catch {}
43
+ if (url && (url.username || url.password)) throw OAuth2RequestError.malformed("The redirect_uri must not carry userinfo.");
44
+ if (url && !isSafeRedirectURLScheme(data.redirect_uri)) throw OAuth2RequestError.malformed("The redirect_uri scheme is not allowed.");
39
45
  const redirectUris = client.redirectUri.split(",");
40
46
  if (!isSimpleURLMatch(data.redirect_uri, redirectUris)) throw OAuth2GrantError.redirectUriMismatch();
47
+ redirectUriVerified = true;
41
48
  }
42
49
  return {
43
50
  data,
@@ -1 +1 @@
1
- {"version":3,"file":"module.mjs","names":["ScopeName","isClientPublic","isSimpleURLMatch","isUUID","OAuth2ClientError","OAuth2GrantError","OAuth2RequestError","OAuth2ScopeError","OAuth2TokenGrant","hasOAuth2Scopes","assertClientGrantAllowed","OAuth2AuthorizationCodeRequestVerifier","clientRepository","scopeRepository","ctx","verify","data","client_id","invalid","realm_id","malformed","client","findOneByIdOrName","active","inactive","AUTHORIZATION_CODE","redirectUri","redirectUriMismatch","code_challenge","state","id","realmId","scopes","findByClientId","scopeNames","map","scope","name","GLOBAL","insufficient","join","redirectUriVerified","redirect_uri","redirectUris","split"],"sources":["../../../../../../src/core/oauth2/authorization/code-request/verifier/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 { OAuth2AuthorizationCodeRequest } from '@authup/core-kit';\nimport { ScopeName, isClientPublic } from '@authup/core-kit';\nimport { isSimpleURLMatch, isUUID } from '@authup/kit';\nimport {\n OAuth2ClientError,\n OAuth2GrantError,\n OAuth2RequestError,\n OAuth2ScopeError,\n OAuth2TokenGrant,\n hasOAuth2Scopes,\n} from '@authup/specs';\nimport type { IOAuth2ClientRepository } from '../../../client/index.ts';\nimport { assertClientGrantAllowed } from '../../../client/index.ts';\nimport type { IOAuth2ScopeRepository } from '../../../scope/index.ts';\nimport type {\n IOAuth2AuthorizationCodeRequestVerifier,\n OAuth2AuthorizationCodeRequestVerificationResult,\n OAuth2AuthorizationCodeRequestVerifierContext,\n} from './types.ts';\n\nexport class OAuth2AuthorizationCodeRequestVerifier implements IOAuth2AuthorizationCodeRequestVerifier {\n protected clientRepository: IOAuth2ClientRepository;\n\n protected scopeRepository: IOAuth2ScopeRepository;\n\n constructor(ctx: OAuth2AuthorizationCodeRequestVerifierContext) {\n this.clientRepository = ctx.clientRepository;\n this.scopeRepository = ctx.scopeRepository;\n }\n\n /**\n * Verify validated authorization code request.\n * @param data\n */\n async verify(\n data: OAuth2AuthorizationCodeRequest,\n ) : Promise<OAuth2AuthorizationCodeRequestVerificationResult> {\n if (!data.client_id) {\n throw OAuth2ClientError.invalid();\n }\n\n // A name-identified client needs a realm hint to resolve deterministically\n // — client names are only unique per realm (every realm carries the\n // same-named system clients, and a wildcard-provisioned client exists in\n // every realm), so a bare name would bind to an arbitrary realm's client\n // (and, post realm-gate, produce a confusing mismatch against a random\n // realm). Require the hint.\n if (!isUUID(data.client_id) && !data.realm_id) {\n throw OAuth2RequestError.malformed('A realm is required to resolve a client by name.');\n }\n\n const client = await this.clientRepository.findOneByIdOrName(data.client_id, data.realm_id);\n if (!client) {\n throw OAuth2ClientError.invalid();\n }\n\n if (!client.active) {\n throw OAuth2ClientError.inactive();\n }\n\n // A non-null grant_types allowlist must cover the code flow — an RP\n // misconfiguration fails at the front door, not at code redemption.\n assertClientGrantAllowed(client, OAuth2TokenGrant.AUTHORIZATION_CODE);\n\n // OAuth 2.1 posture: a client with no registered redirect_uri pattern\n // can never be matched — reject outright instead of trusting whatever\n // redirect_uri the request carries (the server would otherwise issue a\n // code to an attacker-supplied URI).\n if (!client.redirectUri) {\n throw OAuth2GrantError.redirectUriMismatch();\n }\n\n // Public clients MUST use PKCE (RFC 7636 §4.4.1, OAuth 2.1). Without\n // PKCE a public client's code flow has no second factor — anyone who\n // intercepts the redirect can redeem the code at /token. The code flow\n // is the only supported response type, so this holds unconditionally.\n if (isClientPublic(client) && !data.code_challenge) {\n throw OAuth2RequestError.malformed('PKCE code_challenge is required for public clients.');\n }\n\n // Public clients SHOULD include state to bind the redirect to the\n // initiating session and prevent CSRF (RFC 6749 §10.12). Confidential\n // clients are exempt because the /token exchange already authenticates\n // them via client_secret.\n if (isClientPublic(client) && !data.state) {\n throw OAuth2RequestError.malformed('state is required for public clients in the code flow.');\n }\n\n data.client_id = client.id;\n data.realm_id = client.realmId;\n\n const scopes = await this.scopeRepository.findByClientId(client.id);\n const scopeNames = scopes.map((scope) => scope.name);\n if (data.scope) {\n if (\n !hasOAuth2Scopes(scopeNames, data.scope) &&\n !hasOAuth2Scopes(data.scope, ScopeName.GLOBAL)\n ) {\n throw OAuth2ScopeError.insufficient();\n }\n } else {\n data.scope = scopeNames.join(' ');\n }\n\n // Verified only when the request's redirect_uri matched a registered\n // pattern (pattern-less clients were rejected above). A request without\n // a redirect_uri (e.g. the GET page render) stays unverified so\n // consumers never auto-redirect without a match.\n const redirectUriVerified = !!data.redirect_uri;\n if (data.redirect_uri) {\n const redirectUris = client.redirectUri.split(',');\n\n // isSimpleURLMatch, never isSimpleMatch: the raw matcher treats `/`\n // as its only boundary, so a `*` in a registered pattern's host\n // absorbs a `?`, `#` or `\\` and the pattern's remaining host\n // literal lands in the query of a foreign origin. The code would\n // then be issued to that origin.\n if (!isSimpleURLMatch(data.redirect_uri, redirectUris)) {\n throw OAuth2GrantError.redirectUriMismatch();\n }\n }\n\n return {\n data,\n client,\n scopes,\n redirectUriVerified,\n };\n }\n}\n"],"mappings":";;;;;;;;;AA2BA,IAAaW,yCAAb,MAAaA;CACCC;CAEAC;CAEV,YAAYC,KAAoD;EAC5D,KAAKF,mBAAmBE,IAAIF;EAC5B,KAAKC,kBAAkBC,IAAID;CAC/B;;;;IAMA,MAAME,OACFC,MAC0D;EAC1D,IAAI,CAACA,KAAKC,WACN,MAAMb,kBAAkBc,QAAO;EASnC,IAAI,CAACf,OAAOa,KAAKC,SAAS,KAAK,CAACD,KAAKG,UACjC,MAAMb,mBAAmBc,UAAU,kDAAA;EAGvC,MAAMC,SAAS,MAAM,KAAKT,iBAAiBU,kBAAkBN,KAAKC,WAAWD,KAAKG,QAAQ;EAC1F,IAAI,CAACE,QACD,MAAMjB,kBAAkBc,QAAO;EAGnC,IAAI,CAACG,OAAOE,QACR,MAAMnB,kBAAkBoB,SAAQ;EAKpCd,yBAAyBW,QAAQb,iBAAiBiB,kBAAkB;EAMpE,IAAI,CAACJ,OAAOK,aACR,MAAMrB,iBAAiBsB,oBAAmB;EAO9C,IAAI1B,eAAeoB,MAAAA,KAAW,CAACL,KAAKY,gBAChC,MAAMtB,mBAAmBc,UAAU,qDAAA;EAOvC,IAAInB,eAAeoB,MAAAA,KAAW,CAACL,KAAKa,OAChC,MAAMvB,mBAAmBc,UAAU,wDAAA;EAGvCJ,KAAKC,YAAYI,OAAOS;EACxBd,KAAKG,WAAWE,OAAOU;EAEvB,MAAMC,SAAS,MAAM,KAAKnB,gBAAgBoB,eAAeZ,OAAOS,EAAE;EAClE,MAAMI,aAAaF,OAAOG,KAAKC,UAAUA,MAAMC,IAAI;EACnD,IAAIrB,KAAKoB,OAED;OAAA,CAAC3B,gBAAgByB,YAAYlB,KAAKoB,KAAK,KACvC,CAAC3B,gBAAgBO,KAAKoB,OAAOpC,UAAUsC,MAAM,GAE7C,MAAM/B,iBAAiBgC,aAAY;EAAA,OAGvCvB,KAAKoB,QAAQF,WAAWM,KAAK,GAAA;EAOjC,MAAMC,sBAAsB,CAAC,CAACzB,KAAK0B;EACnC,IAAI1B,KAAK0B,cAAc;GACnB,MAAMC,eAAetB,OAAOK,YAAYkB,MAAM,GAAA;GAO9C,IAAI,CAAC1C,iBAAiBc,KAAK0B,cAAcC,YAAAA,GACrC,MAAMtC,iBAAiBsB,oBAAmB;EAElD;EAEA,OAAO;GACHX;GACAK;GACAW;GACAS;EACJ;CACJ;AACJ"}
1
+ {"version":3,"file":"module.mjs","names":["ScopeName","isClientPublic","isSafeRedirectURLScheme","isSimpleURLMatch","isUUID","OAuth2ClientError","OAuth2GrantError","OAuth2RequestError","OAuth2ScopeError","OAuth2TokenGrant","hasOAuth2Scopes","assertClientGrantAllowed","OAuth2AuthorizationCodeRequestVerifier","clientRepository","scopeRepository","ctx","verify","data","client_id","invalid","realm_id","malformed","client","findOneByIdOrName","active","inactive","AUTHORIZATION_CODE","redirectUri","redirectUriMismatch","code_challenge","state","id","realmId","scopes","findByClientId","scopeNames","map","scope","name","GLOBAL","insufficient","join","redirectUriVerified","redirect_uri","url","URL","username","password","redirectUris","split"],"sources":["../../../../../../src/core/oauth2/authorization/code-request/verifier/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 { OAuth2AuthorizationCodeRequest } from '@authup/core-kit';\nimport { ScopeName, isClientPublic } from '@authup/core-kit';\nimport { isSafeRedirectURLScheme, isSimpleURLMatch, isUUID } from '@authup/kit';\nimport {\n OAuth2ClientError,\n OAuth2GrantError,\n OAuth2RequestError,\n OAuth2ScopeError,\n OAuth2TokenGrant,\n hasOAuth2Scopes,\n} from '@authup/specs';\nimport type { IOAuth2ClientRepository } from '../../../client/index.ts';\nimport { assertClientGrantAllowed } from '../../../client/index.ts';\nimport type { IOAuth2ScopeRepository } from '../../../scope/index.ts';\nimport type {\n IOAuth2AuthorizationCodeRequestVerifier,\n OAuth2AuthorizationCodeRequestVerificationResult,\n OAuth2AuthorizationCodeRequestVerifierContext,\n} from './types.ts';\n\nexport class OAuth2AuthorizationCodeRequestVerifier implements IOAuth2AuthorizationCodeRequestVerifier {\n protected clientRepository: IOAuth2ClientRepository;\n\n protected scopeRepository: IOAuth2ScopeRepository;\n\n constructor(ctx: OAuth2AuthorizationCodeRequestVerifierContext) {\n this.clientRepository = ctx.clientRepository;\n this.scopeRepository = ctx.scopeRepository;\n }\n\n /**\n * Verify validated authorization code request.\n * @param data\n */\n async verify(\n data: OAuth2AuthorizationCodeRequest,\n ) : Promise<OAuth2AuthorizationCodeRequestVerificationResult> {\n if (!data.client_id) {\n throw OAuth2ClientError.invalid();\n }\n\n // A name-identified client needs a realm hint to resolve deterministically\n // — client names are only unique per realm (every realm carries the\n // same-named system clients, and a wildcard-provisioned client exists in\n // every realm), so a bare name would bind to an arbitrary realm's client\n // (and, post realm-gate, produce a confusing mismatch against a random\n // realm). Require the hint.\n if (!isUUID(data.client_id) && !data.realm_id) {\n throw OAuth2RequestError.malformed('A realm is required to resolve a client by name.');\n }\n\n const client = await this.clientRepository.findOneByIdOrName(data.client_id, data.realm_id);\n if (!client) {\n throw OAuth2ClientError.invalid();\n }\n\n if (!client.active) {\n throw OAuth2ClientError.inactive();\n }\n\n // A non-null grant_types allowlist must cover the code flow — an RP\n // misconfiguration fails at the front door, not at code redemption.\n assertClientGrantAllowed(client, OAuth2TokenGrant.AUTHORIZATION_CODE);\n\n // OAuth 2.1 posture: a client with no registered redirect_uri pattern\n // can never be matched — reject outright instead of trusting whatever\n // redirect_uri the request carries (the server would otherwise issue a\n // code to an attacker-supplied URI).\n if (!client.redirectUri) {\n throw OAuth2GrantError.redirectUriMismatch();\n }\n\n // Public clients MUST use PKCE (RFC 7636 §4.4.1, OAuth 2.1). Without\n // PKCE a public client's code flow has no second factor — anyone who\n // intercepts the redirect can redeem the code at /token. The code flow\n // is the only supported response type, so this holds unconditionally.\n if (isClientPublic(client) && !data.code_challenge) {\n throw OAuth2RequestError.malformed('PKCE code_challenge is required for public clients.');\n }\n\n // Public clients SHOULD include state to bind the redirect to the\n // initiating session and prevent CSRF (RFC 6749 §10.12). Confidential\n // clients are exempt because the /token exchange already authenticates\n // them via client_secret.\n if (isClientPublic(client) && !data.state) {\n throw OAuth2RequestError.malformed('state is required for public clients in the code flow.');\n }\n\n data.client_id = client.id;\n data.realm_id = client.realmId;\n\n const scopes = await this.scopeRepository.findByClientId(client.id);\n const scopeNames = scopes.map((scope) => scope.name);\n if (data.scope) {\n if (\n !hasOAuth2Scopes(scopeNames, data.scope) &&\n !hasOAuth2Scopes(data.scope, ScopeName.GLOBAL)\n ) {\n throw OAuth2ScopeError.insufficient();\n }\n } else {\n data.scope = scopeNames.join(' ');\n }\n\n // Verified only when the request's redirect_uri matched a registered\n // pattern (pattern-less clients were rejected above). A request without\n // a redirect_uri (e.g. the GET page render) stays unverified so\n // consumers never auto-redirect without a match.\n //\n // Set from the match itself, never from mere presence: consumers make\n // redirect decisions on this flag, so it must not survive a refactor\n // that turns the throw below into a soft branch.\n let redirectUriVerified = false;\n if (data.redirect_uri) {\n // The matcher canonicalizes the value, which drops userinfo, while\n // the redirect is built from the raw string: `https://u:p@app/cb`\n // matched `https://app/**` and the credential blob then rode the\n // Location header. Refuse it, so the matched value is the\n // navigated value. An unparsable value falls through to the\n // matcher, which rejects it.\n let url : URL | undefined;\n try {\n url = new URL(data.redirect_uri);\n } catch {\n // handled by the matcher below\n }\n if (url && (url.username || url.password)) {\n throw OAuth2RequestError.malformed('The redirect_uri must not carry userinfo.');\n }\n\n // A non-http(s) target is matched verbatim and later navigated\n // client-side (the federated callback's interstitial), so a\n // script-capable scheme would run on the IdP origin.\n if (url && !isSafeRedirectURLScheme(data.redirect_uri)) {\n throw OAuth2RequestError.malformed('The redirect_uri scheme is not allowed.');\n }\n\n const redirectUris = client.redirectUri.split(',');\n\n // isSimpleURLMatch, never isSimpleMatch: the raw matcher treats `/`\n // as its only boundary, so a `*` in a registered pattern's host\n // absorbs a `?`, `#` or `\\` and the pattern's remaining host\n // literal lands in the query of a foreign origin. The code would\n // then be issued to that origin.\n if (!isSimpleURLMatch(data.redirect_uri, redirectUris)) {\n throw OAuth2GrantError.redirectUriMismatch();\n }\n\n redirectUriVerified = true;\n }\n\n return {\n data,\n client,\n scopes,\n redirectUriVerified,\n };\n }\n}\n"],"mappings":";;;;;;;;;AA2BA,IAAaY,yCAAb,MAAaA;CACCC;CAEAC;CAEV,YAAYC,KAAoD;EAC5D,KAAKF,mBAAmBE,IAAIF;EAC5B,KAAKC,kBAAkBC,IAAID;CAC/B;;;;IAMA,MAAME,OACFC,MAC0D;EAC1D,IAAI,CAACA,KAAKC,WACN,MAAMb,kBAAkBc,QAAO;EASnC,IAAI,CAACf,OAAOa,KAAKC,SAAS,KAAK,CAACD,KAAKG,UACjC,MAAMb,mBAAmBc,UAAU,kDAAA;EAGvC,MAAMC,SAAS,MAAM,KAAKT,iBAAiBU,kBAAkBN,KAAKC,WAAWD,KAAKG,QAAQ;EAC1F,IAAI,CAACE,QACD,MAAMjB,kBAAkBc,QAAO;EAGnC,IAAI,CAACG,OAAOE,QACR,MAAMnB,kBAAkBoB,SAAQ;EAKpCd,yBAAyBW,QAAQb,iBAAiBiB,kBAAkB;EAMpE,IAAI,CAACJ,OAAOK,aACR,MAAMrB,iBAAiBsB,oBAAmB;EAO9C,IAAI3B,eAAeqB,MAAAA,KAAW,CAACL,KAAKY,gBAChC,MAAMtB,mBAAmBc,UAAU,qDAAA;EAOvC,IAAIpB,eAAeqB,MAAAA,KAAW,CAACL,KAAKa,OAChC,MAAMvB,mBAAmBc,UAAU,wDAAA;EAGvCJ,KAAKC,YAAYI,OAAOS;EACxBd,KAAKG,WAAWE,OAAOU;EAEvB,MAAMC,SAAS,MAAM,KAAKnB,gBAAgBoB,eAAeZ,OAAOS,EAAE;EAClE,MAAMI,aAAaF,OAAOG,KAAKC,UAAUA,MAAMC,IAAI;EACnD,IAAIrB,KAAKoB,OAED;OAAA,CAAC3B,gBAAgByB,YAAYlB,KAAKoB,KAAK,KACvC,CAAC3B,gBAAgBO,KAAKoB,OAAOrC,UAAUuC,MAAM,GAE7C,MAAM/B,iBAAiBgC,aAAY;EAAA,OAGvCvB,KAAKoB,QAAQF,WAAWM,KAAK,GAAA;EAWjC,IAAIC,sBAAsB;EAC1B,IAAIzB,KAAK0B,cAAc;GAOnB,IAAIC;GACJ,IAAI;IACAA,MAAM,IAAIC,IAAI5B,KAAK0B,YAAY;GACnC,QAAQ,CAER;GACA,IAAIC,QAAQA,IAAIE,YAAYF,IAAIG,WAC5B,MAAMxC,mBAAmBc,UAAU,2CAAA;GAMvC,IAAIuB,OAAO,CAAC1C,wBAAwBe,KAAK0B,YAAY,GACjD,MAAMpC,mBAAmBc,UAAU,yCAAA;GAGvC,MAAM2B,eAAe1B,OAAOK,YAAYsB,MAAM,GAAA;GAO9C,IAAI,CAAC9C,iBAAiBc,KAAK0B,cAAcK,YAAAA,GACrC,MAAM1C,iBAAiBsB,oBAAmB;GAG9Cc,sBAAsB;EAC1B;EAEA,OAAO;GACHzB;GACAK;GACAW;GACAS;EACJ;CACJ;AACJ"}
@@ -12,9 +12,8 @@ var OAuth2AuthorizationStateManager = class {
12
12
  return this.repository.insert(data);
13
13
  }
14
14
  async verify(state, input) {
15
- const payload = await this.repository.findOneById(state);
15
+ const payload = await this.repository.popOneById(state);
16
16
  if (!payload) throw OAuth2RequestError.stateInvalid();
17
- await this.repository.remove(state);
18
17
  if (payload.ip && input.ip !== payload.ip) throw OAuth2RequestError.stateInvalid();
19
18
  if (payload.userAgent && input.userAgent !== payload.userAgent) throw OAuth2RequestError.stateInvalid();
20
19
  return payload;
@@ -1 +1 @@
1
- {"version":3,"file":"module.mjs","names":["OAuth2RequestError","OAuth2AuthorizationStateManager","repository","save","data","insert","verify","state","input","payload","findOneById","stateInvalid","remove","ip","userAgent"],"sources":["../../../../../src/core/oauth2/authorization/state/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 { OAuth2RequestError } from '@authup/specs';\nimport type {\n IOAuth2AuthorizationStateManager,\n IOAuth2AuthorizeStateRepository,\n OAuth2AuthorizationState,\n} from './types.ts';\n\nexport class OAuth2AuthorizationStateManager implements IOAuth2AuthorizationStateManager {\n protected repository: IOAuth2AuthorizeStateRepository;\n\n constructor(repository: IOAuth2AuthorizeStateRepository) {\n this.repository = repository;\n }\n\n async save(data: OAuth2AuthorizationState) : Promise<string> {\n return this.repository.insert(data);\n }\n\n async verify(state: string, input: Partial<OAuth2AuthorizationState>): Promise<OAuth2AuthorizationState> {\n const payload = await this.repository.findOneById(state);\n if (!payload) {\n throw OAuth2RequestError.stateInvalid();\n }\n\n // avoid replay attacks\n await this.repository.remove(state);\n\n if (\n payload.ip &&\n input.ip !== payload.ip\n ) {\n throw OAuth2RequestError.stateInvalid();\n }\n\n if (\n payload.userAgent &&\n input.userAgent !== payload.userAgent\n ) {\n throw OAuth2RequestError.stateInvalid();\n }\n\n return payload;\n }\n}\n"],"mappings":";;;;;AAcA,IAAaC,kCAAb,MAAaA;CACCC;CAEV,YAAYA,YAA6C;EACrD,KAAKA,aAAaA;CACtB;CAEA,MAAMC,KAAKC,MAAkD;EACzD,OAAO,KAAKF,WAAWG,OAAOD,IAAAA;CAClC;CAEA,MAAME,OAAOC,OAAeC,OAA6E;EACrG,MAAMC,UAAU,MAAM,KAAKP,WAAWQ,YAAYH,KAAAA;EAClD,IAAI,CAACE,SACD,MAAMT,mBAAmBW,aAAY;EAIzC,MAAM,KAAKT,WAAWU,OAAOL,KAAAA;EAE7B,IACIE,QAAQI,MACRL,MAAMK,OAAOJ,QAAQI,IAErB,MAAMb,mBAAmBW,aAAY;EAGzC,IACIF,QAAQK,aACRN,MAAMM,cAAcL,QAAQK,WAE5B,MAAMd,mBAAmBW,aAAY;EAGzC,OAAOF;CACX;AACJ"}
1
+ {"version":3,"file":"module.mjs","names":["OAuth2RequestError","OAuth2AuthorizationStateManager","repository","save","data","insert","verify","state","input","payload","popOneById","stateInvalid","ip","userAgent"],"sources":["../../../../../src/core/oauth2/authorization/state/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 { OAuth2RequestError } from '@authup/specs';\nimport type {\n IOAuth2AuthorizationStateManager,\n IOAuth2AuthorizeStateRepository,\n OAuth2AuthorizationState,\n} from './types.ts';\n\nexport class OAuth2AuthorizationStateManager implements IOAuth2AuthorizationStateManager {\n protected repository: IOAuth2AuthorizeStateRepository;\n\n constructor(repository: IOAuth2AuthorizeStateRepository) {\n this.repository = repository;\n }\n\n async save(data: OAuth2AuthorizationState) : Promise<string> {\n return this.repository.insert(data);\n }\n\n async verify(state: string, input: Partial<OAuth2AuthorizationState>): Promise<OAuth2AuthorizationState> {\n // The pop is the replay guard: a state is consumed by whichever\n // callback presents it first, whether or not it then passes the\n // binding checks below.\n const payload = await this.repository.popOneById(state);\n if (!payload) {\n throw OAuth2RequestError.stateInvalid();\n }\n\n if (\n payload.ip &&\n input.ip !== payload.ip\n ) {\n throw OAuth2RequestError.stateInvalid();\n }\n\n if (\n payload.userAgent &&\n input.userAgent !== payload.userAgent\n ) {\n throw OAuth2RequestError.stateInvalid();\n }\n\n return payload;\n }\n}\n"],"mappings":";;;;;AAcA,IAAaC,kCAAb,MAAaA;CACCC;CAEV,YAAYA,YAA6C;EACrD,KAAKA,aAAaA;CACtB;CAEA,MAAMC,KAAKC,MAAkD;EACzD,OAAO,KAAKF,WAAWG,OAAOD,IAAAA;CAClC;CAEA,MAAME,OAAOC,OAAeC,OAA6E;EAIrG,MAAMC,UAAU,MAAM,KAAKP,WAAWQ,WAAWH,KAAAA;EACjD,IAAI,CAACE,SACD,MAAMT,mBAAmBW,aAAY;EAGzC,IACIF,QAAQG,MACRJ,MAAMI,OAAOH,QAAQG,IAErB,MAAMZ,mBAAmBW,aAAY;EAGzC,IACIF,QAAQI,aACRL,MAAMK,cAAcJ,QAAQI,WAE5B,MAAMb,mBAAmBW,aAAY;EAGzC,OAAOF;CACX;AACJ"}
@@ -16,9 +16,12 @@ export type OAuth2AuthorizationState = {
16
16
  userAgent?: string | null;
17
17
  };
18
18
  export interface IOAuth2AuthorizeStateRepository {
19
- findOneById(id: string): Promise<OAuth2AuthorizationState | null>;
20
19
  insert(data: OAuth2AuthorizationState): Promise<string>;
21
- remove(id: string): Promise<void>;
20
+ /**
21
+ * Read and delete in one step, so two callbacks presenting the same
22
+ * state cannot both obtain the payload.
23
+ */
24
+ popOneById(id: string): Promise<OAuth2AuthorizationState | null>;
22
25
  }
23
26
  export interface IOAuth2AuthorizationStateManager {
24
27
  save(data: OAuth2AuthorizationState): Promise<string>;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/core/oauth2/authorization/state/types.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,kBAAkB,CAAC;AAEvE,MAAM,MAAM,4BAA4B,GAAG;IACvC,MAAM,EAAE,MAAM,CAAC;IAKf,UAAU,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACnC,WAAW,CAAC,EAAE,8BAA8B,CAAC;IAC7C;;;;;OAKG;IACH,IAAI,CAAC,EAAE,4BAA4B,CAAC;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC5B,CAAC;AAEF,MAAM,WAAW,+BAA+B;IAC5C,WAAW,CAAC,EAAE,EAAE,MAAM,GAAI,OAAO,CAAC,wBAAwB,GAAG,IAAI,CAAC,CAAC;IAEnE,MAAM,CAAC,IAAI,EAAE,wBAAwB,GAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAEzD,MAAM,CAAC,EAAE,EAAE,MAAM,GAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,gCAAgC;IAC7C,IAAI,CAAC,IAAI,EAAE,wBAAwB,GAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAEvD,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,wBAAwB,CAAC,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAAC;CACtG"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/core/oauth2/authorization/state/types.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,kBAAkB,CAAC;AAEvE,MAAM,MAAM,4BAA4B,GAAG;IACvC,MAAM,EAAE,MAAM,CAAC;IAKf,UAAU,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACnC,WAAW,CAAC,EAAE,8BAA8B,CAAC;IAC7C;;;;;OAKG;IACH,IAAI,CAAC,EAAE,4BAA4B,CAAC;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC5B,CAAC;AAEF,MAAM,WAAW,+BAA+B;IAC5C,MAAM,CAAC,IAAI,EAAE,wBAAwB,GAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAEzD;;;OAGG;IACH,UAAU,CAAC,EAAE,EAAE,MAAM,GAAI,OAAO,CAAC,wBAAwB,GAAG,IAAI,CAAC,CAAC;CACrE;AAED,MAAM,WAAW,gCAAgC;IAC7C,IAAI,CAAC,IAAI,EAAE,wBAAwB,GAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAEvD,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,wBAAwB,CAAC,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAAC;CACtG"}
@@ -1 +1 @@
1
- {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../../src/core/oauth2/end-session/service.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAExD,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC/E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAClE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,KAAK,EACR,wBAAwB,EACxB,uBAAuB,EACvB,sBAAsB,EACtB,8BAA8B,EACjC,MAAM,YAAY,CAAC;AAEpB,qBAAa,uBAAwB,YAAW,wBAAwB;IACpE,SAAS,CAAC,aAAa,EAAE,oBAAoB,CAAC;IAE9C,SAAS,CAAC,cAAc,EAAE,eAAe,CAAC;IAE1C,SAAS,CAAC,gBAAgB,EAAE,uBAAuB,CAAC;IAEpD,SAAS,CAAC,eAAe,EAAE,gBAAgB,CAAC;IAE5C,SAAS,CAAC,YAAY,CAAC,EAAE,aAAa,CAAC;IAEvC,SAAS,CAAC,eAAe,EAAE,MAAM,CAAC;gBAEtB,GAAG,EAAE,8BAA8B;IASzC,MAAM,CAAC,IAAI,EAAE,uBAAuB,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAgHtE,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IA2B/E;;;;;OAKG;IACH,SAAS,CAAC,uBAAuB,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO;IAcvE;;;;OAIG;IACH,SAAS,CAAC,yBAAyB,CAC/B,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACrC,SAAS,EAAE,MAAM,GAClB,OAAO;CAsBb"}
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../../src/core/oauth2/end-session/service.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAExD,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC/E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAClE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,KAAK,EACR,wBAAwB,EACxB,uBAAuB,EACvB,sBAAsB,EACtB,8BAA8B,EACjC,MAAM,YAAY,CAAC;AAEpB,qBAAa,uBAAwB,YAAW,wBAAwB;IACpE,SAAS,CAAC,aAAa,EAAE,oBAAoB,CAAC;IAE9C,SAAS,CAAC,cAAc,EAAE,eAAe,CAAC;IAE1C,SAAS,CAAC,gBAAgB,EAAE,uBAAuB,CAAC;IAEpD,SAAS,CAAC,eAAe,EAAE,gBAAgB,CAAC;IAE5C,SAAS,CAAC,YAAY,CAAC,EAAE,aAAa,CAAC;IAEvC,SAAS,CAAC,eAAe,EAAE,MAAM,CAAC;gBAEtB,GAAG,EAAE,8BAA8B;IASzC,MAAM,CAAC,IAAI,EAAE,uBAAuB,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAgHtE,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IA2B/E;;;;;OAKG;IACH,SAAS,CAAC,uBAAuB,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO;IAcvE;;;;OAIG;IACH,SAAS,CAAC,yBAAyB,CAC/B,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACrC,SAAS,EAAE,MAAM,GAClB,OAAO;CA6Bb"}
@@ -120,6 +120,7 @@ var OAuth2EndSessionService = class {
120
120
  return false;
121
121
  }
122
122
  if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return false;
123
+ if (parsed.username || parsed.password) return false;
123
124
  if (!registered) return false;
124
125
  return isSimpleURLMatch(candidate, registered.split(","));
125
126
  }
@@ -1 +1 @@
1
- {"version":3,"file":"service.mjs","names":["isSimpleURLMatch","isUUID","EventName","EventRefType","EventScope","OAuth2TokenKind","OAuth2EndSessionService","tokenVerifier","sessionManager","clientRepository","realmRepository","eventService","hintGracePeriod","ctx","verify","data","hintVerified","sub","subKind","sessionId","hintRealmId","audiences","id_token_hint","payload","ignoreExpiry","skipActiveCheck","kind","ID_TOKEN","isWithinHintGraceWindow","sub_kind","sid","session_id","undefined","realm_id","aud","Array","isArray","filter","entry","clientId","client_id","length","client","realmKey","realm_name","realm","resolve","findOneByIdOrName","id","expected","includes","clientName","redirectUri","name","post_logout_redirect_uri","isValidPostLogoutRedirect","postLogoutRedirectUri","state","revoke","session","findOneById","record","scope","OAUTH2","LOGOUT","refType","SESSION","refId","actorType","actorId","realmId","exp","nowSeconds","Math","floor","Date","now","registered","candidate","parsed","URL","protocol","split"],"sources":["../../../../src/core/oauth2/end-session/service.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 { isSimpleURLMatch, isUUID } from '@authup/kit';\nimport type { Client, IdentityType } from '@authup/core-kit';\nimport { EventName, EventRefType, EventScope } from '@authup/core-kit';\nimport type { OAuth2TokenPayload } from '@authup/specs';\nimport { OAuth2TokenKind } from '@authup/specs';\nimport type { IEventService, IRealmRepository } from '../../entities/index.ts';\nimport type { ISessionManager } from '../../authentication/index.ts';\nimport type { IOAuth2ClientRepository } from '../client/index.ts';\nimport type { IOAuth2TokenVerifier } from '../token/index.ts';\nimport type {\n IOAuth2EndSessionService,\n OAuth2EndSessionRequest,\n OAuth2EndSessionResult,\n OAuth2EndSessionServiceContext,\n} from './types.ts';\n\nexport class OAuth2EndSessionService implements IOAuth2EndSessionService {\n protected tokenVerifier: IOAuth2TokenVerifier;\n\n protected sessionManager: ISessionManager;\n\n protected clientRepository: IOAuth2ClientRepository;\n\n protected realmRepository: IRealmRepository;\n\n protected eventService?: IEventService;\n\n protected hintGracePeriod: number;\n\n constructor(ctx: OAuth2EndSessionServiceContext) {\n this.tokenVerifier = ctx.tokenVerifier;\n this.sessionManager = ctx.sessionManager;\n this.clientRepository = ctx.clientRepository;\n this.realmRepository = ctx.realmRepository;\n this.eventService = ctx.eventService;\n this.hintGracePeriod = ctx.hintGracePeriod ?? 0;\n }\n\n async verify(data: OAuth2EndSessionRequest): Promise<OAuth2EndSessionResult> {\n let hintVerified = false;\n let sub: string | undefined;\n let subKind: string | undefined;\n let sessionId: string | undefined;\n let hintRealmId: string | undefined;\n let audiences: string[] = [];\n\n if (data.id_token_hint) {\n try {\n // Signature + kind are the anchors; exp is skipped (a logout hint\n // is routinely expired) and the cache blocklist is irrelevant.\n const payload = await this.tokenVerifier.verify(data.id_token_hint, {\n ignoreExpiry: true,\n skipActiveCheck: true,\n });\n\n // MUST be an id_token — access/refresh tokens also carry\n // session_id, so accepting them would let a leaked access token\n // act as a logout weapon.\n if (payload.kind === OAuth2TokenKind.ID_TOKEN && this.isWithinHintGraceWindow(payload)) {\n hintVerified = true;\n sub = payload.sub;\n subKind = payload.sub_kind;\n sessionId = payload.sid ?? payload.session_id ?? undefined;\n hintRealmId = payload.realm_id ?? undefined;\n // aud may be a single string or an array (JWT spec).\n const { aud } = payload;\n if (Array.isArray(aud)) {\n audiences = aud.filter((entry): entry is string => typeof entry === 'string');\n } else if (typeof aud === 'string') {\n audiences = [aud];\n }\n }\n } catch {\n // forged / unverifiable hint → stays unverified (no revoke)\n }\n }\n\n // Resolve a single client for the aud cross-check and redirect\n // validation: the request's client_id, else the hint's sole audience\n // (ambiguous for a multi-aud hint).\n const clientId = data.client_id ?? (audiences.length === 1 ? audiences[0] : undefined);\n\n let client: Client | null = null;\n if (clientId) {\n // Scope a name-identified client to the request's realm hint; a\n // VERIFIED hint's own realm claim serves as fallback (claims from an\n // unverified hint are never trusted). No master fallback here\n // (unlike /token): a bogus hint must NOT silently resolve a\n // same-named client in master → null realm.\n const realmKey = data.realm_id ?? data.realm_name ?? (hintVerified ? hintRealmId : undefined);\n const realm = await this.realmRepository.resolve(realmKey, false);\n\n // Fail closed instead of dropping the realm predicate: a supplied\n // realm key that does not resolve never degrades to an unscoped\n // lookup, and a NAME-form client_id without any realm key is\n // ambiguous (client names are only unique per realm, and every\n // realm carries the same-named system clients — same rule as the\n // /authorize verifier). A UUID is globally unique and needs no\n // scope; the sole-aud-derived clientId is always a UUID.\n if (realm) {\n client = await this.clientRepository.findOneByIdOrName(clientId, realm.id);\n } else if (!realmKey && isUUID(clientId)) {\n client = await this.clientRepository.findOneByIdOrName(clientId);\n }\n }\n\n // aud/client_id cross-check: when the request supplies a client_id on a\n // verified hint, it MUST denote one of the hint's audiences. The aud is\n // the client UUID, so a name-form client_id is compared via its resolved\n // client's id. Fail closed: an aud-less hint, or a name that did not\n // resolve, counts as unverified.\n if (hintVerified && data.client_id) {\n const expected = isUUID(data.client_id) ? data.client_id : client?.id;\n if (audiences.length === 0 || !expected || !audiences.includes(expected)) {\n hintVerified = false;\n sub = undefined;\n subKind = undefined;\n sessionId = undefined;\n }\n }\n\n let clientName: string | undefined;\n let redirectUri: string | undefined;\n\n // Redirect validation is independent of hint verification — the\n // click-gated confirm page (unverified hint / hint-less request) still\n // honors a registered post-logout redirect.\n if (client) {\n clientName = client.name;\n if (\n data.post_logout_redirect_uri &&\n this.isValidPostLogoutRedirect(client.postLogoutRedirectUri, data.post_logout_redirect_uri)\n ) {\n redirectUri = data.post_logout_redirect_uri;\n }\n }\n\n return {\n hintVerified,\n ...(hintVerified ? {\n sub,\n subKind,\n sessionId,\n } : {}),\n ...(clientId ? { clientId } : {}),\n ...(clientName ? { clientName } : {}),\n ...(redirectUri ? { redirectUri, ...(data.state ? { state: data.state } : {}) } : {}),\n };\n }\n\n async revoke(sessionId: string, sub: string, subKind: string): Promise<boolean> {\n const session = await this.sessionManager.findOneById(sessionId);\n if (!session) {\n return false;\n }\n\n // Never revoke a session that does not belong to the hint's subject.\n if (session.sub !== sub || session.subKind !== subKind) {\n return false;\n }\n\n await this.sessionManager.revoke(sessionId);\n\n await this.eventService?.record({\n scope: EventScope.OAUTH2,\n name: EventName.LOGOUT,\n refType: EventRefType.SESSION,\n refId: sessionId,\n sessionId,\n actorType: session.subKind as `${IdentityType}`,\n actorId: session.sub,\n realmId: session.realmId,\n });\n\n return true;\n }\n\n /**\n * With a positive `hintGracePeriod`, an expired hint is honored only within\n * the window past `exp` — beyond it a leaked id_token stops being a\n * replayable remote logout (the hint counts as unverified; the click-gated\n * confirm page still works). 0 keeps spec/Keycloak parity: any expired hint.\n */\n protected isWithinHintGraceWindow(payload: OAuth2TokenPayload): boolean {\n if (this.hintGracePeriod <= 0) {\n return true;\n }\n\n // a bounded window without an exp claim to judge against fails closed\n if (typeof payload.exp !== 'number') {\n return false;\n }\n\n const nowSeconds = Math.floor(Date.now() / 1000);\n return nowSeconds - payload.exp <= this.hintGracePeriod;\n }\n\n /**\n * A post-logout redirect is honored only when it is an absolute http(s) URL\n * that matches a registered client redirect pattern (defense against open\n * redirects to attacker-controlled URLs).\n */\n protected isValidPostLogoutRedirect(\n registered: string | null | undefined,\n candidate: string,\n ): boolean {\n let parsed: URL;\n try {\n parsed = new URL(candidate);\n } catch {\n return false;\n }\n\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n return false;\n }\n\n if (!registered) {\n return false;\n }\n\n // isSimpleURLMatch, never isSimpleMatch: matched against the raw\n // string, a `*` in a registered pattern's host absorbs a `?`, `#` or\n // `\\`, which would turn this endpoint into a server-issued open\n // redirect carrying `state`.\n return isSimpleURLMatch(candidate, registered.split(','));\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"service.mjs","names":["isSimpleURLMatch","isUUID","EventName","EventRefType","EventScope","OAuth2TokenKind","OAuth2EndSessionService","tokenVerifier","sessionManager","clientRepository","realmRepository","eventService","hintGracePeriod","ctx","verify","data","hintVerified","sub","subKind","sessionId","hintRealmId","audiences","id_token_hint","payload","ignoreExpiry","skipActiveCheck","kind","ID_TOKEN","isWithinHintGraceWindow","sub_kind","sid","session_id","undefined","realm_id","aud","Array","isArray","filter","entry","clientId","client_id","length","client","realmKey","realm_name","realm","resolve","findOneByIdOrName","id","expected","includes","clientName","redirectUri","name","post_logout_redirect_uri","isValidPostLogoutRedirect","postLogoutRedirectUri","state","revoke","session","findOneById","record","scope","OAUTH2","LOGOUT","refType","SESSION","refId","actorType","actorId","realmId","exp","nowSeconds","Math","floor","Date","now","registered","candidate","parsed","URL","protocol","username","password","split"],"sources":["../../../../src/core/oauth2/end-session/service.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 { isSimpleURLMatch, isUUID } from '@authup/kit';\nimport type { Client, IdentityType } from '@authup/core-kit';\nimport { EventName, EventRefType, EventScope } from '@authup/core-kit';\nimport type { OAuth2TokenPayload } from '@authup/specs';\nimport { OAuth2TokenKind } from '@authup/specs';\nimport type { IEventService, IRealmRepository } from '../../entities/index.ts';\nimport type { ISessionManager } from '../../authentication/index.ts';\nimport type { IOAuth2ClientRepository } from '../client/index.ts';\nimport type { IOAuth2TokenVerifier } from '../token/index.ts';\nimport type {\n IOAuth2EndSessionService,\n OAuth2EndSessionRequest,\n OAuth2EndSessionResult,\n OAuth2EndSessionServiceContext,\n} from './types.ts';\n\nexport class OAuth2EndSessionService implements IOAuth2EndSessionService {\n protected tokenVerifier: IOAuth2TokenVerifier;\n\n protected sessionManager: ISessionManager;\n\n protected clientRepository: IOAuth2ClientRepository;\n\n protected realmRepository: IRealmRepository;\n\n protected eventService?: IEventService;\n\n protected hintGracePeriod: number;\n\n constructor(ctx: OAuth2EndSessionServiceContext) {\n this.tokenVerifier = ctx.tokenVerifier;\n this.sessionManager = ctx.sessionManager;\n this.clientRepository = ctx.clientRepository;\n this.realmRepository = ctx.realmRepository;\n this.eventService = ctx.eventService;\n this.hintGracePeriod = ctx.hintGracePeriod ?? 0;\n }\n\n async verify(data: OAuth2EndSessionRequest): Promise<OAuth2EndSessionResult> {\n let hintVerified = false;\n let sub: string | undefined;\n let subKind: string | undefined;\n let sessionId: string | undefined;\n let hintRealmId: string | undefined;\n let audiences: string[] = [];\n\n if (data.id_token_hint) {\n try {\n // Signature + kind are the anchors; exp is skipped (a logout hint\n // is routinely expired) and the cache blocklist is irrelevant.\n const payload = await this.tokenVerifier.verify(data.id_token_hint, {\n ignoreExpiry: true,\n skipActiveCheck: true,\n });\n\n // MUST be an id_token — access/refresh tokens also carry\n // session_id, so accepting them would let a leaked access token\n // act as a logout weapon.\n if (payload.kind === OAuth2TokenKind.ID_TOKEN && this.isWithinHintGraceWindow(payload)) {\n hintVerified = true;\n sub = payload.sub;\n subKind = payload.sub_kind;\n sessionId = payload.sid ?? payload.session_id ?? undefined;\n hintRealmId = payload.realm_id ?? undefined;\n // aud may be a single string or an array (JWT spec).\n const { aud } = payload;\n if (Array.isArray(aud)) {\n audiences = aud.filter((entry): entry is string => typeof entry === 'string');\n } else if (typeof aud === 'string') {\n audiences = [aud];\n }\n }\n } catch {\n // forged / unverifiable hint → stays unverified (no revoke)\n }\n }\n\n // Resolve a single client for the aud cross-check and redirect\n // validation: the request's client_id, else the hint's sole audience\n // (ambiguous for a multi-aud hint).\n const clientId = data.client_id ?? (audiences.length === 1 ? audiences[0] : undefined);\n\n let client: Client | null = null;\n if (clientId) {\n // Scope a name-identified client to the request's realm hint; a\n // VERIFIED hint's own realm claim serves as fallback (claims from an\n // unverified hint are never trusted). No master fallback here\n // (unlike /token): a bogus hint must NOT silently resolve a\n // same-named client in master → null realm.\n const realmKey = data.realm_id ?? data.realm_name ?? (hintVerified ? hintRealmId : undefined);\n const realm = await this.realmRepository.resolve(realmKey, false);\n\n // Fail closed instead of dropping the realm predicate: a supplied\n // realm key that does not resolve never degrades to an unscoped\n // lookup, and a NAME-form client_id without any realm key is\n // ambiguous (client names are only unique per realm, and every\n // realm carries the same-named system clients — same rule as the\n // /authorize verifier). A UUID is globally unique and needs no\n // scope; the sole-aud-derived clientId is always a UUID.\n if (realm) {\n client = await this.clientRepository.findOneByIdOrName(clientId, realm.id);\n } else if (!realmKey && isUUID(clientId)) {\n client = await this.clientRepository.findOneByIdOrName(clientId);\n }\n }\n\n // aud/client_id cross-check: when the request supplies a client_id on a\n // verified hint, it MUST denote one of the hint's audiences. The aud is\n // the client UUID, so a name-form client_id is compared via its resolved\n // client's id. Fail closed: an aud-less hint, or a name that did not\n // resolve, counts as unverified.\n if (hintVerified && data.client_id) {\n const expected = isUUID(data.client_id) ? data.client_id : client?.id;\n if (audiences.length === 0 || !expected || !audiences.includes(expected)) {\n hintVerified = false;\n sub = undefined;\n subKind = undefined;\n sessionId = undefined;\n }\n }\n\n let clientName: string | undefined;\n let redirectUri: string | undefined;\n\n // Redirect validation is independent of hint verification — the\n // click-gated confirm page (unverified hint / hint-less request) still\n // honors a registered post-logout redirect.\n if (client) {\n clientName = client.name;\n if (\n data.post_logout_redirect_uri &&\n this.isValidPostLogoutRedirect(client.postLogoutRedirectUri, data.post_logout_redirect_uri)\n ) {\n redirectUri = data.post_logout_redirect_uri;\n }\n }\n\n return {\n hintVerified,\n ...(hintVerified ? {\n sub,\n subKind,\n sessionId,\n } : {}),\n ...(clientId ? { clientId } : {}),\n ...(clientName ? { clientName } : {}),\n ...(redirectUri ? { redirectUri, ...(data.state ? { state: data.state } : {}) } : {}),\n };\n }\n\n async revoke(sessionId: string, sub: string, subKind: string): Promise<boolean> {\n const session = await this.sessionManager.findOneById(sessionId);\n if (!session) {\n return false;\n }\n\n // Never revoke a session that does not belong to the hint's subject.\n if (session.sub !== sub || session.subKind !== subKind) {\n return false;\n }\n\n await this.sessionManager.revoke(sessionId);\n\n await this.eventService?.record({\n scope: EventScope.OAUTH2,\n name: EventName.LOGOUT,\n refType: EventRefType.SESSION,\n refId: sessionId,\n sessionId,\n actorType: session.subKind as `${IdentityType}`,\n actorId: session.sub,\n realmId: session.realmId,\n });\n\n return true;\n }\n\n /**\n * With a positive `hintGracePeriod`, an expired hint is honored only within\n * the window past `exp` — beyond it a leaked id_token stops being a\n * replayable remote logout (the hint counts as unverified; the click-gated\n * confirm page still works). 0 keeps spec/Keycloak parity: any expired hint.\n */\n protected isWithinHintGraceWindow(payload: OAuth2TokenPayload): boolean {\n if (this.hintGracePeriod <= 0) {\n return true;\n }\n\n // a bounded window without an exp claim to judge against fails closed\n if (typeof payload.exp !== 'number') {\n return false;\n }\n\n const nowSeconds = Math.floor(Date.now() / 1000);\n return nowSeconds - payload.exp <= this.hintGracePeriod;\n }\n\n /**\n * A post-logout redirect is honored only when it is an absolute http(s) URL\n * that matches a registered client redirect pattern (defense against open\n * redirects to attacker-controlled URLs).\n */\n protected isValidPostLogoutRedirect(\n registered: string | null | undefined,\n candidate: string,\n ): boolean {\n let parsed: URL;\n try {\n parsed = new URL(candidate);\n } catch {\n return false;\n }\n\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n return false;\n }\n\n // The matcher canonicalizes the candidate (userinfo dropped) while the\n // redirect is built from the raw value, so a credential blob would\n // ride the Location header (#3455).\n if (parsed.username || parsed.password) {\n return false;\n }\n\n if (!registered) {\n return false;\n }\n\n // isSimpleURLMatch, never isSimpleMatch: matched against the raw\n // string, a `*` in a registered pattern's host absorbs a `?`, `#` or\n // `\\`, which would turn this endpoint into a server-issued open\n // redirect carrying `state`.\n return isSimpleURLMatch(candidate, registered.split(','));\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export * from './module.ts';
2
+ export * from './types.ts';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/core/oauth2/federated-login/index.ts"],"names":[],"mappings":"AAOA,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC"}
@@ -0,0 +1,6 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ import { OAuth2FederatedLoginRefusal } from "./types.mjs";
5
+ import { OAuth2FederatedLoginService } from "./module.mjs";
6
+ export { OAuth2FederatedLoginRefusal, OAuth2FederatedLoginService };