@tstdl/base 0.93.178 → 0.93.180

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 (207) hide show
  1. package/api/response.js +4 -3
  2. package/api/server/gateway.js +9 -3
  3. package/audit/auditor.d.ts +1 -2
  4. package/audit/drizzle/{0000_lumpy_thunderball.sql → 0000_shallow_elektra.sql} +1 -1
  5. package/audit/drizzle/meta/0000_snapshot.json +2 -2
  6. package/audit/drizzle/meta/_journal.json +2 -2
  7. package/authentication/README.md +87 -42
  8. package/authentication/authentication.api.d.ts +392 -53
  9. package/authentication/authentication.api.js +133 -28
  10. package/authentication/client/api.client.d.ts +3 -3
  11. package/authentication/client/api.client.js +4 -4
  12. package/authentication/client/authentication.service.d.ts +93 -23
  13. package/authentication/client/authentication.service.js +113 -28
  14. package/authentication/client/http-client.middleware.d.ts +1 -1
  15. package/authentication/client/http-client.middleware.js +5 -4
  16. package/authentication/client/module.d.ts +1 -1
  17. package/authentication/client/module.js +2 -2
  18. package/authentication/errors/index.d.ts +1 -1
  19. package/authentication/errors/index.js +1 -1
  20. package/authentication/errors/password-requirements.error.d.ts +5 -0
  21. package/authentication/errors/{secret-requirements.error.js → password-requirements.error.js} +2 -2
  22. package/authentication/models/authentication-password.model.d.ts +8 -0
  23. package/authentication/models/{authentication-credentials.model.js → authentication-password.model.js} +11 -17
  24. package/authentication/models/authentication-session.model.d.ts +0 -2
  25. package/authentication/models/authentication-session.model.js +1 -7
  26. package/authentication/models/authentication-totp-recovery-code.model.d.ts +6 -0
  27. package/authentication/models/authentication-totp-recovery-code.model.js +34 -0
  28. package/authentication/models/authentication-totp.model.d.ts +19 -0
  29. package/authentication/models/authentication-totp.model.js +51 -0
  30. package/authentication/models/authentication-used-totp-token.model.d.ts +5 -0
  31. package/authentication/models/authentication-used-totp-token.model.js +32 -0
  32. package/authentication/models/index.d.ts +6 -3
  33. package/authentication/models/index.js +6 -3
  34. package/authentication/models/{init-secret-reset-data.model.d.ts → init-password-reset-data.model.d.ts} +3 -3
  35. package/authentication/models/{init-secret-reset-data.model.js → init-password-reset-data.model.js} +5 -5
  36. package/authentication/models/password-check-result.model.d.ts +3 -0
  37. package/authentication/models/{secret-check-result.model.js → password-check-result.model.js} +6 -6
  38. package/authentication/models/subject.model.d.ts +0 -6
  39. package/authentication/models/subject.model.js +0 -6
  40. package/authentication/models/token.model.d.ts +16 -2
  41. package/authentication/server/authentication-ancillary.service.d.ts +6 -6
  42. package/authentication/server/authentication-ancillary.service.js +1 -1
  43. package/authentication/server/authentication-password-requirements.validator.d.ts +55 -0
  44. package/authentication/server/{authentication-secret-requirements.validator.js → authentication-password-requirements.validator.js} +22 -22
  45. package/authentication/server/authentication.api-controller.d.ts +55 -27
  46. package/authentication/server/authentication.api-controller.js +214 -39
  47. package/authentication/server/authentication.audit.d.ts +42 -5
  48. package/authentication/server/authentication.service.d.ts +182 -93
  49. package/authentication/server/authentication.service.js +628 -206
  50. package/authentication/server/drizzle/{0000_soft_tag.sql → 0000_odd_echo.sql} +59 -13
  51. package/authentication/server/drizzle/meta/0000_snapshot.json +345 -32
  52. package/authentication/server/drizzle/meta/_journal.json +2 -2
  53. package/authentication/server/helper.d.ts +16 -16
  54. package/authentication/server/helper.js +33 -34
  55. package/authentication/server/index.d.ts +1 -1
  56. package/authentication/server/index.js +1 -1
  57. package/authentication/server/module.d.ts +2 -2
  58. package/authentication/server/module.js +4 -2
  59. package/authentication/server/schemas.d.ts +11 -7
  60. package/authentication/server/schemas.js +7 -3
  61. package/authentication/tests/authentication-password-requirements.validator.test.js +29 -0
  62. package/authentication/tests/authentication.api-controller.test.js +49 -15
  63. package/authentication/tests/authentication.client-error-handling.test.js +3 -2
  64. package/authentication/tests/authentication.client-middleware.test.js +5 -5
  65. package/authentication/tests/authentication.client-service-methods.test.js +28 -14
  66. package/authentication/tests/authentication.client-service-refresh.test.js +7 -6
  67. package/authentication/tests/authentication.client-service.test.js +10 -8
  68. package/authentication/tests/authentication.service.test.js +37 -29
  69. package/authentication/tests/authentication.test-ancillary-service.d.ts +1 -1
  70. package/authentication/tests/authentication.test-ancillary-service.js +1 -1
  71. package/authentication/tests/brute-force-protection.test.js +211 -0
  72. package/authentication/tests/helper.test.js +25 -21
  73. package/authentication/tests/password-requirements.error.test.js +14 -0
  74. package/authentication/tests/remember.api.test.js +22 -14
  75. package/authentication/tests/remember.service.test.js +23 -16
  76. package/authentication/tests/subject.service.test.js +2 -2
  77. package/authentication/tests/suspended-subject.test.d.ts +1 -0
  78. package/authentication/tests/suspended-subject.test.js +120 -0
  79. package/authentication/tests/totp.enrollment.test.d.ts +1 -0
  80. package/authentication/tests/totp.enrollment.test.js +123 -0
  81. package/authentication/tests/totp.login.test.d.ts +1 -0
  82. package/authentication/tests/totp.login.test.js +213 -0
  83. package/authentication/tests/totp.recovery-codes.test.d.ts +1 -0
  84. package/authentication/tests/totp.recovery-codes.test.js +97 -0
  85. package/authentication/tests/totp.status.test.d.ts +1 -0
  86. package/authentication/tests/totp.status.test.js +72 -0
  87. package/circuit-breaker/postgres/drizzle/{0000_cooing_korath.sql → 0000_same_captain_cross.sql} +1 -1
  88. package/circuit-breaker/postgres/drizzle/meta/0000_snapshot.json +2 -2
  89. package/circuit-breaker/postgres/drizzle/meta/_journal.json +2 -2
  90. package/cryptography/cryptography.d.ts +336 -0
  91. package/cryptography/cryptography.js +328 -0
  92. package/cryptography/index.d.ts +4 -0
  93. package/cryptography/index.js +4 -0
  94. package/{utils → cryptography}/jwt.d.ts +22 -4
  95. package/{utils → cryptography}/jwt.js +36 -18
  96. package/cryptography/module.d.ts +35 -0
  97. package/cryptography/module.js +148 -0
  98. package/cryptography/tests/cryptography.test.d.ts +1 -0
  99. package/cryptography/tests/cryptography.test.js +175 -0
  100. package/cryptography/tests/jwt.test.d.ts +1 -0
  101. package/cryptography/tests/jwt.test.js +54 -0
  102. package/cryptography/tests/modern.test.d.ts +1 -0
  103. package/cryptography/tests/modern.test.js +105 -0
  104. package/cryptography/tests/module.test.d.ts +1 -0
  105. package/cryptography/tests/module.test.js +100 -0
  106. package/cryptography/tests/totp.test.d.ts +1 -0
  107. package/cryptography/tests/totp.test.js +108 -0
  108. package/cryptography/totp.d.ts +96 -0
  109. package/cryptography/totp.js +123 -0
  110. package/document-management/server/drizzle/{0000_curious_nighthawk.sql → 0000_sharp_scream.sql} +21 -21
  111. package/document-management/server/drizzle/meta/0000_snapshot.json +22 -22
  112. package/document-management/server/drizzle/meta/_journal.json +2 -2
  113. package/document-management/server/services/document-file.service.js +1 -1
  114. package/errors/errors.localization.d.ts +2 -2
  115. package/errors/errors.localization.js +2 -2
  116. package/errors/index.d.ts +1 -0
  117. package/errors/index.js +1 -0
  118. package/errors/too-many-requests.error.d.ts +5 -0
  119. package/errors/too-many-requests.error.js +7 -0
  120. package/examples/api/authentication.js +5 -5
  121. package/examples/api/custom-authentication.js +4 -3
  122. package/file/server/mime-type.js +1 -1
  123. package/http/http-body.d.ts +1 -0
  124. package/http/http-body.js +3 -0
  125. package/image-service/imgproxy/imgproxy-image-service.d.ts +0 -1
  126. package/image-service/imgproxy/imgproxy-image-service.js +9 -27
  127. package/key-value-store/postgres/drizzle/{0000_shocking_slipstream.sql → 0000_moaning_calypso.sql} +1 -1
  128. package/key-value-store/postgres/drizzle/meta/0000_snapshot.json +2 -2
  129. package/key-value-store/postgres/drizzle/meta/_journal.json +2 -2
  130. package/lock/postgres/drizzle/{0000_busy_tattoo.sql → 0000_nappy_wraith.sql} +1 -1
  131. package/lock/postgres/drizzle/meta/0000_snapshot.json +2 -2
  132. package/lock/postgres/drizzle/meta/_journal.json +2 -2
  133. package/logger/formatters/json.js +1 -1
  134. package/logger/formatters/pretty-print.js +1 -1
  135. package/mail/drizzle/{0000_numerous_the_watchers.sql → 0000_cultured_quicksilver.sql} +2 -2
  136. package/mail/drizzle/meta/0000_snapshot.json +4 -4
  137. package/mail/drizzle/meta/_journal.json +2 -9
  138. package/notification/server/drizzle/{0000_wise_pyro.sql → 0000_new_tenebrous.sql} +6 -6
  139. package/notification/server/drizzle/meta/0000_snapshot.json +7 -7
  140. package/notification/server/drizzle/meta/_journal.json +2 -2
  141. package/notification/tests/notification-flow.test.js +1 -8
  142. package/notification/tests/notification-type.service.test.js +3 -3
  143. package/openid-connect/oidc.service.js +2 -3
  144. package/orm/data-types/common.js +1 -1
  145. package/orm/server/drizzle/schema-converter.js +9 -4
  146. package/orm/server/encryption.js +1 -1
  147. package/orm/server/module.d.ts +0 -1
  148. package/orm/server/module.js +0 -4
  149. package/orm/server/repository.d.ts +2 -1
  150. package/orm/server/repository.js +7 -10
  151. package/orm/tests/encryption.test.js +4 -6
  152. package/orm/tests/repository-extra-coverage.test.js +0 -2
  153. package/orm/tests/repository-regression.test.js +0 -3
  154. package/package.json +9 -8
  155. package/password/README.md +1 -1
  156. package/password/have-i-been-pwned.js +1 -1
  157. package/rate-limit/postgres/drizzle/{0000_watery_rage.sql → 0000_serious_sauron.sql} +1 -1
  158. package/rate-limit/postgres/drizzle/meta/0000_snapshot.json +2 -2
  159. package/rate-limit/postgres/drizzle/meta/_journal.json +2 -2
  160. package/rate-limit/postgres/postgres-rate-limiter.d.ts +1 -1
  161. package/rate-limit/postgres/postgres-rate-limiter.js +1 -1
  162. package/rate-limit/rate-limiter.d.ts +1 -1
  163. package/rpc/tests/rpc.integration.test.js +25 -31
  164. package/supports.d.ts +1 -0
  165. package/supports.js +1 -0
  166. package/task-queue/postgres/drizzle/{0000_faithful_daimon_hellstrom.sql → 0000_dark_ronan.sql} +5 -5
  167. package/task-queue/postgres/drizzle/meta/0000_snapshot.json +10 -10
  168. package/task-queue/postgres/drizzle/meta/_journal.json +2 -9
  169. package/task-queue/postgres/task-queue.js +2 -2
  170. package/task-queue/tests/coverage-enhancement.test.js +2 -2
  171. package/test/drizzle/{0000_natural_cannonball.sql → 0000_organic_gamora.sql} +2 -2
  172. package/test/drizzle/meta/0000_snapshot.json +3 -4
  173. package/test/drizzle/meta/_journal.json +2 -9
  174. package/testing/integration-setup.d.ts +7 -3
  175. package/testing/integration-setup.js +119 -96
  176. package/utils/alphabet.d.ts +1 -0
  177. package/utils/alphabet.js +1 -0
  178. package/utils/base32.d.ts +4 -0
  179. package/utils/base32.js +49 -0
  180. package/utils/base64.d.ts +0 -2
  181. package/utils/base64.js +6 -70
  182. package/utils/equals.d.ts +13 -3
  183. package/utils/equals.js +29 -9
  184. package/utils/index.d.ts +1 -2
  185. package/utils/index.js +1 -2
  186. package/utils/random.d.ts +1 -0
  187. package/utils/random.js +14 -8
  188. package/authentication/errors/secret-requirements.error.d.ts +0 -5
  189. package/authentication/models/authentication-credentials.model.d.ts +0 -10
  190. package/authentication/models/secret-check-result.model.d.ts +0 -3
  191. package/authentication/server/authentication-secret-requirements.validator.d.ts +0 -55
  192. package/authentication/tests/authentication-ancillary.service.test.js +0 -13
  193. package/authentication/tests/authentication-secret-requirements.validator.test.js +0 -29
  194. package/authentication/tests/secret-requirements.error.test.js +0 -14
  195. package/mail/drizzle/0001_married_tarantula.sql +0 -12
  196. package/mail/drizzle/meta/0001_snapshot.json +0 -69
  197. package/orm/server/tokens.d.ts +0 -1
  198. package/orm/server/tokens.js +0 -2
  199. package/task-queue/postgres/drizzle/0001_rapid_infant_terrible.sql +0 -16
  200. package/task-queue/postgres/drizzle/meta/0001_snapshot.json +0 -753
  201. package/test/drizzle/0001_closed_the_captain.sql +0 -2
  202. package/test/drizzle/meta/0001_snapshot.json +0 -117
  203. package/utils/cryptography.d.ts +0 -137
  204. package/utils/cryptography.js +0 -201
  205. /package/authentication/tests/{authentication-ancillary.service.test.d.ts → authentication-password-requirements.validator.test.d.ts} +0 -0
  206. /package/authentication/tests/{authentication-secret-requirements.validator.test.d.ts → brute-force-protection.test.d.ts} +0 -0
  207. /package/authentication/tests/{secret-requirements.error.test.d.ts → password-requirements.error.test.d.ts} +0 -0
@@ -1,6 +1,6 @@
1
1
  import type { HttpServerRequest } from '../../http/server/index.js';
2
- import type { BinaryData, Record } from '../../types/index.js';
3
- import type { RefreshToken, SecretResetToken, Token } from '../models/index.js';
2
+ import type { Record } from '../../types/index.js';
3
+ import type { PasswordResetToken, RefreshToken, Token } from '../models/index.js';
4
4
  /**
5
5
  * Tries to get the authorization token string from a request.
6
6
  * @param request The request to get the token from.
@@ -13,42 +13,42 @@ export declare function tryGetAuthorizationTokenStringFromRequest(request: HttpS
13
13
  * Tries to get a token from a request.
14
14
  * @param request The request to get the token from.
15
15
  * @param tokenVersion The version of the token to get.
16
- * @param secret The secret to use for validation.
16
+ * @param key The CryptoKey to use for validation.
17
17
  * @returns The token or undefined if not found.
18
18
  * @throws {InvalidTokenError} If the token is invalid.
19
19
  */
20
- export declare function tryGetTokenFromRequest<AdditionalTokenPayload extends Record = Record<never>>(request: HttpServerRequest, tokenVersion: number, secret: string | BinaryData<ArrayBuffer>): Promise<Token<AdditionalTokenPayload> | undefined>;
20
+ export declare function tryGetTokenFromRequest<AdditionalTokenPayload extends Record = Record<never>>(request: HttpServerRequest, tokenVersion: number, key: CryptoKey): Promise<Token<AdditionalTokenPayload> | undefined>;
21
21
  /**
22
22
  * Gets a token from a request.
23
23
  * @param request The request to get the token from.
24
24
  * @param tokenVersion The version of the token to get.
25
- * @param secret The secret to use for validation.
25
+ * @param key The CryptoKey to use for validation.
26
26
  * @returns The token.
27
27
  * @throws {InvalidTokenError} If the token is invalid or not found.
28
28
  */
29
- export declare function getTokenFromRequest<AdditionalTokenPayload extends Record = Record<never>>(request: HttpServerRequest, tokenVersion: number, secret: string | BinaryData<ArrayBuffer>): Promise<Token<AdditionalTokenPayload>>;
29
+ export declare function getTokenFromRequest<AdditionalTokenPayload extends Record = Record<never>>(request: HttpServerRequest, tokenVersion: number, key: CryptoKey): Promise<Token<AdditionalTokenPayload>>;
30
30
  /**
31
31
  * Gets a token from a token string.
32
32
  * @param tokenString The token string to get the token from.
33
33
  * @param tokenVersion The version of the token to get.
34
- * @param secret The secret to use for validation.
34
+ * @param key The CryptoKey to use for validation.
35
35
  * @returns The token.
36
36
  * @throws {InvalidTokenError} If the token is invalid.
37
37
  */
38
- export declare function getTokenFromString<AdditionalTokenPayload extends Record = Record<never>>(tokenString: string, tokenVersion: number, secret: string | BinaryData<ArrayBuffer>): Promise<Token<AdditionalTokenPayload>>;
38
+ export declare function getTokenFromString<AdditionalTokenPayload extends Record = Record<never>>(tokenString: string, tokenVersion: number, key: CryptoKey): Promise<Token<AdditionalTokenPayload>>;
39
39
  /**
40
40
  * Gets a refresh token from a token string.
41
41
  * @param tokenString The token string to get the refresh token from.
42
- * @param secret The secret to use for validation.
42
+ * @param key The CryptoKey to use for validation.
43
43
  * @returns The refresh token.
44
44
  * @throws {InvalidTokenError} If the refresh token is invalid.
45
45
  */
46
- export declare function getRefreshTokenFromString(tokenString: string, secret: string | BinaryData<ArrayBuffer>): Promise<RefreshToken>;
46
+ export declare function getRefreshTokenFromString(tokenString: string, key: CryptoKey): Promise<RefreshToken>;
47
47
  /**
48
- * Gets a secret reset token from a token string.
49
- * @param tokenString The token string to get the secret reset token from.
50
- * @param secret The secret to use for validation.
51
- * @returns The secret reset token.
52
- * @throws {InvalidTokenError} If the secret reset token is invalid.
48
+ * Gets a password reset token from a token string.
49
+ * @param tokenString The token string to get the password reset token from.
50
+ * @param key The CryptoKey to use for validation.
51
+ * @returns The password reset token.
52
+ * @throws {InvalidTokenError} If the password reset token is invalid.
53
53
  */
54
- export declare function getSecretResetTokenFromString(tokenString: string, secret: string | BinaryData<ArrayBuffer>): Promise<SecretResetToken>;
54
+ export declare function getPasswordResetTokenFromString(tokenString: string, key: CryptoKey): Promise<PasswordResetToken>;
@@ -1,7 +1,7 @@
1
+ import { parseAndValidateJwtTokenString } from '../../cryptography/index.js';
1
2
  import { BadRequestError } from '../../errors/bad-request.error.js';
2
3
  import { InvalidTokenError } from '../../errors/invalid-token.error.js';
3
4
  import { currentTimestampSeconds } from '../../utils/date-time.js';
4
- import { parseAndValidateJwtTokenString } from '../../utils/jwt.js';
5
5
  import { isArray, isDefined, isUndefined } from '../../utils/type-guards.js';
6
6
  /**
7
7
  * Tries to get the authorization token string from a request.
@@ -10,8 +10,8 @@ import { isArray, isDefined, isUndefined } from '../../utils/type-guards.js';
10
10
  * @param fromCookieOnly Whether to only get the token from the cookie.
11
11
  * @returns The token string or undefined if not found.
12
12
  */
13
- export function tryGetAuthorizationTokenStringFromRequest(request, cookieName = 'authorization', fromCookieOnly = false) {
14
- const headerName = (cookieName.toLocaleLowerCase() == 'authorization')
13
+ export function tryGetAuthorizationTokenStringFromRequest(request, cookieName = 'token', fromCookieOnly = false) {
14
+ const headerName = (cookieName.toLocaleLowerCase() == 'token')
15
15
  ? 'Authorization'
16
16
  : (cookieName.toLocaleLowerCase() == 'refreshtoken')
17
17
  ? 'X-Refresh-Token'
@@ -19,47 +19,46 @@ export function tryGetAuthorizationTokenStringFromRequest(request, cookieName =
19
19
  ? 'X-Impersonator-Refresh-Token'
20
20
  : undefined;
21
21
  const headerValue = (fromCookieOnly || isUndefined(headerName)) ? undefined : request.headers.tryGet(headerName);
22
- const authorizationString = (isArray(headerValue) ? headerValue[0] : headerValue)
23
- ?? request.cookies.tryGet(cookieName);
24
- if (isDefined(authorizationString)) {
25
- const spaceIndex = authorizationString.indexOf(' ');
22
+ const headerTokenString = (isArray(headerValue) ? headerValue[0] : headerValue);
23
+ if (isDefined(headerTokenString)) {
24
+ const spaceIndex = headerTokenString.indexOf(' ');
26
25
  if (spaceIndex == -1) {
27
- return authorizationString;
26
+ throw new BadRequestError('Missing authorization scheme.');
28
27
  }
29
- const authorizationScheme = authorizationString.slice(0, spaceIndex).trim().toLowerCase();
28
+ const authorizationScheme = headerTokenString.slice(0, spaceIndex).trim().toLowerCase();
30
29
  if (authorizationScheme != 'bearer') {
31
30
  throw new BadRequestError(`Unsupported authorization scheme "${authorizationScheme}".`);
32
31
  }
33
- const authorization = authorizationString.slice(spaceIndex).trim();
32
+ const authorization = headerTokenString.slice(spaceIndex).trim();
34
33
  return authorization;
35
34
  }
36
- return undefined;
35
+ return request.cookies.tryGet(cookieName);
37
36
  }
38
37
  /**
39
38
  * Tries to get a token from a request.
40
39
  * @param request The request to get the token from.
41
40
  * @param tokenVersion The version of the token to get.
42
- * @param secret The secret to use for validation.
41
+ * @param key The CryptoKey to use for validation.
43
42
  * @returns The token or undefined if not found.
44
43
  * @throws {InvalidTokenError} If the token is invalid.
45
44
  */
46
- export async function tryGetTokenFromRequest(request, tokenVersion, secret) {
45
+ export async function tryGetTokenFromRequest(request, tokenVersion, key) {
47
46
  const tokenString = tryGetAuthorizationTokenStringFromRequest(request);
48
47
  if (isUndefined(tokenString)) {
49
48
  return undefined;
50
49
  }
51
- return await getTokenFromString(tokenString, tokenVersion, secret);
50
+ return await getTokenFromString(tokenString, tokenVersion, key);
52
51
  }
53
52
  /**
54
53
  * Gets a token from a request.
55
54
  * @param request The request to get the token from.
56
55
  * @param tokenVersion The version of the token to get.
57
- * @param secret The secret to use for validation.
56
+ * @param key The CryptoKey to use for validation.
58
57
  * @returns The token.
59
58
  * @throws {InvalidTokenError} If the token is invalid or not found.
60
59
  */
61
- export async function getTokenFromRequest(request, tokenVersion, secret) {
62
- const token = await tryGetTokenFromRequest(request, tokenVersion, secret);
60
+ export async function getTokenFromRequest(request, tokenVersion, key) {
61
+ const token = await tryGetTokenFromRequest(request, tokenVersion, key);
63
62
  if (isUndefined(token)) {
64
63
  throw new InvalidTokenError('Missing authorization token');
65
64
  }
@@ -69,15 +68,15 @@ export async function getTokenFromRequest(request, tokenVersion, secret) {
69
68
  * Gets a token from a token string.
70
69
  * @param tokenString The token string to get the token from.
71
70
  * @param tokenVersion The version of the token to get.
72
- * @param secret The secret to use for validation.
71
+ * @param key The CryptoKey to use for validation.
73
72
  * @returns The token.
74
73
  * @throws {InvalidTokenError} If the token is invalid.
75
74
  */
76
- export async function getTokenFromString(tokenString, tokenVersion, secret) {
75
+ export async function getTokenFromString(tokenString, tokenVersion, key) {
77
76
  if (isUndefined(tokenString)) {
78
77
  throw new InvalidTokenError('Missing authorization token');
79
78
  }
80
- const validatedToken = await parseAndValidateJwtTokenString(tokenString, 'HS256', secret);
79
+ const validatedToken = await parseAndValidateJwtTokenString(tokenString, 'KMAC256', key);
81
80
  if (validatedToken.header.v != tokenVersion) {
82
81
  throw new InvalidTokenError('Invalid token version');
83
82
  }
@@ -89,34 +88,34 @@ export async function getTokenFromString(tokenString, tokenVersion, secret) {
89
88
  /**
90
89
  * Gets a refresh token from a token string.
91
90
  * @param tokenString The token string to get the refresh token from.
92
- * @param secret The secret to use for validation.
91
+ * @param key The CryptoKey to use for validation.
93
92
  * @returns The refresh token.
94
93
  * @throws {InvalidTokenError} If the refresh token is invalid.
95
94
  */
96
- export async function getRefreshTokenFromString(tokenString, secret) {
95
+ export async function getRefreshTokenFromString(tokenString, key) {
97
96
  if (isUndefined(tokenString)) {
98
97
  throw new InvalidTokenError('Missing refresh token');
99
98
  }
100
- const validatedRefreshToken = await parseAndValidateJwtTokenString(tokenString, 'HS256', secret);
99
+ const validatedRefreshToken = await parseAndValidateJwtTokenString(tokenString, 'KMAC256', key);
101
100
  if (validatedRefreshToken.payload.exp <= currentTimestampSeconds()) {
102
101
  throw new InvalidTokenError('Refresh token expired.');
103
102
  }
104
103
  return validatedRefreshToken;
105
104
  }
106
105
  /**
107
- * Gets a secret reset token from a token string.
108
- * @param tokenString The token string to get the secret reset token from.
109
- * @param secret The secret to use for validation.
110
- * @returns The secret reset token.
111
- * @throws {InvalidTokenError} If the secret reset token is invalid.
106
+ * Gets a password reset token from a token string.
107
+ * @param tokenString The token string to get the password reset token from.
108
+ * @param key The CryptoKey to use for validation.
109
+ * @returns The password reset token.
110
+ * @throws {InvalidTokenError} If the password reset token is invalid.
112
111
  */
113
- export async function getSecretResetTokenFromString(tokenString, secret) {
112
+ export async function getPasswordResetTokenFromString(tokenString, key) {
114
113
  if (isUndefined(tokenString)) {
115
- throw new InvalidTokenError('Missing secret reset token');
114
+ throw new InvalidTokenError('Missing password reset token');
116
115
  }
117
- const validatedSecretResetToken = await parseAndValidateJwtTokenString(tokenString, 'HS256', secret);
118
- if (validatedSecretResetToken.payload.exp <= currentTimestampSeconds()) {
119
- throw new InvalidTokenError('Secret reset token expired.');
116
+ const validatedPasswordResetToken = await parseAndValidateJwtTokenString(tokenString, 'KMAC256', key);
117
+ if (validatedPasswordResetToken.payload.exp <= currentTimestampSeconds()) {
118
+ throw new InvalidTokenError('Password reset token expired.');
120
119
  }
121
- return validatedSecretResetToken;
120
+ return validatedPasswordResetToken;
122
121
  }
@@ -1,6 +1,6 @@
1
1
  export * from './authentication-ancillary.service.js';
2
2
  export * from './authentication-api-request-token.provider.js';
3
- export * from './authentication-secret-requirements.validator.js';
3
+ export * from './authentication-password-requirements.validator.js';
4
4
  export * from './authentication.api-controller.js';
5
5
  export * from './authentication.service.js';
6
6
  export * from './helper.js';
@@ -1,6 +1,6 @@
1
1
  export * from './authentication-ancillary.service.js';
2
2
  export * from './authentication-api-request-token.provider.js';
3
- export * from './authentication-secret-requirements.validator.js';
3
+ export * from './authentication-password-requirements.validator.js';
4
4
  export * from './authentication.api-controller.js';
5
5
  export * from './authentication.service.js';
6
6
  export * from './helper.js';
@@ -16,7 +16,7 @@ export declare class AuthenticationModuleConfig {
16
16
  /**
17
17
  * Options for {@link AuthenticationService}.
18
18
  */
19
- serviceOptions: AuthenticationServiceOptions | Provider<AuthenticationServiceOptions>;
19
+ serviceOptions?: AuthenticationServiceOptions | Provider<AuthenticationServiceOptions>;
20
20
  /**
21
21
  * Override default {@link AuthenticationService}.
22
22
  */
@@ -35,7 +35,7 @@ export declare class AuthenticationModuleConfig {
35
35
  * Configures authentication server services.
36
36
  * @param config Configuration.
37
37
  */
38
- export declare function configureAuthenticationServer({ injector, ...config }: AuthenticationModuleConfig & {
38
+ export declare function configureAuthenticationServer({ injector, ...config }?: AuthenticationModuleConfig & {
39
39
  injector?: Injector;
40
40
  }): void;
41
41
  /**
@@ -38,11 +38,13 @@ export class AuthenticationModuleConfig {
38
38
  * Configures authentication server services.
39
39
  * @param config Configuration.
40
40
  */
41
- export function configureAuthenticationServer({ injector, ...config }) {
41
+ export function configureAuthenticationServer({ injector, ...config } = {}) {
42
42
  const targetInjector = injector ?? Injector;
43
43
  targetInjector.register(AuthenticationModuleConfig, { useValue: config });
44
- targetInjector.register(AuthenticationServiceOptions, isProvider(config.serviceOptions) ? config.serviceOptions : { useValue: config.serviceOptions });
45
44
  targetInjector.register(ApiRequestTokenProvider, { useToken: AuthenticationApiRequestTokenProvider });
45
+ if (isDefined(config.serviceOptions)) {
46
+ targetInjector.register(AuthenticationServiceOptions, isProvider(config.serviceOptions) ? config.serviceOptions : { useValue: config.serviceOptions });
47
+ }
46
48
  if (isDefined(config.authenticationService)) {
47
49
  targetInjector.registerSingleton(AuthenticationService, { useToken: config.authenticationService });
48
50
  }
@@ -1,19 +1,23 @@
1
- import { AuthenticationCredentials, AuthenticationSession, ServiceAccount, Subject, SystemAccount, User } from '../models/index.js';
1
+ import { AuthenticationPassword, AuthenticationSession, AuthenticationTotp, AuthenticationTotpRecoveryCode, AuthenticationUsedTotpToken, ServiceAccount, Subject, SystemAccount, User } from '../models/index.js';
2
2
  export declare const authenticationSchema: import("../../orm/server/index.js").DatabaseSchema<"authentication">;
3
+ export declare const subjectStatus: import("../../orm/enums.js").PgEnumFromEnumeration<{
4
+ readonly Active: "active";
5
+ readonly Suspended: "suspended";
6
+ }>;
3
7
  export declare const subjectType: import("../../orm/enums.js").PgEnumFromEnumeration<{
4
8
  readonly System: "system";
5
9
  readonly User: "user";
6
10
  readonly ServiceAccount: "service-account";
7
11
  }>;
8
- export declare const subjectStatus: import("../../orm/enums.js").PgEnumFromEnumeration<{
12
+ export declare const totpStatus: import("../../orm/enums.js").PgEnumFromEnumeration<{
13
+ readonly Pending: "pending";
9
14
  readonly Active: "active";
10
- readonly Inactive: "inactive";
11
- readonly Suspended: "suspended";
12
- readonly PendingApproval: "pending-approval";
13
- readonly Invited: "invited";
14
15
  }>;
15
- export declare const authenticationCredentials: import("../../orm/server/types.js").PgTableFromType<typeof AuthenticationCredentials, "authentication">;
16
+ export declare const authenticationPasswords: import("../../orm/server/types.js").PgTableFromType<typeof AuthenticationPassword, "authentication">;
16
17
  export declare const authenticationSession: import("../../orm/server/types.js").PgTableFromType<typeof AuthenticationSession, "authentication">;
18
+ export declare const authenticationTotp: import("../../orm/server/types.js").PgTableFromType<typeof AuthenticationTotp, "authentication">;
19
+ export declare const authenticationTotpRecoveryCode: import("../../orm/server/types.js").PgTableFromType<typeof AuthenticationTotpRecoveryCode, "authentication">;
20
+ export declare const authenticationUsedTotpToken: import("../../orm/server/types.js").PgTableFromType<typeof AuthenticationUsedTotpToken, "authentication">;
17
21
  export declare const serviceAccount: import("../../orm/server/types.js").PgTableFromType<typeof ServiceAccount, "authentication">;
18
22
  export declare const subject: import("../../orm/server/types.js").PgTableFromType<typeof Subject, "authentication">;
19
23
  export declare const systemAccount: import("../../orm/server/types.js").PgTableFromType<typeof SystemAccount, "authentication">;
@@ -1,10 +1,14 @@
1
1
  import { databaseSchema } from '../../orm/server/index.js';
2
- import { AuthenticationCredentials, AuthenticationSession, ServiceAccount, Subject, SubjectStatus, SubjectType, SystemAccount, User } from '../models/index.js';
2
+ import { AuthenticationPassword, AuthenticationSession, AuthenticationTotp, AuthenticationTotpRecoveryCode, AuthenticationUsedTotpToken, ServiceAccount, Subject, SubjectStatus, SubjectType, SystemAccount, TotpStatus, User } from '../models/index.js';
3
3
  export const authenticationSchema = databaseSchema('authentication');
4
- export const subjectType = authenticationSchema.getEnum(SubjectType);
5
4
  export const subjectStatus = authenticationSchema.getEnum(SubjectStatus);
6
- export const authenticationCredentials = authenticationSchema.getTable(AuthenticationCredentials);
5
+ export const subjectType = authenticationSchema.getEnum(SubjectType);
6
+ export const totpStatus = authenticationSchema.getEnum(TotpStatus);
7
+ export const authenticationPasswords = authenticationSchema.getTable(AuthenticationPassword);
7
8
  export const authenticationSession = authenticationSchema.getTable(AuthenticationSession);
9
+ export const authenticationTotp = authenticationSchema.getTable(AuthenticationTotp);
10
+ export const authenticationTotpRecoveryCode = authenticationSchema.getTable(AuthenticationTotpRecoveryCode);
11
+ export const authenticationUsedTotpToken = authenticationSchema.getTable(AuthenticationUsedTotpToken);
8
12
  export const serviceAccount = authenticationSchema.getTable(ServiceAccount);
9
13
  export const subject = authenticationSchema.getTable(Subject);
10
14
  export const systemAccount = authenticationSchema.getTable(SystemAccount);
@@ -0,0 +1,29 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { PasswordRequirementsError } from '../errors/password-requirements.error.js';
3
+ import { DefaultAuthenticationPasswordRequirementsValidator } from '../server/authentication-password-requirements.validator.js';
4
+ describe('DefaultAuthenticationPasswordRequirementsValidator', () => {
5
+ const validator = new DefaultAuthenticationPasswordRequirementsValidator();
6
+ it('should return success when password is strong and not pwned', async () => {
7
+ // A very long random string is unlikely to be pwned and will be strong
8
+ const result = await validator.testPasswordRequirements('Very-Strong-And-Long-Password-2026!@#$%^&*()');
9
+ expect(result.success).toBe(true);
10
+ });
11
+ it('should return failure when password is pwned', async () => {
12
+ // "password" is definitely pwned
13
+ const result = await validator.testPasswordRequirements('password');
14
+ expect(result.success).toBe(false);
15
+ expect(result.reason).toContain('exposed in data breach');
16
+ });
17
+ it('should return failure when password is too weak', async () => {
18
+ // "abc" is too weak (and likely pwned)
19
+ const result = await validator.testPasswordRequirements('abc');
20
+ expect(result.success).toBe(false);
21
+ expect(result.reason).toBeDefined();
22
+ });
23
+ it('should throw PasswordRequirementsError on validation failure', async () => {
24
+ await expect(validator.validatePasswordRequirements('abc')).rejects.toThrow(PasswordRequirementsError);
25
+ });
26
+ it('should not throw on validation success', async () => {
27
+ await expect(validator.validatePasswordRequirements('Very-Strong-And-Long-Password-2026!@#$%^&*()')).resolves.not.toThrow();
28
+ });
29
+ });
@@ -1,10 +1,12 @@
1
1
  import { afterAll, beforeAll, beforeEach, describe, expect, test, vi } from 'vitest';
2
+ import { Auditor } from '../../audit/index.js';
2
3
  import { AuthenticationClientService } from '../../authentication/client/index.js';
3
4
  import { AuthenticationAncillaryService, AuthenticationService as AuthenticationServerService } from '../../authentication/server/index.js';
4
5
  import { HttpClientOptions } from '../../http/client/index.js';
5
6
  import { HttpServer } from '../../http/server/index.js';
6
7
  import { runInInjectionContext } from '../../injector/index.js';
7
8
  import { clearTenantData, setupIntegrationTest } from '../../testing/index.js';
9
+ import { currentTimestampSeconds } from '../../utils/date-time.js';
8
10
  import { SubjectService } from '../server/subject.service.js';
9
11
  import { DefaultAuthenticationAncillaryService } from './authentication.test-ancillary-service.js';
10
12
  describe('AuthenticationApiController Integration', () => {
@@ -26,7 +28,9 @@ describe('AuthenticationApiController Integration', () => {
26
28
  };
27
29
  ({ injector, database } = await setupIntegrationTest({
28
30
  modules: { authentication: true, audit: true, keyValueStore: true, signals: true, api: true, webServer: true },
29
- authenticationAncillaryService: DefaultAuthenticationAncillaryService,
31
+ authentication: {
32
+ ancillaryService: DefaultAuthenticationAncillaryService,
33
+ },
30
34
  }));
31
35
  await runInInjectionContext(injector, async () => {
32
36
  server = injector.resolve(HttpServer);
@@ -44,12 +48,12 @@ describe('AuthenticationApiController Integration', () => {
44
48
  });
45
49
  beforeEach(async () => {
46
50
  globalThis.localStorage?.clear();
47
- await clearTenantData(database, schema, ['credentials', 'session', 'user', 'service_account', 'system_account', 'subject'], tenantId);
51
+ await clearTenantData(database, schema, ['used_totp_tokens', 'totp_recovery_code', 'totp', 'password', 'session', 'user', 'service_account', 'system_account', 'subject'], tenantId);
48
52
  });
49
53
  test('login should work via API client', async () => {
50
54
  await runInInjectionContext(injector, async () => {
51
55
  const user = await subjectService.createUser({ tenantId, email: 'api-login@example.com', firstName: 'A', lastName: 'L' });
52
- await serverService.setCredentials(user, 'Strong-Password-2026!');
56
+ await serverService.setPassword(user, 'Strong-Password-2026!');
53
57
  await service.login({ tenantId, subject: user.id }, 'Strong-Password-2026!');
54
58
  expect(service.isLoggedIn()).toBe(true);
55
59
  expect(service.subjectId()).toBe(user.id);
@@ -58,9 +62,9 @@ describe('AuthenticationApiController Integration', () => {
58
62
  test('login should work even if an expired token is present in the Authorization header', async () => {
59
63
  await runInInjectionContext(injector, async () => {
60
64
  const user = await subjectService.createUser({ tenantId, email: 'expired-token-login@example.com', firstName: 'E', lastName: 'L' });
61
- await serverService.setCredentials(user, 'Strong-Password-2026!');
65
+ await serverService.setPassword(user, 'Strong-Password-2026!');
62
66
  // Create an expired token
63
- const now = Math.floor(Date.now() / 1000);
67
+ const now = currentTimestampSeconds();
64
68
  const expiredTokenResult = await serverService.createToken({
65
69
  subject: user,
66
70
  sessionId: crypto.randomUUID(),
@@ -72,21 +76,21 @@ describe('AuthenticationApiController Integration', () => {
72
76
  timestamp: (now - 7200) * 1000,
73
77
  });
74
78
  // Inject the expired token into the client service
75
- service.updateRawTokens(expiredTokenResult.token);
79
+ service.updateRawTokens(`Bearer ${expiredTokenResult.token}`);
76
80
  // Now try to login
77
81
  await service.login({ tenantId, subject: user.id }, 'Strong-Password-2026!');
78
82
  expect(service.isLoggedIn()).toBe(true);
79
83
  expect(service.subjectId()).toBe(user.id);
80
84
  });
81
85
  });
82
- test('checkSecret should work via API client', async () => {
83
- const result = await service.checkSecret('abc');
86
+ test('checkPassword should work via API client', async () => {
87
+ const result = await service.checkPassword('abc');
84
88
  expect(result.strength).toBeLessThan(2);
85
89
  });
86
90
  test('refresh should work via API client', async () => {
87
91
  await runInInjectionContext(injector, async () => {
88
92
  const user = await subjectService.createUser({ tenantId, email: 'api-refresh@example.com', firstName: 'A', lastName: 'L' });
89
- await serverService.setCredentials(user, 'Strong-Password-2026!');
93
+ await serverService.setPassword(user, 'Strong-Password-2026!');
90
94
  await service.login({ tenantId, subject: user.id }, 'Strong-Password-2026!');
91
95
  const initialToken = service.token()?.jti;
92
96
  await service.refresh();
@@ -97,7 +101,7 @@ describe('AuthenticationApiController Integration', () => {
97
101
  await runInInjectionContext(injector, async () => {
98
102
  const admin = await subjectService.createUser({ tenantId, email: 'api-admin@example.com', firstName: 'A', lastName: 'D' });
99
103
  const user = await subjectService.createUser({ tenantId, email: 'api-user@example.com', firstName: 'U', lastName: 'S' });
100
- await serverService.setCredentials(admin, 'Admin-Pass-123!');
104
+ await serverService.setPassword(admin, 'Admin-Pass-123!');
101
105
  expect(await subjectService.exists(tenantId, admin.id)).toBe(true);
102
106
  expect(await subjectService.exists(tenantId, user.id)).toBe(true);
103
107
  await service.login({ tenantId, subject: admin.id }, 'Admin-Pass-123!');
@@ -108,15 +112,45 @@ describe('AuthenticationApiController Integration', () => {
108
112
  expect(service.subjectId()).toBe(admin.id);
109
113
  });
110
114
  });
111
- test('secret reset should work via API client', async () => {
115
+ test('password reset should work via API client', async () => {
112
116
  await runInInjectionContext(injector, async () => {
113
117
  const user = await subjectService.createUser({ tenantId, email: 'api-reset@example.com', firstName: 'A', lastName: 'L' });
114
- const ancillaryService = injector.resolve(AuthenticationAncillaryService);
115
- await service.initResetSecret({ tenantId, subject: user.id }, undefined);
116
- expect(ancillaryService.lastResetData).toBeDefined();
117
- await service.resetSecret(ancillaryService.lastResetData.token, 'New-API-Pass-123!');
118
+ const ancillaryService = await injector.resolveAsync(AuthenticationAncillaryService);
119
+ await service.initPasswordReset({ tenantId, subject: user.id }, undefined);
120
+ await vi.waitFor(() => expect(ancillaryService.lastResetData).toBeDefined(), { timeout: 5000, interval: 50 });
121
+ await service.resetPassword(ancillaryService.lastResetData.token, 'New-API-Pass-123!');
118
122
  await service.login({ tenantId, subject: user.id }, 'New-API-Pass-123!');
119
123
  expect(service.isLoggedIn()).toBe(true);
120
124
  });
121
125
  });
126
+ test('listSessions and invalidateAllOtherSessions should work via API client', async () => {
127
+ await runInInjectionContext(injector, async () => {
128
+ const user = await subjectService.createUser({ tenantId, email: 'api-sessions@example.com', firstName: 'S', lastName: 'E' });
129
+ await serverService.setPassword(user, 'Strong-Password-2026!');
130
+ // Create two sessions
131
+ const auditor = injector.resolve(Auditor);
132
+ await serverService.login({ tenantId, subject: user.id }, 'Strong-Password-2026!', undefined, auditor);
133
+ await service.login({ tenantId, subject: user.id }, 'Strong-Password-2026!');
134
+ const sessions = await service.listSessions();
135
+ expect(sessions.length).toBe(2);
136
+ expect(sessions[0]).toHaveProperty('id');
137
+ expect(sessions[0]).toHaveProperty('begin');
138
+ expect(sessions[0]).toHaveProperty('end');
139
+ await service.invalidateAllOtherSessions();
140
+ const updatedSessions = await service.listSessions();
141
+ // Only the current session should remain
142
+ expect(updatedSessions.length).toBe(1);
143
+ expect(updatedSessions[0].id).toBe(service.sessionId());
144
+ });
145
+ });
146
+ test('changePassword should work via API client', async () => {
147
+ await runInInjectionContext(injector, async () => {
148
+ const user = await subjectService.createUser({ tenantId, email: 'api-change-secret@example.com', firstName: 'C', lastName: 'S' });
149
+ await serverService.setPassword(user, 'Old-Strong-Password-2026!');
150
+ await service.login({ tenantId, subject: user.id }, 'Old-Strong-Password-2026!');
151
+ await service.changePassword('Old-Strong-Password-2026!', 'New-Strong-Password-2026!');
152
+ const authResult = await serverService.authenticateWithPassword({ tenantId, subject: user.id }, 'New-Strong-Password-2026!');
153
+ expect(authResult.success).toBe(true);
154
+ });
155
+ });
122
156
  });
@@ -14,6 +14,7 @@ import { Lock } from '../../lock/index.js';
14
14
  import { Logger } from '../../logger/index.js';
15
15
  import { MessageBus } from '../../message-bus/index.js';
16
16
  import { configureDefaultSignalsImplementation } from '../../signals/implementation/configure.js';
17
+ import { currentTimestampSeconds } from '../../utils/date-time.js';
17
18
  import { timeout } from '../../utils/timing.js';
18
19
  describe('AuthenticationClientService Error Handling & Stuck States', () => {
19
20
  let injector;
@@ -36,7 +37,7 @@ describe('AuthenticationClientService Error Handling & Stuck States', () => {
36
37
  mockApiClient = {
37
38
  login: vi.fn(),
38
39
  refresh: vi.fn(),
39
- timestamp: vi.fn().mockResolvedValue(Math.floor(Date.now() / 1000)),
40
+ timestamp: vi.fn().mockResolvedValue(currentTimestampSeconds()),
40
41
  endSession: vi.fn().mockResolvedValue(undefined),
41
42
  };
42
43
  mockLock = {
@@ -72,7 +73,7 @@ describe('AuthenticationClientService Error Handling & Stuck States', () => {
72
73
  await injector.dispose();
73
74
  });
74
75
  function setupServiceWithToken(iatOffset = -10, expOffset = 5) {
75
- const now = Math.floor(Date.now() / 1000);
76
+ const now = currentTimestampSeconds();
76
77
  const initialToken = { iat: now + iatOffset, exp: now + expOffset, jti: 'initial' };
77
78
  globalThis.localStorage.setItem('AuthenticationService:token', JSON.stringify(initialToken));
78
79
  service = injector.resolve(AuthenticationClientService);
@@ -2,15 +2,15 @@ import { of } from 'rxjs';
2
2
  import { describe, expect, test, vi } from 'vitest';
3
3
  import { HttpClientRequest, HttpClientResponse, HttpError, HttpErrorReason } from '../../http/index.js';
4
4
  import { dontWaitForValidToken } from '../authentication.api.js';
5
- import { logoutOnUnauthorizedMiddleware, waitForAuthenticationCredentialsMiddleware } from '../client/http-client.middleware.js';
6
- describe('waitForAuthenticationCredentialsMiddleware', () => {
5
+ import { logoutOnUnauthorizedMiddleware, waitForAuthenticationMiddleware } from '../client/http-client.middleware.js';
6
+ describe('waitForAuthenticationMiddleware', () => {
7
7
  test('should wait for token and call next', async () => {
8
8
  const authenticationServiceMock = {
9
9
  token: vi.fn().mockReturnValue({ jti: 'test-token' }),
10
10
  validToken$: of({ jti: 'test-token' }),
11
11
  hasValidToken: true,
12
12
  };
13
- const middleware = waitForAuthenticationCredentialsMiddleware(authenticationServiceMock);
13
+ const middleware = waitForAuthenticationMiddleware(authenticationServiceMock);
14
14
  const request = new HttpClientRequest('http://localhost');
15
15
  request.context = {
16
16
  endpoint: {
@@ -26,7 +26,7 @@ describe('waitForAuthenticationCredentialsMiddleware', () => {
26
26
  isLoggedIn: vi.fn().mockReturnValue(true),
27
27
  hasValidToken: false,
28
28
  };
29
- const middleware = waitForAuthenticationCredentialsMiddleware(authenticationServiceMock);
29
+ const middleware = waitForAuthenticationMiddleware(authenticationServiceMock);
30
30
  const request = new HttpClientRequest('http://localhost');
31
31
  request.context = {
32
32
  endpoint: {
@@ -45,7 +45,7 @@ describe('waitForAuthenticationCredentialsMiddleware', () => {
45
45
  isLoggedIn: vi.fn().mockReturnValue(true),
46
46
  hasValidToken: false,
47
47
  };
48
- const middleware = waitForAuthenticationCredentialsMiddleware(authenticationServiceMock);
48
+ const middleware = waitForAuthenticationMiddleware(authenticationServiceMock);
49
49
  const request = new HttpClientRequest('http://localhost');
50
50
  request.context = {
51
51
  endpoint: {
@@ -8,6 +8,7 @@ import { Lock } from '../../lock/index.js';
8
8
  import { Logger } from '../../logger/index.js';
9
9
  import { MessageBus } from '../../message-bus/index.js';
10
10
  import { configureDefaultSignalsImplementation } from '../../signals/implementation/configure.js';
11
+ import { currentTimestampSeconds } from '../../utils/date-time.js';
11
12
  describe('AuthenticationClientService Methods', () => {
12
13
  let injector;
13
14
  let service;
@@ -31,11 +32,13 @@ describe('AuthenticationClientService Methods', () => {
31
32
  refresh: vi.fn(),
32
33
  impersonate: vi.fn(),
33
34
  unimpersonate: vi.fn(),
34
- changeSecret: vi.fn(),
35
- initSecretReset: vi.fn(),
36
- resetSecret: vi.fn(),
37
- checkSecret: vi.fn(),
38
- timestamp: vi.fn().mockResolvedValue(Math.floor(Date.now() / 1000)),
35
+ changePassword: vi.fn(),
36
+ initPasswordReset: vi.fn(),
37
+ resetPassword: vi.fn(),
38
+ checkPassword: vi.fn(),
39
+ listSessions: vi.fn(),
40
+ invalidateAllOtherSessions: vi.fn(),
41
+ timestamp: vi.fn().mockResolvedValue(currentTimestampSeconds()),
39
42
  endSession: vi.fn().mockResolvedValue(undefined),
40
43
  };
41
44
  mockLock = {
@@ -106,17 +109,17 @@ describe('AuthenticationClientService Methods', () => {
106
109
  expect(mockApiClient.unimpersonate).toHaveBeenCalled();
107
110
  expect(service.token()).toEqual(token);
108
111
  });
109
- test('changeSecret should call api', async () => {
110
- await service.changeSecret({ tenantId: 't', subject: 's' }, 'old', 'new');
111
- expect(mockApiClient.changeSecret).toHaveBeenCalledWith({ tenantId: 't', subject: 's', currentSecret: 'old', newSecret: 'new' });
112
+ test('changePassword should call api', async () => {
113
+ await service.changePassword('old', 'new');
114
+ expect(mockApiClient.changePassword).toHaveBeenCalledWith({ currentPassword: 'old', newPassword: 'new' });
112
115
  });
113
- test('initResetSecret should call api', async () => {
114
- await service.initResetSecret({ tenantId: 't', subject: 's' }, { some: 'data' });
115
- expect(mockApiClient.initSecretReset).toHaveBeenCalledWith({ tenantId: 't', subject: 's', data: { some: 'data' } });
116
+ test('initPasswordReset should call api', async () => {
117
+ await service.initPasswordReset({ tenantId: 't', subject: 's' }, { some: 'data' });
118
+ expect(mockApiClient.initPasswordReset).toHaveBeenCalledWith({ tenantId: 't', subject: 's', data: { some: 'data' } });
116
119
  });
117
- test('resetSecret should call api', async () => {
118
- await service.resetSecret('token', 'new-secret');
119
- expect(mockApiClient.resetSecret).toHaveBeenCalledWith({ token: 'token', newSecret: 'new-secret' });
120
+ test('resetPassword should call api', async () => {
121
+ await service.resetPassword('token', 'new-password');
122
+ expect(mockApiClient.resetPassword).toHaveBeenCalledWith({ token: 'token', newPassword: 'new-password' });
120
123
  });
121
124
  test('updateRawTokens should update signals and storage', async () => {
122
125
  service.updateRawTokens('raw', 'refresh', 'impersonator');
@@ -160,4 +163,15 @@ describe('AuthenticationClientService Methods', () => {
160
163
  expect(mockLogger.warn).toHaveBeenCalledWith(expect.stringContaining('Failed to synchronize clock'));
161
164
  expect(service.clockOffset).toBe(0);
162
165
  });
166
+ test('listSessions should call api', async () => {
167
+ const sessions = [{ id: '1', begin: 100, end: 200 }];
168
+ mockApiClient.listSessions.mockResolvedValue(sessions);
169
+ const result = await service.listSessions();
170
+ expect(mockApiClient.listSessions).toHaveBeenCalled();
171
+ expect(result).toEqual(sessions);
172
+ });
173
+ test('invalidateAllOtherSessions should call api', async () => {
174
+ await service.invalidateAllOtherSessions();
175
+ expect(mockApiClient.invalidateAllOtherSessions).toHaveBeenCalled();
176
+ });
163
177
  });