@soapjs/soap-auth 0.1.0 → 0.1.1
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/factories/http-auth-strategy.factory.js +1 -2
- package/build/strategies/base-auth.strategy.js +5 -5
- package/build/strategies/credential-based-auth.strategy.js +1 -1
- package/build/strategies/jwt/jwt.strategy.js +18 -6
- package/build/strategies/jwt/jwt.tools.d.ts +3 -3
- package/build/strategies/jwt/jwt.tools.js +30 -30
- package/build/strategies/jwt/jwt.types.d.ts +23 -9
- package/build/strategies/oauth2/oauth2.strategy.d.ts +4 -4
- package/build/strategies/oauth2/oauth2.strategy.js +21 -21
- package/build/strategies/oauth2/oauth2.types.d.ts +8 -8
- package/build/strategies/token-based-auth.strategy.d.ts +4 -4
- package/build/strategies/token-based-auth.strategy.js +33 -27
- package/build/types.d.ts +11 -15
- package/package.json +3 -3
|
@@ -6,7 +6,6 @@ const api_key_strategy_1 = require("../strategies/api-key/api-key.strategy");
|
|
|
6
6
|
const jwt_strategy_1 = require("../strategies/jwt/jwt.strategy");
|
|
7
7
|
const basic_strategy_1 = require("../strategies/basic/basic.strategy");
|
|
8
8
|
const auth_strategy_factory_1 = require("./auth-strategy.factory");
|
|
9
|
-
const tools_1 = require("../tools/tools");
|
|
10
9
|
const local_strategy_1 = require("../strategies/local/local.strategy");
|
|
11
10
|
class HttpAuthStrategyFactory extends auth_strategy_factory_1.AuthStrategyFactory {
|
|
12
11
|
createStrategies(config) {
|
|
@@ -16,7 +15,7 @@ class HttpAuthStrategyFactory extends auth_strategy_factory_1.AuthStrategyFactor
|
|
|
16
15
|
}
|
|
17
16
|
if (config.http.oauth2) {
|
|
18
17
|
for (const provider in config.http.oauth2) {
|
|
19
|
-
strategies.set(provider, new oauth2_strategy_1.OAuth2Strategy(config.http.oauth2[provider],
|
|
18
|
+
strategies.set(provider, new oauth2_strategy_1.OAuth2Strategy(config.http.oauth2[provider], config.http.oauth2[provider].accessToken, config.http.oauth2[provider].refreshToken, this.getSessionHandler(config.http.oauth2[provider].session, config.session), config.logger));
|
|
20
19
|
}
|
|
21
20
|
}
|
|
22
21
|
if (config.http.apiKey) {
|
|
@@ -15,14 +15,14 @@ class BaseAuthStrategy {
|
|
|
15
15
|
return Promise.resolve();
|
|
16
16
|
}
|
|
17
17
|
async isAccountLocked(account, ...args) {
|
|
18
|
-
if (await this.config.isAccountLocked?.(account, ...args)) {
|
|
18
|
+
if (await this.config.lock.isAccountLocked?.(account, ...args)) {
|
|
19
19
|
throw new errors_1.AccountLockedError();
|
|
20
20
|
}
|
|
21
21
|
return false;
|
|
22
22
|
}
|
|
23
23
|
async isAuthorized(user) {
|
|
24
|
-
if (this.config.authorizeByRoles && this.config.roles) {
|
|
25
|
-
const hasAccess = await this.config.authorizeByRoles(user, this.config.roles);
|
|
24
|
+
if (this.config.role.authorizeByRoles && this.config.role.roles) {
|
|
25
|
+
const hasAccess = await this.config.role.authorizeByRoles(user, this.config.role.roles);
|
|
26
26
|
if (!hasAccess) {
|
|
27
27
|
throw new errors_1.UnauthorizedRoleError();
|
|
28
28
|
}
|
|
@@ -30,8 +30,8 @@ class BaseAuthStrategy {
|
|
|
30
30
|
return true;
|
|
31
31
|
}
|
|
32
32
|
async checkRateLimit(data) {
|
|
33
|
-
if (this.config.checkRateLimit &&
|
|
34
|
-
(await this.config.checkRateLimit(data))) {
|
|
33
|
+
if (this.config.rateLimit.checkRateLimit &&
|
|
34
|
+
(await this.config.rateLimit.checkRateLimit(data))) {
|
|
35
35
|
throw new errors_1.RateLimitExceededError();
|
|
36
36
|
}
|
|
37
37
|
}
|
|
@@ -152,7 +152,7 @@ class CredentialBasedAuthStrategy extends base_auth_strategy_1.BaseAuthStrategy
|
|
|
152
152
|
}
|
|
153
153
|
}
|
|
154
154
|
async isAccountLocked(account, ...args) {
|
|
155
|
-
if (await this.config.isAccountLocked?.(account, ...args)) {
|
|
155
|
+
if (await this.config.lock.isAccountLocked?.(account, ...args)) {
|
|
156
156
|
throw new errors_1.AccountLockedError();
|
|
157
157
|
}
|
|
158
158
|
if (typeof account === "string" && this.config.security?.lockoutDuration) {
|
|
@@ -5,20 +5,18 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.JwtStrategy = void 0;
|
|
7
7
|
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
|
8
|
-
const util_1 = require("util");
|
|
9
8
|
const token_based_auth_strategy_1 = require("../token-based-auth.strategy");
|
|
10
9
|
const errors_1 = require("../../errors");
|
|
11
10
|
const jwt_tools_1 = require("./jwt.tools");
|
|
12
|
-
const verifyAsync = (0, util_1.promisify)(jsonwebtoken_1.default.verify);
|
|
13
11
|
class JwtStrategy extends token_based_auth_strategy_1.TokenBasedAuthStrategy {
|
|
14
12
|
config;
|
|
15
13
|
session;
|
|
16
14
|
logger;
|
|
17
15
|
constructor(config, session, logger) {
|
|
18
|
-
if (!config.
|
|
16
|
+
if (!config.accessToken.secretKey) {
|
|
19
17
|
throw new errors_1.UndefinedTokenSecretError("Access");
|
|
20
18
|
}
|
|
21
|
-
if (config.
|
|
19
|
+
if (config.refreshToken && !config.refreshToken.secretKey) {
|
|
22
20
|
throw new errors_1.UndefinedTokenSecretError("Refresh");
|
|
23
21
|
}
|
|
24
22
|
const accessTokenConfig = (0, jwt_tools_1.prepareAccessTokenConfig)(config);
|
|
@@ -34,7 +32,14 @@ class JwtStrategy extends token_based_auth_strategy_1.TokenBasedAuthStrategy {
|
|
|
34
32
|
throw new errors_1.UndefinedTokenError("Access");
|
|
35
33
|
if (!accessTokenConfig.secretKey)
|
|
36
34
|
throw new errors_1.UndefinedTokenSecretError("Access");
|
|
37
|
-
return
|
|
35
|
+
return new Promise((resolve, reject) => {
|
|
36
|
+
jsonwebtoken_1.default.verify(token, accessTokenConfig.secretKey, accessTokenConfig.verifyOptions, (err, payload) => {
|
|
37
|
+
if (err)
|
|
38
|
+
reject(err);
|
|
39
|
+
else
|
|
40
|
+
resolve(payload);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
38
43
|
}
|
|
39
44
|
catch (error) {
|
|
40
45
|
this.logger?.error("JWT verification failed:", error);
|
|
@@ -52,7 +57,14 @@ class JwtStrategy extends token_based_auth_strategy_1.TokenBasedAuthStrategy {
|
|
|
52
57
|
throw new errors_1.UndefinedTokenError("Refresh");
|
|
53
58
|
if (!refreshTokenConfig.secretKey)
|
|
54
59
|
throw new errors_1.UndefinedTokenSecretError("Refresh");
|
|
55
|
-
return
|
|
60
|
+
return new Promise((resolve, reject) => {
|
|
61
|
+
jsonwebtoken_1.default.verify(token, refreshTokenConfig.secretKey, refreshTokenConfig.verifyOptions, (err, payload) => {
|
|
62
|
+
if (err)
|
|
63
|
+
reject(err);
|
|
64
|
+
else
|
|
65
|
+
resolve(payload);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
56
68
|
}
|
|
57
69
|
catch (error) {
|
|
58
70
|
this.logger?.error("JWT verification failed:", error);
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export declare const prepareAccessTokenConfig: (config: JwtConfig) =>
|
|
3
|
-
export declare const prepareRefreshTokenConfig: (config: JwtConfig) =>
|
|
1
|
+
import { JwtAccessTokenConfig, JwtConfig, JwtRefreshTokenConfig } from "./jwt.types";
|
|
2
|
+
export declare const prepareAccessTokenConfig: (config: JwtConfig) => JwtAccessTokenConfig;
|
|
3
|
+
export declare const prepareRefreshTokenConfig: (config: JwtConfig) => JwtRefreshTokenConfig;
|
|
@@ -27,25 +27,25 @@ exports.prepareRefreshTokenConfig = exports.prepareAccessTokenConfig = void 0;
|
|
|
27
27
|
const Soap = __importStar(require("@soapjs/soap"));
|
|
28
28
|
const prepareAccessTokenConfig = (config) => {
|
|
29
29
|
return Soap.removeUndefinedProperties({
|
|
30
|
-
...config.
|
|
30
|
+
...config.accessToken,
|
|
31
31
|
tokenType: "Access",
|
|
32
|
-
expiresIn: config.
|
|
32
|
+
expiresIn: config.accessToken.expiresIn || "1h",
|
|
33
33
|
signOptions: {
|
|
34
|
-
...config.
|
|
35
|
-
algorithm: config.
|
|
36
|
-
expiresIn: config.
|
|
37
|
-
audience: config.
|
|
38
|
-
issuer: config.
|
|
39
|
-
subject: config.
|
|
34
|
+
...config.accessToken.signOptions,
|
|
35
|
+
algorithm: config.accessToken.signOptions.algorithm || "HS256",
|
|
36
|
+
expiresIn: config.accessToken.expiresIn || "1h",
|
|
37
|
+
audience: config.accessToken.audience,
|
|
38
|
+
issuer: config.accessToken.issuer,
|
|
39
|
+
subject: config.accessToken.subject,
|
|
40
40
|
},
|
|
41
|
-
verifyOptions: config.
|
|
41
|
+
verifyOptions: config.accessToken.verifyOptions
|
|
42
42
|
? {
|
|
43
|
-
...config.
|
|
44
|
-
algorithms: config.
|
|
45
|
-
expiresIn: config.
|
|
46
|
-
audience: config.
|
|
47
|
-
issuer: config.
|
|
48
|
-
subject: config.
|
|
43
|
+
...config.accessToken.verifyOptions,
|
|
44
|
+
algorithms: config.accessToken.verifyOptions.algorithms || ["HS256"],
|
|
45
|
+
expiresIn: config.accessToken.expiresIn || "1h",
|
|
46
|
+
audience: config.accessToken.audience,
|
|
47
|
+
issuer: config.accessToken.issuer,
|
|
48
|
+
subject: config.accessToken.subject,
|
|
49
49
|
}
|
|
50
50
|
: {},
|
|
51
51
|
});
|
|
@@ -53,25 +53,25 @@ const prepareAccessTokenConfig = (config) => {
|
|
|
53
53
|
exports.prepareAccessTokenConfig = prepareAccessTokenConfig;
|
|
54
54
|
const prepareRefreshTokenConfig = (config) => {
|
|
55
55
|
return Soap.removeUndefinedProperties({
|
|
56
|
-
...config.
|
|
57
|
-
secretKey: config.
|
|
56
|
+
...config.refreshToken,
|
|
57
|
+
secretKey: config.refreshToken.secretKey,
|
|
58
58
|
tokenType: "Refresh",
|
|
59
59
|
signOptions: {
|
|
60
|
-
...config.
|
|
61
|
-
algorithm: config.
|
|
62
|
-
expiresIn: config.
|
|
63
|
-
audience: config.
|
|
64
|
-
issuer: config.
|
|
65
|
-
subject: config.
|
|
60
|
+
...config.refreshToken.signOptions,
|
|
61
|
+
algorithm: config.refreshToken.signOptions.algorithm || "HS256",
|
|
62
|
+
expiresIn: config.refreshToken.expiresIn || "7d",
|
|
63
|
+
audience: config.refreshToken.audience,
|
|
64
|
+
issuer: config.refreshToken.issuer,
|
|
65
|
+
subject: config.refreshToken.subject,
|
|
66
66
|
},
|
|
67
|
-
verifyOptions: config.
|
|
67
|
+
verifyOptions: config.refreshToken.verifyOptions
|
|
68
68
|
? {
|
|
69
|
-
...config.
|
|
70
|
-
algorithm: config.
|
|
71
|
-
expiresIn: config.
|
|
72
|
-
audience: config.
|
|
73
|
-
issuer: config.
|
|
74
|
-
subject: config.
|
|
69
|
+
...config.refreshToken.verifyOptions,
|
|
70
|
+
algorithm: config.refreshToken.verifyOptions.algorithms || ["HS256"],
|
|
71
|
+
expiresIn: config.refreshToken.expiresIn || "7d",
|
|
72
|
+
audience: config.refreshToken.audience,
|
|
73
|
+
issuer: config.refreshToken.issuer,
|
|
74
|
+
subject: config.refreshToken.subject,
|
|
75
75
|
}
|
|
76
76
|
: {},
|
|
77
77
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TokenBasedAuthStrategyConfig,
|
|
1
|
+
import { TokenBasedAuthStrategyConfig, TokenConfig } from "../../types";
|
|
2
2
|
export interface JwtVerifyOptions {
|
|
3
3
|
algorithms?: string[];
|
|
4
4
|
notBefore?: string | number;
|
|
@@ -19,15 +19,29 @@ export interface JwtSignOptions {
|
|
|
19
19
|
keyid?: string;
|
|
20
20
|
allowUnsafe?: boolean;
|
|
21
21
|
}
|
|
22
|
-
export type
|
|
22
|
+
export type JwtAccessTokenConfig = {
|
|
23
23
|
verifyOptions?: JwtVerifyOptions;
|
|
24
24
|
signOptions: JwtSignOptions;
|
|
25
|
-
} &
|
|
26
|
-
export type
|
|
25
|
+
} & TokenConfig;
|
|
26
|
+
export type JwtRefreshTokenConfig = {
|
|
27
27
|
verifyOptions?: JwtVerifyOptions;
|
|
28
28
|
signOptions: JwtSignOptions;
|
|
29
|
-
} &
|
|
30
|
-
export
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
} & TokenConfig;
|
|
30
|
+
export interface JwtConfig<TContext = unknown, TUser = unknown> extends TokenBasedAuthStrategyConfig<TContext, TUser> {
|
|
31
|
+
accessToken: JwtAccessTokenConfig;
|
|
32
|
+
refreshToken?: JwtRefreshTokenConfig;
|
|
33
|
+
routes?: {
|
|
34
|
+
login?: {
|
|
35
|
+
path: string;
|
|
36
|
+
method?: "POST" | "GET";
|
|
37
|
+
};
|
|
38
|
+
logout?: {
|
|
39
|
+
path: string;
|
|
40
|
+
method?: "POST" | "GET";
|
|
41
|
+
};
|
|
42
|
+
refresh?: {
|
|
43
|
+
path: string;
|
|
44
|
+
method?: "POST" | "GET";
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import * as Soap from "@soapjs/soap";
|
|
2
|
-
import {
|
|
2
|
+
import { TokenConfig, AuthResult } from "../../types";
|
|
3
3
|
import { TokenBasedAuthStrategy } from "../token-based-auth.strategy";
|
|
4
4
|
import { OAuth2StrategyConfig } from "./oauth2.types";
|
|
5
5
|
import { SessionHandler } from "../../session/session-handler";
|
|
6
6
|
export declare class OAuth2Strategy<TContext = unknown, TUser = unknown> extends TokenBasedAuthStrategy<TContext, TUser> {
|
|
7
7
|
protected config: OAuth2StrategyConfig<TContext, TUser>;
|
|
8
|
-
protected
|
|
9
|
-
protected
|
|
8
|
+
protected accessTokenConfig: TokenConfig;
|
|
9
|
+
protected refreshTokenConfig?: TokenConfig;
|
|
10
10
|
protected session?: SessionHandler;
|
|
11
11
|
protected logger?: Soap.Logger;
|
|
12
|
-
constructor(config: OAuth2StrategyConfig<TContext, TUser>,
|
|
12
|
+
constructor(config: OAuth2StrategyConfig<TContext, TUser>, accessTokenConfig: TokenConfig, refreshTokenConfig?: TokenConfig, session?: SessionHandler, logger?: Soap.Logger);
|
|
13
13
|
authenticate(context: TContext): Promise<AuthResult<TUser>>;
|
|
14
14
|
protected verifyAuthorizationCode(context: TContext, code: string): void;
|
|
15
15
|
protected extractAuthorizationCode(context: TContext): string | null;
|
|
@@ -10,35 +10,35 @@ const token_based_auth_strategy_1 = require("../token-based-auth.strategy");
|
|
|
10
10
|
const oauth2_tools_1 = require("./oauth2.tools");
|
|
11
11
|
class OAuth2Strategy extends token_based_auth_strategy_1.TokenBasedAuthStrategy {
|
|
12
12
|
config;
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
accessTokenConfig;
|
|
14
|
+
refreshTokenConfig;
|
|
15
15
|
session;
|
|
16
16
|
logger;
|
|
17
|
-
constructor(config,
|
|
18
|
-
super(config,
|
|
17
|
+
constructor(config, accessTokenConfig, refreshTokenConfig, session, logger) {
|
|
18
|
+
super(config, accessTokenConfig, refreshTokenConfig, session, logger);
|
|
19
19
|
this.config = config;
|
|
20
|
-
this.
|
|
21
|
-
this.
|
|
20
|
+
this.accessTokenConfig = accessTokenConfig;
|
|
21
|
+
this.refreshTokenConfig = refreshTokenConfig;
|
|
22
22
|
this.session = session;
|
|
23
23
|
this.logger = logger;
|
|
24
|
-
this.config.scope = this.config.scope ?? "
|
|
24
|
+
this.config.scope = this.config.scope ?? "email";
|
|
25
25
|
}
|
|
26
26
|
async authenticate(context) {
|
|
27
27
|
try {
|
|
28
28
|
let user;
|
|
29
29
|
let refreshToken;
|
|
30
|
-
let accessToken = await this.
|
|
30
|
+
let accessToken = await this.accessTokenConfig.retrieve?.(context);
|
|
31
31
|
if (!accessToken) {
|
|
32
32
|
this.logger?.info("No access token found, checking for refresh token.");
|
|
33
|
-
refreshToken = await this.
|
|
33
|
+
refreshToken = await this.refreshTokenConfig?.retrieve?.(context);
|
|
34
34
|
if (refreshToken) {
|
|
35
35
|
this.logger?.info("Found refresh token, attempting to refresh access token.");
|
|
36
36
|
const newTokens = await this.refreshAccessToken(context);
|
|
37
37
|
accessToken = newTokens.accessToken;
|
|
38
|
-
this.
|
|
38
|
+
this.accessTokenConfig.embed?.(context, accessToken);
|
|
39
39
|
if (newTokens.refreshToken) {
|
|
40
40
|
refreshToken = newTokens.refreshToken;
|
|
41
|
-
this.
|
|
41
|
+
this.refreshTokenConfig?.embed?.(context, newTokens.refreshToken);
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
else {
|
|
@@ -67,24 +67,24 @@ class OAuth2Strategy extends token_based_auth_strategy_1.TokenBasedAuthStrategy
|
|
|
67
67
|
default:
|
|
68
68
|
throw new Error(`Unsupported grant type: ${this.config.grantType}`);
|
|
69
69
|
}
|
|
70
|
-
this.
|
|
70
|
+
this.accessTokenConfig.embed?.(context, accessToken);
|
|
71
71
|
if (refreshToken) {
|
|
72
|
-
this.
|
|
72
|
+
this.refreshTokenConfig?.embed?.(context, refreshToken);
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
76
|
else if (accessToken && (await this.isTokenExpired(accessToken))) {
|
|
77
77
|
this.logger?.info("Access token expired, attempting refresh.");
|
|
78
|
-
refreshToken = await this.
|
|
78
|
+
refreshToken = await this.refreshTokenConfig?.retrieve?.(context);
|
|
79
79
|
if (!refreshToken) {
|
|
80
80
|
throw new errors_1.MissingTokenError("Refresh");
|
|
81
81
|
}
|
|
82
82
|
const newTokens = await this.refreshAccessToken(context);
|
|
83
83
|
accessToken = newTokens.accessToken;
|
|
84
|
-
this.
|
|
84
|
+
this.accessTokenConfig.embed?.(context, accessToken);
|
|
85
85
|
if (newTokens.refreshToken && newTokens.refreshToken !== refreshToken) {
|
|
86
86
|
refreshToken = newTokens.refreshToken;
|
|
87
|
-
this.
|
|
87
|
+
this.refreshTokenConfig?.embed?.(context, newTokens.refreshToken);
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
90
|
if (!accessToken) {
|
|
@@ -126,7 +126,7 @@ class OAuth2Strategy extends token_based_auth_strategy_1.TokenBasedAuthStrategy
|
|
|
126
126
|
}
|
|
127
127
|
buildAuthorizationUrl(context) {
|
|
128
128
|
let authorizationUrl = `${this.config.endpoints.authorizationUrl}?client_id=${this.config.clientId}&redirect_uri=${encodeURIComponent(this.config.redirectUri)}&response_type=code&scope=${this.config.scope ?? ""}`;
|
|
129
|
-
if (this.config.pkce
|
|
129
|
+
if (this.config.pkce) {
|
|
130
130
|
const codeVerifier = this.config.pkce.generateCodeVerifier
|
|
131
131
|
? this.config.pkce.generateCodeVerifier()
|
|
132
132
|
: oauth2_tools_1.OAuth2Tools.generateCodeVerifier();
|
|
@@ -143,7 +143,7 @@ class OAuth2Strategy extends token_based_auth_strategy_1.TokenBasedAuthStrategy
|
|
|
143
143
|
code,
|
|
144
144
|
redirect_uri: this.config.redirectUri,
|
|
145
145
|
};
|
|
146
|
-
if (this.config.pkce
|
|
146
|
+
if (this.config.pkce) {
|
|
147
147
|
const codeVerifier = this.config.pkce.retrieveCodeVerifier?.(context);
|
|
148
148
|
if (!codeVerifier) {
|
|
149
149
|
throw new Error("Missing PKCE code verifier in context.");
|
|
@@ -215,7 +215,7 @@ class OAuth2Strategy extends token_based_auth_strategy_1.TokenBasedAuthStrategy
|
|
|
215
215
|
}
|
|
216
216
|
async refreshAccessToken(context) {
|
|
217
217
|
try {
|
|
218
|
-
const refreshToken = await this.
|
|
218
|
+
const refreshToken = await this.refreshTokenConfig?.retrieve?.(context);
|
|
219
219
|
if (!refreshToken) {
|
|
220
220
|
throw new errors_1.MissingTokenError("Refresh");
|
|
221
221
|
}
|
|
@@ -229,9 +229,9 @@ class OAuth2Strategy extends token_based_auth_strategy_1.TokenBasedAuthStrategy
|
|
|
229
229
|
accessToken: response.data.access_token,
|
|
230
230
|
refreshToken: response.data.refresh_token,
|
|
231
231
|
};
|
|
232
|
-
await this.
|
|
232
|
+
await this.accessTokenConfig.embed?.(context, refreshedTokens.accessToken);
|
|
233
233
|
if (refreshedTokens.refreshToken) {
|
|
234
|
-
await this.
|
|
234
|
+
await this.refreshTokenConfig?.embed?.(context, refreshedTokens.refreshToken);
|
|
235
235
|
}
|
|
236
236
|
return refreshedTokens;
|
|
237
237
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TokenBasedAuthStrategyConfig } from "../../types";
|
|
1
|
+
import { TokenBasedAuthStrategyConfig, TokenConfig } from "../../types";
|
|
2
2
|
export interface OAuth2Endpoints {
|
|
3
3
|
authorizationUrl: string;
|
|
4
4
|
tokenUrl: string;
|
|
@@ -6,7 +6,7 @@ export interface OAuth2Endpoints {
|
|
|
6
6
|
introspectionUrl?: string;
|
|
7
7
|
revocationUrl?: string;
|
|
8
8
|
}
|
|
9
|
-
export interface OAuth2StrategyConfig<TContext = unknown, TUser = unknown> extends TokenBasedAuthStrategyConfig<TContext, TUser
|
|
9
|
+
export interface OAuth2StrategyConfig<TContext = unknown, TUser = unknown> extends TokenBasedAuthStrategyConfig<TContext, TUser> {
|
|
10
10
|
clientId: string;
|
|
11
11
|
clientSecret: string;
|
|
12
12
|
redirectUri: string;
|
|
@@ -18,12 +18,12 @@ export interface OAuth2StrategyConfig<TContext = unknown, TUser = unknown> exten
|
|
|
18
18
|
username: string;
|
|
19
19
|
password: string;
|
|
20
20
|
};
|
|
21
|
+
pkce?: PKCEConfig<TContext>;
|
|
22
|
+
accessToken?: TokenConfig;
|
|
23
|
+
refreshToken?: TokenConfig;
|
|
21
24
|
}
|
|
22
25
|
export interface PKCEConfig<TContext> {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
storeCodeVerifier?: (context: TContext, codeVerifier: string) => void;
|
|
27
|
-
retrieveCodeVerifier?: (context: TContext) => string | null;
|
|
28
|
-
};
|
|
26
|
+
generateCodeVerifier?: () => string;
|
|
27
|
+
storeCodeVerifier?: (context: TContext, codeVerifier: string) => void;
|
|
28
|
+
retrieveCodeVerifier?: (context: TContext) => string | null;
|
|
29
29
|
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import * as Soap from "@soapjs/soap";
|
|
2
2
|
import { AuthResult, TokenBasedAuthStrategyConfig } from "../types";
|
|
3
3
|
import { BaseAuthStrategy } from "./base-auth.strategy";
|
|
4
|
-
import {
|
|
4
|
+
import { TokenConfig } from "../types";
|
|
5
5
|
import { SessionHandler } from "../session/session-handler";
|
|
6
6
|
export declare abstract class TokenBasedAuthStrategy<TContext = unknown, TUser = unknown> extends BaseAuthStrategy<TContext, TUser> {
|
|
7
7
|
protected config: TokenBasedAuthStrategyConfig<TContext, TUser>;
|
|
8
|
-
protected
|
|
9
|
-
protected
|
|
8
|
+
protected accessTokenConfig?: TokenConfig;
|
|
9
|
+
protected refreshTokenConfig?: TokenConfig;
|
|
10
10
|
protected session?: SessionHandler;
|
|
11
11
|
protected logger?: Soap.Logger;
|
|
12
|
-
constructor(config: TokenBasedAuthStrategyConfig<TContext, TUser>,
|
|
12
|
+
constructor(config: TokenBasedAuthStrategyConfig<TContext, TUser>, accessTokenConfig?: TokenConfig, refreshTokenConfig?: TokenConfig, session?: SessionHandler, logger?: Soap.Logger);
|
|
13
13
|
protected retrieveUser(decodedToken: any): Promise<TUser | null>;
|
|
14
14
|
authenticate(context: TContext): Promise<AuthResult<TUser>>;
|
|
15
15
|
logout(context: TContext): Promise<void>;
|
|
@@ -5,15 +5,15 @@ const base_auth_strategy_1 = require("./base-auth.strategy");
|
|
|
5
5
|
const errors_1 = require("../errors");
|
|
6
6
|
class TokenBasedAuthStrategy extends base_auth_strategy_1.BaseAuthStrategy {
|
|
7
7
|
config;
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
accessTokenConfig;
|
|
9
|
+
refreshTokenConfig;
|
|
10
10
|
session;
|
|
11
11
|
logger;
|
|
12
|
-
constructor(config,
|
|
12
|
+
constructor(config, accessTokenConfig, refreshTokenConfig, session, logger) {
|
|
13
13
|
super(config, session, logger);
|
|
14
14
|
this.config = config;
|
|
15
|
-
this.
|
|
16
|
-
this.
|
|
15
|
+
this.accessTokenConfig = accessTokenConfig;
|
|
16
|
+
this.refreshTokenConfig = refreshTokenConfig;
|
|
17
17
|
this.session = session;
|
|
18
18
|
this.logger = logger;
|
|
19
19
|
}
|
|
@@ -22,11 +22,12 @@ class TokenBasedAuthStrategy extends base_auth_strategy_1.BaseAuthStrategy {
|
|
|
22
22
|
}
|
|
23
23
|
async authenticate(context) {
|
|
24
24
|
try {
|
|
25
|
-
let accessToken = await this.
|
|
25
|
+
let accessToken = await this.accessTokenConfig.retrieve?.(context);
|
|
26
|
+
let refreshToken;
|
|
26
27
|
await this.checkRateLimit(context);
|
|
27
28
|
if (accessToken) {
|
|
28
29
|
try {
|
|
29
|
-
const decoded = await this.
|
|
30
|
+
const decoded = await this.accessTokenConfig.verify?.(accessToken);
|
|
30
31
|
const user = await this.retrieveUser(decoded);
|
|
31
32
|
if (!user) {
|
|
32
33
|
throw new errors_1.UserNotFoundError();
|
|
@@ -38,13 +39,18 @@ class TokenBasedAuthStrategy extends base_auth_strategy_1.BaseAuthStrategy {
|
|
|
38
39
|
this.logger?.warn("Access token is invalid or expired, trying refresh token...");
|
|
39
40
|
}
|
|
40
41
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
if (this.refreshTokenConfig) {
|
|
43
|
+
refreshToken = await this.refreshTokenConfig?.retrieve?.(context);
|
|
44
|
+
if (!refreshToken) {
|
|
45
|
+
throw new errors_1.MissingTokenError("Refresh");
|
|
46
|
+
}
|
|
47
|
+
accessToken = await this.refreshTokenConfig?.rotate?.(refreshToken);
|
|
48
|
+
if (!accessToken) {
|
|
49
|
+
throw new errors_1.MissingTokenError("Access");
|
|
50
|
+
}
|
|
44
51
|
}
|
|
45
|
-
|
|
46
|
-
this.
|
|
47
|
-
const decoded = await this.accessTokenHandler.verify?.(accessToken);
|
|
52
|
+
this.accessTokenConfig.embed?.(context, accessToken);
|
|
53
|
+
const decoded = await this.accessTokenConfig.verify?.(accessToken);
|
|
48
54
|
const user = await this.retrieveUser(decoded);
|
|
49
55
|
if (!user) {
|
|
50
56
|
throw new errors_1.UserNotFoundError();
|
|
@@ -59,9 +65,9 @@ class TokenBasedAuthStrategy extends base_auth_strategy_1.BaseAuthStrategy {
|
|
|
59
65
|
}
|
|
60
66
|
async logout(context) {
|
|
61
67
|
try {
|
|
62
|
-
await this.
|
|
63
|
-
if (this.
|
|
64
|
-
await this.
|
|
68
|
+
await this.accessTokenConfig.remove?.(context);
|
|
69
|
+
if (this.refreshTokenConfig) {
|
|
70
|
+
await this.refreshTokenConfig.remove?.(context);
|
|
65
71
|
}
|
|
66
72
|
await this.config.logout.onSuccess?.(context);
|
|
67
73
|
this.logger?.info("User logged out successfully.");
|
|
@@ -74,32 +80,32 @@ class TokenBasedAuthStrategy extends base_auth_strategy_1.BaseAuthStrategy {
|
|
|
74
80
|
}
|
|
75
81
|
async generateTokens(user, context) {
|
|
76
82
|
const payload = { userId: user.id, roles: user.roles };
|
|
77
|
-
const accessToken = this.
|
|
83
|
+
const accessToken = this.accessTokenConfig.generate?.(payload);
|
|
78
84
|
if (!accessToken)
|
|
79
85
|
throw new Error("Failed to generate access token.");
|
|
80
|
-
await this.
|
|
81
|
-
this.
|
|
86
|
+
await this.accessTokenConfig.store?.(accessToken, user, +this.accessTokenConfig.expiresIn);
|
|
87
|
+
this.accessTokenConfig.embed?.(context, accessToken);
|
|
82
88
|
let refreshToken;
|
|
83
|
-
if (this.
|
|
84
|
-
refreshToken = this.
|
|
89
|
+
if (this.refreshTokenConfig) {
|
|
90
|
+
refreshToken = this.refreshTokenConfig.generate?.(payload);
|
|
85
91
|
if (refreshToken) {
|
|
86
|
-
await this.
|
|
87
|
-
this.
|
|
92
|
+
await this.refreshTokenConfig.store?.(refreshToken, user, +this.refreshTokenConfig.expiresIn);
|
|
93
|
+
this.refreshTokenConfig.embed?.(context, refreshToken);
|
|
88
94
|
}
|
|
89
95
|
}
|
|
90
96
|
return { accessToken, refreshToken };
|
|
91
97
|
}
|
|
92
98
|
async rotateToken(context) {
|
|
93
99
|
try {
|
|
94
|
-
if (!this.
|
|
100
|
+
if (!this.refreshTokenConfig) {
|
|
95
101
|
throw new Error("Refresh token handler is not configured.");
|
|
96
102
|
}
|
|
97
|
-
const refreshToken = await this.
|
|
103
|
+
const refreshToken = await this.refreshTokenConfig.retrieve?.(context);
|
|
98
104
|
if (!refreshToken) {
|
|
99
105
|
throw new errors_1.MissingTokenError("Refresh");
|
|
100
106
|
}
|
|
101
|
-
const newAccessToken = await this.
|
|
102
|
-
this.
|
|
107
|
+
const newAccessToken = await this.refreshTokenConfig.rotate?.(refreshToken);
|
|
108
|
+
this.accessTokenConfig.embed?.(context, newAccessToken);
|
|
103
109
|
return { accessToken: newAccessToken, refreshToken };
|
|
104
110
|
}
|
|
105
111
|
catch (error) {
|
package/build/types.d.ts
CHANGED
|
@@ -40,15 +40,17 @@ export interface AuthResultConfig<TContext = unknown, TUser = unknown> {
|
|
|
40
40
|
onFailure?: (context: AuthFailureContext<TContext>) => Promise<void> | void;
|
|
41
41
|
}
|
|
42
42
|
export interface SecurityConfig {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
notifyOnLockout?: (account: any) => Promise<void>;
|
|
47
|
-
};
|
|
43
|
+
maxFailedLoginAttempts?: number;
|
|
44
|
+
lockoutDuration?: number;
|
|
45
|
+
notifyOnLockout?: (account: any) => Promise<void>;
|
|
48
46
|
}
|
|
49
|
-
export interface BaseAuthStrategyConfig<TContext = unknown, TUser = unknown>
|
|
47
|
+
export interface BaseAuthStrategyConfig<TContext = unknown, TUser = unknown> {
|
|
50
48
|
mfa?: MfaConfig<TUser, TContext>;
|
|
51
49
|
session?: SessionConfig;
|
|
50
|
+
role?: RoleAuthorizationConfig<TUser>;
|
|
51
|
+
rateLimit?: RateLimitConfig;
|
|
52
|
+
security?: SecurityConfig;
|
|
53
|
+
lock?: AccountLockConfig<TContext>;
|
|
52
54
|
}
|
|
53
55
|
export interface AuditLoggingConfig<TContext = unknown> {
|
|
54
56
|
logAttempt?: (userId: string, success: boolean, context?: TContext) => Promise<void>;
|
|
@@ -98,15 +100,14 @@ export interface CredentialBasedAuthStrategyConfig<TContext = unknown, TUser = u
|
|
|
98
100
|
updatePassword?: (identifier: string, newPassword: string) => Promise<void>;
|
|
99
101
|
} & AuthResultConfig<TContext, TUser>;
|
|
100
102
|
}
|
|
101
|
-
export interface TokenBasedAuthStrategyConfig<TContext = unknown, TUser = unknown> extends BaseAuthStrategyConfig<TContext, TUser
|
|
102
|
-
|
|
103
|
+
export interface TokenBasedAuthStrategyConfig<TContext = unknown, TUser = unknown> extends BaseAuthStrategyConfig<TContext, TUser> {
|
|
104
|
+
rotation?: TokenRotationConfig<TUser>;
|
|
103
105
|
login: {
|
|
104
106
|
retrieveUserData: (identifier: string) => Promise<TUser | null>;
|
|
105
107
|
} & AuthResultConfig<TContext, TUser>;
|
|
106
108
|
logout: {} & AuthResultConfig<TContext, TUser>;
|
|
107
109
|
}
|
|
108
110
|
export interface TokenRotationConfig<TUser = unknown> {
|
|
109
|
-
enableRotation?: boolean;
|
|
110
111
|
maxRotations?: number;
|
|
111
112
|
storeUsedTokens?: boolean;
|
|
112
113
|
getRefreshToken?: (user: TUser) => Promise<string>;
|
|
@@ -184,7 +185,6 @@ export interface SoapSocketAuthConfig<TContext = unknown, TUser = unknown> {
|
|
|
184
185
|
}
|
|
185
186
|
export interface SoapAuthConfig<TContext = unknown, TUser = unknown> {
|
|
186
187
|
session?: SessionConfig;
|
|
187
|
-
tokens?: TokenHandlersConfig;
|
|
188
188
|
http?: SoapHttpAuthConfig<TContext, TUser>;
|
|
189
189
|
socket?: SoapSocketAuthConfig<TContext, TUser>;
|
|
190
190
|
logger?: Soap.Logger;
|
|
@@ -230,7 +230,7 @@ export interface StorageContext {
|
|
|
230
230
|
encrypt?: (data: string) => Promise<string> | string;
|
|
231
231
|
decrypt?: (data: string) => Promise<string> | string;
|
|
232
232
|
}
|
|
233
|
-
export interface
|
|
233
|
+
export interface TokenConfig {
|
|
234
234
|
secretKey: string;
|
|
235
235
|
expiresIn: string | number;
|
|
236
236
|
audience?: string | string[];
|
|
@@ -245,7 +245,3 @@ export interface TokenHandlerConfig {
|
|
|
245
245
|
embed?: (context: any, token: string) => void;
|
|
246
246
|
rotate?: (oldToken: string) => Promise<string>;
|
|
247
247
|
}
|
|
248
|
-
export interface TokenHandlersConfig {
|
|
249
|
-
access: TokenHandlerConfig;
|
|
250
|
-
refresh?: TokenHandlerConfig;
|
|
251
|
-
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@soapjs/soap-auth",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"homepage": "https://docs.soapjs.com",
|
|
6
6
|
"repository": "https://github.com/soapjs/soap-auth",
|
|
@@ -15,14 +15,14 @@
|
|
|
15
15
|
"prepublish": "npm run clean && tsc --project tsconfig.build.json"
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
18
|
-
"@soapjs/soap": "^0.5.
|
|
18
|
+
"@soapjs/soap": "^0.5.8",
|
|
19
19
|
"@types/jest": "^27.0.3",
|
|
20
20
|
"jest": "^27.4.5",
|
|
21
21
|
"ts-jest": "^27.1.3",
|
|
22
22
|
"typescript": "^4.8.2"
|
|
23
23
|
},
|
|
24
24
|
"peerDependencies": {
|
|
25
|
-
"@soapjs/soap": ">=0.5.
|
|
25
|
+
"@soapjs/soap": ">=0.5.8"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"axios": "^1.7.9",
|