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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/dist/adapters/database/domains/client-scope/entity.d.ts.map +1 -1
  2. package/dist/adapters/database/domains/client-scope/entity.mjs +16 -8
  3. package/dist/adapters/database/domains/client-scope/entity.mjs.map +1 -1
  4. package/dist/adapters/database/domains/identity-provider-account/entity.d.ts.map +1 -1
  5. package/dist/adapters/database/domains/identity-provider-account/entity.mjs +1 -1
  6. package/dist/adapters/database/domains/identity-provider-account/entity.mjs.map +1 -1
  7. package/dist/adapters/database/migrations/mysql/1786631686318-SortIndexesAndAccountUniqueness.d.ts +50 -0
  8. package/dist/adapters/database/migrations/mysql/1786631686318-SortIndexesAndAccountUniqueness.d.ts.map +1 -0
  9. package/dist/adapters/database/migrations/mysql/1786631686318-SortIndexesAndAccountUniqueness.mjs +86 -0
  10. package/dist/adapters/database/migrations/mysql/1786631686318-SortIndexesAndAccountUniqueness.mjs.map +1 -0
  11. package/dist/adapters/database/migrations/postgres/1786631686318-SortIndexesAndAccountUniqueness.d.ts +42 -0
  12. package/dist/adapters/database/migrations/postgres/1786631686318-SortIndexesAndAccountUniqueness.d.ts.map +1 -0
  13. package/dist/adapters/database/migrations/postgres/1786631686318-SortIndexesAndAccountUniqueness.mjs +80 -0
  14. package/dist/adapters/database/migrations/postgres/1786631686318-SortIndexesAndAccountUniqueness.mjs.map +1 -0
  15. package/dist/adapters/http/adapters/oauth2/grant-types/utils/credentials.mjs +1 -1
  16. package/dist/adapters/http/adapters/oauth2/grant-types/utils/guess.mjs +1 -1
  17. package/dist/adapters/http/controllers/entities/client/module.mjs +2 -2
  18. package/dist/adapters/http/controllers/entities/identity-provider/module.d.ts +3 -5
  19. package/dist/adapters/http/controllers/entities/identity-provider/module.d.ts.map +1 -1
  20. package/dist/adapters/http/controllers/entities/identity-provider/module.mjs +54 -82
  21. package/dist/adapters/http/controllers/entities/identity-provider/module.mjs.map +1 -1
  22. package/dist/adapters/http/controllers/entities/identity-provider/types.d.ts +6 -4
  23. package/dist/adapters/http/controllers/entities/identity-provider/types.d.ts.map +1 -1
  24. package/dist/adapters/http/controllers/entities/realm/module.mjs +2 -2
  25. package/dist/adapters/http/controllers/entities/session/module.mjs +2 -2
  26. package/dist/adapters/http/controllers/entities/user/module.mjs +2 -2
  27. package/dist/adapters/http/controllers/entities/user-authenticator/module.mjs +2 -2
  28. package/dist/adapters/http/controllers/workflows/authorize/module.d.ts.map +1 -1
  29. package/dist/adapters/http/controllers/workflows/authorize/module.mjs +9 -5
  30. package/dist/adapters/http/controllers/workflows/authorize/module.mjs.map +1 -1
  31. package/dist/adapters/http/controllers/workflows/token/utils/extract.mjs +2 -1
  32. package/dist/adapters/http/controllers/workflows/token/utils/extract.mjs.map +1 -1
  33. package/dist/adapters/http/middleware/built-in/assets.mjs +1 -1
  34. package/dist/adapters/http/request/helpers/param-id.d.ts.map +1 -1
  35. package/dist/adapters/http/request/helpers/param-id.mjs +2 -1
  36. package/dist/adapters/http/request/helpers/param-id.mjs.map +1 -1
  37. package/dist/adapters/http/ui/account-console/module.mjs +1 -1
  38. package/dist/adapters/http/ui/auth-console/http-client.mjs +1 -1
  39. package/dist/adapters/http/ui/auth-console/module.mjs +1 -1
  40. package/dist/adapters/http/ui/console-packages/module.d.ts +1 -1
  41. package/dist/adapters/http/ui/console-packages/module.mjs +2 -2
  42. package/dist/adapters/http/ui/console-packages/module.mjs.map +1 -1
  43. package/dist/adapters/http/ui/shared/html.d.ts.map +1 -1
  44. package/dist/adapters/http/ui/shared/html.mjs +1 -0
  45. package/dist/adapters/http/ui/shared/html.mjs.map +1 -1
  46. package/dist/app/modules/config/read/env.mjs +1 -1
  47. package/dist/app/modules/database/repositories/event/repository.d.ts +2 -2
  48. package/dist/app/modules/database/repositories/event/repository.d.ts.map +1 -1
  49. package/dist/app/modules/database/repositories/event/repository.mjs +23 -6
  50. package/dist/app/modules/database/repositories/event/repository.mjs.map +1 -1
  51. package/dist/app/modules/database/repositories/identity-provider-account/repository.d.ts.map +1 -1
  52. package/dist/app/modules/database/repositories/identity-provider-account/repository.mjs +9 -1
  53. package/dist/app/modules/database/repositories/identity-provider-account/repository.mjs.map +1 -1
  54. package/dist/app/modules/http/modules/controller.d.ts.map +1 -1
  55. package/dist/app/modules/http/modules/controller.mjs +16 -5
  56. package/dist/app/modules/http/modules/controller.mjs.map +1 -1
  57. package/dist/app/modules/identity/module.d.ts.map +1 -1
  58. package/dist/app/modules/identity/module.mjs +6 -3
  59. package/dist/app/modules/identity/module.mjs.map +1 -1
  60. package/dist/app/modules/identity/repositories/user.d.ts +1 -0
  61. package/dist/app/modules/identity/repositories/user.d.ts.map +1 -1
  62. package/dist/app/modules/identity/repositories/user.mjs +3 -0
  63. package/dist/app/modules/identity/repositories/user.mjs.map +1 -1
  64. package/dist/app/modules/oauth2/repositories/authorize/state/repository.d.ts +1 -2
  65. package/dist/app/modules/oauth2/repositories/authorize/state/repository.d.ts.map +1 -1
  66. package/dist/app/modules/oauth2/repositories/authorize/state/repository.mjs +7 -16
  67. package/dist/app/modules/oauth2/repositories/authorize/state/repository.mjs.map +1 -1
  68. package/dist/app/modules/runtime/module.mjs +2 -2
  69. package/dist/cli/commands/migration.mjs +1 -1
  70. package/dist/core/entities/client-scope/schema.d.ts.map +1 -1
  71. package/dist/core/entities/client-scope/schema.mjs +16 -1
  72. package/dist/core/entities/client-scope/schema.mjs.map +1 -1
  73. package/dist/core/entities/event/constants.d.ts +9 -0
  74. package/dist/core/entities/event/constants.d.ts.map +1 -1
  75. package/dist/core/entities/event/constants.mjs +9 -1
  76. package/dist/core/entities/event/constants.mjs.map +1 -1
  77. package/dist/core/entities/event/index.mjs +2 -2
  78. package/dist/core/entities/event/types.d.ts +11 -2
  79. package/dist/core/entities/event/types.d.ts.map +1 -1
  80. package/dist/core/entities/identity-provider-account/types.d.ts +6 -0
  81. package/dist/core/entities/identity-provider-account/types.d.ts.map +1 -1
  82. package/dist/core/entities/identity-provider-account/types.mjs.map +1 -1
  83. package/dist/core/entities/index.mjs +2 -2
  84. package/dist/core/identity/entities/user/types.d.ts +1 -0
  85. package/dist/core/identity/entities/user/types.d.ts.map +1 -1
  86. package/dist/core/identity/index.mjs +10 -10
  87. package/dist/core/identity/provider/account/module.d.ts +3 -0
  88. package/dist/core/identity/provider/account/module.d.ts.map +1 -1
  89. package/dist/core/identity/provider/account/module.mjs +54 -27
  90. package/dist/core/identity/provider/account/module.mjs.map +1 -1
  91. package/dist/core/identity/provider/account/types.d.ts +7 -0
  92. package/dist/core/identity/provider/account/types.d.ts.map +1 -1
  93. package/dist/core/identity/provider/authentication/index.mjs +1 -1
  94. package/dist/core/identity/provider/index.mjs +10 -10
  95. package/dist/core/index.mjs +14 -12
  96. package/dist/core/oauth2/authorization/code-request/verifier/module.d.ts.map +1 -1
  97. package/dist/core/oauth2/authorization/code-request/verifier/module.mjs +7 -1
  98. package/dist/core/oauth2/authorization/code-request/verifier/module.mjs.map +1 -1
  99. package/dist/core/oauth2/authorization/state/module.mjs +1 -2
  100. package/dist/core/oauth2/authorization/state/module.mjs.map +1 -1
  101. package/dist/core/oauth2/authorization/state/types.d.ts +5 -2
  102. package/dist/core/oauth2/authorization/state/types.d.ts.map +1 -1
  103. package/dist/core/oauth2/end-session/service.d.ts.map +1 -1
  104. package/dist/core/oauth2/end-session/service.mjs +1 -0
  105. package/dist/core/oauth2/end-session/service.mjs.map +1 -1
  106. package/dist/core/oauth2/federated-login/index.d.ts +3 -0
  107. package/dist/core/oauth2/federated-login/index.d.ts.map +1 -0
  108. package/dist/core/oauth2/federated-login/index.mjs +6 -0
  109. package/dist/core/oauth2/federated-login/module.d.ts +20 -0
  110. package/dist/core/oauth2/federated-login/module.d.ts.map +1 -0
  111. package/dist/core/oauth2/federated-login/module.mjs +137 -0
  112. package/dist/core/oauth2/federated-login/module.mjs.map +1 -0
  113. package/dist/core/oauth2/federated-login/types.d.ts +138 -0
  114. package/dist/core/oauth2/federated-login/types.d.ts.map +1 -0
  115. package/dist/core/oauth2/federated-login/types.mjs +31 -0
  116. package/dist/core/oauth2/federated-login/types.mjs.map +1 -0
  117. package/dist/core/oauth2/index.d.ts +1 -0
  118. package/dist/core/oauth2/index.d.ts.map +1 -1
  119. package/dist/core/oauth2/index.mjs +4 -1
  120. package/dist/core/provisioning/entities/policy/validator.d.ts.map +1 -1
  121. package/dist/core/provisioning/entities/policy/validator.mjs +2 -1
  122. package/dist/core/provisioning/entities/policy/validator.mjs.map +1 -1
  123. package/dist/core/provisioning/entities/realm/wildcard-validator.d.ts.map +1 -1
  124. package/dist/core/provisioning/entities/realm/wildcard-validator.mjs +2 -1
  125. package/dist/core/provisioning/entities/realm/wildcard-validator.mjs.map +1 -1
  126. package/dist/core/security/policy/evaluator.mjs.map +1 -1
  127. package/dist/node_modules/@ebec/core/dist/index.mjs +130 -0
  128. package/dist/node_modules/@ebec/core/dist/index.mjs.map +1 -0
  129. package/dist/swagger.json +30 -2
  130. package/dist/utils/error.d.ts.map +1 -1
  131. package/dist/utils/error.mjs +5 -6
  132. package/dist/utils/error.mjs.map +1 -1
  133. package/package.json +28 -28
@@ -1,4 +1,5 @@
1
1
  import type { IdentityProviderAccount } from '@authup/core-kit';
2
+ import type { Logger } from '@authup/server-kit';
2
3
  import type { IUserIdentityRepository } from '../../entities/index.ts';
3
4
  import type { IIdentityProviderMapper } from '../mapper/index.ts';
4
5
  import type { IdentityProviderIdentity } from '../types.ts';
@@ -43,6 +44,12 @@ export type IdentityProviderAccountManagerContext = {
43
44
  attributeMapper: IIdentityProviderMapper;
44
45
  permissionMapper: IIdentityProviderMapper;
45
46
  roleMapper: IIdentityProviderMapper;
47
+ /**
48
+ * Reports the compensating actions `save()` takes when it loses a
49
+ * concurrent first login. They must not fail the login, so they are
50
+ * swallowed. Without a logger they are also invisible.
51
+ */
52
+ logger?: Logger;
46
53
  repository: IIdentityProviderAccountRepository;
47
54
  userRepository: IUserIdentityRepository;
48
55
  };
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/core/identity/provider/account/types.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AACvE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAClE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAG5D,OAAO,KAAK,EAAE,kCAAkC,EAAE,MAAM,sDAAsD,CAAC;AAE/G,YAAY,EAAE,kCAAkC,EAAE,MAAM,sDAAsD,CAAC;AAE/G;;;;;;;;;;GAUG;AACH,MAAM,MAAM,2BAA2B,GAAG;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;OAKG;IACH,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACrC,CAAC;AAEF,MAAM,WAAW,iCAAiC;IAC9C;;OAEG;IACH,IAAI,CAAC,IAAI,EAAE,2BAA2B,GAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAE1D;;;OAGG;IACH,OAAO,CAAC,MAAM,EAAE,MAAM,GAAI,OAAO,CAAC,2BAA2B,GAAG,IAAI,CAAC,CAAC;CACzE;AAED,MAAM,MAAM,qCAAqC,GAAG;IAChD,eAAe,EAAE,uBAAuB,CAAC;IACzC,gBAAgB,EAAE,uBAAuB,CAAC;IAC1C,UAAU,EAAE,uBAAuB,CAAC;IAEpC,UAAU,EAAE,kCAAkC,CAAC;IAC/C,cAAc,EAAE,uBAAuB,CAAA;CAC1C,CAAC;AAEF,MAAM,WAAW,+BAA+B;IAC5C;;;;OAIG;IACH,IAAI,CAAC,QAAQ,EAAE,wBAAwB,GAAI,OAAO,CAAC,uBAAuB,CAAC,CAAC;IAE5E;;;;;;;;OAQG;IACH,IAAI,CAAC,QAAQ,EAAE,wBAAwB,EAAE,MAAM,EAAE,MAAM,GAAI,OAAO,CAAC,uBAAuB,CAAC,CAAC;CAC/F"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/core/identity/provider/account/types.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AACvE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAClE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAG5D,OAAO,KAAK,EAAE,kCAAkC,EAAE,MAAM,sDAAsD,CAAC;AAE/G,YAAY,EAAE,kCAAkC,EAAE,MAAM,sDAAsD,CAAC;AAE/G;;;;;;;;;;GAUG;AACH,MAAM,MAAM,2BAA2B,GAAG;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;OAKG;IACH,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACrC,CAAC;AAEF,MAAM,WAAW,iCAAiC;IAC9C;;OAEG;IACH,IAAI,CAAC,IAAI,EAAE,2BAA2B,GAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAE1D;;;OAGG;IACH,OAAO,CAAC,MAAM,EAAE,MAAM,GAAI,OAAO,CAAC,2BAA2B,GAAG,IAAI,CAAC,CAAC;CACzE;AAED,MAAM,MAAM,qCAAqC,GAAG;IAChD,eAAe,EAAE,uBAAuB,CAAC;IACzC,gBAAgB,EAAE,uBAAuB,CAAC;IAC1C,UAAU,EAAE,uBAAuB,CAAC;IACpC;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,UAAU,EAAE,kCAAkC,CAAC;IAC/C,cAAc,EAAE,uBAAuB,CAAA;CAC1C,CAAC;AAEF,MAAM,WAAW,+BAA+B;IAC5C;;;;OAIG;IACH,IAAI,CAAC,QAAQ,EAAE,wBAAwB,GAAI,OAAO,CAAC,uBAAuB,CAAC,CAAC;IAE5E;;;;;;;;OAQG;IACH,IAAI,CAAC,QAAQ,EAAE,wBAAwB,EAAE,MAAM,EAAE,MAAM,GAAI,OAAO,CAAC,uBAAuB,CAAC,CAAC;CAC/F"}
@@ -12,6 +12,6 @@ import { IdentityProviderGoogleAuthenticator } from "./presets/google.mjs";
12
12
  import { IdentityProviderInstagramAuthenticator } from "./presets/instagram.mjs";
13
13
  import { IdentityProviderPaypalAuthenticator } from "./presets/paypal.mjs";
14
14
  import "./presets/index.mjs";
15
- import { IDENTITY_PROVIDER_LDAP_COLLECTION_AUTHENTICATOR_TOKEN } from "./constants.mjs";
16
15
  import { createIdentityProviderOAuth2Authenticator } from "./factory.mjs";
16
+ import { IDENTITY_PROVIDER_LDAP_COLLECTION_AUTHENTICATOR_TOKEN } from "./constants.mjs";
17
17
  export { IDENTITY_PROVIDER_LDAP_COLLECTION_AUTHENTICATOR_TOKEN, IdentityProviderFacebookAuthenticator, IdentityProviderGithubAuthenticator, IdentityProviderGoogleAuthenticator, IdentityProviderInstagramAuthenticator, IdentityProviderLdapAuthenticator, IdentityProviderLdapCollectionAuthenticator, IdentityProviderOAuth2Authenticator, IdentityProviderOpenIDAuthenticator, IdentityProviderPaypalAuthenticator, createIdentityProviderOAuth2Authenticator };
@@ -1,6 +1,16 @@
1
1
  import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
+ import { IdentityProviderLdapAuthenticator } from "./authentication/protocols/ldap/module.mjs";
5
+ import { IdentityProviderLdapCollectionAuthenticator } from "./authentication/protocols/ldap/collection.mjs";
6
+ import { IdentityProviderOAuth2Authenticator } from "./authentication/protocols/oauth2/module.mjs";
7
+ import { IdentityProviderOpenIDAuthenticator } from "./authentication/protocols/open-id/module.mjs";
8
+ import { IdentityProviderFacebookAuthenticator } from "./authentication/presets/facebook.mjs";
9
+ import { IdentityProviderGithubAuthenticator } from "./authentication/presets/github.mjs";
10
+ import { IdentityProviderGoogleAuthenticator } from "./authentication/presets/google.mjs";
11
+ import { IdentityProviderInstagramAuthenticator } from "./authentication/presets/instagram.mjs";
12
+ import { IdentityProviderPaypalAuthenticator } from "./authentication/presets/paypal.mjs";
13
+ import { createIdentityProviderOAuth2Authenticator } from "./authentication/factory.mjs";
4
14
  import { IDENTITY_PROVIDER_ACCOUNT_LINK_TTL } from "./account/constants.mjs";
5
15
  import { IDENTITY_PROVIDER_ACCOUNT_ALREADY_LINKED_ERROR_INSTANCE, IdentityProviderAccountAlreadyLinkedError, isIdentityProviderAccountAlreadyLinkedError } from "./account/error.mjs";
6
16
  import { IdentityProviderMapperOperation } from "./mapper/constants.mjs";
@@ -11,16 +21,6 @@ import { IdentityProviderPermissionMapper } from "./mapper/permission/module.mjs
11
21
  import "./mapper/index.mjs";
12
22
  import { IdentityProviderAccountManager } from "./account/module.mjs";
13
23
  import "./account/index.mjs";
14
- import { IdentityProviderLdapAuthenticator } from "./authentication/protocols/ldap/module.mjs";
15
- import { IdentityProviderLdapCollectionAuthenticator } from "./authentication/protocols/ldap/collection.mjs";
16
- import { IdentityProviderOAuth2Authenticator } from "./authentication/protocols/oauth2/module.mjs";
17
- import { IdentityProviderOpenIDAuthenticator } from "./authentication/protocols/open-id/module.mjs";
18
- import { IdentityProviderFacebookAuthenticator } from "./authentication/presets/facebook.mjs";
19
- import { IdentityProviderGithubAuthenticator } from "./authentication/presets/github.mjs";
20
- import { IdentityProviderGoogleAuthenticator } from "./authentication/presets/google.mjs";
21
- import { IdentityProviderInstagramAuthenticator } from "./authentication/presets/instagram.mjs";
22
- import { IdentityProviderPaypalAuthenticator } from "./authentication/presets/paypal.mjs";
23
24
  import { IDENTITY_PROVIDER_LDAP_COLLECTION_AUTHENTICATOR_TOKEN } from "./authentication/constants.mjs";
24
- import { createIdentityProviderOAuth2Authenticator } from "./authentication/factory.mjs";
25
25
  import "./authentication/index.mjs";
26
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";
@@ -144,17 +156,7 @@ import { IdentityProviderAttributeMapper } from "./identity/provider/mapper/attr
144
156
  import { IdentityProviderRoleMapper } from "./identity/provider/mapper/role/module.mjs";
145
157
  import { IdentityProviderPermissionMapper } from "./identity/provider/mapper/permission/module.mjs";
146
158
  import { IdentityProviderAccountManager } from "./identity/provider/account/module.mjs";
147
- import { IdentityProviderLdapAuthenticator } from "./identity/provider/authentication/protocols/ldap/module.mjs";
148
- import { IdentityProviderLdapCollectionAuthenticator } from "./identity/provider/authentication/protocols/ldap/collection.mjs";
149
- import { IdentityProviderOAuth2Authenticator } from "./identity/provider/authentication/protocols/oauth2/module.mjs";
150
- import { IdentityProviderOpenIDAuthenticator } from "./identity/provider/authentication/protocols/open-id/module.mjs";
151
- import { IdentityProviderFacebookAuthenticator } from "./identity/provider/authentication/presets/facebook.mjs";
152
- import { IdentityProviderGithubAuthenticator } from "./identity/provider/authentication/presets/github.mjs";
153
- import { IdentityProviderGoogleAuthenticator } from "./identity/provider/authentication/presets/google.mjs";
154
- import { IdentityProviderInstagramAuthenticator } from "./identity/provider/authentication/presets/instagram.mjs";
155
- import { IdentityProviderPaypalAuthenticator } from "./identity/provider/authentication/presets/paypal.mjs";
156
159
  import { IDENTITY_PROVIDER_LDAP_COLLECTION_AUTHENTICATOR_TOKEN } from "./identity/provider/authentication/constants.mjs";
157
- import { createIdentityProviderOAuth2Authenticator } from "./identity/provider/authentication/factory.mjs";
158
160
  import { RegistrationService } from "./identity/registration/service.mjs";
159
161
  import { IdentityResolver } from "./identity/resolver/module.mjs";
160
162
  import { IdentityRoleProvider } from "./identity/role/module.mjs";
@@ -188,4 +190,4 @@ import { UserProvisioningSynchronizer } from "./provisioning/synchronizer/user/m
188
190
  import { WildcardRealmProvisioner, expandWildcardRealmEntry, extractWildcardRealmEntry } from "./provisioning/wildcard/module.mjs";
189
191
  import "./provisioning/index.mjs";
190
192
  import "./security/index.mjs";
191
- 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_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, 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;CAmGhE"}
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
@@ -36,6 +36,12 @@ var OAuth2AuthorizationCodeRequestVerifier = class {
36
36
  } else data.scope = scopeNames.join(" ");
37
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();
41
47
  redirectUriVerified = true;
@@ -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 //\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 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,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;EAWjC,IAAIC,sBAAsB;EAC1B,IAAIzB,KAAK0B,cAAc;GACnB,MAAMC,eAAetB,OAAOK,YAAYkB,MAAM,GAAA;GAO9C,IAAI,CAAC1C,iBAAiBc,KAAK0B,cAAcC,YAAAA,GACrC,MAAMtC,iBAAiBsB,oBAAmB;GAG9Cc,sBAAsB;EAC1B;EAEA,OAAO;GACHzB;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 };
@@ -0,0 +1,20 @@
1
+ import type { Logger } from '@authup/server-kit';
2
+ import type { IEventService, IRealmRepository } from '../../entities/index.ts';
3
+ import type { IIdentityProviderAccountManager } from '../../identity/provider/account/types.ts';
4
+ import type { IOAuth2AccessPolicyEvaluator } from '../access-policy/index.ts';
5
+ import type { IOAuth2AuthorizationCodeIssuer, IOAuth2AuthorizationCodeRequestVerifier } from '../authorization/index.ts';
6
+ import type { IOAuth2FederatedLoginService, OAuth2FederatedLoginAuthenticatorFactory, OAuth2FederatedLoginCompleteInput, OAuth2FederatedLoginCompleteResult, OAuth2FederatedLoginServiceContext, OAuth2FederatedLoginServiceOptions } from './types.ts';
7
+ export declare class OAuth2FederatedLoginService implements IOAuth2FederatedLoginService {
8
+ protected options: OAuth2FederatedLoginServiceOptions;
9
+ protected accountManager: IIdentityProviderAccountManager;
10
+ protected realmRepository: IRealmRepository;
11
+ protected codeRequestVerifier: IOAuth2AuthorizationCodeRequestVerifier;
12
+ protected codeIssuer: IOAuth2AuthorizationCodeIssuer;
13
+ protected accessPolicyEvaluator?: IOAuth2AccessPolicyEvaluator;
14
+ protected eventService?: IEventService;
15
+ protected logger?: Logger;
16
+ protected authenticatorFactory: OAuth2FederatedLoginAuthenticatorFactory;
17
+ constructor(ctx: OAuth2FederatedLoginServiceContext);
18
+ complete(input: OAuth2FederatedLoginCompleteInput): Promise<OAuth2FederatedLoginCompleteResult>;
19
+ }
20
+ //# sourceMappingURL=module.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../../../src/core/oauth2/federated-login/module.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAEjD,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAG/E,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,0CAA0C,CAAC;AAGhG,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,2BAA2B,CAAC;AAC9E,OAAO,KAAK,EACR,8BAA8B,EAC9B,uCAAuC,EAE1C,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EACR,4BAA4B,EAC5B,wCAAwC,EACxC,iCAAiC,EACjC,kCAAkC,EAClC,kCAAkC,EAClC,kCAAkC,EACrC,MAAM,YAAY,CAAC;AAGpB,qBAAa,2BAA4B,YAAW,4BAA4B;IAC5E,SAAS,CAAC,OAAO,EAAG,kCAAkC,CAAC;IAEvD,SAAS,CAAC,cAAc,EAAG,+BAA+B,CAAC;IAE3D,SAAS,CAAC,eAAe,EAAG,gBAAgB,CAAC;IAE7C,SAAS,CAAC,mBAAmB,EAAG,uCAAuC,CAAC;IAExE,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;IAE3B,SAAS,CAAC,oBAAoB,EAAG,wCAAwC,CAAC;gBAE9D,GAAG,EAAE,kCAAkC;IAqB7C,QAAQ,CAAC,KAAK,EAAE,iCAAiC,GAAG,OAAO,CAAC,kCAAkC,CAAC;CA0LxG"}
@@ -0,0 +1,137 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ import { toIdentityPolicyData } from "../../identity/permission/identity-policy-data.mjs";
5
+ import { createIdentityProviderOAuth2Authenticator } from "../../identity/provider/authentication/factory.mjs";
6
+ import { OAuth2FederatedLoginRefusal } from "./types.mjs";
7
+ import { isSafeRedirectURLScheme } from "@authup/kit";
8
+ import { EventName, EventRefType, EventScope, IdentityType, SessionAuthMethod } from "@authup/core-kit";
9
+ import { InternalError } from "@authup/errors";
10
+ import { OAuth2ErrorCode, OAuth2RequestError, isOAuth2Error } from "@authup/specs";
11
+ //#region src/core/oauth2/federated-login/module.ts
12
+ var OAuth2FederatedLoginService = class {
13
+ options;
14
+ accountManager;
15
+ realmRepository;
16
+ codeRequestVerifier;
17
+ codeIssuer;
18
+ accessPolicyEvaluator;
19
+ eventService;
20
+ logger;
21
+ authenticatorFactory;
22
+ constructor(ctx) {
23
+ this.options = ctx.options;
24
+ this.accountManager = ctx.accountManager;
25
+ this.realmRepository = ctx.realmRepository;
26
+ this.codeRequestVerifier = ctx.codeRequestVerifier;
27
+ this.codeIssuer = ctx.codeIssuer;
28
+ this.accessPolicyEvaluator = ctx.accessPolicyEvaluator;
29
+ this.eventService = ctx.eventService;
30
+ this.logger = ctx.logger;
31
+ this.authenticatorFactory = ctx.authenticatorFactory ?? ((provider, options) => createIdentityProviderOAuth2Authenticator({
32
+ accountManager: this.accountManager,
33
+ provider,
34
+ logger: options.logger,
35
+ options: {
36
+ baseURL: options.baseURL,
37
+ clientId: options.clientId
38
+ }
39
+ }));
40
+ }
41
+ async complete(input) {
42
+ const { code, codeRequest, provider } = input;
43
+ let verified;
44
+ try {
45
+ verified = await this.codeRequestVerifier.verify(codeRequest);
46
+ } catch (e) {
47
+ if (!isOAuth2Error(e)) throw e;
48
+ return {
49
+ kind: "refused",
50
+ refusal: OAuth2FederatedLoginRefusal.CODE_REQUEST,
51
+ codeRequest
52
+ };
53
+ }
54
+ if (provider.realmId && verified.client.realmId !== provider.realmId) throw OAuth2RequestError.malformed("The provider and client realm do not match.");
55
+ const redirectUri = verified.data.redirect_uri;
56
+ if (!verified.redirectUriVerified || !redirectUri) throw OAuth2RequestError.malformed("The redirect_uri was not verified.");
57
+ if (!isSafeRedirectURLScheme(redirectUri)) throw new InternalError("The redirect_uri scheme is not allowed.");
58
+ if (!provider.enabled) return {
59
+ kind: "refused",
60
+ refusal: OAuth2FederatedLoginRefusal.PROVIDER_DISABLED,
61
+ error: OAuth2ErrorCode.LOGIN_REQUIRED,
62
+ codeRequest: verified.data
63
+ };
64
+ const user = await this.authenticatorFactory(provider, {
65
+ baseURL: this.options.baseURL,
66
+ clientId: verified.data.client_id,
67
+ logger: this.logger
68
+ }).authenticate({ code });
69
+ if (!user.active) return {
70
+ kind: "refused",
71
+ refusal: OAuth2FederatedLoginRefusal.USER_INACTIVE,
72
+ error: OAuth2ErrorCode.ACCESS_DENIED,
73
+ codeRequest: verified.data
74
+ };
75
+ const realm = await this.realmRepository.resolve(provider.realmId, true);
76
+ if (verified.client.accessPolicyId) {
77
+ let allowed = false;
78
+ const subject = toIdentityPolicyData({
79
+ type: IdentityType.USER,
80
+ data: {
81
+ ...user,
82
+ realm
83
+ }
84
+ });
85
+ if (this.accessPolicyEvaluator && subject) allowed = await this.accessPolicyEvaluator.evaluate(verified.client.accessPolicyId, subject);
86
+ if (!allowed) return {
87
+ kind: "refused",
88
+ refusal: OAuth2FederatedLoginRefusal.ACCESS_DENIED,
89
+ error: OAuth2ErrorCode.ACCESS_DENIED,
90
+ codeRequest: verified.data
91
+ };
92
+ }
93
+ const authorizationCode = await this.codeIssuer.issue(verified.data, {
94
+ type: IdentityType.USER,
95
+ data: {
96
+ ...user,
97
+ realm
98
+ }
99
+ }, { authMethod: SessionAuthMethod.EXTERNAL });
100
+ await this.eventService?.record({
101
+ scope: EventScope.OAUTH2,
102
+ name: EventName.AUTHORIZE,
103
+ refType: EventRefType.CLIENT,
104
+ refId: verified.data.client_id ?? null,
105
+ clientId: verified.data.client_id ?? null,
106
+ sessionId: null,
107
+ actorType: IdentityType.USER,
108
+ actorId: user.id,
109
+ actorName: user.name,
110
+ realmId: verified.data.realm_id ?? realm.id,
111
+ requestIpAddress: input.request?.ipAddress ?? null,
112
+ requestUserAgent: input.request?.userAgent ?? null,
113
+ data: {
114
+ reason: "federated",
115
+ providerId: provider.id,
116
+ providerName: provider.name,
117
+ ...verified.data.scope ? { scope: verified.data.scope } : {}
118
+ }
119
+ });
120
+ return {
121
+ kind: "issued",
122
+ redirectUri,
123
+ code: authorizationCode.id,
124
+ state: verified.data.state,
125
+ codeRequest: verified.data,
126
+ client: {
127
+ id: verified.client.id,
128
+ name: verified.client.name,
129
+ displayName: verified.client.displayName ?? null
130
+ }
131
+ };
132
+ }
133
+ };
134
+ //#endregion
135
+ export { OAuth2FederatedLoginService };
136
+
137
+ //# sourceMappingURL=module.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module.mjs","names":["isSafeRedirectURLScheme","EventName","EventRefType","EventScope","IdentityType","SessionAuthMethod","InternalError","OAuth2ErrorCode","OAuth2RequestError","isOAuth2Error","createIdentityProviderOAuth2Authenticator","toIdentityPolicyData","OAuth2FederatedLoginRefusal","OAuth2FederatedLoginService","options","accountManager","realmRepository","codeRequestVerifier","codeIssuer","accessPolicyEvaluator","eventService","logger","authenticatorFactory","ctx","provider","baseURL","clientId","complete","input","code","codeRequest","verified","verify","e","kind","refusal","CODE_REQUEST","realmId","client","malformed","redirectUri","data","redirect_uri","redirectUriVerified","enabled","PROVIDER_DISABLED","error","LOGIN_REQUIRED","authenticator","client_id","user","authenticate","active","USER_INACTIVE","ACCESS_DENIED","realm","resolve","accessPolicyId","allowed","subject","type","USER","evaluate","authorizationCode","issue","authMethod","EXTERNAL","record","scope","OAUTH2","name","AUTHORIZE","refType","CLIENT","refId","sessionId","actorType","actorId","id","actorName","realm_id","requestIpAddress","request","ipAddress","requestUserAgent","userAgent","reason","providerId","providerName","state","displayName"],"sources":["../../../../src/core/oauth2/federated-login/module.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 { isSafeRedirectURLScheme } from '@authup/kit';\nimport {\n EventName,\n EventRefType,\n EventScope,\n IdentityType,\n SessionAuthMethod,\n} from '@authup/core-kit';\nimport { InternalError } from '@authup/errors';\nimport type { Logger } from '@authup/server-kit';\nimport { OAuth2ErrorCode, OAuth2RequestError, isOAuth2Error } from '@authup/specs';\nimport type { IEventService, IRealmRepository } from '../../entities/index.ts';\n// Deep imports, never the `core/identity` barrel: it reaches back into\n// this module through the core barrel, and the cycle would TDZ-crash.\nimport type { IIdentityProviderAccountManager } from '../../identity/provider/account/types.ts';\nimport { createIdentityProviderOAuth2Authenticator } from '../../identity/provider/authentication/factory.ts';\nimport { toIdentityPolicyData } from '../../identity/permission/identity-policy-data.ts';\nimport type { IOAuth2AccessPolicyEvaluator } from '../access-policy/index.ts';\nimport type {\n IOAuth2AuthorizationCodeIssuer,\n IOAuth2AuthorizationCodeRequestVerifier,\n OAuth2AuthorizationCodeRequestVerificationResult,\n} from '../authorization/index.ts';\nimport type {\n IOAuth2FederatedLoginService,\n OAuth2FederatedLoginAuthenticatorFactory,\n OAuth2FederatedLoginCompleteInput,\n OAuth2FederatedLoginCompleteResult,\n OAuth2FederatedLoginServiceContext,\n OAuth2FederatedLoginServiceOptions,\n} from './types.ts';\nimport { OAuth2FederatedLoginRefusal } from './types.ts';\n\nexport class OAuth2FederatedLoginService implements IOAuth2FederatedLoginService {\n protected options : OAuth2FederatedLoginServiceOptions;\n\n protected accountManager : IIdentityProviderAccountManager;\n\n protected realmRepository : IRealmRepository;\n\n protected codeRequestVerifier : IOAuth2AuthorizationCodeRequestVerifier;\n\n protected codeIssuer : IOAuth2AuthorizationCodeIssuer;\n\n protected accessPolicyEvaluator? : IOAuth2AccessPolicyEvaluator;\n\n protected eventService? : IEventService;\n\n protected logger? : Logger;\n\n protected authenticatorFactory : OAuth2FederatedLoginAuthenticatorFactory;\n\n constructor(ctx: OAuth2FederatedLoginServiceContext) {\n this.options = ctx.options;\n this.accountManager = ctx.accountManager;\n this.realmRepository = ctx.realmRepository;\n this.codeRequestVerifier = ctx.codeRequestVerifier;\n this.codeIssuer = ctx.codeIssuer;\n this.accessPolicyEvaluator = ctx.accessPolicyEvaluator;\n this.eventService = ctx.eventService;\n this.logger = ctx.logger;\n this.authenticatorFactory = ctx.authenticatorFactory ??\n ((provider, options) => createIdentityProviderOAuth2Authenticator({\n accountManager: this.accountManager,\n provider,\n logger: options.logger,\n options: {\n baseURL: options.baseURL,\n clientId: options.clientId,\n },\n }));\n }\n\n async complete(input: OAuth2FederatedLoginCompleteInput): Promise<OAuth2FederatedLoginCompleteResult> {\n const {\n code, \n codeRequest, \n provider, \n } = input;\n\n // Re-verify the request BEFORE the provider's single-use code is\n // spent. It re-resolves the client (active, grant allowlist, scopes)\n // and re-matches the redirect_uri against the client's registered\n // patterns, so a client deactivated (or a pattern removed) while the\n // person was away at the provider cannot still receive a code, and a\n // refused completion provisions no user.\n //\n // Only a refusal is reported as one; a server failure keeps throwing.\n let verified : OAuth2AuthorizationCodeRequestVerificationResult;\n try {\n verified = await this.codeRequestVerifier.verify(codeRequest);\n } catch (e) {\n if (!isOAuth2Error(e)) {\n throw e;\n }\n\n return {\n kind: 'refused',\n refusal: OAuth2FederatedLoginRefusal.CODE_REQUEST,\n codeRequest,\n };\n }\n\n // The provider and the client must share a realm, the completion-side\n // half of the plan-041 binding. It rests on the client the\n // verification just resolved rather than the `realm_id` carried on\n // the state blob: that value is only there because `authorize-out`\n // stores the VERIFIED request, so a guard reading it would disappear\n // silently for any state that reached here without the stamp. A\n // realm-less (global) provider matches every client by design.\n if (provider.realmId && verified.client.realmId !== provider.realmId) {\n throw OAuth2RequestError.malformed('The provider and client realm do not match.');\n }\n\n // A stored code request always carries a redirect_uri (that mount is\n // required in OAuth2AuthorizationCodeRequestValidator, unlike\n // `state`), so a verified request is a verified redirect target. The\n // guard keeps the redirect decision resting on the match itself,\n // which is the only thing here that knows the uri was ever matched.\n const redirectUri = verified.data.redirect_uri;\n if (!verified.redirectUriVerified || !redirectUri) {\n throw OAuth2RequestError.malformed('The redirect_uri was not verified.');\n }\n\n // A non-http(s) target is navigated from the interstitial page, which\n // `location.assign`s it and renders it as an href, so a\n // script-capable scheme would execute on the IdP origin. The client\n // validator and the code-request verifier both refuse such a scheme;\n // this fails closed should either gap, and it runs before the\n // provider's single-use code is spent, a user provisioned or a code\n // minted.\n if (!isSafeRedirectURLScheme(redirectUri)) {\n throw new InternalError('The redirect_uri scheme is not allowed.');\n }\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. Checked before the exchange, so\n // the refusal contacts the provider not at all.\n if (!provider.enabled) {\n return {\n kind: 'refused',\n refusal: OAuth2FederatedLoginRefusal.PROVIDER_DISABLED,\n error: OAuth2ErrorCode.LOGIN_REQUIRED,\n codeRequest: verified.data,\n };\n }\n\n const authenticator = this.authenticatorFactory(provider, {\n baseURL: this.options.baseURL,\n clientId: verified.data.client_id,\n logger: this.logger,\n });\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\n // reachable for an already-linked user: a first login provisions an\n // active one.\n if (!user.active) {\n return {\n kind: 'refused',\n refusal: OAuth2FederatedLoginRefusal.USER_INACTIVE,\n error: OAuth2ErrorCode.ACCESS_DENIED,\n codeRequest: verified.data,\n };\n }\n\n const realm = await this.realmRepository.resolve(provider.realmId, true);\n\n // Application access policy (plan 052), federated leg. A policy id\n // with no wired evaluator 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 return {\n kind: 'refused',\n refusal: OAuth2FederatedLoginRefusal.ACCESS_DENIED,\n error: OAuth2ErrorCode.ACCESS_DENIED,\n codeRequest: verified.data,\n };\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\n // all (`PKCE is required for public clients`), which is every console\n // client.\n const authorizationCode = await this.codeIssuer.issue(\n verified.data,\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: input.request?.ipAddress ?? null,\n requestUserAgent: input.request?.userAgent ?? null,\n data: {\n reason: 'federated',\n providerId: provider.id,\n providerName: provider.name,\n ...(verified.data.scope ? { scope: verified.data.scope } : {}),\n },\n });\n\n return {\n kind: 'issued',\n redirectUri,\n code: authorizationCode.id,\n state: verified.data.state,\n codeRequest: verified.data,\n client: {\n id: verified.client.id,\n name: verified.client.name,\n displayName: verified.client.displayName ?? null,\n },\n };\n }\n}\n"],"mappings":"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"}