@soapjs/soap-auth 0.3.1 → 0.3.2

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 (121) hide show
  1. package/build/__tests__/soap-auth.test.d.ts +1 -0
  2. package/build/__tests__/soap-auth.test.js +42 -0
  3. package/build/errors.d.ts +14 -3
  4. package/build/errors.js +29 -8
  5. package/build/index.d.ts +1 -1
  6. package/build/index.js +1 -1
  7. package/build/services/__tests__/account-lock.service.test.d.ts +1 -0
  8. package/build/services/__tests__/account-lock.service.test.js +55 -0
  9. package/build/services/__tests__/auth-throttle.service.test.d.ts +1 -0
  10. package/build/services/__tests__/auth-throttle.service.test.js +48 -0
  11. package/build/services/__tests__/jwks.service.test.d.ts +1 -0
  12. package/build/services/__tests__/jwks.service.test.js +39 -0
  13. package/build/services/__tests__/mfa.service.test.d.ts +1 -0
  14. package/build/services/__tests__/mfa.service.test.js +66 -0
  15. package/build/services/__tests__/password.service.test.d.ts +1 -0
  16. package/build/services/__tests__/password.service.test.js +66 -0
  17. package/build/services/__tests__/pkce.service.test.d.ts +1 -0
  18. package/build/services/__tests__/pkce.service.test.js +77 -0
  19. package/build/services/__tests__/rate-limit.service.test.d.ts +1 -0
  20. package/build/services/__tests__/rate-limit.service.test.js +37 -0
  21. package/build/services/__tests__/role.service.test.d.ts +1 -0
  22. package/build/services/__tests__/role.service.test.js +31 -0
  23. package/build/services/account-lock.service.d.ts +12 -0
  24. package/build/services/account-lock.service.js +39 -0
  25. package/build/services/auth-throttle.service.d.ts +10 -0
  26. package/build/services/auth-throttle.service.js +43 -0
  27. package/build/services/index.d.ts +8 -0
  28. package/build/{factories → services}/index.js +8 -3
  29. package/build/services/jwks.service.d.ts +7 -0
  30. package/build/services/jwks.service.js +41 -0
  31. package/build/services/mfa.service.d.ts +12 -0
  32. package/build/services/mfa.service.js +74 -0
  33. package/build/services/password.service.d.ts +14 -0
  34. package/build/services/password.service.js +78 -0
  35. package/build/services/pkce.service.d.ts +14 -0
  36. package/build/services/pkce.service.js +81 -0
  37. package/build/services/rate-limit.service.d.ts +9 -0
  38. package/build/services/rate-limit.service.js +26 -0
  39. package/build/services/role.service.d.ts +9 -0
  40. package/build/services/role.service.js +26 -0
  41. package/build/session/__tests__/file.session-store.test.d.ts +1 -0
  42. package/build/session/__tests__/file.session-store.test.js +117 -0
  43. package/build/session/__tests__/memory.session-store.test.d.ts +1 -0
  44. package/build/session/__tests__/memory.session-store.test.js +77 -0
  45. package/build/session/__tests__/session-handler.test.d.ts +1 -0
  46. package/build/session/__tests__/session-handler.test.js +337 -0
  47. package/build/session/file.session-store.d.ts +1 -0
  48. package/build/session/file.session-store.js +7 -0
  49. package/build/session/memory.session-store.d.ts +4 -1
  50. package/build/session/memory.session-store.js +11 -5
  51. package/build/session/session-handler.d.ts +12 -7
  52. package/build/session/session-handler.js +46 -13
  53. package/build/session/session.errors.d.ts +6 -0
  54. package/build/session/session.errors.js +15 -0
  55. package/build/soap-auth.d.ts +9 -8
  56. package/build/soap-auth.js +42 -29
  57. package/build/strategies/__tests__/base-auth.strategy.test.d.ts +14 -0
  58. package/build/strategies/__tests__/base-auth.strategy.test.js +137 -0
  59. package/build/strategies/__tests__/credential-auth.strategy.test.d.ts +14 -0
  60. package/build/strategies/__tests__/credential-auth.strategy.test.js +265 -0
  61. package/build/strategies/__tests__/token-auth.strategy.test.d.ts +28 -0
  62. package/build/strategies/__tests__/token-auth.strategy.test.js +298 -0
  63. package/build/strategies/api-key/__tests__/api-key.strategy.test.d.ts +1 -0
  64. package/build/strategies/api-key/__tests__/api-key.strategy.test.js +103 -0
  65. package/build/strategies/api-key/api-key.strategy.d.ts +5 -2
  66. package/build/strategies/api-key/api-key.strategy.js +43 -35
  67. package/build/strategies/api-key/api-key.tools.d.ts +2 -0
  68. package/build/strategies/api-key/api-key.tools.js +39 -0
  69. package/build/strategies/api-key/api-key.types.d.ts +10 -2
  70. package/build/strategies/base-auth.strategy.d.ts +11 -5
  71. package/build/strategies/base-auth.strategy.js +45 -52
  72. package/build/strategies/basic/__tests__/basic.strategy.test.d.ts +1 -0
  73. package/build/strategies/basic/__tests__/basic.strategy.test.js +104 -0
  74. package/build/strategies/basic/basic.strategy.d.ts +5 -7
  75. package/build/strategies/basic/basic.strategy.js +6 -6
  76. package/build/strategies/basic/basic.tools.d.ts +2 -0
  77. package/build/strategies/basic/basic.tools.js +44 -0
  78. package/build/strategies/credential-auth.strategy.d.ts +7 -17
  79. package/build/strategies/credential-auth.strategy.js +116 -181
  80. package/build/strategies/jwt/__tests__/jwt.strategy.test.d.ts +1 -0
  81. package/build/strategies/jwt/__tests__/jwt.strategy.test.js +156 -0
  82. package/build/strategies/jwt/__tests__/jwt.tools.test.d.ts +1 -0
  83. package/build/strategies/jwt/__tests__/jwt.tools.test.js +98 -0
  84. package/build/strategies/jwt/jwt.strategy.d.ts +13 -14
  85. package/build/strategies/jwt/jwt.strategy.js +57 -44
  86. package/build/strategies/jwt/jwt.tools.d.ts +20 -7
  87. package/build/strategies/jwt/jwt.tools.js +180 -81
  88. package/build/strategies/local/__tests__/local.strategy.test.d.ts +1 -0
  89. package/build/strategies/local/__tests__/local.strategy.test.js +115 -0
  90. package/build/strategies/local/local.strategy.d.ts +4 -3
  91. package/build/strategies/local/local.strategy.js +7 -6
  92. package/build/strategies/local/local.tools.d.ts +2 -0
  93. package/build/strategies/local/local.tools.js +44 -0
  94. package/build/strategies/oauth2/hybrid.oauth2.strategy.d.ts +5 -0
  95. package/build/strategies/oauth2/hybrid.oauth2.strategy.js +92 -0
  96. package/build/strategies/oauth2/oauth2.errors.d.ts +12 -0
  97. package/build/strategies/oauth2/oauth2.errors.js +24 -0
  98. package/build/strategies/oauth2/oauth2.strategy.d.ts +25 -15
  99. package/build/strategies/oauth2/oauth2.strategy.js +131 -141
  100. package/build/strategies/oauth2/oauth2.tools.d.ts +7 -2
  101. package/build/strategies/oauth2/oauth2.tools.js +119 -14
  102. package/build/strategies/oauth2/oauth2.types.d.ts +32 -1
  103. package/build/strategies/token-auth.strategy.d.ts +14 -8
  104. package/build/strategies/token-auth.strategy.js +162 -38
  105. package/build/tools/index.d.ts +0 -2
  106. package/build/tools/index.js +0 -2
  107. package/build/tools/tools.d.ts +2 -1
  108. package/build/tools/tools.js +9 -12
  109. package/build/types.d.ts +88 -57
  110. package/package.json +1 -1
  111. package/build/factories/auth-strategy.factory.d.ts +0 -9
  112. package/build/factories/auth-strategy.factory.js +0 -16
  113. package/build/factories/http-auth-strategy.factory.d.ts +0 -5
  114. package/build/factories/http-auth-strategy.factory.js +0 -41
  115. package/build/factories/index.d.ts +0 -3
  116. package/build/factories/socket-auth-strategy.factory.d.ts +0 -5
  117. package/build/factories/socket-auth-strategy.factory.js +0 -27
  118. package/build/tools/session.tools.d.ts +0 -6
  119. package/build/tools/session.tools.js +0 -15
  120. package/build/tools/token.tools.d.ts +0 -7
  121. package/build/tools/token.tools.js +0 -32
@@ -1,8 +1,33 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
2
25
  Object.defineProperty(exports, "__esModule", { value: true });
3
26
  exports.TokenAuthStrategy = void 0;
27
+ const Soap = __importStar(require("@soapjs/soap"));
4
28
  const base_auth_strategy_1 = require("./base-auth.strategy");
5
29
  const errors_1 = require("../errors");
30
+ const jsonwebtoken_1 = require("jsonwebtoken");
6
31
  class TokenAuthStrategy extends base_auth_strategy_1.BaseAuthStrategy {
7
32
  config;
8
33
  session;
@@ -13,66 +38,165 @@ class TokenAuthStrategy extends base_auth_strategy_1.BaseAuthStrategy {
13
38
  this.session = session;
14
39
  this.logger = logger;
15
40
  }
41
+ async fetchUser(payload) {
42
+ if (this.config?.user?.fetchUser) {
43
+ return this.config.user.fetchUser(payload);
44
+ }
45
+ throw new Soap.NotImplementedError("fetchUser");
46
+ }
16
47
  async authenticate(context) {
17
48
  try {
18
- let accessToken = await this.retrieveAccessToken(context);
19
- let refreshToken;
20
- await this.checkRateLimit(context);
49
+ await this.rateLimit?.checkRateLimit(context);
50
+ const accessToken = this.extractAccessToken(context);
51
+ const refreshToken = this.extractRefreshToken(context);
21
52
  if (accessToken) {
22
- try {
23
- const decoded = await this.verifyAccessToken(accessToken);
24
- const user = await this.config.user.getUserData(decoded);
25
- if (!user)
26
- throw new errors_1.UserNotFoundError();
27
- await this.isAuthorized(user);
53
+ const { user, isExpired } = await this.verifyAndFetchUser(accessToken);
54
+ if (user) {
55
+ await this.role?.isAuthorized(user);
28
56
  return { user, tokens: { accessToken } };
29
57
  }
30
- catch (error) {
31
- this.logger?.warn("Access token is invalid or expired, trying refresh token...");
58
+ if (!isExpired) {
59
+ throw new errors_1.InvalidTokenError("Access");
32
60
  }
61
+ this.logger?.warn("Access token expired, attempting refresh...");
33
62
  }
34
- refreshToken = await this.retrieveRefreshToken(context);
35
- if (!refreshToken)
36
- throw new errors_1.MissingTokenError("Refresh");
37
- const newTokens = await this.rotateTokens(context);
38
- accessToken = newTokens.accessToken;
39
- refreshToken = newTokens.refreshToken;
40
- if (!accessToken)
63
+ if (!this.config.refreshToken) {
41
64
  throw new errors_1.MissingTokenError("Access");
65
+ }
66
+ if (!refreshToken) {
67
+ throw new errors_1.MissingTokenError("Refresh");
68
+ }
69
+ return this.refreshTokens(context);
70
+ }
71
+ catch (error) {
72
+ await this.onFailure("authenticate", { error });
73
+ throw error;
74
+ }
75
+ }
76
+ async verifyAndFetchUser(accessToken) {
77
+ try {
42
78
  const decoded = await this.verifyAccessToken(accessToken);
43
- const user = await this.config.user.getUserData(decoded);
44
- if (!user)
79
+ const user = await this.fetchUser(decoded);
80
+ if (!user) {
45
81
  throw new errors_1.UserNotFoundError();
46
- await this.isAuthorized(user);
47
- return { user, tokens: { accessToken, refreshToken } };
82
+ }
83
+ return { user, isExpired: false };
48
84
  }
49
85
  catch (error) {
50
- this.logger?.error("Authentication failed:", error);
51
- throw new errors_1.AuthError(error, "Authentication failed.");
86
+ this.logger?.error(error);
87
+ if (error instanceof jsonwebtoken_1.TokenExpiredError) {
88
+ return { user: null, isExpired: true };
89
+ }
90
+ return { user: null, isExpired: false };
52
91
  }
53
92
  }
54
- async rotateTokens(context) {
93
+ async refreshTokens(context, existingUser) {
55
94
  try {
56
- const refreshToken = await this.retrieveRefreshToken(context);
57
- if (!refreshToken)
95
+ if (!this.config.refreshToken) {
96
+ throw new Error("Refresh tokens are not enabled.");
97
+ }
98
+ const refreshToken = this.extractRefreshToken(context);
99
+ if (!refreshToken) {
58
100
  throw new errors_1.MissingTokenError("Refresh");
101
+ }
59
102
  const payload = await this.verifyRefreshToken(refreshToken);
60
- if (!payload)
103
+ if (!payload) {
61
104
  throw new errors_1.InvalidTokenError("Refresh");
62
- const newAccessToken = await this.generateAccessToken(payload);
63
- let newRefreshToken = await this.generateRefreshToken(payload);
64
- await this.storeAccessToken(newAccessToken, context);
65
- if (newRefreshToken) {
66
- await this.storeRefreshToken(newRefreshToken, context);
67
105
  }
68
- this.embedAccessToken(newAccessToken, context);
69
- this.embedRefreshToken(newRefreshToken, context);
70
- return { accessToken: newAccessToken, refreshToken: newRefreshToken };
106
+ if (this.config.refreshToken.absoluteExpiry) {
107
+ const { payloadField, onExpiry } = this.config.refreshToken.absoluteExpiry;
108
+ const field = payloadField || "absoluteExp";
109
+ const maxTime = payload[field];
110
+ if (maxTime && Date.now() / 1000 > maxTime) {
111
+ this.logger?.warn(`Absolute expiry exceeded for refresh token.`);
112
+ switch (onExpiry) {
113
+ case "logout": {
114
+ await this.session?.logoutSession(context);
115
+ await this.invalidateRefreshToken(refreshToken, context);
116
+ throw new errors_1.InvalidTokenError("Refresh");
117
+ }
118
+ case "error":
119
+ throw new errors_1.InvalidTokenError("Refresh");
120
+ case "ignore":
121
+ break;
122
+ }
123
+ }
124
+ }
125
+ let user = existingUser ?? (await this.fetchUser(payload));
126
+ if (!user) {
127
+ throw new errors_1.UserNotFoundError();
128
+ }
129
+ await this.role?.isAuthorized(user);
130
+ const newTokens = await this.issueTokens(user, context, true);
131
+ return {
132
+ user,
133
+ tokens: newTokens,
134
+ };
71
135
  }
72
136
  catch (error) {
73
- this.logger?.error("Token rotation failed:", error);
74
- throw new errors_1.InvalidTokenError("Refresh");
137
+ await this.onFailure("refresh_tokens", {
138
+ error,
139
+ });
140
+ throw error;
141
+ }
142
+ }
143
+ async issueTokens(user, context, rotate) {
144
+ try {
145
+ const accessToken = await this.generateAccessToken(user, context);
146
+ let refreshToken;
147
+ if (this.config.refreshToken) {
148
+ refreshToken = await this.generateRefreshToken(user, context);
149
+ if (rotate && this.config?.refreshToken?.rotation) {
150
+ const oldRefreshToken = this.extractRefreshToken(context);
151
+ refreshToken = await this.rotateToken(oldRefreshToken, user, context);
152
+ }
153
+ else if (this.generateRefreshToken) {
154
+ refreshToken = await this.generateRefreshToken(user, context);
155
+ }
156
+ }
157
+ await this.storeAccessToken(accessToken);
158
+ this.embedAccessToken(accessToken, context);
159
+ if (refreshToken) {
160
+ await this.storeRefreshToken(refreshToken);
161
+ this.embedRefreshToken(refreshToken, context);
162
+ }
163
+ this.logger?.info(`JWT issued successfully`);
164
+ return { accessToken, refreshToken };
165
+ }
166
+ catch (error) {
167
+ await this.onFailure("issueTokens", {
168
+ context,
169
+ error,
170
+ });
171
+ throw error;
172
+ }
173
+ }
174
+ async rotateToken(refreshToken, user, context) {
175
+ let rotationCount = 0;
176
+ let newRefreshToken;
177
+ if (this.config.refreshToken.rotation.getRotationCount) {
178
+ rotationCount = await this.config.refreshToken.rotation.getRotationCount(refreshToken, user, context);
179
+ }
180
+ if (this.config.refreshToken.rotation.isLimitReached(rotationCount, this.config.refreshToken.rotation.maxRotations, user, context)) {
181
+ throw new errors_1.TokenRotationLimitReachedError();
182
+ }
183
+ if (this.config.refreshToken.rotation.rotateToken) {
184
+ const result = await this.config.refreshToken.rotation.rotateToken(refreshToken, user, context);
185
+ newRefreshToken = result.newToken;
186
+ if (typeof result.newRotationCount === "number") {
187
+ rotationCount = result.newRotationCount;
188
+ }
189
+ else {
190
+ rotationCount += 1;
191
+ }
192
+ if (this.config.refreshToken.rotation.afterRotation) {
193
+ await this.config.refreshToken.rotation.afterRotation(refreshToken, newRefreshToken, user, context, rotationCount);
194
+ }
195
+ }
196
+ else {
197
+ this.logger?.warn("Rotation enabled but rotateToken not provided.");
75
198
  }
199
+ return newRefreshToken;
76
200
  }
77
201
  }
78
202
  exports.TokenAuthStrategy = TokenAuthStrategy;
@@ -1,3 +1 @@
1
- export * from "./session.tools";
2
- export * from "./token.tools";
3
1
  export * from "./tools";
@@ -14,6 +14,4 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- __exportStar(require("./session.tools"), exports);
18
- __exportStar(require("./token.tools"), exports);
19
17
  __exportStar(require("./tools"), exports);
@@ -1,3 +1,4 @@
1
+ /// <reference types="node" />
1
2
  export declare const generateUUID: () => Promise<string>;
2
- export declare const generateSecureToken: (tokenLength?: number, encoding?: string) => Promise<string>;
3
3
  export declare function resolveConfig<T>(strategyConfig: T | undefined, globalConfig: T | undefined): T | undefined;
4
+ export declare const generateRandomString: (size?: number, encoding?: BufferEncoding) => string;
@@ -1,23 +1,20 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.resolveConfig = exports.generateSecureToken = exports.generateUUID = void 0;
4
- const crypto_1 = require("crypto");
6
+ exports.generateRandomString = exports.resolveConfig = exports.generateUUID = void 0;
5
7
  const uuid_1 = require("uuid");
8
+ const crypto_1 = __importDefault(require("crypto"));
6
9
  const generateUUID = async () => {
7
10
  return Promise.resolve((0, uuid_1.v4)());
8
11
  };
9
12
  exports.generateUUID = generateUUID;
10
- const generateSecureToken = async (tokenLength = 32, encoding = "hex") => {
11
- return new Promise((resolve, reject) => {
12
- (0, crypto_1.randomBytes)(tokenLength, (err, buffer) => {
13
- if (err)
14
- return reject(err);
15
- resolve(buffer.toString(encoding));
16
- });
17
- });
18
- };
19
- exports.generateSecureToken = generateSecureToken;
20
13
  function resolveConfig(strategyConfig, globalConfig) {
21
14
  return strategyConfig || globalConfig;
22
15
  }
23
16
  exports.resolveConfig = resolveConfig;
17
+ const generateRandomString = (size = 32, encoding = "hex") => {
18
+ return crypto_1.default.randomBytes(size).toString(encoding);
19
+ };
20
+ exports.generateRandomString = generateRandomString;
package/build/types.d.ts CHANGED
@@ -4,11 +4,13 @@ import { OAuth2StrategyConfig } from "./strategies/oauth2/oauth2.types";
4
4
  import { ApiKeyStrategyConfig } from "./strategies/api-key/api-key.types";
5
5
  import { BasicStrategyConfig } from "./strategies/basic/basic.types";
6
6
  import { JwtConfig } from "./strategies/jwt/jwt.types";
7
+ export type AuthCategories = "http" | "socket" | "grpc" | "webhook" | "edge" | "isa" | "event";
7
8
  export interface SessionStore {
8
9
  getSession<T = SessionData>(sid: string, ...args: unknown[]): Promise<T | null>;
9
10
  setSession<T = SessionData>(sid: string, session: T, ...args: unknown[]): void | Promise<void>;
10
11
  destroySession(sid: string, ...args: unknown[]): void | Promise<void>;
11
12
  touchSession<T = SessionData>(sid: string, session: T, ...args: unknown[]): void | Promise<void>;
13
+ getSessionIds(): Promise<string[]>;
12
14
  }
13
15
  export type SessionData = {
14
16
  [key: string]: any;
@@ -39,23 +41,19 @@ export interface AuthResultConfig<TContext = unknown, TUser = unknown> {
39
41
  onSuccess?: (action: string, context: AuthSuccessContext<TUser, TContext>) => Promise<void> | void;
40
42
  onFailure?: (action: string, context: AuthFailureContext<TContext>) => Promise<void> | void;
41
43
  }
42
- export interface SecurityConfig {
43
- maxFailedLoginAttempts?: number;
44
- lockoutDuration?: number;
45
- notifyOnLockout?: (account: any) => Promise<void>;
44
+ export interface AuthThrottleConfig {
45
+ incrementFailedAttempts?: (identifier: string, ...rest: unknown[]) => Promise<void>;
46
+ resetFailedAttempts?: (identifier: string, ...rest: unknown[]) => Promise<void>;
47
+ getFailedAttempts?: (identifier: string, ...rest: unknown[]) => Promise<number>;
48
+ maxFailedAttempts?: number;
46
49
  }
47
50
  export interface BaseAuthStrategyConfig<TContext = unknown, TUser = unknown> extends AuthResultConfig<TContext, TUser> {
48
51
  mfa?: MfaConfig<TUser, TContext>;
49
52
  session?: SessionConfig;
50
53
  role?: RoleAuthorizationConfig<TUser>;
51
54
  rateLimit?: RateLimitConfig;
52
- security?: SecurityConfig;
55
+ throttle?: AuthThrottleConfig;
53
56
  lock?: AccountLockConfig<TContext>;
54
- failedAttempts?: FailedAttemptsConfig;
55
- }
56
- export interface AuditLoggingConfig<TContext = unknown> {
57
- logAttempt?: (userId: string, success: boolean, context?: TContext) => Promise<void>;
58
- logPasswordChange?: (userId: string, context?: TContext) => Promise<void>;
59
57
  }
60
58
  export interface MfaConfig<TUser = unknown, TContext = unknown> {
61
59
  extractMfaCode?: (context?: TContext) => string;
@@ -74,19 +72,17 @@ export interface MfaConfig<TUser = unknown, TContext = unknown> {
74
72
  incrementMfaAttempts?: (user: TUser) => Promise<void>;
75
73
  }
76
74
  export interface PasswordPolicyConfig {
77
- validatePassword?: (password: string) => boolean;
78
- getLastPasswordChange?: (identifier: string) => Date;
79
- forcePasswordChangeOnFirstLogin?: boolean;
75
+ validatePassword?: (password: string) => Promise<boolean>;
76
+ getLastPasswordChange?: (identifier: string) => Promise<Date>;
80
77
  passwordExpirationDays?: number;
81
- isPasswordChangeRequired?: (identifier: string) => Promise<boolean>;
82
78
  generateResetToken?: (identifier: string) => Promise<string>;
83
79
  sendResetEmail?: (identifier: string, token: string) => Promise<void>;
84
80
  validateResetToken?: (token: string) => Promise<boolean>;
85
81
  updatePassword?: (identifier: string, newPassword: string) => Promise<void>;
86
82
  }
87
- export interface UserConfig {
88
- getUserData: (payload: any) => Promise<any>;
89
- validateUser?: (payload: any) => Promise<any>;
83
+ export interface UserConfig<TUser = unknown> {
84
+ fetchUser: (payload: unknown) => Promise<TUser | null>;
85
+ validateUser?: (payload: unknown) => Promise<any>;
90
86
  }
91
87
  export interface CredentailsConfig<TContext = any> {
92
88
  extractCredentials: <TCredentials = {
@@ -95,44 +91,48 @@ export interface CredentailsConfig<TContext = any> {
95
91
  }>(context: TContext) => TCredentials;
96
92
  verifyCredentials: (identifier: string, password: string) => Promise<boolean>;
97
93
  }
98
- export interface FailedAttemptsConfig {
99
- incrementFailedAttempts?: (identifier: string, ...rest: unknown[]) => Promise<void>;
100
- resetFailedAttempts?: (identifier: string, ...rest: unknown[]) => Promise<void>;
101
- getFailedAttempts?: (identifier: string, ...rest: unknown[]) => Promise<number>;
102
- }
103
94
  export interface CredentialAuthStrategyConfig<TContext = unknown, TUser = unknown> extends BaseAuthStrategyConfig<TContext, TUser> {
104
95
  passwordPolicy?: PasswordPolicyConfig;
105
- audit?: AuditLoggingConfig<TContext>;
106
96
  credentials?: CredentailsConfig<TContext>;
107
- user?: UserConfig;
108
- routes?: {
109
- login?: AuthRouteConfig;
110
- logout?: AuthRouteConfig;
97
+ jwt?: TokenAuthConfig<TContext>;
98
+ user?: UserConfig<TUser>;
99
+ allowGuest?: boolean;
100
+ routes: {
101
+ login: AuthRouteConfig;
102
+ logout: AuthRouteConfig;
111
103
  resetPassword?: AuthRouteConfig;
104
+ changePassword?: AuthRouteConfig;
105
+ requestPasswordReset?: AuthRouteConfig;
112
106
  };
113
107
  }
114
108
  export type AuthRouteConfig = {
115
- path?: string;
116
- method?: string;
109
+ path: string;
110
+ method: string;
117
111
  };
118
- export interface TokenAuthStrategyConfig<TContext = unknown, TUser = unknown> extends BaseAuthStrategyConfig<TContext, TUser> {
119
- user?: UserConfig;
112
+ export interface TokenAuthConfig<TContext = unknown, TUser = unknown> {
113
+ accessToken?: TokenConfig<TContext>;
114
+ refreshToken?: RefreshTokenConfig<TContext, TUser>;
115
+ }
116
+ export interface TokenAuthStrategyConfig<TContext = unknown, TUser = unknown> extends BaseAuthStrategyConfig<TContext, TUser>, TokenAuthConfig<TContext> {
117
+ user?: UserConfig<TUser>;
120
118
  routes: {
121
119
  login?: AuthRouteConfig;
122
120
  logout?: AuthRouteConfig;
123
121
  refresh?: AuthRouteConfig;
124
122
  [key: string]: AuthRouteConfig;
125
123
  };
126
- accessToken?: TokenConfig<TContext>;
127
- refreshToken?: TokenConfig<TContext>;
128
124
  }
129
125
  export interface AccountLockConfig<TContext = unknown> {
130
126
  logFailedAttempt?: (account: any, context?: TContext) => Promise<void>;
131
- lockAccount?: (account: any) => Promise<void>;
132
- isAccountLocked?: (account: any, context?: TContext) => Promise<boolean>;
127
+ lockAccount: (account: any) => Promise<void>;
128
+ removeAccountLock: (account: any) => Promise<void>;
129
+ hasAccountLockExpired: (account: any) => Promise<boolean>;
130
+ isAccountLocked: (account: any, context?: TContext) => Promise<boolean>;
131
+ lockoutDuration?: number;
132
+ notifyOnLockout?: (account: any) => Promise<void>;
133
133
  }
134
134
  export interface RateLimitConfig {
135
- checkRateLimit?: (...args: unknown[]) => Promise<boolean>;
135
+ checkRateLimit: (...args: unknown[]) => Promise<boolean>;
136
136
  incrementRequestCount?: (...args: unknown[]) => Promise<void>;
137
137
  }
138
138
  export interface RoleAuthorizationConfig<TUser = unknown> {
@@ -143,23 +143,26 @@ export type AuthSuccessContext<TUser = unknown, TContext = unknown, TSessionData
143
143
  user?: TUser;
144
144
  context?: TContext;
145
145
  session?: SessionInfo<TSessionData>;
146
- tokens?: string;
146
+ tokens?: Record<string, unknown>;
147
147
  identifier?: string;
148
148
  email?: string;
149
+ additional?: Record<string, unknown>;
149
150
  };
150
151
  export type AuthFailureContext<TContext = unknown> = {
151
152
  error: Error;
152
153
  context?: TContext;
153
154
  identifier?: string;
154
155
  email?: string;
156
+ additional?: Record<string, unknown>;
155
157
  };
156
- export type AuthResult<TUser, TSessionData = unknown> = {
158
+ export type AuthResult<TUser = unknown, TSessionData = unknown> = {
157
159
  user: TUser;
158
160
  session?: SessionInfo<TSessionData>;
159
161
  tokens?: {
160
162
  accessToken?: string;
161
163
  refreshToken?: string | null;
162
164
  apiKey?: string | null;
165
+ [key: string]: string;
163
166
  };
164
167
  };
165
168
  export interface AuthStrategy<TContext = unknown, TUser = unknown> {
@@ -168,6 +171,7 @@ export interface AuthStrategy<TContext = unknown, TUser = unknown> {
168
171
  authorize?(user: any, action: string, resource?: string): Promise<boolean>;
169
172
  refresh?(refreshToken: string): Promise<string>;
170
173
  logout?(context?: TContext): Promise<void>;
174
+ logout?(context?: TContext): Promise<void>;
171
175
  }
172
176
  export interface SoapHttpAuthConfig<TContext = unknown, TUser = unknown> {
173
177
  local?: LocalStrategyConfig<TContext, TUser>;
@@ -175,7 +179,6 @@ export interface SoapHttpAuthConfig<TContext = unknown, TUser = unknown> {
175
179
  [provider: string]: OAuth2StrategyConfig<TContext, TUser>;
176
180
  };
177
181
  apiKey?: ApiKeyStrategyConfig<TContext, TUser>;
178
- jwt?: JwtConfig<TContext, TUser>;
179
182
  basic?: BasicStrategyConfig<TContext, TUser>;
180
183
  custom: {
181
184
  [label: string]: AuthStrategy;
@@ -190,6 +193,7 @@ export interface SoapSocketAuthConfig<TContext = unknown, TUser = unknown> {
190
193
  }
191
194
  export interface SoapAuthConfig<TContext = unknown, TUser = unknown> {
192
195
  session?: SessionConfig;
196
+ jwt?: TokenAuthConfig<TContext>;
193
197
  http?: SoapHttpAuthConfig<TContext, TUser>;
194
198
  socket?: SoapSocketAuthConfig<TContext, TUser>;
195
199
  logger?: Soap.Logger;
@@ -235,7 +239,7 @@ export interface StorageContext {
235
239
  encrypt?: (data: string) => Promise<string> | string;
236
240
  decrypt?: (data: string) => Promise<string> | string;
237
241
  }
238
- export interface TokenIssuerConfig {
242
+ export interface TokenIssuerConfig<TContext = unknown> {
239
243
  secretKey: string;
240
244
  options: {
241
245
  expiresIn: string | number;
@@ -244,7 +248,8 @@ export interface TokenIssuerConfig {
244
248
  subject?: string;
245
249
  [option: string]: unknown;
246
250
  };
247
- generate?: (payload: any) => string;
251
+ buildPayload?: (user: any, context?: TContext) => Record<string, any>;
252
+ generate?: (data: any, context?: TContext) => string;
248
253
  }
249
254
  export interface TokenVerifierConfig {
250
255
  options: {
@@ -252,26 +257,52 @@ export interface TokenVerifierConfig {
252
257
  };
253
258
  verify?: (token: string) => Promise<any>;
254
259
  }
255
- export interface TokenPersistenceConfig {
256
- store?: (token: string, data: any, expiresIn: string | number) => Promise<void>;
257
- read?: (...args: any[]) => Promise<string | null>;
258
- remove?: (...args: any[]) => Promise<void>;
260
+ export interface PersistenceConfig<T = any> {
261
+ store: (data: any, ...args: any[]) => Promise<void>;
262
+ read: (...args: any[]) => Promise<T | null>;
263
+ remove: (...args: any[]) => Promise<void>;
264
+ }
265
+ export interface ContextOperationConfig<TContext = any, TData = any> {
266
+ embed?: (context: TContext, data: TData) => void;
267
+ extract?: (context: TContext) => TData | null;
259
268
  }
260
- export interface TokenConfig<TContext = any> {
261
- embed?: (context: TContext, token: string) => void;
262
- retrieve?: (context: TContext) => Promise<string | null>;
263
- rotation?: TokenRotationConfig;
264
- issuer?: TokenIssuerConfig;
269
+ export interface TokenConfig<TContext = any, TUser = any> extends ContextOperationConfig<TContext, string> {
270
+ rotation?: TokenRotationConfig<TContext, TUser>;
271
+ issuer?: TokenIssuerConfig<TContext>;
265
272
  verifier?: TokenVerifierConfig;
266
- persistence?: TokenPersistenceConfig;
273
+ persistence?: PersistenceConfig;
267
274
  additional?: Record<string, unknown>;
268
275
  }
269
- export interface TokenRotationConfig {
270
- maxRotations?: number;
271
- rotateToken?: (token: string) => Promise<string>;
276
+ export interface RefreshTokenConfig<TContext = any, TUser = any> extends TokenConfig<TContext, TUser> {
277
+ absoluteExpiry?: AbsoluteExpiryConfig;
278
+ }
279
+ export interface AbsoluteExpiryConfig {
280
+ payloadField?: string;
281
+ onExpiry?: "error" | "logout" | "ignore";
282
+ }
283
+ export interface TokenRotationConfig<TContext, TUser> {
284
+ maxRotations: number;
285
+ getRotationCount: (token: string, user: TUser, context: TContext) => Promise<number>;
286
+ rotateToken: (oldToken: string, user: TUser, context: TContext) => Promise<{
287
+ newToken: string;
288
+ newRotationCount?: number;
289
+ }>;
290
+ afterRotation?: (oldToken: string, newToken: string, user: TUser, context: TContext, rotationCount?: number) => Promise<void>;
291
+ isLimitReached?: (rotationCount: number, maxRotations: number | undefined, user: TUser, context: TContext) => boolean;
272
292
  }
273
293
  export interface PKCEConfig<TContext> {
274
- generateCodeVerifier?: () => string;
275
- storeCodeVerifier?: (context: TContext, codeVerifier: string) => void;
276
- retrieveCodeVerifier?: (context: TContext) => string | null;
294
+ challenge: {
295
+ expiresIn?: number;
296
+ generate?: (codeVerifier: string) => string;
297
+ embed?: (context: TContext, challenge: string) => void;
298
+ extract?: (context: TContext) => string | null;
299
+ persistence?: PersistenceConfig;
300
+ };
301
+ verifier: {
302
+ expiresIn?: number;
303
+ generate?: () => string;
304
+ embed?: (context: TContext, codeVerifier: string) => void;
305
+ extract?: (context: TContext) => string | null;
306
+ persistence?: PersistenceConfig;
307
+ };
277
308
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soapjs/soap-auth",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "",
5
5
  "homepage": "https://docs.soapjs.com",
6
6
  "repository": "https://github.com/soapjs/soap-auth",
@@ -1,9 +0,0 @@
1
- import * as Soap from "@soapjs/soap";
2
- import { AuthStrategy, SessionConfig, SoapAuthConfig } from "../types";
3
- import { SessionHandler } from "../session/session-handler";
4
- export declare abstract class AuthStrategyFactory {
5
- protected logger?: Soap.Logger;
6
- abstract createStrategies(config: SoapAuthConfig): Map<string, AuthStrategy>;
7
- constructor(logger?: Soap.Logger);
8
- protected getSessionHandler(strategyConfig: SessionConfig, globalConfig: SessionConfig): SessionHandler<unknown, any>;
9
- }
@@ -1,16 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AuthStrategyFactory = void 0;
4
- const tools_1 = require("../tools/tools");
5
- const session_handler_1 = require("../session/session-handler");
6
- class AuthStrategyFactory {
7
- logger;
8
- constructor(logger) {
9
- this.logger = logger;
10
- }
11
- getSessionHandler(strategyConfig, globalConfig) {
12
- const sessionConfig = (0, tools_1.resolveConfig)(strategyConfig, globalConfig);
13
- return sessionConfig ? new session_handler_1.SessionHandler(sessionConfig) : undefined;
14
- }
15
- }
16
- exports.AuthStrategyFactory = AuthStrategyFactory;
@@ -1,5 +0,0 @@
1
- import { AuthStrategy, SoapAuthConfig } from "../types";
2
- import { AuthStrategyFactory } from "./auth-strategy.factory";
3
- export declare class HttpAuthStrategyFactory extends AuthStrategyFactory {
4
- createStrategies(config: SoapAuthConfig): Map<string, AuthStrategy>;
5
- }
@@ -1,41 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.HttpAuthStrategyFactory = void 0;
4
- const oauth2_strategy_1 = require("../strategies/oauth2/oauth2.strategy");
5
- const api_key_strategy_1 = require("../strategies/api-key/api-key.strategy");
6
- const jwt_strategy_1 = require("../strategies/jwt/jwt.strategy");
7
- const basic_strategy_1 = require("../strategies/basic/basic.strategy");
8
- const auth_strategy_factory_1 = require("./auth-strategy.factory");
9
- const local_strategy_1 = require("../strategies/local/local.strategy");
10
- class HttpAuthStrategyFactory extends auth_strategy_factory_1.AuthStrategyFactory {
11
- createStrategies(config) {
12
- const strategies = new Map();
13
- if (!config.http) {
14
- return strategies;
15
- }
16
- if (config.http.oauth2) {
17
- for (const provider in config.http.oauth2) {
18
- strategies.set(provider, new oauth2_strategy_1.OAuth2Strategy(config.http.oauth2[provider], this.getSessionHandler(config.http.oauth2[provider].session, config.session), config.logger));
19
- }
20
- }
21
- if (config.http.apiKey) {
22
- strategies.set("apiKey", new api_key_strategy_1.ApiKeyStrategy(config.http.apiKey, this.logger));
23
- }
24
- if (config.http.basic) {
25
- strategies.set("basic", new basic_strategy_1.BasicStrategy(config.http.basic, this.getSessionHandler(config.http.basic.session, config.session), config.logger));
26
- }
27
- if (config.http.local) {
28
- strategies.set("local", new local_strategy_1.LocalStrategy(config.http.local, this.getSessionHandler(config.http.local.session, config.session), config.logger));
29
- }
30
- if (config.http.jwt) {
31
- strategies.set("jwt", new jwt_strategy_1.JwtStrategy(config.http.jwt, this.getSessionHandler(config.http.jwt.session, config.session), this.logger));
32
- }
33
- if (config.http.custom) {
34
- Object.entries(config.http.custom).forEach(([key, strategy]) => {
35
- strategies.set(key, strategy);
36
- });
37
- }
38
- return strategies;
39
- }
40
- }
41
- exports.HttpAuthStrategyFactory = HttpAuthStrategyFactory;
@@ -1,3 +0,0 @@
1
- export * from "./auth-strategy.factory";
2
- export * from "./http-auth-strategy.factory";
3
- export * from "./socket-auth-strategy.factory";