@soapjs/soap-auth 0.3.1 → 0.3.3

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,25 +1,24 @@
1
1
  import * as Soap from "@soapjs/soap";
2
2
  import { TokenAuthStrategy } from "../token-auth.strategy";
3
- import { JwtConfig } from "./jwt.types";
4
- import { SessionHandler } from "../../session/session-handler";
5
- import { TokenConfig } from "../../types";
3
+ import { TokenAuthStrategyConfig, TokenConfig } from "../../types";
6
4
  export declare class JwtStrategy<TContext = unknown, TUser = unknown> extends TokenAuthStrategy<TContext, TUser> {
7
- protected config: JwtConfig<TContext, TUser>;
8
- protected session?: SessionHandler;
5
+ protected config: TokenAuthStrategyConfig<TContext, TUser>;
9
6
  protected logger?: Soap.Logger;
10
7
  protected accessTokenConfig: TokenConfig<TContext>;
11
8
  protected refreshTokenConfig: TokenConfig<TContext>;
12
- constructor(config: JwtConfig<TContext, TUser>, session?: SessionHandler, logger?: Soap.Logger);
13
- protected invalidateRefreshToken(token: string): Promise<void>;
9
+ constructor(config: TokenAuthStrategyConfig<TContext, TUser>, logger?: Soap.Logger);
14
10
  protected verifyAccessToken(token: string): Promise<any>;
15
11
  protected verifyRefreshToken(token: string): Promise<any>;
16
- protected generateAccessToken(payload: any): Promise<string>;
17
- protected generateRefreshToken(payload: any): Promise<string>;
18
- protected storeAccessToken(token: string, context: TContext): Promise<void>;
19
- protected storeRefreshToken(token: string, context: TContext): Promise<void>;
12
+ protected generateAccessToken(user: TUser, context: TContext): Promise<string>;
13
+ protected generateRefreshToken(user: TUser, context: TContext): Promise<string>;
14
+ protected storeAccessToken(token: string): Promise<void>;
15
+ protected storeRefreshToken(token: string): Promise<void>;
20
16
  protected embedAccessToken(token: string, context: TContext): void;
21
17
  protected embedRefreshToken(token: string, context: TContext): void;
22
- protected retrieveAccessToken(context: TContext): Promise<string | undefined>;
23
- protected retrieveRefreshToken(context: TContext): Promise<string | undefined>;
24
- logout(context: TContext): Promise<void>;
18
+ protected extractAccessToken(context: TContext): string | undefined;
19
+ protected extractRefreshToken(context: TContext): string | undefined;
20
+ protected buildAccessTokenPayload(user: TUser, context: TContext): Record<string, any>;
21
+ protected buildRefreshTokenPayload(user: TUser, context: TContext): Record<string, any>;
22
+ invalidateRefreshToken(token: string, context?: TContext): Promise<void>;
23
+ protected invalidateAccessToken(token: string, context?: TContext): Promise<void>;
25
24
  }
@@ -10,28 +10,23 @@ const errors_1 = require("../../errors");
10
10
  const jwt_tools_1 = require("./jwt.tools");
11
11
  class JwtStrategy extends token_auth_strategy_1.TokenAuthStrategy {
12
12
  config;
13
- session;
14
13
  logger;
15
14
  accessTokenConfig;
16
15
  refreshTokenConfig;
17
- constructor(config, session, logger) {
16
+ constructor(config, logger) {
18
17
  if (!config.accessToken.issuer.secretKey) {
19
18
  throw new errors_1.UndefinedTokenSecretError("Access");
20
19
  }
21
20
  if (config.refreshToken && !config.refreshToken.issuer.secretKey) {
22
21
  throw new errors_1.UndefinedTokenSecretError("Refresh");
23
22
  }
24
- super(config, session, logger);
23
+ super((0, jwt_tools_1.prepareJwtConfig)(config), null, logger);
25
24
  this.config = config;
26
- this.session = session;
27
25
  this.logger = logger;
28
- this.accessTokenConfig = (0, jwt_tools_1.prepareAccessTokenConfig)(config.accessToken);
29
- this.refreshTokenConfig = (0, jwt_tools_1.prepareRefreshTokenConfig)(config.refreshToken);
26
+ this.accessTokenConfig = jwt_tools_1.JwtTools.prepareAccessTokenConfig(config.accessToken);
27
+ this.refreshTokenConfig = jwt_tools_1.JwtTools.prepareRefreshTokenConfig(config.refreshToken);
30
28
  this.logger?.info("JWTStrategy initialized with provided configurations.");
31
29
  }
32
- async invalidateRefreshToken(token) {
33
- await this.refreshTokenConfig.persistence.remove?.(token);
34
- }
35
30
  verifyAccessToken(token) {
36
31
  try {
37
32
  if (!token)
@@ -49,7 +44,7 @@ class JwtStrategy extends token_auth_strategy_1.TokenAuthStrategy {
49
44
  }
50
45
  catch (error) {
51
46
  this.logger?.error("JWT verification failed:", error);
52
- throw new errors_1.InvalidTokenError("Access");
47
+ throw error;
53
48
  }
54
49
  }
55
50
  verifyRefreshToken(token) {
@@ -69,29 +64,23 @@ class JwtStrategy extends token_auth_strategy_1.TokenAuthStrategy {
69
64
  }
70
65
  catch (error) {
71
66
  this.logger?.error("JWT verification failed:", error);
72
- throw new errors_1.InvalidTokenError("Refresh");
67
+ error;
73
68
  }
74
69
  }
75
- generateAccessToken(payload) {
76
- const options = this.accessTokenConfig.issuer.options || {};
77
- return jsonwebtoken_1.default.sign(payload, this.accessTokenConfig.issuer.secretKey, {
78
- ...options,
79
- jti: payload.jti || crypto.randomUUID(),
80
- });
70
+ async generateAccessToken(user, context) {
71
+ const payload = this.buildAccessTokenPayload(user, context);
72
+ return Promise.resolve(jwt_tools_1.JwtTools.generateAccessToken(payload, this.accessTokenConfig));
81
73
  }
82
- generateRefreshToken(payload) {
83
- const options = this.refreshTokenConfig.issuer.options || {};
84
- return jsonwebtoken_1.default.sign(payload, this.refreshTokenConfig.issuer.secretKey, {
85
- ...options,
86
- jti: payload.jti || crypto.randomUUID(),
87
- });
74
+ async generateRefreshToken(user, context) {
75
+ const payload = this.buildAccessTokenPayload(user, context);
76
+ return Promise.resolve(jwt_tools_1.JwtTools.generateRefreshToken(payload, this.refreshTokenConfig));
88
77
  }
89
- async storeAccessToken(token, context) {
78
+ async storeAccessToken(token) {
90
79
  if (this.accessTokenConfig.persistence.store) {
91
80
  await this.accessTokenConfig.persistence.store(token, null, this.accessTokenConfig.issuer.options.expiresIn);
92
81
  }
93
82
  }
94
- async storeRefreshToken(token, context) {
83
+ async storeRefreshToken(token) {
95
84
  if (this.refreshTokenConfig.persistence.store) {
96
85
  await this.refreshTokenConfig.persistence.store(token, null, this.refreshTokenConfig.issuer.options.expiresIn);
97
86
  }
@@ -101,7 +90,7 @@ class JwtStrategy extends token_auth_strategy_1.TokenAuthStrategy {
101
90
  this.accessTokenConfig.embed(context, token);
102
91
  }
103
92
  else {
104
- (0, jwt_tools_1.setDefaultJwtHeader)(token, context);
93
+ jwt_tools_1.JwtTools.setDefaultJwtHeader(token, context);
105
94
  }
106
95
  }
107
96
  embedRefreshToken(token, context) {
@@ -109,33 +98,57 @@ class JwtStrategy extends token_auth_strategy_1.TokenAuthStrategy {
109
98
  this.refreshTokenConfig.embed(context, token);
110
99
  }
111
100
  else {
112
- (0, jwt_tools_1.setDefaultJwtCookie)(token, context);
101
+ jwt_tools_1.JwtTools.setDefaultJwtCookie(token, context);
113
102
  }
114
103
  }
115
- retrieveAccessToken(context) {
116
- if (this.accessTokenConfig.retrieve) {
117
- return this.accessTokenConfig.retrieve(context);
104
+ extractAccessToken(context) {
105
+ if (this.accessTokenConfig.extract) {
106
+ return this.accessTokenConfig.extract(context);
118
107
  }
119
108
  else {
120
- return (context.req.headers.authorization?.split(" ")[1] ||
121
- context.request.headers.authorization?.split(" ")[1] ||
122
- context.headers.authorization?.split(" ")[1]);
109
+ return (context.req?.headers?.authorization?.split(" ")[1] ||
110
+ context.request?.headers?.authorization?.split(" ")[1] ||
111
+ context.headers?.authorization?.split(" ")[1]);
123
112
  }
124
113
  }
125
- retrieveRefreshToken(context) {
126
- if (this.refreshTokenConfig.retrieve) {
127
- return this.refreshTokenConfig.retrieve(context);
114
+ extractRefreshToken(context) {
115
+ if (this.refreshTokenConfig.extract) {
116
+ return this.refreshTokenConfig.extract(context);
128
117
  }
129
- return (context.req.cookies?.refreshToken ||
130
- context.request.cookies?.refreshToken ||
131
- context.cookies.refreshToken);
118
+ return (context.req?.cookies?.refreshToken ||
119
+ context.request?.cookies?.refreshToken ||
120
+ context.cookies?.refreshToken);
121
+ }
122
+ buildAccessTokenPayload(user, context) {
123
+ return this.config.accessToken.issuer.buildPayload
124
+ ? this.config.accessToken.issuer.buildPayload(user, context)
125
+ : { id: user.id, email: user.email };
126
+ }
127
+ buildRefreshTokenPayload(user, context) {
128
+ return this.config.refreshToken.issuer.buildPayload
129
+ ? this.config.refreshToken.issuer.buildPayload(user, context)
130
+ : { id: user.id };
132
131
  }
133
- async logout(context) {
134
- const refreshToken = await this.retrieveRefreshToken(context);
132
+ async invalidateRefreshToken(token, context) {
133
+ const refreshToken = token || (await this.extractRefreshToken(context));
135
134
  if (refreshToken) {
136
- await this.invalidateRefreshToken(refreshToken);
137
- (0, jwt_tools_1.clearDefaultJwtCookie)(context);
138
- (0, jwt_tools_1.clearDefaultJwtHeader)(context);
135
+ await this.refreshTokenConfig.persistence?.remove?.(refreshToken);
136
+ if (context) {
137
+ jwt_tools_1.JwtTools.clearDefaultJwtCookie(context);
138
+ jwt_tools_1.JwtTools.clearDefaultJwtHeader(context);
139
+ }
140
+ this.logger?.info(`Refresh token invalidated: ${refreshToken}`);
141
+ }
142
+ }
143
+ async invalidateAccessToken(token, context) {
144
+ const accessToken = token || (await this.extractAccessToken(context));
145
+ if (accessToken) {
146
+ await this.accessTokenConfig.persistence?.remove?.(accessToken);
147
+ if (context) {
148
+ jwt_tools_1.JwtTools.clearDefaultJwtCookie(context);
149
+ jwt_tools_1.JwtTools.clearDefaultJwtHeader(context);
150
+ }
151
+ this.logger?.info(`Access token invalidated: ${accessToken}`);
139
152
  }
140
153
  }
141
154
  }
@@ -1,7 +1,20 @@
1
- import { TokenConfig } from "../../types";
2
- export declare const prepareAccessTokenConfig: <TContext = any>(config: TokenConfig<TContext>) => TokenConfig<TContext>;
3
- export declare const prepareRefreshTokenConfig: <TContext = any>(config: TokenConfig<TContext>) => TokenConfig<TContext>;
4
- export declare const setDefaultJwtCookie: (token: string, context: any) => void;
5
- export declare const setDefaultJwtHeader: (token: string, context: any) => void;
6
- export declare const clearDefaultJwtHeader: (context: any) => void;
7
- export declare const clearDefaultJwtCookie: (context: any) => void;
1
+ import { JwtConfig } from "./jwt.types";
2
+ import { TokenAuthStrategyConfig, TokenConfig } from "../../types";
3
+ export declare class JwtTools {
4
+ static generateAccessToken(payload: any, config: JwtConfig["accessToken"]): string;
5
+ static generateRefreshToken(payload: any, config: JwtConfig["refreshToken"]): string;
6
+ static verifyAccessToken(token: string, config: JwtConfig["accessToken"]): any;
7
+ static verifyRefreshToken(token: string, config: JwtConfig["refreshToken"]): any;
8
+ static setAccessTokenHeader(token: string, context: any): void;
9
+ static setRefreshTokenCookie(token: string, context: any): void;
10
+ static clearTokens(context: any): void;
11
+ static getAccessToken(context: any): string | undefined;
12
+ static getRefreshToken(context: any): string | undefined;
13
+ static prepareAccessTokenConfig: <TContext = any>(config: TokenConfig<TContext, any>) => TokenConfig<TContext, any>;
14
+ static prepareRefreshTokenConfig: <TContext = any>(config: TokenConfig<TContext, any>) => TokenConfig<TContext, any>;
15
+ static setDefaultJwtCookie: (token: string, context: any) => void;
16
+ static setDefaultJwtHeader: (token: string, context: any) => void;
17
+ static clearDefaultJwtHeader: (context: any) => void;
18
+ static clearDefaultJwtCookie: (context: any) => void;
19
+ }
20
+ export declare const prepareJwtConfig: <TContext = any, TUser = any>(config: Partial<TokenAuthStrategyConfig<TContext, TUser>>) => TokenAuthStrategyConfig<TContext, TUser>;
@@ -22,97 +22,196 @@ var __importStar = (this && this.__importStar) || function (mod) {
22
22
  __setModuleDefault(result, mod);
23
23
  return result;
24
24
  };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
25
28
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.clearDefaultJwtCookie = exports.clearDefaultJwtHeader = exports.setDefaultJwtHeader = exports.setDefaultJwtCookie = exports.prepareRefreshTokenConfig = exports.prepareAccessTokenConfig = void 0;
29
+ exports.prepareJwtConfig = exports.JwtTools = void 0;
27
30
  const Soap = __importStar(require("@soapjs/soap"));
28
- const prepareAccessTokenConfig = (config) => {
29
- return Soap.removeUndefinedProperties({
30
- ...config,
31
- generation: {
32
- ...config.issuer.options,
33
- expiresIn: config.issuer.options.expiresIn || "1h",
34
- algorithm: config.issuer.options.algorithm || "HS256",
35
- },
36
- verification: {
37
- ...config.verifier.options,
38
- algorithms: config.verifier.options.algorithms || ["HS256"],
39
- expiresIn: config.verifier.options.expiresIn || "1h",
40
- },
41
- });
42
- };
43
- exports.prepareAccessTokenConfig = prepareAccessTokenConfig;
44
- const prepareRefreshTokenConfig = (config) => {
45
- return Soap.removeUndefinedProperties({
46
- ...config,
47
- generation: {
48
- ...config.issuer,
49
- expiresIn: config.issuer.options.expiresIn || "7d",
50
- algorithm: config.issuer.options.algorithm || "HS256",
51
- },
52
- verification: {
53
- ...config.verifier.options,
54
- algorithms: config.verifier.options.algorithms || ["HS256"],
55
- expiresIn: config.verifier.options.expiresIn || "7d",
56
- },
57
- });
58
- };
59
- exports.prepareRefreshTokenConfig = prepareRefreshTokenConfig;
60
- const setDefaultJwtCookie = (token, context) => {
61
- const options = {
62
- httpOnly: true,
63
- secure: true,
64
- sameSite: "Strict",
65
- maxAge: 7 * 24 * 60 * 60 * 1000,
66
- };
67
- if (context?.res) {
68
- context.res.cookie("refreshToken", token, options);
31
+ const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
32
+ const errors_1 = require("../../errors");
33
+ class JwtTools {
34
+ static generateAccessToken(payload, config) {
35
+ if (!config.issuer.secretKey) {
36
+ throw new errors_1.UndefinedTokenSecretError("Access");
37
+ }
38
+ return jsonwebtoken_1.default.sign(payload, config.issuer.secretKey, config.issuer.options);
69
39
  }
70
- else if (context?.response) {
71
- context.response.cookie("refreshToken", token, options);
40
+ static generateRefreshToken(payload, config) {
41
+ if (!config.issuer.secretKey) {
42
+ throw new errors_1.UndefinedTokenSecretError("Refresh");
43
+ }
44
+ return jsonwebtoken_1.default.sign(payload, config.issuer.secretKey, config.issuer.options);
72
45
  }
73
- else if (context?.cookie) {
74
- context.cookie("refreshToken", token, options);
46
+ static verifyAccessToken(token, config) {
47
+ if (!token)
48
+ throw new errors_1.UndefinedTokenError("Access");
49
+ if (!config.issuer.secretKey)
50
+ throw new errors_1.UndefinedTokenSecretError("Access");
51
+ try {
52
+ return jsonwebtoken_1.default.verify(token, config.issuer.secretKey, config.verifier.options);
53
+ }
54
+ catch (error) {
55
+ throw new errors_1.InvalidTokenError("Access");
56
+ }
75
57
  }
76
- };
77
- exports.setDefaultJwtCookie = setDefaultJwtCookie;
78
- const setDefaultJwtHeader = (token, context) => {
79
- if (typeof context?.res?.setHeader === "function") {
80
- context.res.setHeader("Authorization", `Bearer ${token}`);
58
+ static verifyRefreshToken(token, config) {
59
+ if (!token)
60
+ throw new errors_1.UndefinedTokenError("Refresh");
61
+ if (!config.issuer.secretKey)
62
+ throw new errors_1.UndefinedTokenSecretError("Refresh");
63
+ try {
64
+ return jsonwebtoken_1.default.verify(token, config.issuer.secretKey, config.verifier.options);
65
+ }
66
+ catch (error) {
67
+ throw new errors_1.InvalidTokenError("Refresh");
68
+ }
81
69
  }
82
- else if (typeof context?.response?.setHeader === "function") {
83
- context.response.setHeader("Authorization", `Bearer ${token}`);
70
+ static setAccessTokenHeader(token, context) {
71
+ context.res.setHeader("Authorization", `Bearer ${token}`);
84
72
  }
85
- else if (typeof context?.setHeader === "function") {
86
- context.setHeader("Authorization", `Bearer ${token}`);
73
+ static setRefreshTokenCookie(token, context) {
74
+ context.res.cookie("refreshToken", token, {
75
+ httpOnly: true,
76
+ secure: true,
77
+ sameSite: "Strict",
78
+ });
87
79
  }
88
- };
89
- exports.setDefaultJwtHeader = setDefaultJwtHeader;
90
- const clearDefaultJwtHeader = (context) => {
91
- if (typeof context?.res?.setHeader === "function") {
92
- context.res.setHeader("Authorization", ``);
80
+ static clearTokens(context) {
81
+ context.res.clearCookie("refreshToken");
82
+ context.res.setHeader("Authorization", "");
93
83
  }
94
- else if (typeof context?.response?.setHeader === "function") {
95
- context.response.setHeader("Authorization", ``);
84
+ static getAccessToken(context) {
85
+ return context.req.headers.authorization?.split(" ")[1];
96
86
  }
97
- else if (typeof context?.setHeader === "function") {
98
- context.setHeader("Authorization", ``);
87
+ static getRefreshToken(context) {
88
+ return context.req.cookies?.refreshToken;
99
89
  }
100
- };
101
- exports.clearDefaultJwtHeader = clearDefaultJwtHeader;
102
- const clearDefaultJwtCookie = (context) => {
103
- const options = {
104
- httpOnly: true,
105
- secure: true,
106
- sameSite: "Strict",
90
+ static prepareAccessTokenConfig = (config) => {
91
+ return Soap.removeUndefinedProperties({
92
+ ...config,
93
+ issuer: {
94
+ ...config.issuer,
95
+ options: {
96
+ ...config.issuer.options,
97
+ expiresIn: config.issuer.options.expiresIn || "1h",
98
+ algorithm: config.issuer.options.algorithm || "HS256",
99
+ },
100
+ },
101
+ verifier: {
102
+ ...config.verifier,
103
+ options: {
104
+ ...config.verifier.options,
105
+ algorithms: config.verifier.options.algorithms || ["HS256"],
106
+ expiresIn: config.verifier.options.expiresIn || "1h",
107
+ },
108
+ },
109
+ });
107
110
  };
108
- if (typeof context?.res?.clearCookie === "function") {
109
- context.res.clearCookie("refreshToken", options);
110
- }
111
- else if (typeof context?.response?.clearCookie === "function") {
112
- context.response.clearCookie("refreshToken", options);
113
- }
114
- else if (typeof context?.clearCookie === "function") {
115
- context.clearCookie("refreshToken", options);
116
- }
111
+ static prepareRefreshTokenConfig = (config) => {
112
+ return Soap.removeUndefinedProperties({
113
+ ...config,
114
+ issuer: {
115
+ ...config.issuer,
116
+ options: {
117
+ ...config.issuer.options,
118
+ expiresIn: config.issuer.options.expiresIn || "7d",
119
+ algorithm: config.issuer.options.algorithm || "HS256",
120
+ },
121
+ },
122
+ verifier: {
123
+ ...config.verifier,
124
+ options: {
125
+ ...config.verifier.options,
126
+ algorithms: config.verifier.options.algorithms || ["HS256"],
127
+ expiresIn: config.verifier.options.expiresIn || "7d",
128
+ },
129
+ },
130
+ });
131
+ };
132
+ static setDefaultJwtCookie = (token, context) => {
133
+ const options = {
134
+ httpOnly: true,
135
+ secure: true,
136
+ sameSite: "Strict",
137
+ maxAge: 7 * 24 * 60 * 60 * 1000,
138
+ };
139
+ if (context?.res) {
140
+ context.res.cookie("refreshToken", token, options);
141
+ }
142
+ else if (context?.response) {
143
+ context.response.cookie("refreshToken", token, options);
144
+ }
145
+ else if (context?.cookie) {
146
+ context.cookie("refreshToken", token, options);
147
+ }
148
+ };
149
+ static setDefaultJwtHeader = (token, context) => {
150
+ if (typeof context?.res?.setHeader === "function") {
151
+ context.res.setHeader("Authorization", `Bearer ${token}`);
152
+ }
153
+ else if (typeof context?.response?.setHeader === "function") {
154
+ context.response.setHeader("Authorization", `Bearer ${token}`);
155
+ }
156
+ else if (typeof context?.setHeader === "function") {
157
+ context.setHeader("Authorization", `Bearer ${token}`);
158
+ }
159
+ };
160
+ static clearDefaultJwtHeader = (context) => {
161
+ if (typeof context?.res?.setHeader === "function") {
162
+ context.res.setHeader("Authorization", ``);
163
+ }
164
+ else if (typeof context?.response?.setHeader === "function") {
165
+ context.response.setHeader("Authorization", ``);
166
+ }
167
+ else if (typeof context?.setHeader === "function") {
168
+ context.setHeader("Authorization", ``);
169
+ }
170
+ };
171
+ static clearDefaultJwtCookie = (context) => {
172
+ const options = {
173
+ httpOnly: true,
174
+ secure: true,
175
+ sameSite: "Strict",
176
+ };
177
+ if (typeof context?.res?.clearCookie === "function") {
178
+ context.res.clearCookie("refreshToken", options);
179
+ }
180
+ else if (typeof context?.response?.clearCookie === "function") {
181
+ context.response.clearCookie("refreshToken", options);
182
+ }
183
+ else if (typeof context?.clearCookie === "function") {
184
+ context.clearCookie("refreshToken", options);
185
+ }
186
+ };
187
+ }
188
+ exports.JwtTools = JwtTools;
189
+ const prepareJwtConfig = (config) => {
190
+ return Soap.removeUndefinedProperties({
191
+ ...config,
192
+ routes: {
193
+ login: config.routes?.login ?? {
194
+ path: "/auth/jwt/login",
195
+ method: "POST",
196
+ },
197
+ logout: config.routes?.logout ?? {
198
+ path: "/auth/jwt/logout",
199
+ method: "POST",
200
+ },
201
+ refresh: config.routes?.refresh ?? {
202
+ path: "/auth/jwt/refresh",
203
+ method: "POST",
204
+ },
205
+ ...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
+ },
215
+ });
117
216
  };
118
- exports.clearDefaultJwtCookie = clearDefaultJwtCookie;
217
+ exports.prepareJwtConfig = prepareJwtConfig;
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const local_strategy_1 = require("../local.strategy");
4
+ const errors_1 = require("../../../errors");
5
+ describe("LocalStrategy", () => {
6
+ let strategy;
7
+ let mockConfig;
8
+ let mockSession;
9
+ let mockJwt;
10
+ beforeEach(() => {
11
+ mockConfig = {
12
+ credentials: {
13
+ extractCredentials: jest.fn(),
14
+ verifyCredentials: jest.fn(),
15
+ },
16
+ user: {
17
+ fetchUser: jest.fn(),
18
+ },
19
+ routes: {
20
+ login: {},
21
+ logout: {},
22
+ },
23
+ };
24
+ mockSession = {
25
+ issueSession: jest.fn(),
26
+ };
27
+ mockJwt = {
28
+ issueTokens: jest.fn(),
29
+ };
30
+ strategy = new local_strategy_1.LocalStrategy(mockConfig, mockSession, mockJwt);
31
+ });
32
+ describe("extractCredentials", () => {
33
+ it("should extract credentials from context", async () => {
34
+ const mockContext = { body: { username: "test", password: "pass123" } };
35
+ mockConfig.credentials.extractCredentials.mockResolvedValue({
36
+ identifier: "test",
37
+ password: "pass123",
38
+ });
39
+ const credentials = await strategy.extractCredentials(mockContext);
40
+ expect(credentials).toEqual({ identifier: "test", password: "pass123" });
41
+ expect(mockConfig.credentials.extractCredentials).toHaveBeenCalledWith(mockContext);
42
+ });
43
+ it("should throw MissingCredentialsError if no credentials are provided", async () => {
44
+ mockConfig.credentials.extractCredentials.mockRejectedValue(new errors_1.MissingCredentialsError());
45
+ await expect(strategy.extractCredentials({})).rejects.toThrow(errors_1.MissingCredentialsError);
46
+ });
47
+ });
48
+ describe("verifyCredentials", () => {
49
+ it("should verify credentials successfully", async () => {
50
+ mockConfig.credentials.verifyCredentials.mockResolvedValue(true);
51
+ const result = await strategy.verifyCredentials("test", "pass123");
52
+ expect(result).toBe(true);
53
+ expect(mockConfig.credentials.verifyCredentials).toHaveBeenCalledWith("test", "pass123");
54
+ });
55
+ it("should return false if credentials are invalid", async () => {
56
+ mockConfig.credentials.verifyCredentials.mockResolvedValue(false);
57
+ const result = await strategy.verifyCredentials("test", "wrongpass");
58
+ expect(result).toBe(false);
59
+ expect(mockConfig.credentials.verifyCredentials).toHaveBeenCalledWith("test", "wrongpass");
60
+ });
61
+ });
62
+ describe("fetchUser", () => {
63
+ it("should fetch user data", async () => {
64
+ const mockUser = { id: 1, username: "test" };
65
+ mockConfig.user.fetchUser.mockResolvedValue(mockUser);
66
+ const user = await strategy.fetchUser({
67
+ identifier: "test",
68
+ password: "pass123",
69
+ });
70
+ expect(user).toEqual(mockUser);
71
+ expect(mockConfig.user.fetchUser).toHaveBeenCalledWith("test");
72
+ });
73
+ it("should return null if user is not found", async () => {
74
+ mockConfig.user.fetchUser.mockResolvedValue(null);
75
+ const user = await strategy.fetchUser({
76
+ identifier: "unknown",
77
+ password: "pass123",
78
+ });
79
+ expect(user).toBeNull();
80
+ expect(mockConfig.user.fetchUser).toHaveBeenCalledWith("unknown");
81
+ });
82
+ });
83
+ describe("login", () => {
84
+ const mockContext = { body: { username: "test", password: "pass123" } };
85
+ const mockUser = { id: 1, username: "test" };
86
+ const mockTokens = { accessToken: "access", refreshToken: "refresh" };
87
+ const mockSessionData = { sessionId: "session123", data: mockUser };
88
+ beforeEach(() => {
89
+ mockConfig.credentials.extractCredentials.mockResolvedValue({
90
+ identifier: "test",
91
+ password: "pass123",
92
+ });
93
+ mockConfig.credentials.verifyCredentials.mockResolvedValue(true);
94
+ mockConfig.user.fetchUser.mockResolvedValue(mockUser);
95
+ mockJwt.issueTokens.mockResolvedValue(mockTokens);
96
+ mockSession.issueSession.mockResolvedValue(mockSessionData);
97
+ });
98
+ it("should login and return user with tokens and session", async () => {
99
+ const result = await strategy.login(mockContext);
100
+ expect(result.user).toEqual(mockUser);
101
+ expect(result.tokens).toEqual(mockTokens);
102
+ expect(result.session).toEqual(mockSessionData);
103
+ expect(mockJwt.issueTokens).toHaveBeenCalledWith(mockUser, mockContext);
104
+ expect(mockSession.issueSession).toHaveBeenCalledWith(mockUser, mockContext);
105
+ });
106
+ it("should throw InvalidCredentialsError if credentials are incorrect", async () => {
107
+ mockConfig.credentials.verifyCredentials.mockResolvedValue(false);
108
+ await expect(strategy.login(mockContext)).rejects.toThrow(errors_1.InvalidCredentialsError);
109
+ });
110
+ it("should throw UserNotFoundError if user is not found", async () => {
111
+ mockConfig.user.fetchUser.mockResolvedValue(null);
112
+ await expect(strategy.login(mockContext)).rejects.toThrow(errors_1.UserNotFoundError);
113
+ });
114
+ });
115
+ });
@@ -2,17 +2,18 @@ import * as Soap from "@soapjs/soap";
2
2
  import { CredentialAuthStrategy } from "../credential-auth.strategy";
3
3
  import { LocalStrategyConfig } from "./local.types";
4
4
  import { SessionHandler } from "../../session/session-handler";
5
+ import { JwtStrategy } from "../jwt/jwt.strategy";
5
6
  export declare class LocalStrategy<TContext = unknown, TUser = unknown> extends CredentialAuthStrategy<TContext, TUser> {
6
- protected config: LocalStrategyConfig<TContext, TUser>;
7
7
  protected session?: SessionHandler;
8
+ protected jwt?: JwtStrategy<TContext, TUser>;
8
9
  protected logger?: Soap.Logger;
9
- constructor(config: LocalStrategyConfig<TContext, TUser>, session?: SessionHandler, logger?: Soap.Logger);
10
+ constructor(config: LocalStrategyConfig<TContext, TUser>, session?: SessionHandler, jwt?: JwtStrategy<TContext, TUser>, logger?: Soap.Logger);
10
11
  protected extractCredentials(context?: TContext): Promise<{
11
12
  identifier: string;
12
13
  password: string;
13
14
  }>;
14
15
  protected verifyCredentials(identifier: string, password: string): Promise<boolean>;
15
- protected retrieveUser(credentials: {
16
+ protected fetchUser(credentials: {
16
17
  identifier: string;
17
18
  password: string;
18
19
  }): Promise<TUser | null>;