@soapjs/soap-auth 0.3.2 → 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
package/build/types.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  import * as Soap from "@soapjs/soap";
2
+ export type AuthResult<TUser extends Soap.AuthUser = Soap.AuthUser> = Soap.AuthResult<TUser>;
3
+ export type AuthStrategy<TUser extends Soap.AuthUser = Soap.AuthUser> = Soap.AuthStrategy<TUser>;
2
4
  import { LocalStrategyConfig } from "./strategies/local/local.types";
3
5
  import { OAuth2StrategyConfig } from "./strategies/oauth2/oauth2.types";
4
6
  import { ApiKeyStrategyConfig } from "./strategies/api-key/api-key.types";
@@ -71,29 +73,43 @@ export interface MfaConfig<TUser = unknown, TContext = unknown> {
71
73
  resetMfaAttempts?: (user: TUser) => Promise<void>;
72
74
  incrementMfaAttempts?: (user: TUser) => Promise<void>;
73
75
  }
76
+ export type PasswordType = "default" | "one-time" | "temporary";
77
+ export type NewPasswordOptions = {
78
+ expiresIn?: number;
79
+ type: PasswordType;
80
+ additional?: Record<string, unknown>;
81
+ };
82
+ export type PasswordInfo = {
83
+ type: PasswordType;
84
+ expiresIn?: number;
85
+ lastChangeDate?: Date;
86
+ };
74
87
  export interface PasswordPolicyConfig {
75
- validatePassword?: (password: string) => Promise<boolean>;
76
- getLastPasswordChange?: (identifier: string) => Promise<Date>;
88
+ validatePassword?: (password: string, previousPassword?: string) => Promise<void>;
89
+ getPasswordPasswordInfo?: (identifier: string) => Promise<PasswordInfo>;
77
90
  passwordExpirationDays?: number;
78
91
  generateResetToken?: (identifier: string) => Promise<string>;
79
- sendResetEmail?: (identifier: string, token: string) => Promise<void>;
80
- validateResetToken?: (token: string) => Promise<boolean>;
81
- updatePassword?: (identifier: string, newPassword: string) => Promise<void>;
92
+ sendPasswordResetEmail?: (identifier: string, token: string) => Promise<void>;
93
+ validatePasswordResetToken?: (token: string) => Promise<boolean>;
94
+ updatePassword?: (identifier: string, newPassword: string, options?: NewPasswordOptions) => Promise<void>;
95
+ generatePassword?: (identifier: string, options: NewPasswordOptions) => Promise<string>;
82
96
  }
83
97
  export interface UserConfig<TUser = unknown> {
84
98
  fetchUser: (payload: unknown) => Promise<TUser | null>;
85
99
  validateUser?: (payload: unknown) => Promise<any>;
86
100
  }
87
- export interface CredentailsConfig<TContext = any> {
101
+ export type CredentailsConfig<TContext = any> = CredentialsConfig<TContext>;
102
+ export interface CredentialsConfig<TContext = any> {
88
103
  extractCredentials: <TCredentials = {
89
104
  identifier: string;
90
105
  password: string;
106
+ newPassword?: string;
91
107
  }>(context: TContext) => TCredentials;
92
108
  verifyCredentials: (identifier: string, password: string) => Promise<boolean>;
93
109
  }
94
110
  export interface CredentialAuthStrategyConfig<TContext = unknown, TUser = unknown> extends BaseAuthStrategyConfig<TContext, TUser> {
95
111
  passwordPolicy?: PasswordPolicyConfig;
96
- credentials?: CredentailsConfig<TContext>;
112
+ credentials?: CredentialsConfig<TContext>;
97
113
  jwt?: TokenAuthConfig<TContext>;
98
114
  user?: UserConfig<TUser>;
99
115
  allowGuest?: boolean;
@@ -155,43 +171,26 @@ export type AuthFailureContext<TContext = unknown> = {
155
171
  email?: string;
156
172
  additional?: Record<string, unknown>;
157
173
  };
158
- export type AuthResult<TUser = unknown, TSessionData = unknown> = {
159
- user: TUser;
160
- session?: SessionInfo<TSessionData>;
161
- tokens?: {
162
- accessToken?: string;
163
- refreshToken?: string | null;
164
- apiKey?: string | null;
165
- [key: string]: string;
166
- };
167
- };
168
- export interface AuthStrategy<TContext = unknown, TUser = unknown> {
169
- init?(...args: unknown[]): Promise<void>;
170
- authenticate(context?: TContext, ...args: unknown[]): Promise<AuthResult<TUser> | null>;
171
- authorize?(user: any, action: string, resource?: string): Promise<boolean>;
172
- refresh?(refreshToken: string): Promise<string>;
173
- logout?(context?: TContext): Promise<void>;
174
- logout?(context?: TContext): Promise<void>;
175
- }
176
- export interface SoapHttpAuthConfig<TContext = unknown, TUser = unknown> {
174
+ export interface SoapHttpAuthConfig<TContext = unknown, TUser extends Soap.AuthUser = Soap.AuthUser> {
177
175
  local?: LocalStrategyConfig<TContext, TUser>;
176
+ jwt?: JwtConfig<TContext, TUser>;
178
177
  oauth2?: {
179
178
  [provider: string]: OAuth2StrategyConfig<TContext, TUser>;
180
179
  };
181
180
  apiKey?: ApiKeyStrategyConfig<TContext, TUser>;
182
181
  basic?: BasicStrategyConfig<TContext, TUser>;
183
- custom: {
184
- [label: string]: AuthStrategy;
182
+ custom?: {
183
+ [label: string]: Soap.AuthStrategy;
185
184
  };
186
185
  }
187
- export interface SoapSocketAuthConfig<TContext = unknown, TUser = unknown> {
186
+ export interface SoapSocketAuthConfig<TContext = unknown, TUser extends Soap.AuthUser = Soap.AuthUser> {
188
187
  jwt?: JwtConfig<TContext, TUser>;
189
188
  apiKey?: ApiKeyStrategyConfig<TContext, TUser>;
190
- custom: {
191
- [provider: string]: AuthStrategy;
189
+ custom?: {
190
+ [provider: string]: Soap.AuthStrategy;
192
191
  };
193
192
  }
194
- export interface SoapAuthConfig<TContext = unknown, TUser = unknown> {
193
+ export interface SoapAuthConfig<TContext = unknown, TUser extends Soap.AuthUser = Soap.AuthUser> {
195
194
  session?: SessionConfig;
196
195
  jwt?: TokenAuthConfig<TContext>;
197
196
  http?: SoapHttpAuthConfig<TContext, TUser>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,181 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const validation_1 = require("../validation");
4
+ describe("ValidationUtils", () => {
5
+ describe("required", () => {
6
+ it("should not throw for valid values", () => {
7
+ expect(() => validation_1.ValidationUtils.required("test", "field")).not.toThrow();
8
+ expect(() => validation_1.ValidationUtils.required(0, "field")).not.toThrow();
9
+ expect(() => validation_1.ValidationUtils.required(false, "field")).not.toThrow();
10
+ expect(() => validation_1.ValidationUtils.required([], "field")).not.toThrow();
11
+ expect(() => validation_1.ValidationUtils.required({}, "field")).not.toThrow();
12
+ });
13
+ it("should throw ValidationError for null or undefined", () => {
14
+ expect(() => validation_1.ValidationUtils.required(null, "field")).toThrow(validation_1.ValidationError);
15
+ expect(() => validation_1.ValidationUtils.required(undefined, "field")).toThrow(validation_1.ValidationError);
16
+ });
17
+ it("should include field name in error message", () => {
18
+ expect(() => validation_1.ValidationUtils.required(null, "testField")).toThrow("testField is required");
19
+ });
20
+ });
21
+ describe("nonEmptyString", () => {
22
+ it("should return trimmed string for valid input", () => {
23
+ expect(validation_1.ValidationUtils.nonEmptyString(" test ", "field")).toBe("test");
24
+ });
25
+ it("should throw for non-string input", () => {
26
+ expect(() => validation_1.ValidationUtils.nonEmptyString(123, "field")).toThrow(validation_1.ValidationError);
27
+ });
28
+ it("should throw for empty string", () => {
29
+ expect(() => validation_1.ValidationUtils.nonEmptyString("", "field")).toThrow(validation_1.ValidationError);
30
+ expect(() => validation_1.ValidationUtils.nonEmptyString(" ", "field")).toThrow(validation_1.ValidationError);
31
+ });
32
+ it("should throw for null or undefined", () => {
33
+ expect(() => validation_1.ValidationUtils.nonEmptyString(null, "field")).toThrow(validation_1.ValidationError);
34
+ expect(() => validation_1.ValidationUtils.nonEmptyString(undefined, "field")).toThrow(validation_1.ValidationError);
35
+ });
36
+ });
37
+ describe("email", () => {
38
+ it("should return email for valid input", () => {
39
+ expect(validation_1.ValidationUtils.email("test@example.com", "field")).toBe("test@example.com");
40
+ });
41
+ it("should throw for invalid email format", () => {
42
+ expect(() => validation_1.ValidationUtils.email("invalid-email", "field")).toThrow(validation_1.ValidationError);
43
+ expect(() => validation_1.ValidationUtils.email("@example.com", "field")).toThrow(validation_1.ValidationError);
44
+ expect(() => validation_1.ValidationUtils.email("test@", "field")).toThrow(validation_1.ValidationError);
45
+ });
46
+ });
47
+ describe("password", () => {
48
+ it("should return password for valid input", () => {
49
+ expect(validation_1.ValidationUtils.password("password123", "field")).toBe("password123");
50
+ });
51
+ it("should throw for short password", () => {
52
+ expect(() => validation_1.ValidationUtils.password("short", "field", 8)).toThrow(validation_1.ValidationError);
53
+ });
54
+ it("should use default minimum length", () => {
55
+ expect(() => validation_1.ValidationUtils.password("short", "field")).toThrow(validation_1.ValidationError);
56
+ });
57
+ });
58
+ describe("positiveNumber", () => {
59
+ it("should return number for valid input", () => {
60
+ expect(validation_1.ValidationUtils.positiveNumber(5, "field")).toBe(5);
61
+ expect(validation_1.ValidationUtils.positiveNumber("10", "field")).toBe(10);
62
+ });
63
+ it("should throw for non-positive numbers", () => {
64
+ expect(() => validation_1.ValidationUtils.positiveNumber(0, "field")).toThrow(validation_1.ValidationError);
65
+ expect(() => validation_1.ValidationUtils.positiveNumber(-1, "field")).toThrow(validation_1.ValidationError);
66
+ expect(() => validation_1.ValidationUtils.positiveNumber("abc", "field")).toThrow(validation_1.ValidationError);
67
+ });
68
+ });
69
+ describe("range", () => {
70
+ it("should return number within range", () => {
71
+ expect(validation_1.ValidationUtils.range(5, "field", 1, 10)).toBe(5);
72
+ });
73
+ it("should throw for numbers outside range", () => {
74
+ expect(() => validation_1.ValidationUtils.range(0, "field", 1, 10)).toThrow(validation_1.ValidationError);
75
+ expect(() => validation_1.ValidationUtils.range(11, "field", 1, 10)).toThrow(validation_1.ValidationError);
76
+ });
77
+ });
78
+ describe("oneOf", () => {
79
+ it("should return value if it's in allowed values", () => {
80
+ expect(validation_1.ValidationUtils.oneOf("test", "field", ["test", "other"])).toBe("test");
81
+ });
82
+ it("should throw if value is not in allowed values", () => {
83
+ expect(() => validation_1.ValidationUtils.oneOf("invalid", "field", ["test", "other"])).toThrow(validation_1.ValidationError);
84
+ });
85
+ });
86
+ describe("url", () => {
87
+ it("should return URL for valid input", () => {
88
+ expect(validation_1.ValidationUtils.url("https://example.com", "field")).toBe("https://example.com");
89
+ });
90
+ it("should throw for invalid URL", () => {
91
+ expect(() => validation_1.ValidationUtils.url("not-a-url", "field")).toThrow(validation_1.ValidationError);
92
+ });
93
+ });
94
+ describe("jwtToken", () => {
95
+ it("should return token for valid JWT format", () => {
96
+ const token = "header.payload.signature";
97
+ expect(validation_1.ValidationUtils.jwtToken(token, "field")).toBe(token);
98
+ });
99
+ it("should throw for invalid JWT format", () => {
100
+ expect(() => validation_1.ValidationUtils.jwtToken("invalid", "field")).toThrow(validation_1.ValidationError);
101
+ expect(() => validation_1.ValidationUtils.jwtToken("header.payload", "field")).toThrow(validation_1.ValidationError);
102
+ });
103
+ });
104
+ describe("uuid", () => {
105
+ it("should return UUID for valid input", () => {
106
+ const uuid = "123e4567-e89b-12d3-a456-426614174000";
107
+ expect(validation_1.ValidationUtils.uuid(uuid, "field")).toBe(uuid);
108
+ });
109
+ it("should throw for invalid UUID", () => {
110
+ expect(() => validation_1.ValidationUtils.uuid("not-a-uuid", "field")).toThrow(validation_1.ValidationError);
111
+ });
112
+ });
113
+ describe("object", () => {
114
+ it("should return object for valid input", () => {
115
+ const obj = { test: "value" };
116
+ expect(validation_1.ValidationUtils.object(obj, "field")).toBe(obj);
117
+ });
118
+ it("should throw for non-object input", () => {
119
+ expect(() => validation_1.ValidationUtils.object("string", "field")).toThrow(validation_1.ValidationError);
120
+ expect(() => validation_1.ValidationUtils.object([], "field")).toThrow(validation_1.ValidationError);
121
+ });
122
+ });
123
+ describe("array", () => {
124
+ it("should return array for valid input", () => {
125
+ const arr = [1, 2, 3];
126
+ expect(validation_1.ValidationUtils.array(arr, "field")).toBe(arr);
127
+ });
128
+ it("should throw for non-array input", () => {
129
+ expect(() => validation_1.ValidationUtils.array("string", "field")).toThrow(validation_1.ValidationError);
130
+ expect(() => validation_1.ValidationUtils.array({}, "field")).toThrow(validation_1.ValidationError);
131
+ });
132
+ });
133
+ describe("function", () => {
134
+ it("should return function for valid input", () => {
135
+ const fn = () => { };
136
+ expect(validation_1.ValidationUtils.function(fn, "field")).toBe(fn);
137
+ });
138
+ it("should throw for non-function input", () => {
139
+ expect(() => validation_1.ValidationUtils.function("string", "field")).toThrow(validation_1.ValidationError);
140
+ });
141
+ });
142
+ describe("validateConfig", () => {
143
+ it("should not throw for valid config", () => {
144
+ const config = { field1: "value1", field2: "value2" };
145
+ expect(() => validation_1.ValidationUtils.validateConfig(config, ["field1", "field2"])).not.toThrow();
146
+ });
147
+ it("should throw for missing required fields", () => {
148
+ const config = { field1: "value1" };
149
+ expect(() => validation_1.ValidationUtils.validateConfig(config, ["field1", "field2"])).toThrow(validation_1.ValidationError);
150
+ });
151
+ });
152
+ describe("pattern", () => {
153
+ it("should return string for matching pattern", () => {
154
+ const regex = /^[a-z]+$/;
155
+ expect(validation_1.ValidationUtils.pattern("test", "field", regex)).toBe("test");
156
+ });
157
+ it("should throw for non-matching pattern", () => {
158
+ const regex = /^[a-z]+$/;
159
+ expect(() => validation_1.ValidationUtils.pattern("TEST", "field", regex)).toThrow(validation_1.ValidationError);
160
+ });
161
+ });
162
+ describe("date", () => {
163
+ it("should return Date for valid input", () => {
164
+ const date = validation_1.ValidationUtils.date("2023-01-01", "field");
165
+ expect(date).toBeInstanceOf(Date);
166
+ });
167
+ it("should throw for invalid date", () => {
168
+ expect(() => validation_1.ValidationUtils.date("invalid-date", "field")).toThrow(validation_1.ValidationError);
169
+ });
170
+ });
171
+ describe("boolean", () => {
172
+ it("should return boolean for valid input", () => {
173
+ expect(validation_1.ValidationUtils.boolean(true, "field")).toBe(true);
174
+ expect(validation_1.ValidationUtils.boolean(false, "field")).toBe(false);
175
+ });
176
+ it("should throw for non-boolean input", () => {
177
+ expect(() => validation_1.ValidationUtils.boolean("true", "field")).toThrow(validation_1.ValidationError);
178
+ expect(() => validation_1.ValidationUtils.boolean(1, "field")).toThrow(validation_1.ValidationError);
179
+ });
180
+ });
181
+ });
@@ -0,0 +1,23 @@
1
+ export declare class ValidationError extends Error {
2
+ field?: string;
3
+ constructor(message: string, field?: string);
4
+ }
5
+ export declare class ValidationUtils {
6
+ static required(value: any, fieldName: string): void;
7
+ static nonEmptyString(value: any, fieldName: string): string;
8
+ static email(value: any, fieldName: string): string;
9
+ static password(value: any, fieldName: string, minLength?: number): string;
10
+ static positiveNumber(value: any, fieldName: string): number;
11
+ static range(value: any, fieldName: string, min: number, max: number): number;
12
+ static oneOf<T>(value: any, fieldName: string, allowedValues: T[]): T;
13
+ static url(value: any, fieldName: string): string;
14
+ static jwtToken(value: any, fieldName: string): string;
15
+ static uuid(value: any, fieldName: string): string;
16
+ static object(value: any, fieldName: string): object;
17
+ static array(value: any, fieldName: string): any[];
18
+ static function(value: any, fieldName: string): Function;
19
+ static validateConfig(config: any, requiredFields: string[], fieldName?: string): void;
20
+ static pattern(value: any, fieldName: string, regex: RegExp, message?: string): string;
21
+ static date(value: any, fieldName: string): Date;
22
+ static boolean(value: any, fieldName: string): boolean;
23
+ }
@@ -0,0 +1,139 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ValidationUtils = exports.ValidationError = void 0;
4
+ class ValidationError extends Error {
5
+ field;
6
+ constructor(message, field) {
7
+ super(message);
8
+ this.field = field;
9
+ this.name = "ValidationError";
10
+ }
11
+ }
12
+ exports.ValidationError = ValidationError;
13
+ class ValidationUtils {
14
+ static required(value, fieldName) {
15
+ if (value === null || value === undefined) {
16
+ throw new ValidationError(`${fieldName} is required`, fieldName);
17
+ }
18
+ }
19
+ static nonEmptyString(value, fieldName) {
20
+ this.required(value, fieldName);
21
+ if (typeof value !== "string" || value.trim().length === 0) {
22
+ throw new ValidationError(`${fieldName} must be a non-empty string`, fieldName);
23
+ }
24
+ return value.trim();
25
+ }
26
+ static email(value, fieldName) {
27
+ const email = this.nonEmptyString(value, fieldName);
28
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
29
+ if (!emailRegex.test(email)) {
30
+ throw new ValidationError(`${fieldName} must be a valid email address`, fieldName);
31
+ }
32
+ return email;
33
+ }
34
+ static password(value, fieldName, minLength = 8) {
35
+ const password = this.nonEmptyString(value, fieldName);
36
+ if (password.length < minLength) {
37
+ throw new ValidationError(`${fieldName} must be at least ${minLength} characters long`, fieldName);
38
+ }
39
+ return password;
40
+ }
41
+ static positiveNumber(value, fieldName) {
42
+ this.required(value, fieldName);
43
+ const num = Number(value);
44
+ if (isNaN(num) || num <= 0) {
45
+ throw new ValidationError(`${fieldName} must be a positive number`, fieldName);
46
+ }
47
+ return num;
48
+ }
49
+ static range(value, fieldName, min, max) {
50
+ const num = this.positiveNumber(value, fieldName);
51
+ if (num < min || num > max) {
52
+ throw new ValidationError(`${fieldName} must be between ${min} and ${max}`, fieldName);
53
+ }
54
+ return num;
55
+ }
56
+ static oneOf(value, fieldName, allowedValues) {
57
+ this.required(value, fieldName);
58
+ if (!allowedValues.includes(value)) {
59
+ throw new ValidationError(`${fieldName} must be one of: ${allowedValues.join(", ")}`, fieldName);
60
+ }
61
+ return value;
62
+ }
63
+ static url(value, fieldName) {
64
+ const url = this.nonEmptyString(value, fieldName);
65
+ try {
66
+ new URL(url);
67
+ return url;
68
+ }
69
+ catch {
70
+ throw new ValidationError(`${fieldName} must be a valid URL`, fieldName);
71
+ }
72
+ }
73
+ static jwtToken(value, fieldName) {
74
+ const token = this.nonEmptyString(value, fieldName);
75
+ const parts = token.split(".");
76
+ if (parts.length !== 3) {
77
+ throw new ValidationError(`${fieldName} must be a valid JWT token`, fieldName);
78
+ }
79
+ return token;
80
+ }
81
+ static uuid(value, fieldName) {
82
+ const uuid = this.nonEmptyString(value, fieldName);
83
+ const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
84
+ if (!uuidRegex.test(uuid)) {
85
+ throw new ValidationError(`${fieldName} must be a valid UUID`, fieldName);
86
+ }
87
+ return uuid;
88
+ }
89
+ static object(value, fieldName) {
90
+ this.required(value, fieldName);
91
+ if (typeof value !== "object" || Array.isArray(value)) {
92
+ throw new ValidationError(`${fieldName} must be an object`, fieldName);
93
+ }
94
+ return value;
95
+ }
96
+ static array(value, fieldName) {
97
+ this.required(value, fieldName);
98
+ if (!Array.isArray(value)) {
99
+ throw new ValidationError(`${fieldName} must be an array`, fieldName);
100
+ }
101
+ return value;
102
+ }
103
+ static function(value, fieldName) {
104
+ this.required(value, fieldName);
105
+ if (typeof value !== "function") {
106
+ throw new ValidationError(`${fieldName} must be a function`, fieldName);
107
+ }
108
+ return value;
109
+ }
110
+ static validateConfig(config, requiredFields, fieldName = "config") {
111
+ this.object(config, fieldName);
112
+ for (const field of requiredFields) {
113
+ this.required(config[field], `${fieldName}.${field}`);
114
+ }
115
+ }
116
+ static pattern(value, fieldName, regex, message) {
117
+ const str = this.nonEmptyString(value, fieldName);
118
+ if (!regex.test(str)) {
119
+ throw new ValidationError(message || `${fieldName} does not match required pattern`, fieldName);
120
+ }
121
+ return str;
122
+ }
123
+ static date(value, fieldName) {
124
+ this.required(value, fieldName);
125
+ const date = new Date(value);
126
+ if (isNaN(date.getTime())) {
127
+ throw new ValidationError(`${fieldName} must be a valid date`, fieldName);
128
+ }
129
+ return date;
130
+ }
131
+ static boolean(value, fieldName) {
132
+ this.required(value, fieldName);
133
+ if (typeof value !== "boolean") {
134
+ throw new ValidationError(`${fieldName} must be a boolean`, fieldName);
135
+ }
136
+ return value;
137
+ }
138
+ }
139
+ exports.ValidationUtils = ValidationUtils;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soapjs/soap-auth",
3
- "version": "0.3.2",
3
+ "version": "0.4.0",
4
4
  "description": "",
5
5
  "homepage": "https://docs.soapjs.com",
6
6
  "repository": "https://github.com/soapjs/soap-auth",
@@ -15,20 +15,21 @@
15
15
  "prepublish": "npm run clean && tsc --project tsconfig.build.json"
16
16
  },
17
17
  "devDependencies": {
18
- "@soapjs/soap": "^0.5.8",
18
+ "@soapjs/soap": "^0.9.0",
19
19
  "@types/jest": "^27.0.3",
20
20
  "jest": "^27.4.5",
21
21
  "ts-jest": "^27.1.3",
22
22
  "typescript": "^4.8.2"
23
23
  },
24
24
  "peerDependencies": {
25
- "@soapjs/soap": ">=0.5.8"
25
+ "@soapjs/soap": ">=0.8.0"
26
+ },
27
+ "engines": {
28
+ "node": ">=18.0.0"
26
29
  },
27
30
  "dependencies": {
28
- "axios": "^1.7.9",
29
- "bcrypt": "^5.1.1",
31
+ "bcrypt": "^6.0.0",
30
32
  "jsonwebtoken": "^9.0.2",
31
- "jwks-rsa": "^3.1.0",
32
- "uuid": "^9.0.1"
33
+ "jwks-rsa": "^3.1.0"
33
34
  }
34
35
  }