@soapjs/soap-auth 0.3.3 → 0.4.0

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 (77) hide show
  1. package/.claude/settings.local.json +20 -0
  2. package/build/__tests__/soap-auth.test.js +94 -0
  3. package/build/errors.d.ts +1 -1
  4. package/build/errors.js +2 -2
  5. package/build/index.d.ts +1 -0
  6. package/build/index.js +1 -0
  7. package/build/services/__tests__/password.service.test.js +25 -18
  8. package/build/services/__tests__/totp.service.test.d.ts +1 -0
  9. package/build/services/__tests__/totp.service.test.js +120 -0
  10. package/build/services/auth-throttle.service.d.ts +2 -2
  11. package/build/services/auth-throttle.service.js +2 -2
  12. package/build/services/index.d.ts +1 -0
  13. package/build/services/index.js +1 -0
  14. package/build/services/password.service.d.ts +7 -5
  15. package/build/services/password.service.js +76 -18
  16. package/build/services/totp.service.d.ts +16 -0
  17. package/build/services/totp.service.js +96 -0
  18. package/build/session/__tests__/session-handler.test.js +22 -14
  19. package/build/session/session-handler.d.ts +1 -0
  20. package/build/session/session-handler.js +39 -6
  21. package/build/soap-auth.d.ts +13 -6
  22. package/build/soap-auth.js +132 -5
  23. package/build/strategies/__tests__/base-auth.strategy.test.d.ts +3 -2
  24. package/build/strategies/__tests__/base-auth.strategy.test.js +1 -0
  25. package/build/strategies/__tests__/credential-auth.strategy.test.d.ts +1 -0
  26. package/build/strategies/__tests__/credential-auth.strategy.test.js +18 -17
  27. package/build/strategies/__tests__/token-auth.strategy.test.d.ts +1 -0
  28. package/build/strategies/__tests__/token-auth.strategy.test.js +1 -0
  29. package/build/strategies/api-key/api-key.strategy.d.ts +4 -4
  30. package/build/strategies/api-key/api-key.strategy.js +3 -2
  31. package/build/strategies/base-auth.strategy.d.ts +5 -4
  32. package/build/strategies/basic/basic.strategy.d.ts +2 -1
  33. package/build/strategies/basic/basic.strategy.js +1 -0
  34. package/build/strategies/credential-auth.strategy.d.ts +7 -7
  35. package/build/strategies/credential-auth.strategy.js +9 -14
  36. package/build/strategies/index.d.ts +1 -0
  37. package/build/strategies/index.js +1 -0
  38. package/build/strategies/jwt/__tests__/jwt.strategy.test.js +2 -2
  39. package/build/strategies/jwt/jwt.strategy.d.ts +3 -1
  40. package/build/strategies/jwt/jwt.strategy.js +35 -6
  41. package/build/strategies/jwt/jwt.tools.js +8 -8
  42. package/build/strategies/local/__tests__/local.strategy.test.js +8 -2
  43. package/build/strategies/local/local.strategy.d.ts +4 -1
  44. package/build/strategies/local/local.strategy.js +81 -0
  45. package/build/strategies/oauth2/__tests__/oauth2.strategy.test.d.ts +1 -0
  46. package/build/strategies/oauth2/__tests__/oauth2.strategy.test.js +239 -0
  47. package/build/strategies/oauth2/hybrid.oauth2.strategy.d.ts +3 -3
  48. package/build/strategies/oauth2/hybrid.oauth2.strategy.js +1 -6
  49. package/build/strategies/oauth2/oauth2.errors.d.ts +1 -0
  50. package/build/strategies/oauth2/oauth2.errors.js +4 -0
  51. package/build/strategies/oauth2/oauth2.strategy.d.ts +6 -4
  52. package/build/strategies/oauth2/oauth2.strategy.js +114 -46
  53. package/build/strategies/oauth2/oauth2.tools.js +2 -2
  54. package/build/strategies/oauth2/oauth2.types.d.ts +2 -2
  55. package/build/strategies/oauth2/providers/__tests__/social-providers.test.d.ts +1 -0
  56. package/build/strategies/oauth2/providers/__tests__/social-providers.test.js +201 -0
  57. package/build/strategies/oauth2/providers/facebook.strategy.d.ts +11 -0
  58. package/build/strategies/oauth2/providers/facebook.strategy.js +58 -0
  59. package/build/strategies/oauth2/providers/github.strategy.d.ts +11 -0
  60. package/build/strategies/oauth2/providers/github.strategy.js +56 -0
  61. package/build/strategies/oauth2/providers/google.strategy.d.ts +11 -0
  62. package/build/strategies/oauth2/providers/google.strategy.js +52 -0
  63. package/build/strategies/oauth2/providers/http-oauth2.strategy.d.ts +16 -0
  64. package/build/strategies/oauth2/providers/http-oauth2.strategy.js +49 -0
  65. package/build/strategies/oauth2/providers/index.d.ts +5 -0
  66. package/build/strategies/oauth2/providers/index.js +21 -0
  67. package/build/strategies/oauth2/providers/provider.types.d.ts +7 -0
  68. package/build/strategies/oauth2/providers/provider.types.js +2 -0
  69. package/build/strategies/token-auth.strategy.d.ts +4 -4
  70. package/build/strategies/token-auth.strategy.js +2 -3
  71. package/build/tools/tools.js +1 -2
  72. package/build/types.d.ts +31 -32
  73. package/build/utils/__tests__/validation.test.d.ts +1 -0
  74. package/build/utils/__tests__/validation.test.js +181 -0
  75. package/build/utils/validation.d.ts +23 -0
  76. package/build/utils/validation.js +139 -0
  77. package/package.json +8 -7
@@ -30,6 +30,7 @@ const credential_auth_strategy_1 = require("../credential-auth.strategy");
30
30
  jest.mock("../../session/session-handler");
31
31
  jest.mock("../jwt/jwt.strategy");
32
32
  class TestCredentialAuthStrategy extends credential_auth_strategy_1.CredentialAuthStrategy {
33
+ name = "test-credential";
33
34
  constructor(config, session, jwt, logger) {
34
35
  super(config, session, jwt, logger);
35
36
  }
@@ -92,8 +93,8 @@ describe("CredentialAuthStrategy", () => {
92
93
  passwordPolicy: {
93
94
  updatePassword: jest.fn(),
94
95
  generateResetToken: jest.fn(),
95
- validateResetToken: jest.fn(),
96
- sendResetEmail: jest.fn(),
96
+ validatePasswordResetToken: jest.fn(),
97
+ sendPasswordResetEmail: jest.fn(),
97
98
  },
98
99
  security: {
99
100
  maxFailedLoginAttempts: 3,
@@ -189,25 +190,25 @@ describe("CredentialAuthStrategy", () => {
189
190
  describe("requestPasswordReset", () => {
190
191
  it("should throw NotImplementedError if generateResetToken is not configured", async () => {
191
192
  config.passwordPolicy.generateResetToken = undefined;
192
- await expect(strategy.requestPasswordReset("testUser")).rejects.toThrow(Soap.NotImplementedError);
193
+ await expect(strategy.requestPasswordResetLink("testUser")).rejects.toThrow(Soap.NotImplementedError);
193
194
  });
194
195
  it("should call generateResetToken and sendResetEmail (if email is provided)", async () => {
195
196
  config.passwordPolicy.generateResetToken = jest
196
197
  .fn()
197
198
  .mockResolvedValue("mockToken");
198
- config.passwordPolicy.sendResetEmail = jest
199
+ config.passwordPolicy.sendPasswordResetEmail = jest
199
200
  .fn()
200
201
  .mockResolvedValue(undefined);
201
- await strategy.requestPasswordReset("testUser", "test@example.com");
202
+ await strategy.requestPasswordResetLink("testUser", "test@example.com");
202
203
  expect(config.passwordPolicy.generateResetToken).toHaveBeenCalledWith("testUser");
203
- expect(config.passwordPolicy.sendResetEmail).toHaveBeenCalledWith("test@example.com", "mockToken");
204
+ expect(config.passwordPolicy.sendPasswordResetEmail).toHaveBeenCalledWith("test@example.com", "mockToken");
204
205
  });
205
206
  it("should call onSuccess with the correct data", async () => {
206
207
  config.passwordPolicy.generateResetToken = jest
207
208
  .fn()
208
209
  .mockResolvedValue("mockToken");
209
210
  const onSuccessSpy = jest.spyOn(strategy, "onSuccess");
210
- await strategy.requestPasswordReset("testUser");
211
+ await strategy.requestPasswordResetLink("testUser");
211
212
  expect(onSuccessSpy).toHaveBeenCalledWith("request_password_reset", {
212
213
  identifier: "testUser",
213
214
  tokens: { reset: "mockToken" },
@@ -217,30 +218,30 @@ describe("CredentialAuthStrategy", () => {
217
218
  describe("resetPassword", () => {
218
219
  it("should throw NotImplementedError if validateResetToken or updatePassword are not configured", async () => {
219
220
  config.passwordPolicy.updatePassword = undefined;
220
- await expect(strategy.resetPassword("testUser", "token", "newPass")).rejects.toThrow(Soap.NotImplementedError);
221
+ await expect(strategy.resetPasswordWithToken("testUser", "token", "newPass")).rejects.toThrow(Soap.NotImplementedError);
221
222
  });
222
223
  it("should throw ExpiredResetTokenError if validateResetToken returns false", async () => {
223
- config.passwordPolicy.validateResetToken = jest
224
+ config.passwordPolicy.validatePasswordResetToken = jest
224
225
  .fn()
225
226
  .mockResolvedValue(false);
226
227
  config.passwordPolicy.updatePassword = jest.fn();
227
- await expect(strategy.resetPassword("testUser", "token", "newPass")).rejects.toThrow(errors_1.ExpiredResetTokenError);
228
+ await expect(strategy.resetPasswordWithToken("testUser", "token", "newPass")).rejects.toThrow(errors_1.ExpiredResetTokenError);
228
229
  });
229
230
  it("should call updatePassword if the token is valid", async () => {
230
- config.passwordPolicy.validateResetToken = jest
231
+ config.passwordPolicy.validatePasswordResetToken = jest
231
232
  .fn()
232
233
  .mockResolvedValue(true);
233
234
  config.passwordPolicy.updatePassword = jest
234
235
  .fn()
235
236
  .mockResolvedValue(undefined);
236
- await strategy.resetPassword("testUser", "validToken", "newPass");
237
- expect(config.passwordPolicy.updatePassword).toHaveBeenCalledWith("testUser", "newPass");
237
+ await strategy.resetPasswordWithToken("testUser", "validToken", "newPass");
238
+ expect(config.passwordPolicy.updatePassword).toHaveBeenCalledWith("testUser", "newPass", undefined);
238
239
  });
239
240
  });
240
241
  describe("changePassword", () => {
241
242
  it("should throw NotImplementedError if updatePassword is not configured", async () => {
242
243
  config.passwordPolicy.updatePassword = undefined;
243
- await expect(strategy.changePassword("testUser", "oldPass", "newPass")).rejects.toThrow(Soap.NotImplementedError);
244
+ await expect(strategy.replacePassword("testUser", "oldPass", "newPass")).rejects.toThrow(Soap.NotImplementedError);
244
245
  });
245
246
  it("should throw InvalidCredentialsError if verifyCredentials returns false", async () => {
246
247
  config.passwordPolicy.updatePassword = jest
@@ -249,7 +250,7 @@ describe("CredentialAuthStrategy", () => {
249
250
  jest
250
251
  .spyOn(strategy, "verifyCredentials")
251
252
  .mockResolvedValueOnce(false);
252
- await expect(strategy.changePassword("testUser", "oldPass", "newPass")).rejects.toThrow(errors_1.InvalidCredentialsError);
253
+ await expect(strategy.replacePassword("testUser", "oldPass", "newPass")).rejects.toThrow(errors_1.InvalidCredentialsError);
253
254
  });
254
255
  it("should call updatePassword with new password if old credentials are correct", async () => {
255
256
  config.passwordPolicy.updatePassword = jest
@@ -258,8 +259,8 @@ describe("CredentialAuthStrategy", () => {
258
259
  jest
259
260
  .spyOn(strategy, "verifyCredentials")
260
261
  .mockResolvedValueOnce(true);
261
- await strategy.changePassword("testUser", "oldPass", "newPass");
262
- expect(config.passwordPolicy.updatePassword).toHaveBeenCalledWith("testUser", "newPass");
262
+ await strategy.replacePassword("testUser", "oldPass", "newPass");
263
+ expect(config.passwordPolicy.updatePassword).toHaveBeenCalledWith("testUser", "newPass", undefined);
263
264
  });
264
265
  });
265
266
  });
@@ -11,6 +11,7 @@ export interface MockContext {
11
11
  refreshToken?: string;
12
12
  }
13
13
  export declare class TestTokenAuthStrategy extends TokenAuthStrategy<MockContext, MockUser> {
14
+ readonly name = "test-token";
14
15
  protected invalidateAccessToken(token: string, context?: MockContext): Promise<void>;
15
16
  protected invalidateRefreshToken(token: string, context?: MockContext): Promise<void>;
16
17
  constructor(config: TokenAuthStrategyConfig<MockContext, MockUser>, session?: SessionHandler, logger?: Soap.Logger);
@@ -4,6 +4,7 @@ exports.TestTokenAuthStrategy = void 0;
4
4
  const token_auth_strategy_1 = require("../token-auth.strategy");
5
5
  const errors_1 = require("../../../src/errors");
6
6
  class TestTokenAuthStrategy extends token_auth_strategy_1.TokenAuthStrategy {
7
+ name = "test-token";
7
8
  async invalidateAccessToken(token, context) {
8
9
  context.accessToken = undefined;
9
10
  }
@@ -1,17 +1,17 @@
1
1
  import * as Soap from "@soapjs/soap";
2
- import { AuthResult, AuthStrategy } from "../../types";
3
2
  import { ApiKeyStrategyConfig } from "./api-key.types";
4
3
  import { BaseAuthStrategy } from "../base-auth.strategy";
5
- export declare class ApiKeyStrategy<TContext = unknown, TUser = unknown> extends BaseAuthStrategy<TContext, TUser> implements AuthStrategy<TContext, TUser> {
4
+ export declare class ApiKeyStrategy<TContext = Soap.HttpContext, TUser extends Soap.AuthUser = Soap.AuthUser> extends BaseAuthStrategy<TContext, TUser> {
6
5
  protected config: ApiKeyStrategyConfig<TContext, TUser>;
6
+ readonly name = "api-key";
7
7
  protected apiKeyValidity: {
8
8
  sessionDuration: number;
9
9
  longTermDuration: number;
10
10
  };
11
- constructor(config: ApiKeyStrategyConfig<TContext, TUser>, logger: Soap.Logger);
11
+ constructor(config: ApiKeyStrategyConfig<TContext, TUser>, logger?: Soap.Logger);
12
12
  protected fetchUser(apiKey: string, context: TContext): Promise<TUser | null>;
13
13
  init(): Promise<void>;
14
- authenticate(context?: TContext): Promise<AuthResult<TUser>>;
14
+ authenticate(context?: TContext): Promise<Soap.AuthResult<TUser> | null>;
15
15
  authorize(user: TUser, action: string, resource?: string): Promise<boolean>;
16
16
  revoke(apiKey: string): Promise<void>;
17
17
  private trackApiKeyUsage;
@@ -7,6 +7,7 @@ const base_auth_strategy_1 = require("../base-auth.strategy");
7
7
  const api_key_tools_1 = require("./api-key.tools");
8
8
  class ApiKeyStrategy extends base_auth_strategy_1.BaseAuthStrategy {
9
9
  config;
10
+ name = "api-key";
10
11
  apiKeyValidity;
11
12
  constructor(config, logger) {
12
13
  super((0, api_key_tools_1.prepareApiKeyConfig)(config), null, logger);
@@ -28,7 +29,7 @@ class ApiKeyStrategy extends base_auth_strategy_1.BaseAuthStrategy {
28
29
  return user;
29
30
  }
30
31
  catch (error) {
31
- this.logger.warn(`Attempt ${attempt + 1} failed: ${error}`);
32
+ this.logger?.warn(`Attempt ${attempt + 1} failed: ${error}`);
32
33
  if (attempt < maxRetries) {
33
34
  await new Promise((resolve) => setTimeout(resolve, retryDelay));
34
35
  }
@@ -99,7 +100,7 @@ class ApiKeyStrategy extends base_auth_strategy_1.BaseAuthStrategy {
99
100
  await this.config.trackApiKeyUsage?.(apiKey);
100
101
  }
101
102
  catch (error) {
102
- this.logger.warn("Failed to track API key usage:", error);
103
+ this.logger?.warn("Failed to track API key usage:", error);
103
104
  }
104
105
  }
105
106
  }
@@ -1,12 +1,12 @@
1
1
  import * as Soap from "@soapjs/soap";
2
- import { AuthFailureContext, AuthResult, AuthStrategy, AuthSuccessContext, BaseAuthStrategyConfig } from "../types";
2
+ import { AuthFailureContext, AuthSuccessContext, BaseAuthStrategyConfig } from "../types";
3
3
  import { SessionHandler } from "../session/session-handler";
4
4
  import { AccountLockService } from "../services/account-lock.service";
5
5
  import { MfaService } from "../services/mfa.service";
6
6
  import { RateLimitService } from "../services/rate-limit.service";
7
7
  import { RoleService } from "../services/role.service";
8
8
  import { AuthThrottleService } from "../services/auth-throttle.service";
9
- export declare abstract class BaseAuthStrategy<TContext = unknown, TUser = unknown> implements AuthStrategy<TContext, TUser> {
9
+ export declare abstract class BaseAuthStrategy<TContext = Soap.HttpContext, TUser extends Soap.AuthUser = Soap.AuthUser> {
10
10
  protected config: BaseAuthStrategyConfig<TContext, TUser>;
11
11
  protected session?: SessionHandler;
12
12
  protected logger?: Soap.Logger;
@@ -15,10 +15,11 @@ export declare abstract class BaseAuthStrategy<TContext = unknown, TUser = unkno
15
15
  protected rateLimit: RateLimitService;
16
16
  protected role: RoleService<TUser>;
17
17
  protected throttle: AuthThrottleService;
18
- abstract authenticate(context?: TContext): Promise<AuthResult<TUser>>;
18
+ abstract readonly name: string;
19
+ abstract authenticate(ctx: TContext): Promise<Soap.AuthResult<TUser> | null>;
19
20
  constructor(config: BaseAuthStrategyConfig<TContext, TUser>, session?: SessionHandler, logger?: Soap.Logger);
20
21
  init(): Promise<void>;
21
22
  protected onSuccess(action: string, context: AuthSuccessContext<TUser, TContext>): Promise<void>;
22
23
  protected onFailure(action: string, context: AuthFailureContext<TContext>): Promise<void>;
23
- protected authenticateWithSession(context: TContext): Promise<AuthResult<TUser>>;
24
+ protected authenticateWithSession(context: TContext): Promise<Soap.AuthResult<TUser>>;
24
25
  }
@@ -3,11 +3,12 @@ import { CredentialAuthStrategy } from "../credential-auth.strategy";
3
3
  import { BasicStrategyConfig } from "./basic.types";
4
4
  import { SessionHandler } from "../../session/session-handler";
5
5
  import { JwtStrategy } from "../jwt/jwt.strategy";
6
- export declare class BasicStrategy<TContext = unknown, TUser = unknown> extends CredentialAuthStrategy<TContext, TUser> {
6
+ export declare class BasicStrategy<TContext = Soap.HttpContext, TUser extends Soap.AuthUser = Soap.AuthUser> extends CredentialAuthStrategy<TContext, TUser> {
7
7
  protected config: BasicStrategyConfig<TContext, TUser>;
8
8
  protected session?: SessionHandler;
9
9
  protected jwt?: JwtStrategy<TContext, TUser>;
10
10
  protected logger?: Soap.Logger;
11
+ readonly name = "basic";
11
12
  constructor(config: BasicStrategyConfig<TContext, TUser>, session?: SessionHandler, jwt?: JwtStrategy<TContext, TUser>, logger?: Soap.Logger);
12
13
  protected extractCredentials(context?: TContext): {
13
14
  identifier: string;
@@ -9,6 +9,7 @@ class BasicStrategy extends credential_auth_strategy_1.CredentialAuthStrategy {
9
9
  session;
10
10
  jwt;
11
11
  logger;
12
+ name = "basic";
12
13
  constructor(config, session, jwt, logger) {
13
14
  super((0, basic_tools_1.prepareBasicConfig)(config), session, jwt, logger);
14
15
  this.config = config;
@@ -1,10 +1,10 @@
1
1
  import * as Soap from "@soapjs/soap";
2
- import { AuthResult, CredentialAuthStrategyConfig } from "../types";
2
+ import { CredentialAuthStrategyConfig, NewPasswordOptions } from "../types";
3
3
  import { BaseAuthStrategy } from "./base-auth.strategy";
4
4
  import { SessionHandler } from "../session/session-handler";
5
5
  import { JwtStrategy } from "./jwt/jwt.strategy";
6
6
  import { PasswordService } from "../services/password.service";
7
- export declare abstract class CredentialAuthStrategy<TContext = unknown, TUser = unknown> extends BaseAuthStrategy<TContext, TUser> {
7
+ export declare abstract class CredentialAuthStrategy<TContext = Soap.HttpContext, TUser extends Soap.AuthUser = Soap.AuthUser> extends BaseAuthStrategy<TContext, TUser> {
8
8
  protected config: CredentialAuthStrategyConfig<TContext, TUser>;
9
9
  protected session?: SessionHandler;
10
10
  protected jwt?: JwtStrategy<TContext, TUser>;
@@ -14,10 +14,10 @@ export declare abstract class CredentialAuthStrategy<TContext = unknown, TUser =
14
14
  protected abstract extractCredentials(context: TContext): any;
15
15
  constructor(config: CredentialAuthStrategyConfig<TContext, TUser>, session?: SessionHandler, jwt?: JwtStrategy<TContext, TUser>, logger?: Soap.Logger);
16
16
  protected fetchUser(payload: unknown): Promise<TUser | null>;
17
- authenticate(context: TContext): Promise<AuthResult<TUser>>;
18
- login(context: TContext): Promise<AuthResult<TUser>>;
17
+ authenticate(context: TContext): Promise<Soap.AuthResult<TUser> | null>;
18
+ login(context: TContext): Promise<Soap.AuthResult<TUser>>;
19
19
  logout(context: TContext): Promise<void>;
20
- requestPasswordReset(identifier: string, email?: string): Promise<void>;
21
- resetPassword(identifier: string, token: string, newPassword: string): Promise<void>;
22
- changePassword(identifier: string, oldPassword: string, newPassword: string): Promise<void>;
20
+ requestPasswordResetLink(identifier: string, email?: string): Promise<void>;
21
+ resetPasswordWithToken(identifier: string, token: string, newPassword: string, passwordOptions?: NewPasswordOptions): Promise<void>;
22
+ replacePassword(identifier: string, oldPassword: string, newPassword: string, options?: NewPasswordOptions): Promise<void>;
23
23
  }
@@ -126,7 +126,7 @@ class CredentialAuthStrategy extends base_auth_strategy_1.BaseAuthStrategy {
126
126
  throw error;
127
127
  }
128
128
  }
129
- async requestPasswordReset(identifier, email) {
129
+ async requestPasswordResetLink(identifier, email) {
130
130
  try {
131
131
  const token = await this.password.generateResetToken(identifier);
132
132
  if (email) {
@@ -136,7 +136,7 @@ class CredentialAuthStrategy extends base_auth_strategy_1.BaseAuthStrategy {
136
136
  identifier,
137
137
  tokens: { reset: token },
138
138
  });
139
- this.logger.info(`Password reset requested for identifier: ${identifier}`);
139
+ this.logger?.info(`Password reset requested for identifier: ${identifier}`);
140
140
  }
141
141
  catch (error) {
142
142
  await this.onFailure("request_password_reset", {
@@ -147,17 +147,12 @@ class CredentialAuthStrategy extends base_auth_strategy_1.BaseAuthStrategy {
147
147
  throw error;
148
148
  }
149
149
  }
150
- async resetPassword(identifier, token, newPassword) {
150
+ async resetPasswordWithToken(identifier, token, newPassword, passwordOptions) {
151
151
  try {
152
- if (!this.password?.validateResetToken) {
153
- throw new Soap.NotImplementedError("validateResetToken");
154
- }
155
- if ((await this.password.validateResetToken(token)) === false) {
156
- throw new errors_1.ExpiredResetTokenError();
157
- }
158
- await this.password.updatePassword(identifier, newPassword);
152
+ await this.password?.validateResetToken(token);
153
+ await this.password.updatePassword(identifier, newPassword, passwordOptions);
159
154
  await this.onSuccess("password_reset", { identifier });
160
- this.logger.info(`Password successfully reset for identifier: ${identifier}`);
155
+ this.logger?.info(`Password successfully reset for identifier: ${identifier}`);
161
156
  }
162
157
  catch (error) {
163
158
  await this.onFailure("password_reset", {
@@ -168,14 +163,14 @@ class CredentialAuthStrategy extends base_auth_strategy_1.BaseAuthStrategy {
168
163
  throw error;
169
164
  }
170
165
  }
171
- async changePassword(identifier, oldPassword, newPassword) {
166
+ async replacePassword(identifier, oldPassword, newPassword, options) {
172
167
  try {
173
168
  if (!(await this.verifyCredentials(identifier, oldPassword))) {
174
169
  throw new errors_1.InvalidCredentialsError();
175
170
  }
176
- await this.password.updatePassword(identifier, newPassword);
171
+ await this.password.updatePassword(identifier, newPassword, options);
177
172
  await this.onSuccess("change_password", { identifier });
178
- this.logger.info(`Password changed successfully for identifier: ${identifier}`);
173
+ this.logger?.info(`Password changed successfully for identifier: ${identifier}`);
179
174
  }
180
175
  catch (error) {
181
176
  await this.onFailure("change_password", {
@@ -13,4 +13,5 @@ export * from "./local/local.types";
13
13
  export * from "./oauth2/oauth2.strategy";
14
14
  export * from "./oauth2/oauth2.tools";
15
15
  export * from "./oauth2/oauth2.types";
16
+ export * from "./oauth2/providers";
16
17
  export * from "./token-auth.strategy";
@@ -29,4 +29,5 @@ __exportStar(require("./local/local.types"), exports);
29
29
  __exportStar(require("./oauth2/oauth2.strategy"), exports);
30
30
  __exportStar(require("./oauth2/oauth2.tools"), exports);
31
31
  __exportStar(require("./oauth2/oauth2.types"), exports);
32
+ __exportStar(require("./oauth2/providers"), exports);
32
33
  __exportStar(require("./token-auth.strategy"), exports);
@@ -149,8 +149,8 @@ describe("JWTStrategy", () => {
149
149
  const token = await strategy.extractRefreshToken(mockContext);
150
150
  expect(token).toBe("mock-refresh-token");
151
151
  });
152
- it("should throw UndefinedTokenSecretError if access secret key is missing", async () => {
152
+ it("should throw ValidationError if access secret key is missing", async () => {
153
153
  mockConfig.accessToken.issuer.secretKey = undefined;
154
- expect(() => new jwt_strategy_1.JwtStrategy(mockConfig, mockLogger)).toThrow(errors_1.UndefinedTokenSecretError);
154
+ expect(() => new jwt_strategy_1.JwtStrategy(mockConfig, mockLogger)).toThrow("config.accessToken.issuer.secretKey is required");
155
155
  });
156
156
  });
@@ -1,12 +1,14 @@
1
1
  import * as Soap from "@soapjs/soap";
2
2
  import { TokenAuthStrategy } from "../token-auth.strategy";
3
3
  import { TokenAuthStrategyConfig, TokenConfig } from "../../types";
4
- export declare class JwtStrategy<TContext = unknown, TUser = unknown> extends TokenAuthStrategy<TContext, TUser> {
4
+ export declare class JwtStrategy<TContext = Soap.HttpContext, TUser extends Soap.AuthUser = Soap.AuthUser> extends TokenAuthStrategy<TContext, TUser> {
5
5
  protected config: TokenAuthStrategyConfig<TContext, TUser>;
6
6
  protected logger?: Soap.Logger;
7
+ readonly name = "jwt";
7
8
  protected accessTokenConfig: TokenConfig<TContext>;
8
9
  protected refreshTokenConfig: TokenConfig<TContext>;
9
10
  constructor(config: TokenAuthStrategyConfig<TContext, TUser>, logger?: Soap.Logger);
11
+ private static validateConfig;
10
12
  protected verifyAccessToken(token: string): Promise<any>;
11
13
  protected verifyRefreshToken(token: string): Promise<any>;
12
14
  protected generateAccessToken(user: TUser, context: TContext): Promise<string>;
@@ -8,12 +8,15 @@ const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
8
8
  const token_auth_strategy_1 = require("../token-auth.strategy");
9
9
  const errors_1 = require("../../errors");
10
10
  const jwt_tools_1 = require("./jwt.tools");
11
+ const validation_1 = require("../../utils/validation");
11
12
  class JwtStrategy extends token_auth_strategy_1.TokenAuthStrategy {
12
13
  config;
13
14
  logger;
15
+ name = "jwt";
14
16
  accessTokenConfig;
15
17
  refreshTokenConfig;
16
18
  constructor(config, logger) {
19
+ JwtStrategy.validateConfig(config);
17
20
  if (!config.accessToken.issuer.secretKey) {
18
21
  throw new errors_1.UndefinedTokenSecretError("Access");
19
22
  }
@@ -24,13 +27,39 @@ class JwtStrategy extends token_auth_strategy_1.TokenAuthStrategy {
24
27
  this.config = config;
25
28
  this.logger = logger;
26
29
  this.accessTokenConfig = jwt_tools_1.JwtTools.prepareAccessTokenConfig(config.accessToken);
27
- this.refreshTokenConfig = jwt_tools_1.JwtTools.prepareRefreshTokenConfig(config.refreshToken);
30
+ if (config.refreshToken) {
31
+ this.refreshTokenConfig = jwt_tools_1.JwtTools.prepareRefreshTokenConfig(config.refreshToken);
32
+ }
28
33
  this.logger?.info("JWTStrategy initialized with provided configurations.");
29
34
  }
35
+ static validateConfig(config) {
36
+ try {
37
+ validation_1.ValidationUtils.required(config, "config");
38
+ validation_1.ValidationUtils.required(config.accessToken, "config.accessToken");
39
+ validation_1.ValidationUtils.required(config.accessToken.issuer, "config.accessToken.issuer");
40
+ validation_1.ValidationUtils.required(config.accessToken.issuer.secretKey, "config.accessToken.issuer.secretKey");
41
+ validation_1.ValidationUtils.nonEmptyString(config.accessToken.issuer.secretKey, "config.accessToken.issuer.secretKey");
42
+ if (config.refreshToken) {
43
+ validation_1.ValidationUtils.required(config.refreshToken.issuer, "config.refreshToken.issuer");
44
+ validation_1.ValidationUtils.required(config.refreshToken.issuer.secretKey, "config.refreshToken.issuer.secretKey");
45
+ validation_1.ValidationUtils.nonEmptyString(config.refreshToken.issuer.secretKey, "config.refreshToken.issuer.secretKey");
46
+ }
47
+ if (config.user) {
48
+ validation_1.ValidationUtils.required(config.user.fetchUser, "config.user.fetchUser");
49
+ validation_1.ValidationUtils.function(config.user.fetchUser, "config.user.fetchUser");
50
+ }
51
+ }
52
+ catch (error) {
53
+ if (error instanceof validation_1.ValidationError) {
54
+ throw error;
55
+ }
56
+ throw new validation_1.ValidationError(`Invalid JWT configuration: ${error.message}`);
57
+ }
58
+ }
30
59
  verifyAccessToken(token) {
31
60
  try {
32
- if (!token)
33
- throw new errors_1.UndefinedTokenError("Access");
61
+ validation_1.ValidationUtils.required(token, "token");
62
+ validation_1.ValidationUtils.jwtToken(token, "token");
34
63
  if (!this.accessTokenConfig.issuer.secretKey)
35
64
  throw new errors_1.UndefinedTokenSecretError("Access");
36
65
  return new Promise((resolve, reject) => {
@@ -49,8 +78,8 @@ class JwtStrategy extends token_auth_strategy_1.TokenAuthStrategy {
49
78
  }
50
79
  verifyRefreshToken(token) {
51
80
  try {
52
- if (!token)
53
- throw new errors_1.UndefinedTokenError("Refresh");
81
+ validation_1.ValidationUtils.required(token, "token");
82
+ validation_1.ValidationUtils.jwtToken(token, "token");
54
83
  if (!this.refreshTokenConfig.issuer.secretKey)
55
84
  throw new errors_1.UndefinedTokenSecretError("Refresh");
56
85
  return new Promise((resolve, reject) => {
@@ -64,7 +93,7 @@ class JwtStrategy extends token_auth_strategy_1.TokenAuthStrategy {
64
93
  }
65
94
  catch (error) {
66
95
  this.logger?.error("JWT verification failed:", error);
67
- error;
96
+ throw error;
68
97
  }
69
98
  }
70
99
  async generateAccessToken(user, context) {
@@ -189,6 +189,14 @@ exports.JwtTools = JwtTools;
189
189
  const prepareJwtConfig = (config) => {
190
190
  return Soap.removeUndefinedProperties({
191
191
  ...config,
192
+ user: {
193
+ ...config.user,
194
+ validateUser: config.user?.validateUser ?? (() => Promise.resolve(true)),
195
+ },
196
+ accessToken: JwtTools.prepareAccessTokenConfig(config.accessToken),
197
+ refreshToken: config.refreshToken
198
+ ? JwtTools.prepareRefreshTokenConfig(config.refreshToken)
199
+ : undefined,
192
200
  routes: {
193
201
  login: config.routes?.login ?? {
194
202
  path: "/auth/jwt/login",
@@ -203,14 +211,6 @@ const prepareJwtConfig = (config) => {
203
211
  method: "POST",
204
212
  },
205
213
  ...config.routes,
206
- user: {
207
- ...config.user,
208
- validateUser: config.user?.validateUser ?? (() => Promise.resolve(true)),
209
- },
210
- accessToken: JwtTools.prepareAccessTokenConfig(config.accessToken),
211
- refreshToken: config.refreshToken
212
- ? JwtTools.prepareRefreshTokenConfig(config.refreshToken)
213
- : undefined,
214
214
  },
215
215
  });
216
216
  };
@@ -17,8 +17,14 @@ describe("LocalStrategy", () => {
17
17
  fetchUser: jest.fn(),
18
18
  },
19
19
  routes: {
20
- login: {},
21
- logout: {},
20
+ login: {
21
+ path: "/login",
22
+ method: "POST"
23
+ },
24
+ logout: {
25
+ path: "/logout",
26
+ method: "POST"
27
+ },
22
28
  },
23
29
  };
24
30
  mockSession = {
@@ -3,11 +3,13 @@ import { CredentialAuthStrategy } from "../credential-auth.strategy";
3
3
  import { LocalStrategyConfig } from "./local.types";
4
4
  import { SessionHandler } from "../../session/session-handler";
5
5
  import { JwtStrategy } from "../jwt/jwt.strategy";
6
- export declare class LocalStrategy<TContext = unknown, TUser = unknown> extends CredentialAuthStrategy<TContext, TUser> {
6
+ export declare class LocalStrategy<TContext = Soap.HttpContext, TUser extends Soap.AuthUser = Soap.AuthUser> extends CredentialAuthStrategy<TContext, TUser> {
7
7
  protected session?: SessionHandler;
8
8
  protected jwt?: JwtStrategy<TContext, TUser>;
9
9
  protected logger?: Soap.Logger;
10
+ readonly name = "local";
10
11
  constructor(config: LocalStrategyConfig<TContext, TUser>, session?: SessionHandler, jwt?: JwtStrategy<TContext, TUser>, logger?: Soap.Logger);
12
+ private static validateConfig;
11
13
  protected extractCredentials(context?: TContext): Promise<{
12
14
  identifier: string;
13
15
  password: string;
@@ -17,4 +19,5 @@ export declare class LocalStrategy<TContext = unknown, TUser = unknown> extends
17
19
  identifier: string;
18
20
  password: string;
19
21
  }): Promise<TUser | null>;
22
+ changePassword(context: TContext): Promise<void>;
20
23
  }
@@ -3,24 +3,105 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.LocalStrategy = void 0;
4
4
  const credential_auth_strategy_1 = require("../credential-auth.strategy");
5
5
  const local_tools_1 = require("./local.tools");
6
+ const errors_1 = require("../../errors");
7
+ const validation_1 = require("../../utils/validation");
6
8
  class LocalStrategy extends credential_auth_strategy_1.CredentialAuthStrategy {
7
9
  session;
8
10
  jwt;
9
11
  logger;
12
+ name = "local";
10
13
  constructor(config, session, jwt, logger) {
14
+ LocalStrategy.validateConfig(config);
11
15
  super((0, local_tools_1.prepareLocalConfig)(config), session, jwt, logger);
12
16
  this.session = session;
13
17
  this.jwt = jwt;
14
18
  this.logger = logger;
15
19
  }
20
+ static validateConfig(config) {
21
+ try {
22
+ validation_1.ValidationUtils.required(config, "config");
23
+ if (config.credentials) {
24
+ validation_1.ValidationUtils.required(config.credentials.extractCredentials, "config.credentials.extractCredentials");
25
+ validation_1.ValidationUtils.function(config.credentials.extractCredentials, "config.credentials.extractCredentials");
26
+ validation_1.ValidationUtils.required(config.credentials.verifyCredentials, "config.credentials.verifyCredentials");
27
+ validation_1.ValidationUtils.function(config.credentials.verifyCredentials, "config.credentials.verifyCredentials");
28
+ }
29
+ if (config.user) {
30
+ validation_1.ValidationUtils.required(config.user.fetchUser, "config.user.fetchUser");
31
+ validation_1.ValidationUtils.function(config.user.fetchUser, "config.user.fetchUser");
32
+ }
33
+ if (config.routes) {
34
+ validation_1.ValidationUtils.required(config.routes.login, "config.routes.login");
35
+ validation_1.ValidationUtils.required(config.routes.logout, "config.routes.logout");
36
+ validation_1.ValidationUtils.object(config.routes.login, "config.routes.login");
37
+ validation_1.ValidationUtils.object(config.routes.logout, "config.routes.logout");
38
+ if (config.routes.login) {
39
+ validation_1.ValidationUtils.nonEmptyString(config.routes.login.path, "config.routes.login.path");
40
+ validation_1.ValidationUtils.nonEmptyString(config.routes.login.method, "config.routes.login.method");
41
+ }
42
+ if (config.routes.logout) {
43
+ validation_1.ValidationUtils.nonEmptyString(config.routes.logout.path, "config.routes.logout.path");
44
+ validation_1.ValidationUtils.nonEmptyString(config.routes.logout.method, "config.routes.logout.method");
45
+ }
46
+ }
47
+ }
48
+ catch (error) {
49
+ if (error instanceof validation_1.ValidationError) {
50
+ throw error;
51
+ }
52
+ throw new validation_1.ValidationError(`Invalid Local strategy configuration: ${error.message}`);
53
+ }
54
+ }
16
55
  extractCredentials(context) {
56
+ validation_1.ValidationUtils.required(context, "context");
17
57
  return this.config.credentials.extractCredentials(context);
18
58
  }
19
59
  async verifyCredentials(identifier, password) {
60
+ validation_1.ValidationUtils.nonEmptyString(identifier, "identifier");
61
+ validation_1.ValidationUtils.nonEmptyString(password, "password");
20
62
  return this.config.credentials.verifyCredentials(identifier, password);
21
63
  }
22
64
  async fetchUser(credentials) {
65
+ validation_1.ValidationUtils.required(credentials, "credentials");
66
+ if (credentials && typeof credentials === "object" && !Array.isArray(credentials)) {
67
+ validation_1.ValidationUtils.nonEmptyString(credentials.identifier, "credentials.identifier");
68
+ validation_1.ValidationUtils.nonEmptyString(credentials.password, "credentials.password");
69
+ }
23
70
  return this.config.user.fetchUser(credentials.identifier);
24
71
  }
72
+ async changePassword(context) {
73
+ try {
74
+ const credentials = await this.config.credentials.extractCredentials(context);
75
+ if (!credentials) {
76
+ throw new errors_1.MissingCredentialsError();
77
+ }
78
+ if (!credentials.identifier ||
79
+ !credentials.password ||
80
+ !credentials.newPassword) {
81
+ throw new errors_1.InvalidCredentialsError();
82
+ }
83
+ await this.accountLock?.isAccountLocked(credentials.identifier);
84
+ await this.throttle?.checkFailedAttempts(credentials.identifier);
85
+ const user = await this.fetchUser(credentials);
86
+ if (!user) {
87
+ throw new errors_1.UserNotFoundError();
88
+ }
89
+ if ((await this.verifyCredentials(credentials.identifier, credentials.password)) === false) {
90
+ await this.throttle?.incrementFailedAttempts(credentials.identifier);
91
+ throw new errors_1.InvalidCredentialsError();
92
+ }
93
+ await this.password.validatePassword(credentials.newPassword, credentials.password);
94
+ await this.password.updatePassword(credentials.identifier, credentials.newPassword);
95
+ await this.throttle?.resetFailedAttempts(credentials.identifier);
96
+ await this.onSuccess("change_password", {
97
+ identifier: credentials.identifier,
98
+ });
99
+ this.logger?.info(`User password ${credentials.identifier} changed.`);
100
+ }
101
+ catch (error) {
102
+ await this.onFailure("change_password", { context, error });
103
+ throw error;
104
+ }
105
+ }
25
106
  }
26
107
  exports.LocalStrategy = LocalStrategy;