@breadstone/archipel-platform-authentication 0.0.13 → 0.0.14

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 (48) hide show
  1. package/README.md +27 -0
  2. package/package.json +4 -3
  3. package/src/AuthModule.d.ts +17 -1
  4. package/src/AuthModule.js +32 -5
  5. package/src/connectors/AnonymousConnector.d.ts +1 -1
  6. package/src/connectors/AnonymousConnector.js +2 -2
  7. package/src/connectors/AppleConnector.d.ts +1 -1
  8. package/src/connectors/AppleConnector.js +2 -2
  9. package/src/connectors/GithubConnector.d.ts +1 -1
  10. package/src/connectors/GithubConnector.js +3 -2
  11. package/src/connectors/GoogleConnector.d.ts +1 -1
  12. package/src/connectors/GoogleConnector.js +2 -2
  13. package/src/connectors/JwtConnector.d.ts +1 -1
  14. package/src/connectors/JwtConnector.js +2 -2
  15. package/src/connectors/MicrosoftConnector.d.ts +1 -1
  16. package/src/connectors/MicrosoftConnector.js +2 -2
  17. package/src/connectors/apple/env.d.ts +1 -1
  18. package/src/connectors/github/env.d.ts +1 -1
  19. package/src/connectors/google/env.d.ts +1 -1
  20. package/src/connectors/microsoft/env.d.ts +1 -1
  21. package/src/contracts/ChallengeStorePort.d.ts +32 -0
  22. package/src/contracts/ChallengeStorePort.js +16 -0
  23. package/src/contracts/index.d.ts +1 -0
  24. package/src/contracts/index.js +3 -1
  25. package/src/env.d.ts +34 -34
  26. package/src/env.js +34 -34
  27. package/src/guards/index.js +0 -2
  28. package/src/mfa/MfaEncryptionService.d.ts +1 -1
  29. package/src/mfa/MfaEncryptionService.js +2 -2
  30. package/src/mfa/MfaService.js +0 -3
  31. package/src/mfa/channels/EmailOtpMfaChannel.d.ts +10 -1
  32. package/src/mfa/channels/EmailOtpMfaChannel.js +12 -2
  33. package/src/mfa/channels/PushMfaChannel.d.ts +8 -3
  34. package/src/mfa/channels/PushMfaChannel.js +10 -4
  35. package/src/mfa/channels/SmsMfaChannel.d.ts +10 -1
  36. package/src/mfa/channels/SmsMfaChannel.js +12 -2
  37. package/src/mfa/channels/TotpMfaChannel.d.ts +1 -1
  38. package/src/mfa/channels/TotpMfaChannel.js +2 -2
  39. package/src/services/ChallengeService.d.ts +7 -3
  40. package/src/services/ChallengeService.js +14 -9
  41. package/src/services/InMemoryChallengeStore.d.ts +16 -0
  42. package/src/services/InMemoryChallengeStore.js +40 -0
  43. package/src/services/SessionService.d.ts +8 -0
  44. package/src/services/SessionService.js +15 -0
  45. package/src/services/VerificationService.d.ts +10 -1
  46. package/src/services/VerificationService.js +31 -9
  47. package/src/strategies/AnonymousStrategy.d.ts +0 -32
  48. package/src/strategies/AnonymousStrategy.js +0 -54
@@ -3,9 +3,11 @@
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.VerificationService = void 0;
5
5
  const tslib_1 = require("tslib");
6
+ const archipel_platform_configuration_1 = require("@breadstone/archipel-platform-configuration");
6
7
  const archipel_platform_core_1 = require("@breadstone/archipel-platform-core");
7
8
  const common_1 = require("@nestjs/common");
8
9
  const jwt_1 = require("@nestjs/jwt");
10
+ const node_crypto_1 = require("node:crypto");
9
11
  const VerificationSubjectPort_1 = require("../contracts/VerificationSubjectPort");
10
12
  const env_1 = require("../env");
11
13
  // #endregion
@@ -65,6 +67,9 @@ let VerificationService = class VerificationService {
65
67
  async verifyToken(token) {
66
68
  if (this._jwtService.verify(token)) {
67
69
  const jwt = this._jwtService.decode(token);
70
+ if (!jwt) {
71
+ throw new common_1.BadRequestException('Invalid or expired token');
72
+ }
68
73
  // check if the token is expired
69
74
  if (jwt.exp && Number(jwt.exp) < Math.floor(Date.now() / 1000)) {
70
75
  throw new common_1.BadRequestException('Invalid or expired token');
@@ -87,19 +92,19 @@ let VerificationService = class VerificationService {
87
92
  }
88
93
  if (user.verificationToken) {
89
94
  const jwt = await this.redeemToken(user.verificationToken);
90
- if (jwt['email'] !== user.email) {
91
- throw new common_1.BadRequestException('Invalid verification token');
92
- }
93
- if (jwt['pin'] !== pin) {
95
+ const jwtEmail = String(jwt['email'] ?? '');
96
+ const jwtPin = String(jwt['pin'] ?? '');
97
+ const userEmail = String(user.email ?? '');
98
+ const emailMatch = this.safeEqual(jwtEmail, userEmail);
99
+ const pinMatch = this.safeEqual(jwtPin, pin);
100
+ if (!emailMatch || !pinMatch) {
94
101
  throw new common_1.BadRequestException('Invalid verification token');
95
102
  }
96
103
  if (jwt['exp'] && Number(jwt['exp']) < Math.floor(Date.now() / 1000)) {
97
104
  throw new common_1.BadRequestException('Expired verification token');
98
105
  }
99
- if (jwt['pin'] === pin && jwt['email'] === user.email) {
100
- await this._verificationSubject.markVerified(user.id);
101
- return true;
102
- }
106
+ await this._verificationSubject.markVerified(user.id);
107
+ return true;
103
108
  }
104
109
  return false;
105
110
  }
@@ -113,6 +118,23 @@ let VerificationService = class VerificationService {
113
118
  buildVerificationLink(token) {
114
119
  return `${this._hostService.host}/api/auth/verify-token?token=${token}`; // get this dynamically or from config
115
120
  }
121
+ /**
122
+ * Performs a constant-time string comparison to prevent timing attacks.
123
+ *
124
+ * @param a - First string.
125
+ * @param b - Second string.
126
+ * @returns Whether the strings are equal.
127
+ */
128
+ safeEqual(a, b) {
129
+ const bufA = Buffer.from(a, 'utf8');
130
+ const bufB = Buffer.from(b, 'utf8');
131
+ if (bufA.length !== bufB.length) {
132
+ // Perform a dummy comparison to keep constant time even on length mismatch
133
+ (0, node_crypto_1.timingSafeEqual)(bufA, bufA);
134
+ return false;
135
+ }
136
+ return (0, node_crypto_1.timingSafeEqual)(bufA, bufB);
137
+ }
116
138
  };
117
139
  exports.VerificationService = VerificationService;
118
140
  exports.VerificationService = VerificationService = tslib_1.__decorate([
@@ -120,6 +142,6 @@ exports.VerificationService = VerificationService = tslib_1.__decorate([
120
142
  tslib_1.__metadata("design:paramtypes", [jwt_1.JwtService,
121
143
  archipel_platform_core_1.HostService,
122
144
  VerificationSubjectPort_1.VerificationSubjectPort,
123
- archipel_platform_core_1.ConfigService])
145
+ archipel_platform_configuration_1.ConfigService])
124
146
  ], VerificationService);
125
147
  //# sourceMappingURL=VerificationService.js.map
@@ -1,32 +0,0 @@
1
- import { ConfigService } from '@breadstone/archipel-platform-core';
2
- import { Strategy } from 'passport-custom';
3
- import { AuthSubjectPort } from '../contracts/AuthSubjectPort';
4
- import type { IAuthSubject } from '../contracts/IAuthSubject';
5
- declare const AnonymousStrategy_base: new () => Strategy & {
6
- validate(...args: any[]): unknown;
7
- };
8
- /**
9
- * Anonymous strategy for temporary user login.
10
- *
11
- * @public
12
- */
13
- export declare class AnonymousStrategy extends AnonymousStrategy_base {
14
- private readonly _authSubject;
15
- private readonly _configService;
16
- /**
17
- * Constructs a new instance of the `AnonymousStrategy` class.
18
- *
19
- * @public
20
- * @param authSubject Auth subject port instance.
21
- * @param configService Configuration service instance.
22
- */
23
- constructor(authSubject: AuthSubjectPort, configService: ConfigService);
24
- /**
25
- * Validates the authentication credentials.
26
- *
27
- * @public
28
- * @returns The authenticated user and this is also later available in the request.user object
29
- */
30
- validate(): Promise<IAuthSubject>;
31
- }
32
- export {};
@@ -1,54 +0,0 @@
1
- "use strict";
2
- // #region Imports
3
- Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.AnonymousStrategy = void 0;
5
- const tslib_1 = require("tslib");
6
- const archipel_platform_core_1 = require("@breadstone/archipel-platform-core");
7
- const common_1 = require("@nestjs/common");
8
- const passport_1 = require("@nestjs/passport");
9
- const passport_custom_1 = require("passport-custom");
10
- const AuthSubjectPort_1 = require("../contracts/AuthSubjectPort");
11
- const env_1 = require("../env");
12
- // #endregion
13
- /**
14
- * Anonymous strategy for temporary user login.
15
- *
16
- * @public
17
- */
18
- let AnonymousStrategy = class AnonymousStrategy extends (0, passport_1.PassportStrategy)(passport_custom_1.Strategy, 'anonymous') {
19
- // #endregion
20
- // #region Ctor
21
- /**
22
- * Constructs a new instance of the `AnonymousStrategy` class.
23
- *
24
- * @public
25
- * @param authSubject Auth subject port instance.
26
- * @param configService Configuration service instance.
27
- */
28
- constructor(authSubject, configService) {
29
- super();
30
- this._authSubject = authSubject;
31
- this._configService = configService;
32
- }
33
- // #endregion
34
- // #region Methods
35
- /**
36
- * Validates the authentication credentials.
37
- *
38
- * @public
39
- * @returns The authenticated user and this is also later available in the request.user object
40
- */
41
- async validate() {
42
- const user = await this._authSubject.findAnonymous(this._configService.get(env_1.SEED_ANONYMOUS_USERNAME.key));
43
- if (!user) {
44
- throw new common_1.NotFoundException('User not found');
45
- }
46
- return user;
47
- }
48
- };
49
- exports.AnonymousStrategy = AnonymousStrategy;
50
- exports.AnonymousStrategy = AnonymousStrategy = tslib_1.__decorate([
51
- (0, common_1.Injectable)(),
52
- tslib_1.__metadata("design:paramtypes", [AuthSubjectPort_1.AuthSubjectPort, archipel_platform_core_1.ConfigService])
53
- ], AnonymousStrategy);
54
- //# sourceMappingURL=AnonymousStrategy.js.map