@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.
- package/build/__tests__/soap-auth.test.d.ts +1 -0
- package/build/__tests__/soap-auth.test.js +42 -0
- package/build/errors.d.ts +14 -3
- package/build/errors.js +29 -8
- package/build/index.d.ts +1 -1
- package/build/index.js +1 -1
- package/build/services/__tests__/account-lock.service.test.d.ts +1 -0
- package/build/services/__tests__/account-lock.service.test.js +55 -0
- package/build/services/__tests__/auth-throttle.service.test.d.ts +1 -0
- package/build/services/__tests__/auth-throttle.service.test.js +48 -0
- package/build/services/__tests__/jwks.service.test.d.ts +1 -0
- package/build/services/__tests__/jwks.service.test.js +39 -0
- package/build/services/__tests__/mfa.service.test.d.ts +1 -0
- package/build/services/__tests__/mfa.service.test.js +66 -0
- package/build/services/__tests__/password.service.test.d.ts +1 -0
- package/build/services/__tests__/password.service.test.js +66 -0
- package/build/services/__tests__/pkce.service.test.d.ts +1 -0
- package/build/services/__tests__/pkce.service.test.js +77 -0
- package/build/services/__tests__/rate-limit.service.test.d.ts +1 -0
- package/build/services/__tests__/rate-limit.service.test.js +37 -0
- package/build/services/__tests__/role.service.test.d.ts +1 -0
- package/build/services/__tests__/role.service.test.js +31 -0
- package/build/services/account-lock.service.d.ts +12 -0
- package/build/services/account-lock.service.js +39 -0
- package/build/services/auth-throttle.service.d.ts +10 -0
- package/build/services/auth-throttle.service.js +43 -0
- package/build/services/index.d.ts +8 -0
- package/build/{factories → services}/index.js +8 -3
- package/build/services/jwks.service.d.ts +7 -0
- package/build/services/jwks.service.js +41 -0
- package/build/services/mfa.service.d.ts +12 -0
- package/build/services/mfa.service.js +74 -0
- package/build/services/password.service.d.ts +14 -0
- package/build/services/password.service.js +78 -0
- package/build/services/pkce.service.d.ts +14 -0
- package/build/services/pkce.service.js +81 -0
- package/build/services/rate-limit.service.d.ts +9 -0
- package/build/services/rate-limit.service.js +26 -0
- package/build/services/role.service.d.ts +9 -0
- package/build/services/role.service.js +26 -0
- package/build/session/__tests__/file.session-store.test.d.ts +1 -0
- package/build/session/__tests__/file.session-store.test.js +117 -0
- package/build/session/__tests__/memory.session-store.test.d.ts +1 -0
- package/build/session/__tests__/memory.session-store.test.js +77 -0
- package/build/session/__tests__/session-handler.test.d.ts +1 -0
- package/build/session/__tests__/session-handler.test.js +337 -0
- package/build/session/file.session-store.d.ts +1 -0
- package/build/session/file.session-store.js +7 -0
- package/build/session/memory.session-store.d.ts +4 -1
- package/build/session/memory.session-store.js +11 -5
- package/build/session/session-handler.d.ts +12 -7
- package/build/session/session-handler.js +46 -13
- package/build/session/session.errors.d.ts +6 -0
- package/build/session/session.errors.js +15 -0
- package/build/soap-auth.d.ts +9 -8
- package/build/soap-auth.js +42 -29
- package/build/strategies/__tests__/base-auth.strategy.test.d.ts +14 -0
- package/build/strategies/__tests__/base-auth.strategy.test.js +137 -0
- package/build/strategies/__tests__/credential-auth.strategy.test.d.ts +14 -0
- package/build/strategies/__tests__/credential-auth.strategy.test.js +265 -0
- package/build/strategies/__tests__/token-auth.strategy.test.d.ts +28 -0
- package/build/strategies/__tests__/token-auth.strategy.test.js +298 -0
- package/build/strategies/api-key/__tests__/api-key.strategy.test.d.ts +1 -0
- package/build/strategies/api-key/__tests__/api-key.strategy.test.js +103 -0
- package/build/strategies/api-key/api-key.strategy.d.ts +5 -2
- package/build/strategies/api-key/api-key.strategy.js +43 -35
- package/build/strategies/api-key/api-key.tools.d.ts +2 -0
- package/build/strategies/api-key/api-key.tools.js +39 -0
- package/build/strategies/api-key/api-key.types.d.ts +10 -2
- package/build/strategies/base-auth.strategy.d.ts +11 -5
- package/build/strategies/base-auth.strategy.js +45 -52
- package/build/strategies/basic/__tests__/basic.strategy.test.d.ts +1 -0
- package/build/strategies/basic/__tests__/basic.strategy.test.js +104 -0
- package/build/strategies/basic/basic.strategy.d.ts +5 -7
- package/build/strategies/basic/basic.strategy.js +6 -6
- package/build/strategies/basic/basic.tools.d.ts +2 -0
- package/build/strategies/basic/basic.tools.js +44 -0
- package/build/strategies/credential-auth.strategy.d.ts +7 -17
- package/build/strategies/credential-auth.strategy.js +116 -181
- package/build/strategies/jwt/__tests__/jwt.strategy.test.d.ts +1 -0
- package/build/strategies/jwt/__tests__/jwt.strategy.test.js +156 -0
- package/build/strategies/jwt/__tests__/jwt.tools.test.d.ts +1 -0
- package/build/strategies/jwt/__tests__/jwt.tools.test.js +98 -0
- package/build/strategies/jwt/jwt.strategy.d.ts +13 -14
- package/build/strategies/jwt/jwt.strategy.js +57 -44
- package/build/strategies/jwt/jwt.tools.d.ts +20 -7
- package/build/strategies/jwt/jwt.tools.js +180 -81
- package/build/strategies/local/__tests__/local.strategy.test.d.ts +1 -0
- package/build/strategies/local/__tests__/local.strategy.test.js +115 -0
- package/build/strategies/local/local.strategy.d.ts +4 -3
- package/build/strategies/local/local.strategy.js +7 -6
- package/build/strategies/local/local.tools.d.ts +2 -0
- package/build/strategies/local/local.tools.js +44 -0
- package/build/strategies/oauth2/hybrid.oauth2.strategy.d.ts +5 -0
- package/build/strategies/oauth2/hybrid.oauth2.strategy.js +92 -0
- package/build/strategies/oauth2/oauth2.errors.d.ts +12 -0
- package/build/strategies/oauth2/oauth2.errors.js +24 -0
- package/build/strategies/oauth2/oauth2.strategy.d.ts +25 -15
- package/build/strategies/oauth2/oauth2.strategy.js +131 -141
- package/build/strategies/oauth2/oauth2.tools.d.ts +7 -2
- package/build/strategies/oauth2/oauth2.tools.js +119 -14
- package/build/strategies/oauth2/oauth2.types.d.ts +32 -1
- package/build/strategies/token-auth.strategy.d.ts +14 -8
- package/build/strategies/token-auth.strategy.js +162 -38
- package/build/tools/index.d.ts +0 -2
- package/build/tools/index.js +0 -2
- package/build/tools/tools.d.ts +2 -1
- package/build/tools/tools.js +9 -12
- package/build/types.d.ts +88 -57
- package/package.json +1 -1
- package/build/factories/auth-strategy.factory.d.ts +0 -9
- package/build/factories/auth-strategy.factory.js +0 -16
- package/build/factories/http-auth-strategy.factory.d.ts +0 -5
- package/build/factories/http-auth-strategy.factory.js +0 -41
- package/build/factories/index.d.ts +0 -3
- package/build/factories/socket-auth-strategy.factory.d.ts +0 -5
- package/build/factories/socket-auth-strategy.factory.js +0 -27
- package/build/tools/session.tools.d.ts +0 -6
- package/build/tools/session.tools.js +0 -15
- package/build/tools/token.tools.d.ts +0 -7
- 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 {
|
|
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:
|
|
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:
|
|
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(
|
|
17
|
-
protected generateRefreshToken(
|
|
18
|
-
protected storeAccessToken(token: string
|
|
19
|
-
protected storeRefreshToken(token: string
|
|
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
|
|
23
|
-
protected
|
|
24
|
-
|
|
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,
|
|
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,
|
|
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 =
|
|
29
|
-
this.refreshTokenConfig =
|
|
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
|
|
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
|
-
|
|
67
|
+
error;
|
|
73
68
|
}
|
|
74
69
|
}
|
|
75
|
-
generateAccessToken(
|
|
76
|
-
const
|
|
77
|
-
return
|
|
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(
|
|
83
|
-
const
|
|
84
|
-
return
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
101
|
+
jwt_tools_1.JwtTools.setDefaultJwtCookie(token, context);
|
|
113
102
|
}
|
|
114
103
|
}
|
|
115
|
-
|
|
116
|
-
if (this.accessTokenConfig.
|
|
117
|
-
return this.accessTokenConfig.
|
|
104
|
+
extractAccessToken(context) {
|
|
105
|
+
if (this.accessTokenConfig.extract) {
|
|
106
|
+
return this.accessTokenConfig.extract(context);
|
|
118
107
|
}
|
|
119
108
|
else {
|
|
120
|
-
return (context.req
|
|
121
|
-
context.request
|
|
122
|
-
context.headers
|
|
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
|
-
|
|
126
|
-
if (this.refreshTokenConfig.
|
|
127
|
-
return this.refreshTokenConfig.
|
|
114
|
+
extractRefreshToken(context) {
|
|
115
|
+
if (this.refreshTokenConfig.extract) {
|
|
116
|
+
return this.refreshTokenConfig.extract(context);
|
|
128
117
|
}
|
|
129
|
-
return (context.req
|
|
130
|
-
context.request
|
|
131
|
-
context.cookies
|
|
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
|
|
134
|
-
const refreshToken = await this.
|
|
132
|
+
async invalidateRefreshToken(token, context) {
|
|
133
|
+
const refreshToken = token || (await this.extractRefreshToken(context));
|
|
135
134
|
if (refreshToken) {
|
|
136
|
-
await this.
|
|
137
|
-
|
|
138
|
-
|
|
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 {
|
|
2
|
-
|
|
3
|
-
export declare
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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.
|
|
29
|
+
exports.prepareJwtConfig = exports.JwtTools = void 0;
|
|
27
30
|
const Soap = __importStar(require("@soapjs/soap"));
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
|
|
71
|
-
|
|
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
|
-
|
|
74
|
-
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
-
|
|
83
|
-
context.
|
|
70
|
+
static setAccessTokenHeader(token, context) {
|
|
71
|
+
context.res.setHeader("Authorization", `Bearer ${token}`);
|
|
84
72
|
}
|
|
85
|
-
|
|
86
|
-
context.
|
|
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
|
-
|
|
90
|
-
|
|
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
|
-
|
|
95
|
-
context.
|
|
84
|
+
static getAccessToken(context) {
|
|
85
|
+
return context.req.headers.authorization?.split(" ")[1];
|
|
96
86
|
}
|
|
97
|
-
|
|
98
|
-
context.
|
|
87
|
+
static getRefreshToken(context) {
|
|
88
|
+
return context.req.cookies?.refreshToken;
|
|
99
89
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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.
|
|
217
|
+
exports.prepareJwtConfig = prepareJwtConfig;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -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
|
|
16
|
+
protected fetchUser(credentials: {
|
|
16
17
|
identifier: string;
|
|
17
18
|
password: string;
|
|
18
19
|
}): Promise<TUser | null>;
|