@nestjs/authentication 0.0.1

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 (215) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +50 -0
  3. package/dist/account/email-verification.handler.d.ts +23 -0
  4. package/dist/account/email-verification.handler.js +17 -0
  5. package/dist/account/email-verification.service.d.ts +46 -0
  6. package/dist/account/email-verification.service.js +111 -0
  7. package/dist/account/email.util.d.ts +14 -0
  8. package/dist/account/email.util.js +28 -0
  9. package/dist/account/in-memory-email-token.store.d.ts +12 -0
  10. package/dist/account/in-memory-email-token.store.js +60 -0
  11. package/dist/account/password-reset.handler.d.ts +19 -0
  12. package/dist/account/password-reset.handler.js +9 -0
  13. package/dist/account/password-reset.service.d.ts +69 -0
  14. package/dist/account/password-reset.service.js +204 -0
  15. package/dist/authentication.constants.d.ts +13 -0
  16. package/dist/authentication.constants.js +13 -0
  17. package/dist/authentication.module-definition.d.ts +21 -0
  18. package/dist/authentication.module-definition.js +147 -0
  19. package/dist/authentication.module.d.ts +34 -0
  20. package/dist/authentication.module.js +139 -0
  21. package/dist/context/authentication-scope.service.d.ts +39 -0
  22. package/dist/context/authentication-scope.service.js +64 -0
  23. package/dist/context/authentication.context.d.ts +28 -0
  24. package/dist/context/authentication.context.js +58 -0
  25. package/dist/context/index.d.ts +2 -0
  26. package/dist/context/index.js +2 -0
  27. package/dist/decorators/authenticate.decorator.d.ts +10 -0
  28. package/dist/decorators/authenticate.decorator.js +18 -0
  29. package/dist/decorators/current-session.decorator.d.ts +2 -0
  30. package/dist/decorators/current-session.decorator.js +6 -0
  31. package/dist/decorators/current-user.decorator.d.ts +6 -0
  32. package/dist/decorators/current-user.decorator.js +11 -0
  33. package/dist/decorators/index.d.ts +4 -0
  34. package/dist/decorators/index.js +4 -0
  35. package/dist/decorators/public.decorator.d.ts +5 -0
  36. package/dist/decorators/public.decorator.js +6 -0
  37. package/dist/errors/authentication.error.d.ts +27 -0
  38. package/dist/errors/authentication.error.js +33 -0
  39. package/dist/errors/index.d.ts +7 -0
  40. package/dist/errors/index.js +7 -0
  41. package/dist/errors/jwt.error.d.ts +9 -0
  42. package/dist/errors/jwt.error.js +12 -0
  43. package/dist/errors/magic-link.error.d.ts +18 -0
  44. package/dist/errors/magic-link.error.js +21 -0
  45. package/dist/errors/mfa-already-enrolled.error.d.ts +14 -0
  46. package/dist/errors/mfa-already-enrolled.error.js +17 -0
  47. package/dist/errors/oidc.error.d.ts +15 -0
  48. package/dist/errors/oidc.error.js +13 -0
  49. package/dist/errors/refresh-token.error.d.ts +10 -0
  50. package/dist/errors/refresh-token.error.js +12 -0
  51. package/dist/errors/secret-decryption.error.d.ts +3 -0
  52. package/dist/errors/secret-decryption.error.js +6 -0
  53. package/dist/events/authentication-events.interface.d.ts +121 -0
  54. package/dist/events/authentication-events.interface.js +1 -0
  55. package/dist/events/authentication-events.service.d.ts +19 -0
  56. package/dist/events/authentication-events.service.js +39 -0
  57. package/dist/events/authentication.channels.d.ts +4 -0
  58. package/dist/events/authentication.channels.js +11 -0
  59. package/dist/events/index.d.ts +3 -0
  60. package/dist/events/index.js +3 -0
  61. package/dist/guards/authentication.guard.d.ts +57 -0
  62. package/dist/guards/authentication.guard.js +171 -0
  63. package/dist/guards/index.d.ts +1 -0
  64. package/dist/guards/index.js +1 -0
  65. package/dist/index.d.ts +52 -0
  66. package/dist/index.js +52 -0
  67. package/dist/interceptors/authentication-scope.interceptor.d.ts +20 -0
  68. package/dist/interceptors/authentication-scope.interceptor.js +48 -0
  69. package/dist/interceptors/index.d.ts +1 -0
  70. package/dist/interceptors/index.js +1 -0
  71. package/dist/interfaces/api-key.interface.d.ts +41 -0
  72. package/dist/interfaces/api-key.interface.js +1 -0
  73. package/dist/interfaces/authenticate-options.interface.d.ts +31 -0
  74. package/dist/interfaces/authenticate-options.interface.js +1 -0
  75. package/dist/interfaces/authentication-error-options.interface.d.ts +8 -0
  76. package/dist/interfaces/authentication-error-options.interface.js +1 -0
  77. package/dist/interfaces/authentication-module-options.interface.d.ts +58 -0
  78. package/dist/interfaces/authentication-module-options.interface.js +1 -0
  79. package/dist/interfaces/authentication-registry.interface.d.ts +41 -0
  80. package/dist/interfaces/authentication-registry.interface.js +1 -0
  81. package/dist/interfaces/authentication-result.interface.d.ts +25 -0
  82. package/dist/interfaces/authentication-result.interface.js +1 -0
  83. package/dist/interfaces/authentication-storage.interface.d.ts +29 -0
  84. package/dist/interfaces/authentication-storage.interface.js +1 -0
  85. package/dist/interfaces/duration.interface.d.ts +2 -0
  86. package/dist/interfaces/duration.interface.js +1 -0
  87. package/dist/interfaces/email-token-store.interface.d.ts +52 -0
  88. package/dist/interfaces/email-token-store.interface.js +1 -0
  89. package/dist/interfaces/email-verification.interface.d.ts +18 -0
  90. package/dist/interfaces/email-verification.interface.js +1 -0
  91. package/dist/interfaces/index.d.ts +27 -0
  92. package/dist/interfaces/index.js +27 -0
  93. package/dist/interfaces/jwt-options.interface.d.ts +68 -0
  94. package/dist/interfaces/jwt-options.interface.js +1 -0
  95. package/dist/interfaces/jwt.interface.d.ts +36 -0
  96. package/dist/interfaces/jwt.interface.js +1 -0
  97. package/dist/interfaces/magic-link-store.interface.d.ts +36 -0
  98. package/dist/interfaces/magic-link-store.interface.js +1 -0
  99. package/dist/interfaces/magic-link.interface.d.ts +55 -0
  100. package/dist/interfaces/magic-link.interface.js +1 -0
  101. package/dist/interfaces/mfa-options.interface.d.ts +44 -0
  102. package/dist/interfaces/mfa-options.interface.js +1 -0
  103. package/dist/interfaces/mfa-store.interface.d.ts +70 -0
  104. package/dist/interfaces/mfa-store.interface.js +1 -0
  105. package/dist/interfaces/oidc-provider.interface.d.ts +56 -0
  106. package/dist/interfaces/oidc-provider.interface.js +1 -0
  107. package/dist/interfaces/oidc-state-store.interface.d.ts +25 -0
  108. package/dist/interfaces/oidc-state-store.interface.js +1 -0
  109. package/dist/interfaces/oidc.interface.d.ts +84 -0
  110. package/dist/interfaces/oidc.interface.js +1 -0
  111. package/dist/interfaces/password-hasher-options.interface.d.ts +17 -0
  112. package/dist/interfaces/password-hasher-options.interface.js +1 -0
  113. package/dist/interfaces/password-reset.interface.d.ts +49 -0
  114. package/dist/interfaces/password-reset.interface.js +1 -0
  115. package/dist/interfaces/refresh-token-options.interface.d.ts +10 -0
  116. package/dist/interfaces/refresh-token-options.interface.js +1 -0
  117. package/dist/interfaces/refresh-token-store.interface.d.ts +61 -0
  118. package/dist/interfaces/refresh-token-store.interface.js +1 -0
  119. package/dist/interfaces/session-store.interface.d.ts +53 -0
  120. package/dist/interfaces/session-store.interface.js +1 -0
  121. package/dist/interfaces/session.interface.d.ts +50 -0
  122. package/dist/interfaces/session.interface.js +1 -0
  123. package/dist/interfaces/sign-in-options.interface.d.ts +9 -0
  124. package/dist/interfaces/sign-in-options.interface.js +1 -0
  125. package/dist/interfaces/token.interface.d.ts +30 -0
  126. package/dist/interfaces/token.interface.js +1 -0
  127. package/dist/jwt/amr.util.d.ts +9 -0
  128. package/dist/jwt/amr.util.js +11 -0
  129. package/dist/jwt/in-memory-refresh-token.store.d.ts +29 -0
  130. package/dist/jwt/in-memory-refresh-token.store.js +67 -0
  131. package/dist/jwt/jwks.client.d.ts +34 -0
  132. package/dist/jwt/jwks.client.js +154 -0
  133. package/dist/jwt/jws.util.d.ts +45 -0
  134. package/dist/jwt/jws.util.js +282 -0
  135. package/dist/jwt/jwt-bearer.provider.d.ts +48 -0
  136. package/dist/jwt/jwt-bearer.provider.js +126 -0
  137. package/dist/jwt/jwt-signer.service.d.ts +19 -0
  138. package/dist/jwt/jwt-signer.service.js +36 -0
  139. package/dist/jwt/jwt-verifier.service.d.ts +22 -0
  140. package/dist/jwt/jwt-verifier.service.js +109 -0
  141. package/dist/jwt/token.service.d.ts +81 -0
  142. package/dist/jwt/token.service.js +230 -0
  143. package/dist/magic-link/in-memory-magic-link.store.d.ts +12 -0
  144. package/dist/magic-link/in-memory-magic-link.store.js +45 -0
  145. package/dist/magic-link/magic-link.handler.d.ts +25 -0
  146. package/dist/magic-link/magic-link.handler.js +14 -0
  147. package/dist/magic-link/magic-link.service.d.ts +79 -0
  148. package/dist/magic-link/magic-link.service.js +215 -0
  149. package/dist/mfa/in-memory-mfa.store.d.ts +25 -0
  150. package/dist/mfa/in-memory-mfa.store.js +73 -0
  151. package/dist/mfa/mfa.service.d.ts +104 -0
  152. package/dist/mfa/mfa.service.js +346 -0
  153. package/dist/mfa/otp.util.d.ts +7 -0
  154. package/dist/mfa/otp.util.js +52 -0
  155. package/dist/mfa/secret-cipher.service.d.ts +28 -0
  156. package/dist/mfa/secret-cipher.service.js +106 -0
  157. package/dist/oidc/in-memory-oidc-state.store.d.ts +13 -0
  158. package/dist/oidc/in-memory-oidc-state.store.js +45 -0
  159. package/dist/oidc/oidc-account.resolver.d.ts +23 -0
  160. package/dist/oidc/oidc-account.resolver.js +16 -0
  161. package/dist/oidc/oidc.client.d.ts +44 -0
  162. package/dist/oidc/oidc.client.js +286 -0
  163. package/dist/oidc/oidc.presets.d.ts +24 -0
  164. package/dist/oidc/oidc.presets.js +53 -0
  165. package/dist/oidc/oidc.service.d.ts +88 -0
  166. package/dist/oidc/oidc.service.js +259 -0
  167. package/dist/providers/api-key.provider.d.ts +51 -0
  168. package/dist/providers/api-key.provider.js +93 -0
  169. package/dist/providers/authentication.provider.d.ts +40 -0
  170. package/dist/providers/authentication.provider.js +33 -0
  171. package/dist/providers/index.d.ts +2 -0
  172. package/dist/providers/index.js +2 -0
  173. package/dist/services/authentication-registry.service.d.ts +75 -0
  174. package/dist/services/authentication-registry.service.js +235 -0
  175. package/dist/services/index.d.ts +3 -0
  176. package/dist/services/index.js +3 -0
  177. package/dist/services/password-hasher.service.d.ts +47 -0
  178. package/dist/services/password-hasher.service.js +170 -0
  179. package/dist/services/ws-authenticator.service.d.ts +38 -0
  180. package/dist/services/ws-authenticator.service.js +92 -0
  181. package/dist/session/cookies.util.d.ts +34 -0
  182. package/dist/session/cookies.util.js +82 -0
  183. package/dist/session/in-memory-session.store.d.ts +33 -0
  184. package/dist/session/in-memory-session.store.js +60 -0
  185. package/dist/session/session-cookie.provider.d.ts +36 -0
  186. package/dist/session/session-cookie.provider.js +58 -0
  187. package/dist/session/session.service.d.ts +97 -0
  188. package/dist/session/session.service.js +208 -0
  189. package/dist/session/sign-in.service.d.ts +87 -0
  190. package/dist/session/sign-in.service.js +206 -0
  191. package/dist/storage/authentication.storage.d.ts +130 -0
  192. package/dist/storage/authentication.storage.js +350 -0
  193. package/dist/storage/index.d.ts +1 -0
  194. package/dist/storage/index.js +1 -0
  195. package/dist/testing/index.d.ts +39 -0
  196. package/dist/testing/index.js +531 -0
  197. package/dist/utils/api-key.util.d.ts +10 -0
  198. package/dist/utils/api-key.util.js +10 -0
  199. package/dist/utils/auth-state.util.d.ts +54 -0
  200. package/dist/utils/auth-state.util.js +209 -0
  201. package/dist/utils/bearer.util.d.ts +9 -0
  202. package/dist/utils/bearer.util.js +21 -0
  203. package/dist/utils/cross-origin.util.d.ts +29 -0
  204. package/dist/utils/cross-origin.util.js +90 -0
  205. package/dist/utils/crypto.util.d.ts +19 -0
  206. package/dist/utils/crypto.util.js +38 -0
  207. package/dist/utils/duration.util.d.ts +9 -0
  208. package/dist/utils/duration.util.js +44 -0
  209. package/dist/utils/index.d.ts +8 -0
  210. package/dist/utils/index.js +8 -0
  211. package/dist/utils/options.util.d.ts +20 -0
  212. package/dist/utils/options.util.js +44 -0
  213. package/dist/utils/transport-errors.util.d.ts +33 -0
  214. package/dist/utils/transport-errors.util.js +70 -0
  215. package/package.json +108 -0
@@ -0,0 +1,350 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
11
+ return function (target, key) { decorator(target, key, paramIndex); }
12
+ };
13
+ var AuthenticationStorage_1;
14
+ import { Inject, Injectable, Logger, Optional } from '@nestjs/common';
15
+ import { InMemoryEmailTokenStore } from '../account/in-memory-email-token.store.js';
16
+ import { InMemoryRefreshTokenStore } from '../jwt/in-memory-refresh-token.store.js';
17
+ import { InMemoryMagicLinkStore } from '../magic-link/in-memory-magic-link.store.js';
18
+ import { InMemoryMfaStore } from '../mfa/in-memory-mfa.store.js';
19
+ import { InMemoryOidcStateStore } from '../oidc/in-memory-oidc-state.store.js';
20
+ import { InMemorySessionStore } from '../session/in-memory-session.store.js';
21
+ import { AUTHENTICATION_STORAGE_REQUIREMENTS } from '../authentication.constants.js';
22
+ /**
23
+ * @internal What each feature reads or writes, audited against the code
24
+ * paths: the production guard's rule.
25
+ * Any feature that signs someone in reads `mfa`, configured or not: a user
26
+ * may have enrolled an authenticator through another instance, and
27
+ * `MfaService.isEnrolled()` is how a sign-in finds out. Any feature that
28
+ * revokes a user's sign-ins reaches both `sessions` and `refreshTokens`.
29
+ */
30
+ export const CONTRACTS_BY_FEATURE = {
31
+ /**
32
+ * A `SessionCookieProvider` in `providers`: `SessionService` (every
33
+ * request), `SignInService.signIn()` (`isEnrolled()`), `completeMfa()`,
34
+ * and `signOutEverywhere()`, which also revokes the user's refresh-token
35
+ * families.
36
+ */
37
+ sessionCookie: ['sessions', 'refreshTokens', 'mfa'],
38
+ /** `accessToken`: `TokenService` (refresh-token families), whose `issue()` checks for an authenticator. */
39
+ accessToken: ['refreshTokens', 'mfa'],
40
+ /** `mfa`: enrollment, codes, recovery codes and the lockout. */
41
+ mfa: ['mfa'],
42
+ /** `magicLinkHandler`: pending links, then `SignInService.signIn()`. */
43
+ magicLink: ['sessions', 'mfa', 'magicLinks'],
44
+ /** `oidcAccountResolver`: logins in progress, the session that links an account, then `SignInService.signIn()`. */
45
+ oidc: ['sessions', 'mfa', 'oidcStates'],
46
+ /**
47
+ * `passwordResetHandler`: the links; `reset()` revokes every session and
48
+ * refresh-token family of the user, and with `signIn: true` signs in.
49
+ */
50
+ passwordReset: ['sessions', 'refreshTokens', 'mfa', 'emailTokens'],
51
+ /** `emailVerificationHandler`: the links. */
52
+ emailVerification: ['emailTokens'],
53
+ };
54
+ /** @internal The contracts the features use, in the order messages list them. */
55
+ export function contractsUsedBy(features) {
56
+ const used = new Set();
57
+ for (const feature of features) {
58
+ for (const contract of CONTRACTS_BY_FEATURE[feature]) {
59
+ used.add(contract);
60
+ }
61
+ }
62
+ return CONTRACT_NAMES.filter((contract) => used.has(contract));
63
+ }
64
+ /** @internal Every contract, in the order they are listed in messages. */
65
+ export const STORAGE_CONTRACTS = {
66
+ sessions: {
67
+ interfaceName: 'SessionStore',
68
+ holds: 'sessions',
69
+ methods: ['getSession', 'createSession', 'touchSession', 'deleteSession', 'listUserSessions', 'deleteUserSessions'],
70
+ inMemory: () => new InMemorySessionStore(),
71
+ },
72
+ refreshTokens: {
73
+ interfaceName: 'RefreshTokenStore',
74
+ holds: 'refresh tokens',
75
+ methods: [
76
+ 'getRefreshToken',
77
+ 'saveRefreshToken',
78
+ 'markRefreshTokenUsed',
79
+ 'revokeRefreshTokenFamily',
80
+ 'isRefreshTokenFamilyRevoked',
81
+ 'revokeUserRefreshTokens',
82
+ ],
83
+ inMemory: () => new InMemoryRefreshTokenStore(),
84
+ },
85
+ mfa: {
86
+ interfaceName: 'MfaStore',
87
+ holds: 'authenticators and recovery codes',
88
+ methods: [
89
+ 'getTotp',
90
+ 'saveTotp',
91
+ 'claimTotpStep',
92
+ 'saveRecoveryCodes',
93
+ 'consumeRecoveryCode',
94
+ 'countRecoveryCodes',
95
+ 'recordMfaFailure',
96
+ 'countMfaFailures',
97
+ 'clearMfaFailures',
98
+ ],
99
+ inMemory: () => new InMemoryMfaStore(),
100
+ },
101
+ magicLinks: {
102
+ interfaceName: 'MagicLinkStore',
103
+ holds: 'pending magic links',
104
+ methods: ['saveMagicLink', 'consumeMagicLink'],
105
+ inMemory: () => new InMemoryMagicLinkStore(),
106
+ },
107
+ oidcStates: {
108
+ interfaceName: 'OidcStateStore',
109
+ holds: 'OIDC logins in progress',
110
+ methods: ['saveOidcState', 'consumeOidcState'],
111
+ inMemory: () => new InMemoryOidcStateStore(),
112
+ },
113
+ emailTokens: {
114
+ interfaceName: 'EmailTokenStore',
115
+ holds: 'password reset and verification links',
116
+ methods: ['saveEmailToken', 'consumeEmailToken', 'deleteUserEmailTokens'],
117
+ inMemory: () => new InMemoryEmailTokenStore(),
118
+ },
119
+ };
120
+ const CONTRACT_NAMES = Object.keys(STORAGE_CONTRACTS);
121
+ const KNOWN = CONTRACT_NAMES.map((name) => `${name} (${STORAGE_CONTRACTS[name].interfaceName})`).join(', ');
122
+ const DEFAULT = 'in-memory stores (the default: state is lost on restart and not shared between instances)';
123
+ /** @internal Locks the registry. `AuthenticationModule.onModuleInit()` and the first read call it. */
124
+ export const LOCK_STORAGE = Symbol('AuthenticationStorage.lock');
125
+ /**
126
+ * Where the module keeps its state. The app registers its own stores here,
127
+ * from the constructor of an ordinary provider:
128
+ *
129
+ * ```ts
130
+ * @Injectable()
131
+ * export class DrizzleAuthenticationStore implements SessionStore, RefreshTokenStore, MfaStore {
132
+ * constructor(@Inject(DRIZZLE) private readonly db: Database, storage: AuthenticationStorage) {
133
+ * storage.registerSource({ sessions: this, refreshTokens: this, mfa: this });
134
+ * }
135
+ * // ...
136
+ * }
137
+ * ```
138
+ *
139
+ * A contract no source registered uses the in-memory store, so an app that
140
+ * imports the module boots with no storage configured. The registry locks
141
+ * in `AuthenticationModule`'s `onModuleInit` (every provider constructor has
142
+ * run by then), or at the first read of a store if that is earlier (another
143
+ * module's `onModuleInit`): it logs the active sources, and in production
144
+ * refuses to start while a contract a configured feature uses has no
145
+ * source, unless `allowInMemoryStorage` is set. In production, a read of
146
+ * any other contract without a source (an app calling a service outside
147
+ * the features it configured) fails the same way at that read, instead of
148
+ * running on the in-memory default.
149
+ */
150
+ let AuthenticationStorage = AuthenticationStorage_1 = class AuthenticationStorage {
151
+ constructor(requirements) {
152
+ this.requirements = requirements;
153
+ this.registered = new Map();
154
+ this.active = new Map();
155
+ this.locked = false;
156
+ }
157
+ /**
158
+ * Registers stores by contract: `{ sessions: this, refreshTokens: this }`.
159
+ * Call it from the constructor of a singleton provider. Throws, registering
160
+ * nothing, when a store lacks one of its contract's methods, when a
161
+ * contract already has a source (unless `replace` is set: tests,
162
+ * wrappers), and once the registry has locked.
163
+ */
164
+ registerSource(sources, options = {}) {
165
+ const entries = validate(sources);
166
+ if (this.locked) {
167
+ throw new Error(`AuthenticationStorage.registerSource(): ${describe(entries)} registered after AuthenticationModule initialized ` +
168
+ `(or after its storage was first read), which already uses ${this.summary(CONTRACT_NAMES)}. Register from ` +
169
+ 'the constructor of a singleton provider: providers of lazy-loaded modules, request-scoped and transient ' +
170
+ 'providers, and lifecycle hooks run too late.');
171
+ }
172
+ if (!options.replace) {
173
+ for (const [contract, source] of entries) {
174
+ const previous = this.registered.get(contract);
175
+ if (previous) {
176
+ throw new Error(`AuthenticationStorage.registerSource(): ${nameOf(source)} can't register \`${contract}\`, ` +
177
+ `${previous === source ? 'it already did (the same instance, twice)' : `${nameOf(previous)} already did`}. ` +
178
+ 'Register each contract once, or pass { replace: true } to replace it on purpose (tests, wrappers).');
179
+ }
180
+ }
181
+ }
182
+ for (const [contract, source] of entries) {
183
+ this.registered.set(contract, source);
184
+ }
185
+ }
186
+ /** The active session store: the registered one, else the in-memory default. Reading it locks the registry. */
187
+ get sessions() {
188
+ return this.source('sessions');
189
+ }
190
+ get refreshTokens() {
191
+ return this.source('refreshTokens');
192
+ }
193
+ get mfa() {
194
+ return this.source('mfa');
195
+ }
196
+ get magicLinks() {
197
+ return this.source('magicLinks');
198
+ }
199
+ get oidcStates() {
200
+ return this.source('oidcStates');
201
+ }
202
+ get emailTokens() {
203
+ return this.source('emailTokens');
204
+ }
205
+ /**
206
+ * @internal Freezes the sources, logs them, and enforces the production
207
+ * guard (which leaves the registry open): in production, a contract in use
208
+ * with no source fails unless in-memory storage was allowed. `log: false`
209
+ * for the contract suite, which builds a registry per case.
210
+ */
211
+ [LOCK_STORAGE]({ log = true } = {}) {
212
+ if (this.locked) {
213
+ return;
214
+ }
215
+ const inUse = this.requirements?.contracts ?? [];
216
+ const missing = inUse.filter((contract) => !this.registered.has(contract));
217
+ if (missing.length > 0 && this.refusesInMemory()) {
218
+ throw new Error(productionError(missing));
219
+ }
220
+ this.locked = true;
221
+ for (const [contract, source] of this.registered) {
222
+ this.active.set(contract, source);
223
+ }
224
+ if (log) {
225
+ AuthenticationStorage_1.logger.log(`AuthenticationStorage: ${this.summary(inUse)}`);
226
+ }
227
+ }
228
+ source(contract) {
229
+ this[LOCK_STORAGE]();
230
+ let source = this.active.get(contract);
231
+ if (!source) {
232
+ // A contract no configured feature showed as used: never in memory in production either.
233
+ if (this.refusesInMemory()) {
234
+ throw new Error(productionError([contract]));
235
+ }
236
+ source = STORAGE_CONTRACTS[contract].inMemory();
237
+ this.active.set(contract, source);
238
+ }
239
+ return source;
240
+ }
241
+ /**
242
+ * In production, unless `allowInMemoryStorage` is set. A registry built
243
+ * with `new`, outside the module, has no options: it refuses too.
244
+ */
245
+ refusesInMemory() {
246
+ // `Production` and `production ` (a stray space in a .env file) are production too.
247
+ return process.env.NODE_ENV?.trim().toLowerCase() === 'production' && !this.requirements?.allowInMemoryStorage;
248
+ }
249
+ /**
250
+ * The in-memory default, `DrizzleAuthenticationStore` (one source for every
251
+ * contract), or the contracts in use or registered, by source:
252
+ * `RedisSessionStore (sessions); DrizzleAuthenticationStore (mfa); in-memory (oidcStates)`.
253
+ */
254
+ summary(inUse) {
255
+ const sources = new Set(this.registered.values());
256
+ if (sources.size === 0) {
257
+ return DEFAULT;
258
+ }
259
+ if (sources.size === 1 && this.registered.size === CONTRACT_NAMES.length) {
260
+ return nameOf([...sources][0]);
261
+ }
262
+ const listed = CONTRACT_NAMES.filter((contract) => inUse.includes(contract) || this.registered.has(contract));
263
+ const groups = new Map();
264
+ // The registered sources first, then what runs in memory.
265
+ for (const contract of [...listed.filter((c) => this.registered.has(c)), ...listed.filter((c) => !this.registered.has(c))]) {
266
+ const source = this.registered.get(contract);
267
+ const name = source ? nameOf(source) : 'in-memory';
268
+ groups.set(name, [...(groups.get(name) ?? []), contract]);
269
+ }
270
+ return [...groups].map(([name, contracts]) => `${name} (${contracts.join(', ')})`).join('; ');
271
+ }
272
+ };
273
+ AuthenticationStorage.logger = new Logger('AuthenticationModule');
274
+ AuthenticationStorage = AuthenticationStorage_1 = __decorate([
275
+ Injectable(),
276
+ __param(0, Optional()),
277
+ __param(0, Inject(AUTHENTICATION_STORAGE_REQUIREMENTS)),
278
+ __metadata("design:paramtypes", [Object])
279
+ ], AuthenticationStorage);
280
+ export { AuthenticationStorage };
281
+ /** The `[contract, store]` pairs of a `registerSource()` argument, after checking every one. */
282
+ function validate(sources) {
283
+ if (sources === null || typeof sources !== 'object') {
284
+ throw new TypeError(`AuthenticationStorage.registerSource(): the contracts go by name, and got ${nameOf(sources)}. ${usage()}`);
285
+ }
286
+ // A store passed as itself (`registerSource(this)`): say which call it meant.
287
+ const implemented = CONTRACT_NAMES.filter((contract) => STORAGE_CONTRACTS[contract].methods.some((method) => typeof sources[method] === 'function'));
288
+ if (implemented.length > 0) {
289
+ throw new TypeError(`AuthenticationStorage.registerSource(): the contracts go by name, and ${nameOf(sources)} was passed as itself. ` +
290
+ `Name what it implements: \`registerSource({ ${implemented.map((contract) => `${contract}: this`).join(', ')} })\`.`);
291
+ }
292
+ const keys = Object.keys(sources);
293
+ if (keys.length === 0) {
294
+ throw new TypeError(`AuthenticationStorage.registerSource(): the contracts go by name, and got none. ${usage()}`);
295
+ }
296
+ const unknown = keys.filter((key) => !Object.hasOwn(STORAGE_CONTRACTS, key));
297
+ if (unknown.length > 0) {
298
+ throw new TypeError(`AuthenticationStorage.registerSource(): unknown contract ${unknown.map((key) => `\`${key}\``).join(', ')}. ` +
299
+ `The contracts are ${KNOWN}.`);
300
+ }
301
+ return keys.map((contract) => {
302
+ const source = sources[contract];
303
+ const { interfaceName, methods } = STORAGE_CONTRACTS[contract];
304
+ if (source === null || typeof source !== 'object') {
305
+ throw new TypeError(`AuthenticationStorage.registerSource(): expected an object implementing ${interfaceName} as \`${contract}\`, ` +
306
+ `got ${nameOf(source)}.`);
307
+ }
308
+ const missing = methods.filter((method) => typeof source[method] !== 'function');
309
+ if (missing.length > 0) {
310
+ throw new TypeError(`AuthenticationStorage.registerSource(): ${nameOf(source)} doesn't implement ${interfaceName} for \`${contract}\`: ` +
311
+ `${missing.map((method) => `${method}()`).join(', ')} ${missing.length === 1 ? 'is' : 'are'} missing.`);
312
+ }
313
+ return [contract, source];
314
+ });
315
+ }
316
+ function usage() {
317
+ return `Pass the stores a provider implements: \`registerSource({ sessions: this, refreshTokens: this })\`. The contracts are ${KNOWN}.`;
318
+ }
319
+ function productionError(missing) {
320
+ const specs = missing.map((contract) => STORAGE_CONTRACTS[contract]);
321
+ const example = missing.map((contract) => `${contract}: this`).join(', ');
322
+ return (`AuthenticationStorage: no store is registered for ${and(missing.map((contract, i) => `\`${contract}\` (${specs[i].interfaceName})`))}, ` +
323
+ `and NODE_ENV is "production": in memory, ${and(specs.map((spec) => spec.holds))} would be lost on restart and not ` +
324
+ `shared between instances. Implement ${and(specs.map((spec) => spec.interfaceName))} in a provider that injects ` +
325
+ `AuthenticationStorage and calls \`storage.registerSource({ ${example} })\` in its constructor, or set ` +
326
+ '`allowInMemoryStorage: true` in the AuthenticationModule options to run in memory anyway.');
327
+ }
328
+ /** `a`, `a and b`, `a, b, and c`. */
329
+ function and(items) {
330
+ if (items.length <= 2) {
331
+ return items.join(' and ');
332
+ }
333
+ return `${items.slice(0, -1).join(', ')}, and ${items.at(-1)}`;
334
+ }
335
+ /** How a message names a value: its class, or what it is instead of an instance. */
336
+ function nameOf(value) {
337
+ if (typeof value === 'function') {
338
+ return `the class ${value.name || '(anonymous)'} (pass an instance)`;
339
+ }
340
+ if (value === null || typeof value !== 'object') {
341
+ return String(value);
342
+ }
343
+ const name = value.constructor?.name;
344
+ return name && name !== 'Object' ? name : 'an object';
345
+ }
346
+ /** `DrizzleAuthenticationStore (sessions, mfa)`: the sources of a `registerSource()` call. */
347
+ function describe(entries) {
348
+ const names = [...new Set(entries.map(([, source]) => nameOf(source)))].join(', ');
349
+ return `${names} (${entries.map(([contract]) => contract).join(', ')})`;
350
+ }
@@ -0,0 +1 @@
1
+ export * from './authentication.storage.js';
@@ -0,0 +1 @@
1
+ export * from './authentication.storage.js';
@@ -0,0 +1,39 @@
1
+ import { type AuthenticationStorageContract, type AuthenticationStorageSources } from '../interfaces/authentication-storage.interface.js';
2
+ export interface AuthenticationStoreContractOptions {
3
+ /**
4
+ * The contracts to check, which `create()` must return. Default: all six.
5
+ * An app that splits storage runs the suite once per provider:
6
+ * `{ contracts: ['sessions'] }` for its Redis store, the rest for its SQL one.
7
+ */
8
+ contracts?: AuthenticationStorageContract[];
9
+ /**
10
+ * Adds the concurrency cases: parallel calls on one token, one code, one
11
+ * link, which a store that reads and then writes (instead of one
12
+ * conditional write) fails. Set it for every store you run in production,
13
+ * on a pooled connection where you can, so the calls really race.
14
+ */
15
+ concurrent?: boolean;
16
+ /**
17
+ * The most pending magic links, OIDC logins and email tokens your store
18
+ * keeps (per kind). When set, a case per kind saves `maxPending + 2` and
19
+ * checks the two that expire first are gone: configure your store with a
20
+ * small cap in the test.
21
+ */
22
+ maxPending?: number;
23
+ }
24
+ /** One case: `run()` resolves, or rejects with an assertion error. */
25
+ export interface AuthenticationStoreContractCase {
26
+ /** `<contract>: <what it checks>`. */
27
+ name: string;
28
+ contract: AuthenticationStorageContract;
29
+ run(): Promise<void>;
30
+ }
31
+ /** Stores on fresh state: `create()` is called once per case. */
32
+ export type AuthenticationStoreFactory = () => AuthenticationStorageSources | Promise<AuthenticationStorageSources>;
33
+ /**
34
+ * The cases every implementation of the given contracts must pass. Ids and
35
+ * user ids are unique per case, so a store on a shared database works, but
36
+ * the `maxPending` cases need the pending tables empty: return stores on
37
+ * fresh state (new tables, or truncated ones) from `create()`.
38
+ */
39
+ export declare function authenticationStoreContract(create: AuthenticationStoreFactory, options?: AuthenticationStoreContractOptions): AuthenticationStoreContractCase[];