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

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 (60) hide show
  1. package/dist/adapters/http/controllers/entities/identity-provider/module.d.ts +10 -4
  2. package/dist/adapters/http/controllers/entities/identity-provider/module.d.ts.map +1 -1
  3. package/dist/adapters/http/controllers/entities/identity-provider/module.mjs +125 -48
  4. package/dist/adapters/http/controllers/entities/identity-provider/module.mjs.map +1 -1
  5. package/dist/adapters/http/controllers/entities/identity-provider/types.d.ts +2 -2
  6. package/dist/adapters/http/controllers/entities/identity-provider/types.d.ts.map +1 -1
  7. package/dist/app/index.mjs +3 -1
  8. package/dist/app/modules/http/modules/controller.mjs +2 -1
  9. package/dist/app/modules/http/modules/controller.mjs.map +1 -1
  10. package/dist/app/modules/identity/constants.d.ts +2 -1
  11. package/dist/app/modules/identity/constants.d.ts.map +1 -1
  12. package/dist/app/modules/identity/constants.mjs +1 -0
  13. package/dist/app/modules/identity/constants.mjs.map +1 -1
  14. package/dist/app/modules/identity/index.mjs +3 -1
  15. package/dist/app/modules/identity/module.d.ts.map +1 -1
  16. package/dist/app/modules/identity/module.mjs +7 -0
  17. package/dist/app/modules/identity/module.mjs.map +1 -1
  18. package/dist/app/modules/identity/repositories/constants.d.ts +4 -0
  19. package/dist/app/modules/identity/repositories/constants.d.ts.map +1 -0
  20. package/dist/app/modules/identity/repositories/constants.mjs +12 -0
  21. package/dist/app/modules/identity/repositories/constants.mjs.map +1 -0
  22. package/dist/app/modules/identity/repositories/index.d.ts +1 -0
  23. package/dist/app/modules/identity/repositories/index.d.ts.map +1 -1
  24. package/dist/app/modules/identity/repositories/index.mjs +3 -1
  25. package/dist/app/modules/identity/repositories/provider/index.d.ts +1 -0
  26. package/dist/app/modules/identity/repositories/provider/index.d.ts.map +1 -1
  27. package/dist/app/modules/identity/repositories/provider/index.mjs +2 -1
  28. package/dist/app/modules/identity/repositories/provider/link.d.ts +9 -0
  29. package/dist/app/modules/identity/repositories/provider/link.d.ts.map +1 -0
  30. package/dist/app/modules/identity/repositories/provider/link.mjs +34 -0
  31. package/dist/app/modules/identity/repositories/provider/link.mjs.map +1 -0
  32. package/dist/app/modules/index.mjs +3 -1
  33. package/dist/core/identity/index.mjs +2 -1
  34. package/dist/core/identity/provider/account/constants.d.ts +10 -0
  35. package/dist/core/identity/provider/account/constants.d.ts.map +1 -0
  36. package/dist/core/identity/provider/account/constants.mjs +16 -0
  37. package/dist/core/identity/provider/account/constants.mjs.map +1 -0
  38. package/dist/core/identity/provider/account/index.d.ts +1 -0
  39. package/dist/core/identity/provider/account/index.d.ts.map +1 -1
  40. package/dist/core/identity/provider/account/index.mjs +2 -1
  41. package/dist/core/identity/provider/account/types.d.ts +35 -0
  42. package/dist/core/identity/provider/account/types.d.ts.map +1 -1
  43. package/dist/core/identity/provider/authentication/protocols/oauth2/module.d.ts +18 -0
  44. package/dist/core/identity/provider/authentication/protocols/oauth2/module.d.ts.map +1 -1
  45. package/dist/core/identity/provider/authentication/protocols/oauth2/module.mjs +55 -2
  46. package/dist/core/identity/provider/authentication/protocols/oauth2/module.mjs.map +1 -1
  47. package/dist/core/identity/provider/authentication/protocols/oauth2/types.d.ts +2 -0
  48. package/dist/core/identity/provider/authentication/protocols/oauth2/types.d.ts.map +1 -1
  49. package/dist/core/identity/provider/authentication/protocols/open-id/module.d.ts +0 -3
  50. package/dist/core/identity/provider/authentication/protocols/open-id/module.d.ts.map +1 -1
  51. package/dist/core/identity/provider/authentication/protocols/open-id/module.mjs +0 -17
  52. package/dist/core/identity/provider/authentication/protocols/open-id/module.mjs.map +1 -1
  53. package/dist/core/identity/provider/index.mjs +2 -1
  54. package/dist/core/index.mjs +2 -1
  55. package/dist/core/oauth2/authorization/code-request/verifier/module.d.ts.map +1 -1
  56. package/dist/core/oauth2/authorization/code-request/verifier/module.mjs +2 -1
  57. package/dist/core/oauth2/authorization/code-request/verifier/module.mjs.map +1 -1
  58. package/dist/index.mjs +3 -1
  59. package/dist/swagger.json +116 -16
  60. package/package.json +12 -12
@@ -1,16 +1,16 @@
1
1
  import type { IAppEvent } from 'routup';
2
- import type { IdentityProvider } from '@authup/core-kit';
2
+ import type { IdentityProvider, IdentityProviderAccount } from '@authup/core-kit';
3
3
  import type { Logger } from '@authup/server-kit';
4
- import type { EntityCollectionResponse, EntityRecordResponse, IdentityProviderCreatePayload, IdentityProviderLinkRequestResponse, IdentityProviderSavePayload, IdentityProviderUpdatePayload } from '@authup/core-http-kit';
5
- import type { IEventService, IIdentityProviderAccountManager, IIdentityProviderRepository, IOAuth2AccessPolicyEvaluator, IOAuth2AuthorizationCodeIssuer, IOAuth2AuthorizationCodeRequestVerifier, IOAuth2AuthorizationStateManager, IOAuth2ClientRepository, IRealmRepository } from '../../../../../core/index.ts';
4
+ import type { EntityCollectionResponse, EntityRecordResponse, IdentityProviderCreatePayload, IdentityProviderLinkConfirmPayload, IdentityProviderLinkRequestResponse, IdentityProviderSavePayload, IdentityProviderUpdatePayload } from '@authup/core-http-kit';
5
+ import type { IEventService, IIdentityProviderAccountLinkStore, IIdentityProviderAccountManager, IIdentityProviderRepository, IOAuth2AccessPolicyEvaluator, IOAuth2AuthorizationCodeIssuer, IOAuth2AuthorizationCodeRequestVerifier, IOAuth2AuthorizationStateManager, IRealmRepository } from '../../../../../core/index.ts';
6
6
  import { OAuth2AuthorizationCodeRequestValidator } from '../../../../../core/index.ts';
7
7
  import type { IdentityProviderControllerContext, IdentityProviderControllerOptions } from './types.ts';
8
8
  export declare class IdentityProviderController {
9
9
  protected options: IdentityProviderControllerOptions;
10
10
  protected repository: IIdentityProviderRepository;
11
11
  protected realmRepository: IRealmRepository;
12
- protected clientRepository: IOAuth2ClientRepository;
13
12
  protected accountManager: IIdentityProviderAccountManager;
13
+ protected linkStore: IIdentityProviderAccountLinkStore;
14
14
  protected codeRequestVerifier: IOAuth2AuthorizationCodeRequestVerifier;
15
15
  protected codeRequestValidator: OAuth2AuthorizationCodeRequestValidator;
16
16
  protected stateManager: IOAuth2AuthorizationStateManager;
@@ -36,6 +36,12 @@ export declare class IdentityProviderController {
36
36
  * browser lands back on the account console with a marker param.
37
37
  */
38
38
  private completeLink;
39
+ /**
40
+ * The bearer-authenticated half of the link (issue #3439): the account
41
+ * row is written here, for the AUTHENTICATED user, never in the callback.
42
+ */
43
+ confirmLink(_id: string, _data: IdentityProviderLinkConfirmPayload, event: IAppEvent): Promise<EntityRecordResponse<IdentityProviderAccount>>;
44
+ private pickIdentityCandidate;
39
45
  private buildHostedAuthorizeURL;
40
46
  private resolve;
41
47
  private buildProviderAuthenticator;
@@ -1 +1 @@
1
- {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../../../../../src/adapters/http/controllers/entities/identity-provider/module.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAMxC,OAAO,KAAK,EACR,gBAAgB,EAInB,MAAM,kBAAkB,CAAC;AAc1B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAKjD,OAAO,KAAK,EACR,wBAAwB,EACxB,oBAAoB,EACpB,6BAA6B,EAC7B,mCAAmC,EACnC,2BAA2B,EAC3B,6BAA6B,EAChC,MAAM,uBAAuB,CAAC;AAE/B,OAAO,KAAK,EACR,aAAa,EACb,+BAA+B,EAC/B,2BAA2B,EAC3B,4BAA4B,EAC5B,8BAA8B,EAC9B,uCAAuC,EACvC,gCAAgC,EAChC,uBAAuB,EACvB,gBAAgB,EAGnB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACH,uCAAuC,EAQ1C,MAAM,8BAA8B,CAAC;AAYtC,OAAO,KAAK,EAAE,iCAAiC,EAAE,iCAAiC,EAAE,MAAM,YAAY,CAAC;AAEvG,qBAEa,0BAA0B;IACnC,SAAS,CAAC,OAAO,EAAE,iCAAiC,CAAC;IAErD,SAAS,CAAC,UAAU,EAAE,2BAA2B,CAAC;IAElD,SAAS,CAAC,eAAe,EAAE,gBAAgB,CAAC;IAE5C,SAAS,CAAC,gBAAgB,EAAE,uBAAuB,CAAC;IAEpD,SAAS,CAAC,cAAc,EAAE,+BAA+B,CAAC;IAE1D,SAAS,CAAC,mBAAmB,EAAG,uCAAuC,CAAC;IAExE,SAAS,CAAC,oBAAoB,EAAG,uCAAuC,CAAC;IAEzE,SAAS,CAAC,YAAY,EAAG,gCAAgC,CAAC;IAE1D,SAAS,CAAC,UAAU,EAAG,8BAA8B,CAAC;IAEtD,SAAS,CAAC,qBAAqB,CAAC,EAAG,4BAA4B,CAAC;IAEhE,SAAS,CAAC,YAAY,CAAC,EAAG,aAAa,CAAC;IAExC,SAAS,CAAC,MAAM,CAAC,EAAG,MAAM,CAAC;gBAIf,GAAG,EAAE,iCAAiC;IAkB5C,YAAY,CACF,KAAK,EAAE,SAAS,GAC7B,OAAO,CAAC,wBAAwB,CAAC,gBAAgB,CAAC,CAAC;IAuChD,WAAW,CACA,EAAE,EAAE,MAAM,EACX,KAAK,EAAE,SAAS,GAC7B,OAAO,CAAC,oBAAoB,CAAC,gBAAgB,CAAC,CAAC;IA0B5C,YAAY,CACD,EAAE,EAAE,MAAM,EACd,IAAI,EAAE,6BAA6B,EAChC,KAAK,EAAE,SAAS,GAC5B,OAAO,CAAC,oBAAoB,CAAC,gBAAgB,CAAC,CAAC;IAK7C,GAAG,CACQ,EAAE,EAAE,MAAM,EACd,IAAI,EAAE,2BAA2B,EAC9B,KAAK,EAAE,SAAS,GAC5B,OAAO,CAAC,oBAAoB,CAAC,gBAAgB,CAAC,CAAC;IAK7C,YAAY,CACD,EAAE,EAAE,MAAM,EACX,KAAK,EAAE,SAAS,GAC5B,OAAO,CAAC,oBAAoB,CAAC,gBAAgB,CAAC,CAAC;IA6B7C,WAAW,CACJ,IAAI,EAAE,6BAA6B,EAChC,KAAK,EAAE,SAAS,GAC5B,OAAO,CAAC,oBAAoB,CAAC,gBAAgB,CAAC,CAAC;IAO7C,YAAY,CACD,GAAG,EAAE,MAAM,EACZ,KAAK,EAAE,SAAS;IA0C1B,WAAW,CACA,GAAG,EAAE,MAAM,EACZ,KAAK,EAAE,SAAS,GAC5B,OAAO,CAAC,mCAAmC,CAAC;IA6B1C,WAAW,CACA,GAAG,EAAE,MAAM,EACZ,KAAK,EAAE,SAAS;YA4GlB,KAAK;IA+FnB;;;;;OAKG;YACW,YAAY;IAqF1B,OAAO,CAAC,uBAAuB;YAmBjB,OAAO;IAYrB,OAAO,CAAC,0BAA0B;YAcpB,sBAAsB;YAWtB,wBAAwB;CAWzC"}
1
+ {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../../../../../src/adapters/http/controllers/entities/identity-provider/module.ts"],"names":[],"mappings":"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"}
@@ -6,7 +6,6 @@ import { identityProviderSchema } from "../../../../../core/entities/identity-pr
6
6
  import { decodeQuery } from "../../../../../core/query/module.mjs";
7
7
  import { OAuth2AuthorizationCodeRequestValidator } from "../../../../../core/oauth2/authorization/code-request/validator.mjs";
8
8
  import { toIdentityPolicyData } from "../../../../../core/identity/permission/identity-policy-data.mjs";
9
- import { isIdentityProviderAccountAlreadyLinkedError } from "../../../../../core/identity/provider/account/error.mjs";
10
9
  import { createIdentityProviderOAuth2Authenticator } from "../../../../../core/identity/provider/authentication/factory.mjs";
11
10
  import "../../../../../core/index.mjs";
12
11
  import { useRequestPermissionEvaluator } from "../../../request/permission/helper.mjs";
@@ -15,16 +14,17 @@ import { buildActorContext } from "../../../request/helpers/actor.mjs";
15
14
  import { getBodyRealmID } from "../../../request/helpers/body-realm-id.mjs";
16
15
  import { getRequestParamID, useRequestParamID } from "../../../request/helpers/param-id.mjs";
17
16
  import { applyRouteRealmIDToBody, getRequestRealmID } from "../../../request/helpers/realm-id.mjs";
17
+ import { useRequestEventContext } from "../../../request/event-context.mjs";
18
18
  import "../../../request/index.mjs";
19
19
  import { ForceLoggedInMiddleware } from "../../../middleware/built-in/loggedin/module.mjs";
20
20
  import { describeError } from "../../../../../utils/error.mjs";
21
21
  import { resolveURL } from "../../../../../utils/url.mjs";
22
22
  import "../../../../../utils/index.mjs";
23
23
  import "../../../middleware/index.mjs";
24
- import { ValidatorGroup, base64URLDecode, isUUID } from "@authup/kit";
24
+ import { ValidatorGroup, base64URLDecode, isObject, isUUID } from "@authup/kit";
25
25
  import { EventName, EventRefType, EventScope, IdentityProviderAttributesValidator, IdentityProviderValidator, IdentityType, PermissionName, SessionAuthMethod, isOAuth2IdentityProvider, isOpenIDIdentityProvider } from "@authup/core-kit";
26
26
  import { BuiltInPolicyType, definePolicyData } from "@authup/access";
27
- import { BadRequestError, EntityNotFoundError } from "@authup/errors";
27
+ import { BadRequestError, EntityInactiveError, EntityNotFoundError } from "@authup/errors";
28
28
  import { OAuth2ErrorCode, OAuth2RequestError } from "@authup/specs";
29
29
  import { URL } from "node:url";
30
30
  import { getRequestHeader, getRequestIP, sendRedirect } from "routup";
@@ -50,8 +50,8 @@ var IdentityProviderController = class {
50
50
  options;
51
51
  repository;
52
52
  realmRepository;
53
- clientRepository;
54
53
  accountManager;
54
+ linkStore;
55
55
  codeRequestVerifier;
56
56
  codeRequestValidator;
57
57
  stateManager;
@@ -63,8 +63,8 @@ var IdentityProviderController = class {
63
63
  this.options = ctx.options;
64
64
  this.repository = ctx.repository;
65
65
  this.realmRepository = ctx.realmRepository;
66
- this.clientRepository = ctx.clientRepository;
67
66
  this.accountManager = ctx.accountManager;
67
+ this.linkStore = ctx.linkStore;
68
68
  this.codeIssuer = ctx.codeIssuer;
69
69
  this.codeRequestVerifier = ctx.codeRequestVerifier;
70
70
  this.codeRequestValidator = new OAuth2AuthorizationCodeRequestValidator();
@@ -195,42 +195,58 @@ var IdentityProviderController = class {
195
195
  if (entity.realmId && data.codeRequest && data.codeRequest.realm_id && data.codeRequest.realm_id !== entity.realmId) throw OAuth2RequestError.malformed("The provider and client realm do not match.");
196
196
  const { code } = useRequestQuery(event);
197
197
  if (typeof code !== "string" || code.length === 0) throw new BadRequestError("The authorization code is missing.");
198
- const user = await this.buildProviderAuthenticator(entity, { clientId: data.codeRequest?.client_id }).authenticate({ code });
198
+ const verified = data.codeRequest ? await this.codeRequestVerifier.verify(data.codeRequest) : void 0;
199
+ if (!entity.enabled) throw new BadRequestError("The identity provider is not enabled.");
200
+ const user = await this.buildProviderAuthenticator(entity, { clientId: verified?.data.client_id }).authenticate({ code });
201
+ if (!user.active) throw new EntityInactiveError({ entity: "user" });
199
202
  const realm = await this.realmRepository.resolve(entity.realmId, true);
200
- if (data.codeRequest?.client_id) {
201
- const client = await this.clientRepository.findOneByIdOrName(data.codeRequest.client_id, data.codeRequest.realm_id);
202
- if (client?.accessPolicyId) {
203
- let allowed = false;
204
- const subject = toIdentityPolicyData({
205
- type: IdentityType.USER,
206
- data: {
207
- ...user,
208
- realm
209
- }
210
- });
211
- if (this.accessPolicyEvaluator && subject) allowed = await this.accessPolicyEvaluator.evaluate(client.accessPolicyId, subject);
212
- if (!allowed) {
213
- const url = this.buildHostedAuthorizeURL(data.codeRequest);
214
- url.searchParams.set("error", OAuth2ErrorCode.ACCESS_DENIED);
215
- return sendRedirect(event, url.href);
203
+ if (verified?.client.accessPolicyId) {
204
+ let allowed = false;
205
+ const subject = toIdentityPolicyData({
206
+ type: IdentityType.USER,
207
+ data: {
208
+ ...user,
209
+ realm
216
210
  }
211
+ });
212
+ if (this.accessPolicyEvaluator && subject) allowed = await this.accessPolicyEvaluator.evaluate(verified.client.accessPolicyId, subject);
213
+ if (!allowed) {
214
+ const url = this.buildHostedAuthorizeURL(verified.data);
215
+ url.searchParams.set("error", OAuth2ErrorCode.ACCESS_DENIED);
216
+ return sendRedirect(event, url.href);
217
217
  }
218
218
  }
219
- const authorizationCode = await this.codeIssuer.issue({
220
- response_type: "code",
221
- client_id: data.codeRequest?.client_id,
222
- redirect_uri: data.codeRequest?.redirect_uri,
223
- scope: data.codeRequest?.scope
224
- }, {
219
+ const authorizationCode = await this.codeIssuer.issue(verified?.data ?? { response_type: "code" }, {
225
220
  type: IdentityType.USER,
226
221
  data: {
227
222
  ...user,
228
223
  realm
229
224
  }
230
225
  }, { authMethod: SessionAuthMethod.EXTERNAL });
231
- if (data.codeRequest) {
232
- const url = this.buildHostedAuthorizeURL(data.codeRequest);
226
+ await this.eventService?.record({
227
+ scope: EventScope.OAUTH2,
228
+ name: EventName.AUTHORIZE,
229
+ refType: EventRefType.CLIENT,
230
+ refId: verified?.data.client_id ?? null,
231
+ clientId: verified?.data.client_id ?? null,
232
+ sessionId: null,
233
+ actorType: IdentityType.USER,
234
+ actorId: user.id,
235
+ actorName: user.name,
236
+ realmId: verified?.data.realm_id ?? realm.id,
237
+ requestIpAddress: getRequestIP(event) ?? null,
238
+ requestUserAgent: getRequestHeader(event, "user-agent") ?? null,
239
+ data: {
240
+ reason: "federated",
241
+ providerId: entity.id,
242
+ providerName: entity.name,
243
+ ...verified?.data.scope ? { scope: verified.data.scope } : {}
244
+ }
245
+ });
246
+ if (verified?.redirectUriVerified && verified.data.redirect_uri) {
247
+ const url = new URL(verified.data.redirect_uri);
233
248
  url.searchParams.set("code", authorizationCode.id);
249
+ if (verified.data.state) url.searchParams.set("state", verified.data.state);
234
250
  return sendRedirect(event, url.href);
235
251
  }
236
252
  const url = new URL(this.options.baseURL);
@@ -311,32 +327,79 @@ var IdentityProviderController = class {
311
327
  const { code } = useRequestQuery(event);
312
328
  try {
313
329
  if (link.providerId !== provider.id || !provider.enabled) throw new BadRequestError("The identity provider is not available for account linking.");
314
- if (!state.ip || !state.userAgent) throw new BadRequestError("The account link request is not bound to a browser.");
315
330
  if (typeof code !== "string" || code.length === 0) throw new BadRequestError("The authorization code is missing.");
316
331
  const identity = await this.buildProviderAuthenticator(provider).resolveIdentity({ code });
317
- const account = await this.accountManager.link(identity, link.userId);
318
- await this.eventService?.record({
319
- scope: EventScope.IDENTITY,
320
- name: EventName.IDENTITY_PROVIDER_LINKED,
321
- refType: EventRefType.IDENTITY_PROVIDER_ACCOUNT,
322
- refId: account.id,
323
- realmId: account.userRealmId ?? provider.realmId ?? null,
324
- actorType: IdentityType.USER,
325
- actorId: account.userId,
326
- requestIpAddress: getRequestIP(event) ?? null,
327
- requestUserAgent: getRequestHeader(event, "user-agent") ?? null,
328
- data: {
329
- providerId: provider.id,
330
- providerName: provider.name
331
- }
332
+ if (!identity.id) throw new BadRequestError("The identity provider returned no subject.");
333
+ const handle = await this.linkStore.save({
334
+ providerId: provider.id,
335
+ userId: link.userId,
336
+ providerUserId: identity.id,
337
+ providerUserName: this.pickIdentityCandidate(identity, "name"),
338
+ providerUserEmail: this.pickIdentityCandidate(identity, "email")
332
339
  });
333
- url.searchParams.set("linked", provider.id);
340
+ url.searchParams.set("linkHandle", handle);
341
+ url.searchParams.set("provider", provider.id);
334
342
  } catch (e) {
335
343
  this.logger?.error(describeError(e, "The identity-provider account link failed."));
336
- url.searchParams.set("linkError", isIdentityProviderAccountAlreadyLinkedError(e) ? "already_linked" : "link_failed");
344
+ url.searchParams.set("linkError", "link_failed");
337
345
  }
338
346
  return sendRedirect(event, url.href);
339
347
  }
348
+ /**
349
+ * The bearer-authenticated half of the link (issue #3439): the account
350
+ * row is written here, for the AUTHENTICATED user, never in the callback.
351
+ */ async confirmLink(_id, _data, event) {
352
+ const id = useRequestParamID(event);
353
+ const provider = await this.resolve(id);
354
+ if (!isOAuth2IdentityProvider(provider) && !isOpenIDIdentityProvider(provider)) throw new BadRequestError("Only an identity-provider based on the oauth protocol supports account linking.");
355
+ if (!provider.enabled) throw new BadRequestError("The identity provider is not enabled.");
356
+ const identity = useRequestIdentityOrFail(event);
357
+ if (identity.type !== IdentityType.USER) throw new BadRequestError("Only a user can link an identity provider account.");
358
+ const body = await readRequestBody(event);
359
+ const handle = isObject(body) ? body.handle : void 0;
360
+ if (typeof handle !== "string" || handle.length === 0) throw new BadRequestError("The account link handle is missing.");
361
+ const link = await this.linkStore.consume(handle);
362
+ if (!link) throw new BadRequestError("The account link request is unknown or expired.");
363
+ if (link.userId !== identity.id || link.providerId !== provider.id) throw new BadRequestError("The account link request does not belong to the authenticated user.");
364
+ const account = await this.accountManager.link({
365
+ id: link.providerUserId,
366
+ attributeCandidates: {
367
+ name: [link.providerUserName],
368
+ email: [link.providerUserEmail]
369
+ },
370
+ data: {},
371
+ provider
372
+ }, identity.id);
373
+ const requestContext = useRequestEventContext();
374
+ await this.eventService?.record({
375
+ scope: EventScope.IDENTITY,
376
+ name: EventName.IDENTITY_PROVIDER_LINKED,
377
+ refType: EventRefType.IDENTITY_PROVIDER_ACCOUNT,
378
+ refId: account.id,
379
+ realmId: account.userRealmId ?? provider.realmId ?? null,
380
+ actorType: IdentityType.USER,
381
+ actorId: account.userId,
382
+ actorName: identity.data.name ?? null,
383
+ sessionId: requestContext?.sessionId ?? null,
384
+ requestPath: requestContext?.requestPath ?? null,
385
+ requestMethod: requestContext?.requestMethod ?? null,
386
+ requestIpAddress: requestContext?.requestIpAddress ?? getRequestIP(event) ?? null,
387
+ requestUserAgent: requestContext?.requestUserAgent ?? getRequestHeader(event, "user-agent") ?? null,
388
+ data: {
389
+ providerId: provider.id,
390
+ providerName: provider.name
391
+ }
392
+ });
393
+ return {
394
+ data: account,
395
+ meta: {}
396
+ };
397
+ }
398
+ pickIdentityCandidate(identity, key) {
399
+ const candidates = identity.attributeCandidates?.[key] || [];
400
+ for (const candidate of candidates) if (typeof candidate === "string" && candidate.length > 0) return candidate;
401
+ return null;
402
+ }
340
403
  buildHostedAuthorizeURL(codeRequest) {
341
404
  const url = new URL(resolveURL(this.options.baseURL, "authorize"));
342
405
  const codeRequestKeys = Object.keys(codeRequest);
@@ -356,6 +419,7 @@ var IdentityProviderController = class {
356
419
  return createIdentityProviderOAuth2Authenticator({
357
420
  accountManager: this.accountManager,
358
421
  provider,
422
+ logger: this.logger,
359
423
  options: {
360
424
  baseURL: this.options.baseURL,
361
425
  clientId: options.clientId
@@ -459,6 +523,19 @@ _ts_decorate([
459
523
  _ts_metadata("design:paramtypes", [String, typeof IAppEvent === "undefined" ? Object : IAppEvent]),
460
524
  _ts_metadata("design:returntype", Promise)
461
525
  ], IdentityProviderController.prototype, "authorizeIn", null);
526
+ _ts_decorate([
527
+ DPost("/:id/link-confirm", [ForceLoggedInMiddleware]),
528
+ _ts_param(0, DPath("id")),
529
+ _ts_param(1, DBody()),
530
+ _ts_param(2, DContext()),
531
+ _ts_metadata("design:type", Function),
532
+ _ts_metadata("design:paramtypes", [
533
+ String,
534
+ typeof IdentityProviderLinkConfirmPayload === "undefined" ? Object : IdentityProviderLinkConfirmPayload,
535
+ typeof IAppEvent === "undefined" ? Object : IAppEvent
536
+ ]),
537
+ _ts_metadata("design:returntype", Promise)
538
+ ], IdentityProviderController.prototype, "confirmLink", null);
462
539
  IdentityProviderController = _ts_decorate([
463
540
  DTags("identity"),
464
541
  DController(["/identity-providers", "/realms/:realmId/identity-providers"]),
@@ -1 +1 @@
1
- {"version":3,"file":"module.mjs","names":["BuiltInPolicyType","definePolicyData","ValidatorGroup","base64URLDecode","isUUID","DBody","DContext","DController","DDelete","DGet","DPath","DPost","DPut","DTags","getRequestHeader","getRequestIP","sendRedirect","EventName","EventRefType","EventScope","IdentityProviderAttributesValidator","IdentityProviderValidator","IdentityType","PermissionName","SessionAuthMethod","isOAuth2IdentityProvider","isOpenIDIdentityProvider","BadRequestError","EntityNotFoundError","describeError","resolveURL","useRequestQuery","readRequestBody","OAuth2ErrorCode","OAuth2RequestError","URL","OAuth2AuthorizationCodeRequestValidator","RECORD_QUERY_PARAMETERS","createIdentityProviderOAuth2Authenticator","decodeQuery","describeQuerySchema","identityProviderSchema","isIdentityProviderAccountAlreadyLinkedError","toIdentityPolicyData","applyRouteRealmIDToBody","buildActorContext","getBodyRealmID","getRequestParamID","getRequestRealmID","useRequestIdentityOrFail","useRequestParamID","useRequestPermissionEvaluator","ForceLoggedInMiddleware","IdentityProviderController","options","repository","realmRepository","clientRepository","accountManager","codeRequestVerifier","codeRequestValidator","stateManager","codeIssuer","accessPolicyEvaluator","eventService","logger","ctx","getProviders","event","data","meta","findMany","schema","actor","permissionEvaluator","preEvaluate","name","IDENTITY_PROVIDER_READ","datum","evaluate","ATTRIBUTES","REALM_MATCH","realmId","getProvider","id","paramId","entity","findOneByIdOrName","editProvider","user","write","updateOnly","put","dropProvider","IDENTITY_PROVIDER_DELETE","findOneBy","entityId","remove","response","status","addProvider","authorizeOut","_id","resolve","authenticator","buildProviderAuthenticator","codeRequest","query","codeRequestDecoded","JSON","parse","malformed","codeRequestValidated","run","verify","client","state","saveAuthorizationState","buildRedirectURL","linkRequest","enabled","identity","type","USER","link","userId","providerId","url","authorizeIn","Error","protocol","verifyAuthorizationState","completeLink","realm_id","code","length","clientId","client_id","authenticate","realm","accessPolicyId","allowed","subject","buildHostedAuthorizeURL","searchParams","set","ACCESS_DENIED","href","authorizationCode","issue","response_type","redirect_uri","scope","authMethod","EXTERNAL","baseURL","group","body","where","IDENTITY_PROVIDER_UPDATE","UPDATE","IDENTITY_PROVIDER_CREATE","CREATE","validator","attributesValidator","attributes","validateJoinColumns","checkUniqueness","undefined","merge","saveWithEA","create","provider","ip","userAgent","resolveIdentity","account","record","IDENTITY","IDENTITY_PROVIDER_LINKED","refType","IDENTITY_PROVIDER_ACCOUNT","refId","userRealmId","actorType","actorId","requestIpAddress","requestUserAgent","providerName","e","error","codeRequestKeys","Object","keys","codeRequestKey_","codeRequestKey","codeRequestValue","String","findOneById","save","stateInvalid"],"sources":["../../../../../../src/adapters/http/controllers/entities/identity-provider/module.ts"],"sourcesContent":["/*\n * Copyright (c) 2022.\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 { BuiltInPolicyType, definePolicyData } from '@authup/access';\nimport { ValidatorGroup, base64URLDecode, isUUID } from '@authup/kit';\nimport {\n DBody,\n DContext,\n DController,\n DDelete,\n DGet,\n DPath,\n DPost,\n DPut,\n DTags,\n} from '@routup/decorators';\nimport type { IAppEvent } from 'routup';\nimport {\n getRequestHeader,\n getRequestIP,\n sendRedirect,\n} from 'routup';\nimport type {\n IdentityProvider,\n OAuth2AuthorizationCodeRequest,\n OAuth2IdentityProvider,\n OpenIDIdentityProvider,\n} from '@authup/core-kit';\nimport {\n EventName,\n EventRefType,\n EventScope,\n IdentityProviderAttributesValidator,\n IdentityProviderValidator,\n IdentityType,\n PermissionName,\n SessionAuthMethod,\n isOAuth2IdentityProvider,\n isOpenIDIdentityProvider,\n} from '@authup/core-kit';\nimport { BadRequestError, EntityNotFoundError } from '@authup/errors';\nimport type { Logger } from '@authup/server-kit';\nimport { describeError, resolveURL } from '../../../../../utils/index.ts';\nimport { useRequestQuery } from '@routup/basic/query';\nimport { readRequestBody } from '@routup/basic/body';\nimport { OAuth2ErrorCode, OAuth2RequestError } from '@authup/specs';\nimport type {\n EntityCollectionResponse,\n EntityRecordResponse,\n IdentityProviderCreatePayload,\n IdentityProviderLinkRequestResponse,\n IdentityProviderSavePayload,\n IdentityProviderUpdatePayload,\n} from '@authup/core-http-kit';\nimport { URL } from 'node:url';\nimport type {\n IEventService,\n IIdentityProviderAccountManager,\n IIdentityProviderRepository,\n IOAuth2AccessPolicyEvaluator,\n IOAuth2AuthorizationCodeIssuer,\n IOAuth2AuthorizationCodeRequestVerifier,\n IOAuth2AuthorizationStateManager,\n IOAuth2ClientRepository,\n IRealmRepository,\n OAuth2AuthorizationState,\n OAuth2AuthorizationStateLink,\n} from '../../../../../core/index.ts';\nimport {\n OAuth2AuthorizationCodeRequestValidator,\n RECORD_QUERY_PARAMETERS,\n createIdentityProviderOAuth2Authenticator,\n decodeQuery,\n describeQuerySchema,\n identityProviderSchema,\n isIdentityProviderAccountAlreadyLinkedError,\n toIdentityPolicyData,\n} from '../../../../../core/index.ts';\nimport {\n applyRouteRealmIDToBody,\n buildActorContext,\n getBodyRealmID,\n getRequestParamID,\n getRequestRealmID,\n useRequestIdentityOrFail,\n useRequestParamID,\n useRequestPermissionEvaluator,\n} from '../../../request/index.ts';\nimport { ForceLoggedInMiddleware } from '../../../middleware/index.ts';\nimport type { IdentityProviderControllerContext, IdentityProviderControllerOptions } from './types.ts';\n\n@DTags('identity')\n@DController(['/identity-providers', '/realms/:realmId/identity-providers'])\nexport class IdentityProviderController {\n protected options: IdentityProviderControllerOptions;\n\n protected repository: IIdentityProviderRepository;\n\n protected realmRepository: IRealmRepository;\n\n protected clientRepository: IOAuth2ClientRepository;\n\n protected accountManager: IIdentityProviderAccountManager;\n\n protected codeRequestVerifier : IOAuth2AuthorizationCodeRequestVerifier;\n\n protected codeRequestValidator : OAuth2AuthorizationCodeRequestValidator;\n\n protected stateManager : IOAuth2AuthorizationStateManager;\n\n protected codeIssuer : IOAuth2AuthorizationCodeIssuer;\n\n protected accessPolicyEvaluator? : IOAuth2AccessPolicyEvaluator;\n\n protected eventService? : IEventService;\n\n protected logger? : Logger;\n\n // ---------------------------------------------------------\n\n constructor(ctx: IdentityProviderControllerContext) {\n this.options = ctx.options;\n this.repository = ctx.repository;\n this.realmRepository = ctx.realmRepository;\n this.clientRepository = ctx.clientRepository;\n this.accountManager = ctx.accountManager;\n this.codeIssuer = ctx.codeIssuer;\n this.codeRequestVerifier = ctx.codeRequestVerifier;\n this.codeRequestValidator = new OAuth2AuthorizationCodeRequestValidator();\n this.stateManager = ctx.stateManager;\n this.accessPolicyEvaluator = ctx.accessPolicyEvaluator;\n this.eventService = ctx.eventService;\n this.logger = ctx.logger;\n }\n\n // ---------------------------------------------------------\n\n @DGet('', [])\n async getProviders(\n @DContext() event: IAppEvent,\n ): Promise<EntityCollectionResponse<IdentityProvider>> {\n const {\n data,\n meta,\n } = await this.repository.findMany(\n await decodeQuery(useRequestQuery(event), {\n schema: identityProviderSchema,\n actor: buildActorContext(event),\n }),\n );\n\n try {\n const permissionEvaluator = useRequestPermissionEvaluator(event);\n await permissionEvaluator.preEvaluate({ name: PermissionName.IDENTITY_PROVIDER_READ });\n\n for (const datum of data) {\n try {\n await permissionEvaluator.evaluate({\n name: PermissionName.IDENTITY_PROVIDER_READ,\n data: definePolicyData({ [BuiltInPolicyType.ATTRIBUTES]: datum, [BuiltInPolicyType.REALM_MATCH]: datum.realmId ?? null }),\n });\n } catch {\n // do nothing\n }\n }\n } catch {\n // do nothing\n }\n\n return {\n data,\n meta: {\n ...meta,\n schema: describeQuerySchema(identityProviderSchema),\n },\n };\n }\n\n @DGet('/:id', [])\n async getProvider(\n @DPath('id') id: string,\n @DContext() event: IAppEvent,\n ): Promise<EntityRecordResponse<IdentityProvider>> {\n const paramId = useRequestParamID(event, { isUUID: false });\n\n const entity = await this.repository.findOneByIdOrName(\n paramId,\n getRequestRealmID(event),\n );\n\n if (!entity) {\n throw new EntityNotFoundError();\n }\n\n try {\n const permissionEvaluator = useRequestPermissionEvaluator(event);\n await permissionEvaluator.evaluate({\n name: PermissionName.IDENTITY_PROVIDER_READ,\n data: definePolicyData({ [BuiltInPolicyType.ATTRIBUTES]: entity, [BuiltInPolicyType.REALM_MATCH]: entity.realmId ?? null }),\n });\n } catch {\n // do nothing\n }\n\n return { data: entity, meta: { schema: describeQuerySchema(identityProviderSchema, RECORD_QUERY_PARAMETERS) } };\n }\n\n @DPost('/:id', [ForceLoggedInMiddleware])\n async editProvider(\n @DPath('id') id: string,\n @DBody() user: IdentityProviderUpdatePayload,\n @DContext() event: IAppEvent,\n ) : Promise<EntityRecordResponse<IdentityProvider>> {\n return this.write(event, { updateOnly: true });\n }\n\n @DPut('/:id', [ForceLoggedInMiddleware])\n async put(\n @DPath('id') id: string,\n @DBody() user: IdentityProviderSavePayload,\n @DContext() event: IAppEvent,\n ) : Promise<EntityRecordResponse<IdentityProvider>> {\n return this.write(event);\n }\n\n @DDelete('/:id', [ForceLoggedInMiddleware])\n async dropProvider(\n @DPath('id') id: string,\n @DContext() event: IAppEvent,\n ) : Promise<EntityRecordResponse<IdentityProvider>> {\n const paramId = useRequestParamID(event);\n\n const permissionEvaluator = useRequestPermissionEvaluator(event);\n await permissionEvaluator.preEvaluate({ name: PermissionName.IDENTITY_PROVIDER_DELETE });\n\n const entity = await this.repository.findOneBy({ id: paramId });\n\n if (!entity) {\n throw new EntityNotFoundError();\n }\n\n await permissionEvaluator.evaluate({\n name: PermissionName.IDENTITY_PROVIDER_DELETE,\n data: definePolicyData({ [BuiltInPolicyType.ATTRIBUTES]: entity, [BuiltInPolicyType.REALM_MATCH]: entity.realmId ?? null }),\n });\n\n const { id: entityId } = entity;\n\n await this.repository.remove(entity);\n\n entity.id = entityId;\n\n event.response.status = 202;\n\n return { data: entity, meta: {} };\n }\n\n @DPost('', [ForceLoggedInMiddleware])\n async addProvider(\n @DBody() user: IdentityProviderCreatePayload,\n @DContext() event: IAppEvent,\n ) : Promise<EntityRecordResponse<IdentityProvider>> {\n return this.write(event);\n }\n\n // ---------------------------------------------------------\n\n @DGet('/:id/authorize-out', [])\n async authorizeOut(\n @DPath('id') _id: string,\n @DContext() event: IAppEvent,\n ) {\n const id = useRequestParamID(event);\n const entity = await this.resolve(id);\n\n if (!isOAuth2IdentityProvider(entity) && !isOpenIDIdentityProvider(entity)) {\n throw new BadRequestError('Only an identity-provider based on the oauth protocol supports authorize redirect.');\n }\n\n const authenticator = this.buildProviderAuthenticator(entity);\n\n let codeRequest: OAuth2AuthorizationCodeRequest | undefined;\n const query = useRequestQuery(event);\n if (typeof query.codeRequest === 'string') {\n let codeRequestDecoded: OAuth2AuthorizationCodeRequest;\n\n try {\n codeRequestDecoded = JSON.parse(base64URLDecode(query.codeRequest));\n } catch {\n throw OAuth2RequestError.malformed('The code request is malformed and can not be parsed.');\n }\n\n const codeRequestValidated = await this.codeRequestValidator.run(codeRequestDecoded);\n const data = await this.codeRequestVerifier.verify(codeRequestValidated);\n\n if (\n data.client.realmId &&\n entity.realmId &&\n entity.realmId !== data.client.realmId\n ) {\n throw OAuth2RequestError.malformed('The provider and client realm do not match.');\n }\n\n codeRequest = data.data;\n }\n\n const state = await this.saveAuthorizationState(event, { codeRequest });\n\n return sendRedirect(event, authenticator.buildRedirectURL({ state }));\n }\n\n @DPost('/:id/link-request', [ForceLoggedInMiddleware])\n async linkRequest(\n @DPath('id') _id: string,\n @DContext() event: IAppEvent,\n ) : Promise<IdentityProviderLinkRequestResponse> {\n const id = useRequestParamID(event);\n const entity = await this.resolve(id);\n\n if (!isOAuth2IdentityProvider(entity) && !isOpenIDIdentityProvider(entity)) {\n throw new BadRequestError('Only an identity-provider based on the oauth protocol supports account linking.');\n }\n\n if (!entity.enabled) {\n throw new BadRequestError('The identity provider is not enabled.');\n }\n\n const identity = useRequestIdentityOrFail(event);\n if (identity.type !== IdentityType.USER) {\n throw new BadRequestError('Only a user can link an identity provider account.');\n }\n\n if (entity.realmId && entity.realmId !== identity.realmId) {\n throw new BadRequestError('The identity provider does not belong to the user realm.');\n }\n\n const authenticator = this.buildProviderAuthenticator(entity);\n\n const state = await this.saveAuthorizationState(event, { link: { userId: identity.id, providerId: entity.id } });\n\n return { url: authenticator.buildRedirectURL({ state }) };\n }\n\n @DGet('/:id/authorize-in', [])\n async authorizeIn(\n @DPath('id') _id: string,\n @DContext() event: IAppEvent,\n ) {\n const id = useRequestParamID(event);\n\n const entity = await this.resolve(id);\n\n if (!isOAuth2IdentityProvider(entity) && !isOpenIDIdentityProvider(entity)) {\n throw new Error(`The provider protocol ${entity.protocol} is not valid.`);\n }\n\n const data = await this.verifyAuthorizationState(event);\n\n const { link } = data;\n if (link) {\n return this.completeLink(event, entity, data, link);\n }\n\n if (\n entity.realmId &&\n data.codeRequest &&\n data.codeRequest.realm_id &&\n data.codeRequest.realm_id !== entity.realmId\n ) {\n throw OAuth2RequestError.malformed('The provider and client realm do not match.');\n }\n\n const { code } = useRequestQuery(event);\n\n if (typeof code !== 'string' || code.length === 0) {\n throw new BadRequestError('The authorization code is missing.');\n }\n\n const authenticator = this.buildProviderAuthenticator(entity, { clientId: data.codeRequest?.client_id });\n\n const user = await authenticator.authenticate({ code });\n\n const realm = await this.realmRepository.resolve(entity.realmId, true);\n\n // Application access policy (plan 052), federated leg: the callback\n // never redirects to the RP directly — a denial bounces back to the\n // hosted authorize page with error=access_denied, so no\n // redirectUriVerified threading through the state blob is needed.\n // A policy id with no wired evaluator denies (fail closed); a\n // since-deleted client is skipped (the /token backstop still covers it).\n if (data.codeRequest?.client_id) {\n const client = await this.clientRepository.findOneByIdOrName(\n data.codeRequest.client_id,\n data.codeRequest.realm_id,\n );\n\n if (client?.accessPolicyId) {\n let allowed = false;\n\n const subject = toIdentityPolicyData({\n type: IdentityType.USER,\n data: {\n ...user,\n realm,\n },\n });\n if (this.accessPolicyEvaluator && subject) {\n allowed = await this.accessPolicyEvaluator.evaluate(\n client.accessPolicyId,\n subject,\n );\n }\n\n if (!allowed) {\n const url = this.buildHostedAuthorizeURL(data.codeRequest);\n url.searchParams.set('error', OAuth2ErrorCode.ACCESS_DENIED);\n\n return sendRedirect(event, url.href);\n }\n }\n }\n\n const authorizationCode = await this.codeIssuer.issue(\n {\n response_type: 'code',\n client_id: data.codeRequest?.client_id,\n redirect_uri: data.codeRequest?.redirect_uri,\n scope: data.codeRequest?.scope,\n },\n {\n type: IdentityType.USER,\n data: {\n ...user,\n realm,\n },\n },\n { authMethod: SessionAuthMethod.EXTERNAL },\n );\n\n if (data.codeRequest) {\n const url = this.buildHostedAuthorizeURL(data.codeRequest);\n url.searchParams.set('code', authorizationCode.id);\n\n return sendRedirect(event, url.href);\n }\n\n const url = new URL(this.options.baseURL);\n url.searchParams.set('code', authorizationCode.id);\n\n return sendRedirect(event, url.href);\n }\n\n // ---------------------------------------------------------\n\n private async write(event: IAppEvent, options: {\n updateOnly?: boolean\n } = {}): Promise<EntityRecordResponse<IdentityProvider>> {\n let group: string;\n const id = getRequestParamID(event, { isUUID: false });\n const body = await readRequestBody(event);\n applyRouteRealmIDToBody(event, body);\n const realmId = getRequestRealmID(event) ?? getBodyRealmID(body);\n\n let entity: IdentityProvider | null | undefined;\n if (id) {\n const where: Record<string, any> = {};\n if (isUUID(id)) {\n where.id = id;\n } else {\n where.name = id;\n }\n\n if (realmId) {\n where.realmId = realmId;\n }\n\n entity = await this.repository.findOneBy(where);\n // Only a NAME key may upsert-create. A UUID addresses one specific\n // row, so a miss is a 404 (creating would write a different id).\n if (!entity && (options.updateOnly || where.id)) {\n throw new EntityNotFoundError();\n }\n } else if (options.updateOnly) {\n throw new EntityNotFoundError();\n }\n\n const permissionEvaluator = useRequestPermissionEvaluator(event);\n if (entity) {\n await permissionEvaluator.preEvaluate({ name: PermissionName.IDENTITY_PROVIDER_UPDATE });\n\n group = ValidatorGroup.UPDATE;\n } else {\n await permissionEvaluator.preEvaluate({ name: PermissionName.IDENTITY_PROVIDER_CREATE });\n\n group = ValidatorGroup.CREATE;\n }\n\n const validator = new IdentityProviderValidator();\n const data = await validator.run(body, { group });\n\n const attributesValidator = new IdentityProviderAttributesValidator();\n const attributes = await attributesValidator.run(body);\n\n await this.repository.validateJoinColumns(data);\n\n if (entity) {\n await permissionEvaluator.evaluate({\n name: PermissionName.IDENTITY_PROVIDER_UPDATE,\n data: definePolicyData({\n [BuiltInPolicyType.ATTRIBUTES]: {\n ...entity,\n ...data,\n },\n [BuiltInPolicyType.REALM_MATCH]: data.realmId ?? entity.realmId ?? null,\n }),\n });\n } else {\n if (!data.realmId) {\n const identity = useRequestIdentityOrFail(event);\n data.realmId = identity.realmId;\n }\n\n await permissionEvaluator.evaluate({\n name: PermissionName.IDENTITY_PROVIDER_CREATE,\n data: definePolicyData({ [BuiltInPolicyType.ATTRIBUTES]: data, [BuiltInPolicyType.REALM_MATCH]: data.realmId ?? null }),\n });\n }\n\n await this.repository.checkUniqueness(data, entity || undefined);\n\n if (entity) {\n entity = this.repository.merge(entity, data);\n await this.repository.saveWithEA(entity, attributes);\n\n event.response.status = 202;\n\n return { data: entity, meta: {} };\n }\n\n entity = this.repository.create(data);\n await this.repository.saveWithEA(entity, attributes);\n\n event.response.status = 201;\n\n return { data: entity, meta: {} };\n }\n\n // ---------------------------------------------------------\n\n /**\n * Callback half of the account-linking round-trip (plan 091): the\n * external identity is bound to the state-referenced user instead of\n * running the login path. No auth code and no session is minted; the\n * browser lands back on the account console with a marker param.\n */\n private async completeLink(\n event: IAppEvent,\n provider: OAuth2IdentityProvider | OpenIDIdentityProvider,\n state: OAuth2AuthorizationState,\n link: OAuth2AuthorizationStateLink,\n ) {\n // Fixed, server-derived return target (the account console page).\n // No client-supplied redirect exists on this path.\n const url = new URL(resolveURL(this.options.baseURL, 'account/connected-accounts'));\n\n const { code } = useRequestQuery(event);\n\n try {\n // The state is bound to the provider it was minted for, and the\n // provider must still be enabled at completion — a state cannot\n // be replayed against a different provider's callback, nor\n // complete a link after its provider was disabled.\n if (link.providerId !== provider.id || !provider.enabled) {\n throw new BadRequestError('The identity provider is not available for account linking.');\n }\n\n // The state must be bound to the browser that minted it. Both\n // bindings come from the minting request itself\n // (`saveAuthorizationState` stores its ip and user-agent) and\n // `OAuth2AuthorizationStateManager.verify` skips a check whose\n // STORED value is falsy, so an absent binding is a choice the\n // minter made rather than a property of the network — and it\n // leaves the state completable in any browser.\n //\n // On the login path that is a session-fixation nuisance. Here it\n // is a durable credential binding: an attacker who mints a state\n // carrying their OWN userId and gets a victim to follow it binds\n // the VICTIM's external identity to the attacker's account, and\n // the victim's next federated login then lands in it. Refuse an\n // unbound state instead of linking on it.\n //\n // This closes the absent-binding hole only. ip + user-agent\n // remain guessable, and with the shipped `trustProxy: true` the\n // ip is the client-supplied left-most X-Forwarded-For entry, so\n // this is a stopgap: see issue #3439 for moving the write behind\n // a bearer-authenticated confirmation.\n if (!state.ip || !state.userAgent) {\n throw new BadRequestError('The account link request is not bound to a browser.');\n }\n\n if (typeof code !== 'string' || code.length === 0) {\n throw new BadRequestError('The authorization code is missing.');\n }\n\n const authenticator = this.buildProviderAuthenticator(provider);\n\n const identity = await authenticator.resolveIdentity({ code });\n const account = await this.accountManager.link(identity, link.userId);\n\n await this.eventService?.record({\n scope: EventScope.IDENTITY,\n name: EventName.IDENTITY_PROVIDER_LINKED,\n refType: EventRefType.IDENTITY_PROVIDER_ACCOUNT,\n refId: account.id,\n realmId: account.userRealmId ?? provider.realmId ?? null,\n actorType: IdentityType.USER,\n actorId: account.userId,\n requestIpAddress: getRequestIP(event) ?? null,\n requestUserAgent: getRequestHeader(event, 'user-agent') ?? null,\n data: { providerId: provider.id, providerName: provider.name },\n });\n\n url.searchParams.set('linked', provider.id);\n } catch (e) {\n // The failure is swallowed into a redirect marker, so the log is\n // the only surface it can reach. Without this an unreachable or\n // rejecting provider is indistinguishable from a stale state.\n this.logger?.error(describeError(e, 'The identity-provider account link failed.'));\n\n url.searchParams.set(\n 'linkError',\n isIdentityProviderAccountAlreadyLinkedError(e) ? 'already_linked' : 'link_failed',\n );\n }\n\n return sendRedirect(event, url.href);\n }\n\n // ---------------------------------------------------------\n\n private buildHostedAuthorizeURL(codeRequest: OAuth2AuthorizationCodeRequest): URL {\n const url = new URL(resolveURL(this.options.baseURL, 'authorize'));\n\n const codeRequestKeys = Object.keys(codeRequest);\n for (const codeRequestKey_ of codeRequestKeys) {\n const codeRequestKey = codeRequestKey_ as keyof OAuth2AuthorizationCodeRequest;\n const codeRequestValue = codeRequest[codeRequestKey];\n // Preserve meaningful falsy values (e.g. max_age=0, which OIDC\n // treats as prompt=login) — only skip absent ones.\n if (typeof codeRequestValue !== 'undefined' && codeRequestValue !== null) {\n url.searchParams.set(codeRequestKey, String(codeRequestValue));\n }\n }\n\n return url;\n }\n\n // ---------------------------------------------------------\n\n private async resolve(id: string) {\n const entity = await this.repository.findOneById(id);\n\n if (!entity) {\n throw new EntityNotFoundError();\n }\n\n return entity;\n }\n\n // ---------------------------------------------------------\n\n private buildProviderAuthenticator(\n provider: OAuth2IdentityProvider | OpenIDIdentityProvider,\n options: { clientId?: string } = {},\n ) {\n return createIdentityProviderOAuth2Authenticator({\n accountManager: this.accountManager,\n provider,\n options: {\n baseURL: this.options.baseURL,\n clientId: options.clientId,\n },\n });\n }\n\n private async saveAuthorizationState(\n event: IAppEvent,\n data: Pick<OAuth2AuthorizationState, 'codeRequest' | 'link'> = {},\n ) : Promise<string> {\n return this.stateManager.save({\n ...data,\n ip: getRequestIP(event) ?? '',\n userAgent: getRequestHeader(event, 'user-agent') ?? undefined,\n });\n }\n\n private async verifyAuthorizationState(event: IAppEvent): Promise<OAuth2AuthorizationState> {\n const query = useRequestQuery(event);\n if (typeof query.state !== 'string') {\n throw OAuth2RequestError.stateInvalid();\n }\n\n return this.stateManager.verify(query.state, {\n ip: getRequestIP(event) ?? '',\n userAgent: getRequestHeader(event, 'user-agent') ?? undefined,\n });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"module.mjs","names":["BuiltInPolicyType","definePolicyData","ValidatorGroup","base64URLDecode","isObject","isUUID","DBody","DContext","DController","DDelete","DGet","DPath","DPost","DPut","DTags","getRequestHeader","getRequestIP","sendRedirect","EventName","EventRefType","EventScope","IdentityProviderAttributesValidator","IdentityProviderValidator","IdentityType","PermissionName","SessionAuthMethod","isOAuth2IdentityProvider","isOpenIDIdentityProvider","BadRequestError","EntityInactiveError","EntityNotFoundError","describeError","resolveURL","useRequestQuery","readRequestBody","OAuth2ErrorCode","OAuth2RequestError","URL","OAuth2AuthorizationCodeRequestValidator","RECORD_QUERY_PARAMETERS","createIdentityProviderOAuth2Authenticator","decodeQuery","describeQuerySchema","identityProviderSchema","toIdentityPolicyData","applyRouteRealmIDToBody","buildActorContext","getBodyRealmID","getRequestParamID","getRequestRealmID","useRequestEventContext","useRequestIdentityOrFail","useRequestParamID","useRequestPermissionEvaluator","ForceLoggedInMiddleware","IdentityProviderController","options","repository","realmRepository","accountManager","linkStore","codeRequestVerifier","codeRequestValidator","stateManager","codeIssuer","accessPolicyEvaluator","eventService","logger","ctx","getProviders","event","data","meta","findMany","schema","actor","permissionEvaluator","preEvaluate","name","IDENTITY_PROVIDER_READ","datum","evaluate","ATTRIBUTES","REALM_MATCH","realmId","getProvider","id","paramId","entity","findOneByIdOrName","editProvider","user","write","updateOnly","put","dropProvider","IDENTITY_PROVIDER_DELETE","findOneBy","entityId","remove","response","status","addProvider","authorizeOut","_id","resolve","authenticator","buildProviderAuthenticator","codeRequest","query","codeRequestDecoded","JSON","parse","malformed","codeRequestValidated","run","verify","client","state","saveAuthorizationState","buildRedirectURL","linkRequest","enabled","identity","type","USER","link","userId","providerId","url","authorizeIn","Error","protocol","verifyAuthorizationState","completeLink","realm_id","code","length","verified","undefined","clientId","client_id","authenticate","active","realm","accessPolicyId","allowed","subject","buildHostedAuthorizeURL","searchParams","set","ACCESS_DENIED","href","authorizationCode","issue","response_type","authMethod","EXTERNAL","record","scope","OAUTH2","AUTHORIZE","refType","CLIENT","refId","sessionId","actorType","actorId","actorName","requestIpAddress","requestUserAgent","reason","providerName","redirectUriVerified","redirect_uri","baseURL","group","body","where","IDENTITY_PROVIDER_UPDATE","UPDATE","IDENTITY_PROVIDER_CREATE","CREATE","validator","attributesValidator","attributes","validateJoinColumns","checkUniqueness","merge","saveWithEA","create","provider","resolveIdentity","handle","save","providerUserId","providerUserName","pickIdentityCandidate","providerUserEmail","e","error","confirmLink","_data","consume","account","attributeCandidates","email","requestContext","IDENTITY","IDENTITY_PROVIDER_LINKED","IDENTITY_PROVIDER_ACCOUNT","userRealmId","requestPath","requestMethod","key","candidates","candidate","codeRequestKeys","Object","keys","codeRequestKey_","codeRequestKey","codeRequestValue","String","findOneById","ip","userAgent","stateInvalid"],"sources":["../../../../../../src/adapters/http/controllers/entities/identity-provider/module.ts"],"sourcesContent":["/*\n * Copyright (c) 2022.\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 { BuiltInPolicyType, definePolicyData } from '@authup/access';\nimport { \n ValidatorGroup, \n base64URLDecode, \n isObject, \n isUUID, \n} from '@authup/kit';\nimport {\n DBody,\n DContext,\n DController,\n DDelete,\n DGet,\n DPath,\n DPost,\n DPut,\n DTags,\n} from '@routup/decorators';\nimport type { IAppEvent } from 'routup';\nimport {\n getRequestHeader,\n getRequestIP,\n sendRedirect,\n} from 'routup';\nimport type {\n IdentityProvider,\n IdentityProviderAccount,\n OAuth2AuthorizationCodeRequest,\n OAuth2IdentityProvider,\n OpenIDIdentityProvider,\n} from '@authup/core-kit';\nimport {\n EventName,\n EventRefType,\n EventScope,\n IdentityProviderAttributesValidator,\n IdentityProviderValidator,\n IdentityType,\n PermissionName,\n SessionAuthMethod,\n isOAuth2IdentityProvider,\n isOpenIDIdentityProvider,\n} from '@authup/core-kit';\nimport { BadRequestError, EntityInactiveError, EntityNotFoundError } from '@authup/errors';\nimport type { Logger } from '@authup/server-kit';\nimport { describeError, resolveURL } from '../../../../../utils/index.ts';\nimport { useRequestQuery } from '@routup/basic/query';\nimport { readRequestBody } from '@routup/basic/body';\nimport { OAuth2ErrorCode, OAuth2RequestError } from '@authup/specs';\nimport type {\n EntityCollectionResponse,\n EntityRecordResponse,\n IdentityProviderCreatePayload,\n IdentityProviderLinkConfirmPayload,\n IdentityProviderLinkRequestResponse,\n IdentityProviderSavePayload,\n IdentityProviderUpdatePayload,\n} from '@authup/core-http-kit';\nimport { URL } from 'node:url';\nimport type {\n IEventService,\n IIdentityProviderAccountLinkStore,\n IIdentityProviderAccountManager,\n IIdentityProviderRepository,\n IOAuth2AccessPolicyEvaluator,\n IOAuth2AuthorizationCodeIssuer,\n IOAuth2AuthorizationCodeRequestVerifier,\n IOAuth2AuthorizationStateManager,\n IRealmRepository,\n IdentityProviderIdentity,\n OAuth2AuthorizationState,\n OAuth2AuthorizationStateLink,\n} from '../../../../../core/index.ts';\nimport {\n OAuth2AuthorizationCodeRequestValidator,\n RECORD_QUERY_PARAMETERS,\n createIdentityProviderOAuth2Authenticator,\n decodeQuery,\n describeQuerySchema,\n identityProviderSchema,\n toIdentityPolicyData,\n} from '../../../../../core/index.ts';\nimport {\n applyRouteRealmIDToBody,\n buildActorContext,\n getBodyRealmID,\n getRequestParamID,\n getRequestRealmID,\n useRequestEventContext,\n useRequestIdentityOrFail,\n useRequestParamID,\n useRequestPermissionEvaluator,\n} from '../../../request/index.ts';\nimport { ForceLoggedInMiddleware } from '../../../middleware/index.ts';\nimport type { IdentityProviderControllerContext, IdentityProviderControllerOptions } from './types.ts';\n\n@DTags('identity')\n@DController(['/identity-providers', '/realms/:realmId/identity-providers'])\nexport class IdentityProviderController {\n protected options: IdentityProviderControllerOptions;\n\n protected repository: IIdentityProviderRepository;\n\n protected realmRepository: IRealmRepository;\n\n protected accountManager: IIdentityProviderAccountManager;\n\n protected linkStore: IIdentityProviderAccountLinkStore;\n\n protected codeRequestVerifier : IOAuth2AuthorizationCodeRequestVerifier;\n\n protected codeRequestValidator : OAuth2AuthorizationCodeRequestValidator;\n\n protected stateManager : IOAuth2AuthorizationStateManager;\n\n protected codeIssuer : IOAuth2AuthorizationCodeIssuer;\n\n protected accessPolicyEvaluator? : IOAuth2AccessPolicyEvaluator;\n\n protected eventService? : IEventService;\n\n protected logger? : Logger;\n\n // ---------------------------------------------------------\n\n constructor(ctx: IdentityProviderControllerContext) {\n this.options = ctx.options;\n this.repository = ctx.repository;\n this.realmRepository = ctx.realmRepository;\n this.accountManager = ctx.accountManager;\n this.linkStore = ctx.linkStore;\n this.codeIssuer = ctx.codeIssuer;\n this.codeRequestVerifier = ctx.codeRequestVerifier;\n this.codeRequestValidator = new OAuth2AuthorizationCodeRequestValidator();\n this.stateManager = ctx.stateManager;\n this.accessPolicyEvaluator = ctx.accessPolicyEvaluator;\n this.eventService = ctx.eventService;\n this.logger = ctx.logger;\n }\n\n // ---------------------------------------------------------\n\n @DGet('', [])\n async getProviders(\n @DContext() event: IAppEvent,\n ): Promise<EntityCollectionResponse<IdentityProvider>> {\n const {\n data,\n meta,\n } = await this.repository.findMany(\n await decodeQuery(useRequestQuery(event), {\n schema: identityProviderSchema,\n actor: buildActorContext(event),\n }),\n );\n\n try {\n const permissionEvaluator = useRequestPermissionEvaluator(event);\n await permissionEvaluator.preEvaluate({ name: PermissionName.IDENTITY_PROVIDER_READ });\n\n for (const datum of data) {\n try {\n await permissionEvaluator.evaluate({\n name: PermissionName.IDENTITY_PROVIDER_READ,\n data: definePolicyData({ [BuiltInPolicyType.ATTRIBUTES]: datum, [BuiltInPolicyType.REALM_MATCH]: datum.realmId ?? null }),\n });\n } catch {\n // do nothing\n }\n }\n } catch {\n // do nothing\n }\n\n return {\n data,\n meta: {\n ...meta,\n schema: describeQuerySchema(identityProviderSchema),\n },\n };\n }\n\n @DGet('/:id', [])\n async getProvider(\n @DPath('id') id: string,\n @DContext() event: IAppEvent,\n ): Promise<EntityRecordResponse<IdentityProvider>> {\n const paramId = useRequestParamID(event, { isUUID: false });\n\n const entity = await this.repository.findOneByIdOrName(\n paramId,\n getRequestRealmID(event),\n );\n\n if (!entity) {\n throw new EntityNotFoundError();\n }\n\n try {\n const permissionEvaluator = useRequestPermissionEvaluator(event);\n await permissionEvaluator.evaluate({\n name: PermissionName.IDENTITY_PROVIDER_READ,\n data: definePolicyData({ [BuiltInPolicyType.ATTRIBUTES]: entity, [BuiltInPolicyType.REALM_MATCH]: entity.realmId ?? null }),\n });\n } catch {\n // do nothing\n }\n\n return { data: entity, meta: { schema: describeQuerySchema(identityProviderSchema, RECORD_QUERY_PARAMETERS) } };\n }\n\n @DPost('/:id', [ForceLoggedInMiddleware])\n async editProvider(\n @DPath('id') id: string,\n @DBody() user: IdentityProviderUpdatePayload,\n @DContext() event: IAppEvent,\n ) : Promise<EntityRecordResponse<IdentityProvider>> {\n return this.write(event, { updateOnly: true });\n }\n\n @DPut('/:id', [ForceLoggedInMiddleware])\n async put(\n @DPath('id') id: string,\n @DBody() user: IdentityProviderSavePayload,\n @DContext() event: IAppEvent,\n ) : Promise<EntityRecordResponse<IdentityProvider>> {\n return this.write(event);\n }\n\n @DDelete('/:id', [ForceLoggedInMiddleware])\n async dropProvider(\n @DPath('id') id: string,\n @DContext() event: IAppEvent,\n ) : Promise<EntityRecordResponse<IdentityProvider>> {\n const paramId = useRequestParamID(event);\n\n const permissionEvaluator = useRequestPermissionEvaluator(event);\n await permissionEvaluator.preEvaluate({ name: PermissionName.IDENTITY_PROVIDER_DELETE });\n\n const entity = await this.repository.findOneBy({ id: paramId });\n\n if (!entity) {\n throw new EntityNotFoundError();\n }\n\n await permissionEvaluator.evaluate({\n name: PermissionName.IDENTITY_PROVIDER_DELETE,\n data: definePolicyData({ [BuiltInPolicyType.ATTRIBUTES]: entity, [BuiltInPolicyType.REALM_MATCH]: entity.realmId ?? null }),\n });\n\n const { id: entityId } = entity;\n\n await this.repository.remove(entity);\n\n entity.id = entityId;\n\n event.response.status = 202;\n\n return { data: entity, meta: {} };\n }\n\n @DPost('', [ForceLoggedInMiddleware])\n async addProvider(\n @DBody() user: IdentityProviderCreatePayload,\n @DContext() event: IAppEvent,\n ) : Promise<EntityRecordResponse<IdentityProvider>> {\n return this.write(event);\n }\n\n // ---------------------------------------------------------\n\n @DGet('/:id/authorize-out', [])\n async authorizeOut(\n @DPath('id') _id: string,\n @DContext() event: IAppEvent,\n ) {\n const id = useRequestParamID(event);\n const entity = await this.resolve(id);\n\n if (!isOAuth2IdentityProvider(entity) && !isOpenIDIdentityProvider(entity)) {\n throw new BadRequestError('Only an identity-provider based on the oauth protocol supports authorize redirect.');\n }\n\n const authenticator = this.buildProviderAuthenticator(entity);\n\n let codeRequest: OAuth2AuthorizationCodeRequest | undefined;\n const query = useRequestQuery(event);\n if (typeof query.codeRequest === 'string') {\n let codeRequestDecoded: OAuth2AuthorizationCodeRequest;\n\n try {\n codeRequestDecoded = JSON.parse(base64URLDecode(query.codeRequest));\n } catch {\n throw OAuth2RequestError.malformed('The code request is malformed and can not be parsed.');\n }\n\n const codeRequestValidated = await this.codeRequestValidator.run(codeRequestDecoded);\n const data = await this.codeRequestVerifier.verify(codeRequestValidated);\n\n if (\n data.client.realmId &&\n entity.realmId &&\n entity.realmId !== data.client.realmId\n ) {\n throw OAuth2RequestError.malformed('The provider and client realm do not match.');\n }\n\n codeRequest = data.data;\n }\n\n const state = await this.saveAuthorizationState(event, { codeRequest });\n\n return sendRedirect(event, authenticator.buildRedirectURL({ state }));\n }\n\n @DPost('/:id/link-request', [ForceLoggedInMiddleware])\n async linkRequest(\n @DPath('id') _id: string,\n @DContext() event: IAppEvent,\n ) : Promise<IdentityProviderLinkRequestResponse> {\n const id = useRequestParamID(event);\n const entity = await this.resolve(id);\n\n if (!isOAuth2IdentityProvider(entity) && !isOpenIDIdentityProvider(entity)) {\n throw new BadRequestError('Only an identity-provider based on the oauth protocol supports account linking.');\n }\n\n if (!entity.enabled) {\n throw new BadRequestError('The identity provider is not enabled.');\n }\n\n const identity = useRequestIdentityOrFail(event);\n if (identity.type !== IdentityType.USER) {\n throw new BadRequestError('Only a user can link an identity provider account.');\n }\n\n if (entity.realmId && entity.realmId !== identity.realmId) {\n throw new BadRequestError('The identity provider does not belong to the user realm.');\n }\n\n const authenticator = this.buildProviderAuthenticator(entity);\n\n const state = await this.saveAuthorizationState(event, { link: { userId: identity.id, providerId: entity.id } });\n\n return { url: authenticator.buildRedirectURL({ state }) };\n }\n\n @DGet('/:id/authorize-in', [])\n async authorizeIn(\n @DPath('id') _id: string,\n @DContext() event: IAppEvent,\n ) {\n const id = useRequestParamID(event);\n\n const entity = await this.resolve(id);\n\n if (!isOAuth2IdentityProvider(entity) && !isOpenIDIdentityProvider(entity)) {\n throw new Error(`The provider protocol ${entity.protocol} is not valid.`);\n }\n\n const data = await this.verifyAuthorizationState(event);\n\n const { link } = data;\n if (link) {\n return this.completeLink(event, entity, data, link);\n }\n\n if (\n entity.realmId &&\n data.codeRequest &&\n data.codeRequest.realm_id &&\n data.codeRequest.realm_id !== entity.realmId\n ) {\n throw OAuth2RequestError.malformed('The provider and client realm do not match.');\n }\n\n const { code } = useRequestQuery(event);\n\n if (typeof code !== 'string' || code.length === 0) {\n throw new BadRequestError('The authorization code is missing.');\n }\n\n // Re-verify the code request that authorize-out stored on the state,\n // BEFORE the provider's single-use code is spent. It re-resolves the\n // client (active, grant allowlist, scopes) and re-matches the\n // redirect_uri against the client's registered patterns, so a client\n // deactivated (or a pattern removed) while the user was away at the\n // provider cannot still receive a code, and a refused completion\n // provisions no user. Same fail-closed-at-completion rule the link path\n // applies to the provider.\n //\n // The redirect below rests on the MATCH, which the verifier enforces by\n // throwing `redirectUriMismatch`; `redirectUriVerified` is the flag\n // that survives to report it. Nothing else on this path knows whether\n // the uri was ever matched.\n const verified = data.codeRequest ?\n await this.codeRequestVerifier.verify(data.codeRequest) :\n undefined;\n\n // The provider must still be enabled, the rule the link path already\n // applies. Disabling a provider has to stop logins in flight too,\n // otherwise it only stops new ones.\n if (!entity.enabled) {\n throw new BadRequestError('The identity provider is not enabled.');\n }\n\n const authenticator = this.buildProviderAuthenticator(entity, { clientId: verified?.data.client_id });\n\n const user = await authenticator.authenticate({ code });\n\n // The local login path refuses an inactive user (EntityInactiveError),\n // and a federated login must not be the way around that. Only reachable\n // for an already-linked user: a first login provisions an active one.\n if (!user.active) {\n throw new EntityInactiveError({ entity: 'user' });\n }\n\n const realm = await this.realmRepository.resolve(entity.realmId, true);\n\n // Application access policy (plan 052), federated leg. The denial\n // bounces back to the hosted authorize page, which renders it as a\n // denial card, rather than becoming an error redirect to the RP: the\n // person is standing in front of the browser and the card is the only\n // surface that can tell them why. A policy id with no wired evaluator\n // denies (fail closed).\n if (verified?.client.accessPolicyId) {\n let allowed = false;\n\n const subject = toIdentityPolicyData({\n type: IdentityType.USER,\n data: {\n ...user,\n realm,\n },\n });\n if (this.accessPolicyEvaluator && subject) {\n allowed = await this.accessPolicyEvaluator.evaluate(\n verified.client.accessPolicyId,\n subject,\n );\n }\n\n if (!allowed) {\n const url = this.buildHostedAuthorizeURL(verified.data);\n url.searchParams.set('error', OAuth2ErrorCode.ACCESS_DENIED);\n\n return sendRedirect(event, url.href);\n }\n }\n\n // The WHOLE verified request reaches the issuer, never a hand-picked\n // subset: it carries code_challenge / code_challenge_method and nonce\n // (plus acr_values, which no redemption path reads yet). A code that\n // lost its PKCE challenge cannot be redeemed by a public client at all\n // (`PKCE is required for public clients`), which is every console\n // client.\n const authorizationCode = await this.codeIssuer.issue(\n verified?.data ?? { response_type: 'code' },\n {\n type: IdentityType.USER,\n data: {\n ...user,\n realm,\n },\n },\n { authMethod: SessionAuthMethod.EXTERNAL },\n );\n\n // The interactive path records this in OAuth2Authorization.authorize();\n // this leg issues its code directly, so without an emit here a\n // federated authorization leaves no trace in auth_events while every\n // other one does. `reason: federated` is what tells the two apart:\n // there was no consent step to report. No session exists yet (the\n // /token exchange creates it), hence a null sessionId. Metrics stay\n // uninstrumented on this leg, as they already are.\n await this.eventService?.record({\n scope: EventScope.OAUTH2,\n name: EventName.AUTHORIZE,\n refType: EventRefType.CLIENT,\n refId: verified?.data.client_id ?? null,\n clientId: verified?.data.client_id ?? null,\n sessionId: null,\n actorType: IdentityType.USER,\n actorId: user.id,\n actorName: user.name,\n realmId: verified?.data.realm_id ?? realm.id,\n requestIpAddress: getRequestIP(event) ?? null,\n requestUserAgent: getRequestHeader(event, 'user-agent') ?? null,\n data: {\n reason: 'federated',\n providerId: entity.id,\n providerName: entity.name,\n ...(verified?.data.scope ? { scope: verified.data.scope } : {}),\n },\n });\n\n // RFC 6749 §4.1.2: the code goes back to the client that asked for it.\n // It is bound to that client_id and that redirect_uri, so the RP is the\n // only party able to redeem it. Handing it to any other page strands\n // the login there (issue #3446).\n if (verified?.redirectUriVerified && verified.data.redirect_uri) {\n const url = new URL(verified.data.redirect_uri);\n url.searchParams.set('code', authorizationCode.id);\n if (verified.data.state) {\n url.searchParams.set('state', verified.data.state);\n }\n\n return sendRedirect(event, url.href);\n }\n\n // Only reachable when the state carried no code request at all: a\n // stored one always has a redirect_uri, because that mount is required\n // in OAuth2AuthorizationCodeRequestValidator (unlike `state`), so a\n // verified request is always a verified redirect target.\n const url = new URL(this.options.baseURL);\n url.searchParams.set('code', authorizationCode.id);\n\n return sendRedirect(event, url.href);\n }\n\n // ---------------------------------------------------------\n\n private async write(event: IAppEvent, options: {\n updateOnly?: boolean\n } = {}): Promise<EntityRecordResponse<IdentityProvider>> {\n let group: string;\n const id = getRequestParamID(event, { isUUID: false });\n const body = await readRequestBody(event);\n applyRouteRealmIDToBody(event, body);\n const realmId = getRequestRealmID(event) ?? getBodyRealmID(body);\n\n let entity: IdentityProvider | null | undefined;\n if (id) {\n const where: Record<string, any> = {};\n if (isUUID(id)) {\n where.id = id;\n } else {\n where.name = id;\n }\n\n if (realmId) {\n where.realmId = realmId;\n }\n\n entity = await this.repository.findOneBy(where);\n // Only a NAME key may upsert-create. A UUID addresses one specific\n // row, so a miss is a 404 (creating would write a different id).\n if (!entity && (options.updateOnly || where.id)) {\n throw new EntityNotFoundError();\n }\n } else if (options.updateOnly) {\n throw new EntityNotFoundError();\n }\n\n const permissionEvaluator = useRequestPermissionEvaluator(event);\n if (entity) {\n await permissionEvaluator.preEvaluate({ name: PermissionName.IDENTITY_PROVIDER_UPDATE });\n\n group = ValidatorGroup.UPDATE;\n } else {\n await permissionEvaluator.preEvaluate({ name: PermissionName.IDENTITY_PROVIDER_CREATE });\n\n group = ValidatorGroup.CREATE;\n }\n\n const validator = new IdentityProviderValidator();\n const data = await validator.run(body, { group });\n\n const attributesValidator = new IdentityProviderAttributesValidator();\n const attributes = await attributesValidator.run(body);\n\n await this.repository.validateJoinColumns(data);\n\n if (entity) {\n await permissionEvaluator.evaluate({\n name: PermissionName.IDENTITY_PROVIDER_UPDATE,\n data: definePolicyData({\n [BuiltInPolicyType.ATTRIBUTES]: {\n ...entity,\n ...data,\n },\n [BuiltInPolicyType.REALM_MATCH]: data.realmId ?? entity.realmId ?? null,\n }),\n });\n } else {\n if (!data.realmId) {\n const identity = useRequestIdentityOrFail(event);\n data.realmId = identity.realmId;\n }\n\n await permissionEvaluator.evaluate({\n name: PermissionName.IDENTITY_PROVIDER_CREATE,\n data: definePolicyData({ [BuiltInPolicyType.ATTRIBUTES]: data, [BuiltInPolicyType.REALM_MATCH]: data.realmId ?? null }),\n });\n }\n\n await this.repository.checkUniqueness(data, entity || undefined);\n\n if (entity) {\n entity = this.repository.merge(entity, data);\n await this.repository.saveWithEA(entity, attributes);\n\n event.response.status = 202;\n\n return { data: entity, meta: {} };\n }\n\n entity = this.repository.create(data);\n await this.repository.saveWithEA(entity, attributes);\n\n event.response.status = 201;\n\n return { data: entity, meta: {} };\n }\n\n // ---------------------------------------------------------\n\n /**\n * Callback half of the account-linking round-trip (plan 091): the\n * external identity is bound to the state-referenced user instead of\n * running the login path. No auth code and no session is minted; the\n * browser lands back on the account console with a marker param.\n */\n private async completeLink(\n event: IAppEvent,\n provider: OAuth2IdentityProvider | OpenIDIdentityProvider,\n state: OAuth2AuthorizationState,\n link: OAuth2AuthorizationStateLink,\n ) {\n // Fixed, server-derived return target (the account console page).\n // No client-supplied redirect exists on this path.\n const url = new URL(resolveURL(this.options.baseURL, 'account/connected-accounts'));\n\n const { code } = useRequestQuery(event);\n\n try {\n // The state is bound to the provider it was minted for, and the\n // provider must still be enabled at completion — a state cannot\n // be replayed against a different provider's callback, nor\n // complete a link after its provider was disabled.\n if (link.providerId !== provider.id || !provider.enabled) {\n throw new BadRequestError('The identity provider is not available for account linking.');\n }\n\n if (typeof code !== 'string' || code.length === 0) {\n throw new BadRequestError('The authorization code is missing.');\n }\n\n const authenticator = this.buildProviderAuthenticator(provider);\n\n const identity = await authenticator.resolveIdentity({ code });\n\n // A provider answering without a subject has nothing to bind. The\n // callback used to reach `link()`, which rejected it; the stash\n // would otherwise carry an empty providerUserId to the confirm.\n if (!identity.id) {\n throw new BadRequestError('The identity provider returned no subject.');\n }\n\n // Nothing is written here. This request is unauthenticated — the\n // browser round-trip cannot carry a bearer — so the only thing\n // distinguishing the browser that started the link from any other\n // would be the state's ip / user-agent binding, and both values\n // are chosen by whoever minted the state. An attacker minting a\n // state carrying their OWN userId and getting a victim to follow\n // it would otherwise bind the VICTIM's external identity to the\n // attacker's account (issue #3439).\n //\n // The resolved identity is stashed under a one-time handle\n // instead, and the account console confirms it with its bearer.\n // The stash keeps the requesting userId so the confirm can\n // require it to equal the AUTHENTICATED user: the handle is a\n // URL parameter, so it reaches browser history and proxy logs,\n // and on its own it must authorize nothing.\n const handle = await this.linkStore.save({\n providerId: provider.id,\n userId: link.userId,\n providerUserId: identity.id,\n providerUserName: this.pickIdentityCandidate(identity, 'name'),\n providerUserEmail: this.pickIdentityCandidate(identity, 'email'),\n });\n\n url.searchParams.set('linkHandle', handle);\n url.searchParams.set('provider', provider.id);\n } catch (e) {\n // The failure is swallowed into a redirect marker, so the log is\n // the only surface it can reach. Without this an unreachable or\n // rejecting provider is indistinguishable from a stale state.\n this.logger?.error(describeError(e, 'The identity-provider account link failed.'));\n\n url.searchParams.set('linkError', 'link_failed');\n }\n\n return sendRedirect(event, url.href);\n }\n\n /**\n * The bearer-authenticated half of the link (issue #3439): the account\n * row is written here, for the AUTHENTICATED user, never in the callback.\n */\n @DPost('/:id/link-confirm', [ForceLoggedInMiddleware])\n async confirmLink(\n @DPath('id') _id: string,\n @DBody() _data: IdentityProviderLinkConfirmPayload,\n @DContext() event: IAppEvent,\n ) : Promise<EntityRecordResponse<IdentityProviderAccount>> {\n const id = useRequestParamID(event);\n const provider = await this.resolve(id);\n\n if (!isOAuth2IdentityProvider(provider) && !isOpenIDIdentityProvider(provider)) {\n throw new BadRequestError('Only an identity-provider based on the oauth protocol supports account linking.');\n }\n\n if (!provider.enabled) {\n throw new BadRequestError('The identity provider is not enabled.');\n }\n\n const identity = useRequestIdentityOrFail(event);\n if (identity.type !== IdentityType.USER) {\n throw new BadRequestError('Only a user can link an identity provider account.');\n }\n\n const body = await readRequestBody(event);\n const handle = isObject(body) ? body.handle : undefined;\n if (typeof handle !== 'string' || handle.length === 0) {\n throw new BadRequestError('The account link handle is missing.');\n }\n\n const link = await this.linkStore.consume(handle);\n if (!link) {\n throw new BadRequestError('The account link request is unknown or expired.');\n }\n\n // The two checks that make the handle inert on its own: it may only\n // be redeemed by the user it was minted for, and only against the\n // provider it was minted for.\n if (link.userId !== identity.id || link.providerId !== provider.id) {\n throw new BadRequestError('The account link request does not belong to the authenticated user.');\n }\n\n const account = await this.accountManager.link(\n {\n id: link.providerUserId,\n // deliberately rebuilt from the stashed scalars: the resolved\n // identity carries the provider secret and the raw external\n // token payload, neither of which belongs in a cache\n attributeCandidates: {\n name: [link.providerUserName],\n email: [link.providerUserEmail],\n },\n data: {},\n provider,\n },\n identity.id,\n );\n\n // the confirm runs under a bearer, so unlike the callback it CAN be\n // attributed to a session and a route (the unlink emit's shape)\n const requestContext = useRequestEventContext();\n\n await this.eventService?.record({\n scope: EventScope.IDENTITY,\n name: EventName.IDENTITY_PROVIDER_LINKED,\n refType: EventRefType.IDENTITY_PROVIDER_ACCOUNT,\n refId: account.id,\n realmId: account.userRealmId ?? provider.realmId ?? null,\n actorType: IdentityType.USER,\n actorId: account.userId,\n actorName: identity.data.name ?? null,\n sessionId: requestContext?.sessionId ?? null,\n requestPath: requestContext?.requestPath ?? null,\n requestMethod: requestContext?.requestMethod ?? null,\n requestIpAddress: requestContext?.requestIpAddress ?? getRequestIP(event) ?? null,\n requestUserAgent: requestContext?.requestUserAgent ?? getRequestHeader(event, 'user-agent') ?? null,\n data: { providerId: provider.id, providerName: provider.name },\n });\n\n return { data: account, meta: {} };\n }\n\n private pickIdentityCandidate(\n identity: IdentityProviderIdentity,\n key: 'name' | 'email',\n ) : string | null {\n const candidates = identity.attributeCandidates?.[key] || [];\n for (const candidate of candidates) {\n if (typeof candidate === 'string' && candidate.length > 0) {\n return candidate;\n }\n }\n\n return null;\n }\n\n // ---------------------------------------------------------\n\n private buildHostedAuthorizeURL(codeRequest: OAuth2AuthorizationCodeRequest): URL {\n const url = new URL(resolveURL(this.options.baseURL, 'authorize'));\n\n const codeRequestKeys = Object.keys(codeRequest);\n for (const codeRequestKey_ of codeRequestKeys) {\n const codeRequestKey = codeRequestKey_ as keyof OAuth2AuthorizationCodeRequest;\n const codeRequestValue = codeRequest[codeRequestKey];\n // Preserve meaningful falsy values (e.g. max_age=0, which OIDC\n // treats as prompt=login) — only skip absent ones.\n if (typeof codeRequestValue !== 'undefined' && codeRequestValue !== null) {\n url.searchParams.set(codeRequestKey, String(codeRequestValue));\n }\n }\n\n return url;\n }\n\n // ---------------------------------------------------------\n\n private async resolve(id: string) {\n const entity = await this.repository.findOneById(id);\n\n if (!entity) {\n throw new EntityNotFoundError();\n }\n\n return entity;\n }\n\n // ---------------------------------------------------------\n\n private buildProviderAuthenticator(\n provider: OAuth2IdentityProvider | OpenIDIdentityProvider,\n options: { clientId?: string } = {},\n ) {\n return createIdentityProviderOAuth2Authenticator({\n accountManager: this.accountManager,\n provider,\n logger: this.logger,\n options: {\n baseURL: this.options.baseURL,\n clientId: options.clientId,\n },\n });\n }\n\n private async saveAuthorizationState(\n event: IAppEvent,\n data: Pick<OAuth2AuthorizationState, 'codeRequest' | 'link'> = {},\n ) : Promise<string> {\n return this.stateManager.save({\n ...data,\n ip: getRequestIP(event) ?? '',\n userAgent: getRequestHeader(event, 'user-agent') ?? undefined,\n });\n }\n\n private async verifyAuthorizationState(event: IAppEvent): Promise<OAuth2AuthorizationState> {\n const query = useRequestQuery(event);\n if (typeof query.state !== 'string') {\n throw OAuth2RequestError.stateInvalid();\n }\n\n return this.stateManager.verify(query.state, {\n ip: getRequestIP(event) ?? '',\n userAgent: getRequestHeader(event, 'user-agent') ?? undefined,\n });\n }\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import type { Logger } from '@authup/server-kit';
2
- import type { IEventService, IIdentityProviderAccountManager, IIdentityProviderRepository, IOAuth2AccessPolicyEvaluator, IOAuth2AuthorizationCodeIssuer, IOAuth2AuthorizationCodeRequestVerifier, IOAuth2AuthorizationStateManager, IOAuth2ClientRepository, IRealmRepository } from '../../../../../core/index.ts';
2
+ import type { IEventService, IIdentityProviderAccountLinkStore, IIdentityProviderAccountManager, IIdentityProviderRepository, IOAuth2AccessPolicyEvaluator, IOAuth2AuthorizationCodeIssuer, IOAuth2AuthorizationCodeRequestVerifier, IOAuth2AuthorizationStateManager, IRealmRepository } from '../../../../../core/index.ts';
3
3
  export type IdentityProviderControllerOptions = {
4
4
  baseURL: string;
5
5
  };
@@ -7,8 +7,8 @@ export type IdentityProviderControllerContext = {
7
7
  options: IdentityProviderControllerOptions;
8
8
  repository: IIdentityProviderRepository;
9
9
  realmRepository: IRealmRepository;
10
- clientRepository: IOAuth2ClientRepository;
11
10
  accountManager: IIdentityProviderAccountManager;
11
+ linkStore: IIdentityProviderAccountLinkStore;
12
12
  codeIssuer: IOAuth2AuthorizationCodeIssuer;
13
13
  codeRequestVerifier: IOAuth2AuthorizationCodeRequestVerifier;
14
14
  stateManager: IOAuth2AuthorizationStateManager;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../../src/adapters/http/controllers/entities/identity-provider/types.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,KAAK,EACR,aAAa,EACb,+BAA+B,EAC/B,2BAA2B,EAC3B,4BAA4B,EAC5B,8BAA8B,EAC9B,uCAAuC,EACvC,gCAAgC,EAChC,uBAAuB,EACvB,gBAAgB,EACnB,MAAM,8BAA8B,CAAC;AAEtC,MAAM,MAAM,iCAAiC,GAAG;IAC5C,OAAO,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,iCAAiC,GAAG;IAC5C,OAAO,EAAE,iCAAiC,CAAC;IAE3C,UAAU,EAAE,2BAA2B,CAAC;IACxC,eAAe,EAAE,gBAAgB,CAAC;IAClC,gBAAgB,EAAE,uBAAuB,CAAC;IAE1C,cAAc,EAAE,+BAA+B,CAAC;IAChD,UAAU,EAAE,8BAA8B,CAAC;IAC3C,mBAAmB,EAAE,uCAAuC,CAAC;IAC7D,YAAY,EAAE,gCAAgC,CAAC;IAE/C,qBAAqB,CAAC,EAAE,4BAA4B,CAAC;IACrD,YAAY,CAAC,EAAE,aAAa,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../../src/adapters/http/controllers/entities/identity-provider/types.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,KAAK,EACR,aAAa,EACb,iCAAiC,EACjC,+BAA+B,EAC/B,2BAA2B,EAC3B,4BAA4B,EAC5B,8BAA8B,EAC9B,uCAAuC,EACvC,gCAAgC,EAChC,gBAAgB,EACnB,MAAM,8BAA8B,CAAC;AAEtC,MAAM,MAAM,iCAAiC,GAAG;IAC5C,OAAO,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,iCAAiC,GAAG;IAC5C,OAAO,EAAE,iCAAiC,CAAC;IAE3C,UAAU,EAAE,2BAA2B,CAAC;IACxC,eAAe,EAAE,gBAAgB,CAAC;IAElC,cAAc,EAAE,+BAA+B,CAAC;IAChD,SAAS,EAAE,iCAAiC,CAAC;IAC7C,UAAU,EAAE,8BAA8B,CAAC;IAC3C,mBAAmB,EAAE,uCAAuC,CAAC;IAC7D,YAAY,EAAE,gCAAgC,CAAC;IAE/C,qBAAqB,CAAC,EAAE,4BAA4B,CAAC;IACrD,YAAY,CAAC,EAAE,aAAa,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC"}
@@ -51,6 +51,8 @@ import { IdentityInjectionKey } from "./modules/identity/constants.mjs";
51
51
  import { IdentityProviderAttributeMappingRepository } from "./modules/identity/repositories/provider/mapper/attribute.mjs";
52
52
  import { IdentityProviderPermissionMappingRepository } from "./modules/identity/repositories/provider/mapper/permission.mjs";
53
53
  import { IdentityProviderRoleMappingRepository } from "./modules/identity/repositories/provider/mapper/role.mjs";
54
+ import { CacheIdentityPrefix } from "./modules/identity/repositories/constants.mjs";
55
+ import { IdentityProviderAccountLinkStore } from "./modules/identity/repositories/provider/link.mjs";
54
56
  import { ClientIdentityRepository } from "./modules/identity/repositories/client.mjs";
55
57
  import { UserIdentityRepository } from "./modules/identity/repositories/user.mjs";
56
58
  import { LDAPInjectionKey } from "./modules/ldap/constants.mjs";
@@ -70,4 +72,4 @@ import { RuntimeModule } from "./modules/runtime/module.mjs";
70
72
  import "./modules/index.mjs";
71
73
  import { ApplicationBuilder } from "./builder.mjs";
72
74
  import { createApplication } from "./factory.mjs";
73
- export { ApplicationBuilder, AuthenticationInjectionKey, AuthenticationModule, CacheInjectionKey, CacheModule, ClientIdentityRepository, ClientPermissionRepositoryAdapter, ClientRepositoryAdapter, ClientRoleRepositoryAdapter, ClientScopeRepositoryAdapter, ComponentsModule, CompositeProvisioningSource, ConfigEnvironmentVariableName, ConfigInjectionKey, ConfigModule, DatabaseInjectionKey, DatabaseModule, DefaultProvisioningSource, EventRepositoryAdapter, FileProvisioningSource, HTTPInjectionKey, HTTPModule, IdentityInjectionKey, IdentityModule, IdentityProviderAccountRepositoryAdapter, IdentityProviderAttributeMappingRepository, IdentityProviderPermissionMappingRepository, IdentityProviderRepositoryAdapter, IdentityProviderRoleMappingRepository, IdentityProviderRoleMappingRepositoryAdapter, KeyRepositoryAdapter, LDAPInjectionKey, LazyWildcardRealmProvisioner, LdapModule, LoggerInjectionKey, LoggerModule, MailInjectionKey, MailModule, MailTemplateRendererInjectionKey, MetricsInjectionKey, ModuleName, OAuth2InjectionToken, OAuth2Module, PermissionDatabaseProvider, PermissionPolicyRepositoryAdapter, PermissionRepositoryAdapter, PolicyRepositoryAdapter, PromAuthFlowMetrics, ProvisionerModule, ProvisioningInjectionKey, RealmRepositoryAdapter, RoleAttributeRepositoryAdapter, RolePermissionRepositoryAdapter, RoleRepositoryAdapter, RuntimeModule, ScopeRepositoryAdapter, SessionRepository, TrustAnchorRepositoryAdapter, UserAttributeRepositoryAdapter, UserAuthenticatorRepositoryAdapter, UserIdentityRepository, UserPermissionRepositoryAdapter, UserRepositoryAdapter, UserRoleRepositoryAdapter, createApplication, expandToOrigins, getAppOrigins, readConfig, readConfigRaw, readConfigRawFromEnv, readConfigRawFromFS };
75
+ export { ApplicationBuilder, AuthenticationInjectionKey, AuthenticationModule, CacheIdentityPrefix, CacheInjectionKey, CacheModule, ClientIdentityRepository, ClientPermissionRepositoryAdapter, ClientRepositoryAdapter, ClientRoleRepositoryAdapter, ClientScopeRepositoryAdapter, ComponentsModule, CompositeProvisioningSource, ConfigEnvironmentVariableName, ConfigInjectionKey, ConfigModule, DatabaseInjectionKey, DatabaseModule, DefaultProvisioningSource, EventRepositoryAdapter, FileProvisioningSource, HTTPInjectionKey, HTTPModule, IdentityInjectionKey, IdentityModule, IdentityProviderAccountLinkStore, IdentityProviderAccountRepositoryAdapter, IdentityProviderAttributeMappingRepository, IdentityProviderPermissionMappingRepository, IdentityProviderRepositoryAdapter, IdentityProviderRoleMappingRepository, IdentityProviderRoleMappingRepositoryAdapter, KeyRepositoryAdapter, LDAPInjectionKey, LazyWildcardRealmProvisioner, LdapModule, LoggerInjectionKey, LoggerModule, MailInjectionKey, MailModule, MailTemplateRendererInjectionKey, MetricsInjectionKey, ModuleName, OAuth2InjectionToken, OAuth2Module, PermissionDatabaseProvider, PermissionPolicyRepositoryAdapter, PermissionRepositoryAdapter, PolicyRepositoryAdapter, PromAuthFlowMetrics, ProvisionerModule, ProvisioningInjectionKey, RealmRepositoryAdapter, RoleAttributeRepositoryAdapter, RolePermissionRepositoryAdapter, RoleRepositoryAdapter, RuntimeModule, ScopeRepositoryAdapter, SessionRepository, TrustAnchorRepositoryAdapter, UserAttributeRepositoryAdapter, UserAuthenticatorRepositoryAdapter, UserIdentityRepository, UserPermissionRepositoryAdapter, UserRepositoryAdapter, UserRoleRepositoryAdapter, createApplication, expandToOrigins, getAppOrigins, readConfig, readConfigRaw, readConfigRawFromEnv, readConfigRawFromFS };
@@ -438,6 +438,7 @@ var HTTPControllerModule = class {
438
438
  const config = container.resolve(ConfigInjectionKey);
439
439
  const dataSource = container.resolve(DatabaseInjectionKey.DataSource);
440
440
  const accountManager = container.resolve(IdentityInjectionKey.ProviderAccountManager);
441
+ const linkStore = container.resolve(IdentityInjectionKey.ProviderAccountLinkStore);
441
442
  const codeIssuer = container.resolve(OAuth2InjectionToken.AuthorizationCodeIssuer);
442
443
  const codeRequestVerifier = container.resolve(OAuth2InjectionToken.AuthorizationCodeRequestVerifier);
443
444
  const stateManager = container.resolve(OAuth2InjectionToken.AuthorizationStateManager);
@@ -450,8 +451,8 @@ var HTTPControllerModule = class {
450
451
  options: { baseURL: config.publicUrl },
451
452
  repository,
452
453
  realmRepository: new RealmRepositoryAdapter(realmRepository),
453
- clientRepository: container.resolve(OAuth2InjectionToken.ClientRepository),
454
454
  accountManager,
455
+ linkStore,
455
456
  codeIssuer,
456
457
  codeRequestVerifier,
457
458
  stateManager,