@soapjs/soap-auth 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +223 -0
- package/build/errors.d.ts +53 -0
- package/build/errors.js +117 -0
- package/build/factories/auth-strategy.factory.d.ts +9 -0
- package/build/factories/auth-strategy.factory.js +16 -0
- package/build/factories/http-auth-strategy.factory.d.ts +5 -0
- package/build/factories/http-auth-strategy.factory.js +42 -0
- package/build/factories/socket-auth-strategy.factory.d.ts +5 -0
- package/build/factories/socket-auth-strategy.factory.js +27 -0
- package/build/index.d.ts +28 -0
- package/build/index.js +44 -0
- package/build/session/file.session-store.d.ts +10 -0
- package/build/session/file.session-store.js +59 -0
- package/build/session/memory.session-store.d.ts +7 -0
- package/build/session/memory.session-store.js +19 -0
- package/build/session/session-handler.d.ts +17 -0
- package/build/session/session-handler.js +145 -0
- package/build/soap-auth.d.ts +16 -0
- package/build/soap-auth.js +75 -0
- package/build/strategies/api-key/api-key.errors.d.ts +9 -0
- package/build/strategies/api-key/api-key.errors.js +24 -0
- package/build/strategies/api-key/api-key.strategy.d.ts +14 -0
- package/build/strategies/api-key/api-key.strategy.js +95 -0
- package/build/strategies/api-key/api-key.types.d.ts +13 -0
- package/build/strategies/api-key/api-key.types.js +2 -0
- package/build/strategies/base-auth.strategy.d.ts +17 -0
- package/build/strategies/base-auth.strategy.js +69 -0
- package/build/strategies/basic/basic.strategy.d.ts +25 -0
- package/build/strategies/basic/basic.strategy.js +53 -0
- package/build/strategies/basic/basic.types.d.ts +10 -0
- package/build/strategies/basic/basic.types.js +2 -0
- package/build/strategies/credential-based-auth.strategy.d.ts +30 -0
- package/build/strategies/credential-based-auth.strategy.js +205 -0
- package/build/strategies/jwt/jwt.strategy.d.ts +10 -0
- package/build/strategies/jwt/jwt.strategy.js +69 -0
- package/build/strategies/jwt/jwt.tools.d.ts +3 -0
- package/build/strategies/jwt/jwt.tools.js +79 -0
- package/build/strategies/jwt/jwt.types.d.ts +33 -0
- package/build/strategies/jwt/jwt.types.js +2 -0
- package/build/strategies/local/local.strategy.d.ts +25 -0
- package/build/strategies/local/local.strategy.js +76 -0
- package/build/strategies/local/local.types.d.ts +3 -0
- package/build/strategies/local/local.types.js +2 -0
- package/build/strategies/oauth2/oauth2.strategy.d.ts +35 -0
- package/build/strategies/oauth2/oauth2.strategy.js +259 -0
- package/build/strategies/oauth2/oauth2.tools.d.ts +4 -0
- package/build/strategies/oauth2/oauth2.tools.js +22 -0
- package/build/strategies/oauth2/oauth2.types.d.ts +29 -0
- package/build/strategies/oauth2/oauth2.types.js +2 -0
- package/build/strategies/token-based-auth.strategy.d.ts +25 -0
- package/build/strategies/token-based-auth.strategy.js +124 -0
- package/build/tools/session.tools.d.ts +6 -0
- package/build/tools/session.tools.js +15 -0
- package/build/tools/token.tools.d.ts +7 -0
- package/build/tools/token.tools.js +32 -0
- package/build/tools/tools.d.ts +3 -0
- package/build/tools/tools.js +23 -0
- package/build/types.d.ts +251 -0
- package/build/types.js +2 -0
- package/jest.config.unit.json +10 -0
- package/ldap.md +62 -0
- package/package.json +33 -0
- package/saml.md +244 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CredentialBasedAuthStrategy = void 0;
|
|
4
|
+
const errors_1 = require("../errors");
|
|
5
|
+
const base_auth_strategy_1 = require("./base-auth.strategy");
|
|
6
|
+
class CredentialBasedAuthStrategy extends base_auth_strategy_1.BaseAuthStrategy {
|
|
7
|
+
config;
|
|
8
|
+
session;
|
|
9
|
+
logger;
|
|
10
|
+
constructor(config, session, logger) {
|
|
11
|
+
super(config, session, logger);
|
|
12
|
+
this.config = config;
|
|
13
|
+
this.session = session;
|
|
14
|
+
this.logger = logger;
|
|
15
|
+
}
|
|
16
|
+
async authenticate(context) {
|
|
17
|
+
try {
|
|
18
|
+
const credentials = await this.extractCredentials(context);
|
|
19
|
+
if (!credentials)
|
|
20
|
+
throw new errors_1.MissingCredentialsError();
|
|
21
|
+
await this.isAccountLocked(credentials.identifier);
|
|
22
|
+
await this.checkFailedAttempts(credentials.identifier);
|
|
23
|
+
await this.checkRateLimit(credentials.identifier);
|
|
24
|
+
await this.checkPasswordExpiry(credentials.identifier);
|
|
25
|
+
const valid = await this.verifyCredentials(credentials);
|
|
26
|
+
if (!valid) {
|
|
27
|
+
await this.config.login.incrementFailedAttempts?.(credentials.identifier);
|
|
28
|
+
throw new errors_1.InvalidCredentialsError();
|
|
29
|
+
}
|
|
30
|
+
await this.config.login.resetFailedAttempts?.(credentials.identifier);
|
|
31
|
+
const user = await this.retrieveUser(credentials);
|
|
32
|
+
if (!user)
|
|
33
|
+
throw new errors_1.UserNotFoundError();
|
|
34
|
+
await this.checkMfa(user, context);
|
|
35
|
+
await this.isAuthorized(user);
|
|
36
|
+
await this.handleSession(user, context);
|
|
37
|
+
return { user };
|
|
38
|
+
}
|
|
39
|
+
catch (e) {
|
|
40
|
+
const error = new errors_1.AuthError(e, "Authentication failed.");
|
|
41
|
+
this.logger.error("Authentication failed:", error);
|
|
42
|
+
await this.config.logout.onFailure?.({ context, error: error });
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async handleSession(user, context) {
|
|
47
|
+
if (this.session) {
|
|
48
|
+
const sessionId = this.session.generateSessionId();
|
|
49
|
+
await this.session.set(sessionId, user);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
async logout(context) {
|
|
53
|
+
try {
|
|
54
|
+
if (this.session) {
|
|
55
|
+
const sessionId = this.session.getSessionId?.(context);
|
|
56
|
+
if (!sessionId) {
|
|
57
|
+
throw new Error("Session ID is missing in the context.");
|
|
58
|
+
}
|
|
59
|
+
await this.session.destroy(sessionId);
|
|
60
|
+
this.logger.info(`Session destroyed successfully, sessionId: ${sessionId}`);
|
|
61
|
+
}
|
|
62
|
+
await this.config.logout.onSuccess?.(context);
|
|
63
|
+
this.logger.info(`User logged out successfully.`);
|
|
64
|
+
}
|
|
65
|
+
catch (e) {
|
|
66
|
+
const error = new errors_1.AuthError(e, "Logout process failed.");
|
|
67
|
+
this.logger.error("Error during logout:", e);
|
|
68
|
+
await this.config.logout.onFailure?.({ context, error: e });
|
|
69
|
+
throw error;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
async requestPasswordReset(identifier, email) {
|
|
73
|
+
try {
|
|
74
|
+
if (!this.config.passwordReset?.generateResetToken) {
|
|
75
|
+
throw new Error("Password reset token generation is not configured.");
|
|
76
|
+
}
|
|
77
|
+
const token = await this.config.passwordReset.generateResetToken(identifier);
|
|
78
|
+
if (email) {
|
|
79
|
+
await this.config.passwordReset.sendResetEmail?.(email, token);
|
|
80
|
+
}
|
|
81
|
+
await this.config.passwordReset.onSuccess?.({ identifier });
|
|
82
|
+
this.logger.info(`Password reset requested for identifier: ${identifier}`);
|
|
83
|
+
}
|
|
84
|
+
catch (e) {
|
|
85
|
+
const error = new errors_1.AuthError(e, "Password reset request error.");
|
|
86
|
+
this.logger.error("Password reset request error:", e);
|
|
87
|
+
await this.config.passwordReset.onFailure?.({ identifier, error: e });
|
|
88
|
+
throw error;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
async resetPassword(identifier, token, newPassword) {
|
|
92
|
+
try {
|
|
93
|
+
if (!this.config.passwordReset?.validateResetToken) {
|
|
94
|
+
throw new Error("Password reset token validation is not configured.");
|
|
95
|
+
}
|
|
96
|
+
const isValid = await this.config.passwordReset.validateResetToken(token);
|
|
97
|
+
if (!isValid) {
|
|
98
|
+
throw new Error("Invalid or expired reset token.");
|
|
99
|
+
}
|
|
100
|
+
await this.config.passwordReset.updatePassword(identifier, newPassword);
|
|
101
|
+
await this.config.passwordReset.onSuccess?.({ identifier });
|
|
102
|
+
this.logger.info(`Password successfully reset for identifier: ${identifier}`);
|
|
103
|
+
}
|
|
104
|
+
catch (e) {
|
|
105
|
+
const error = new errors_1.AuthError(e, "Password reset error");
|
|
106
|
+
this.logger.error("Password reset error:", e);
|
|
107
|
+
await this.config.passwordReset.onFailure?.({ identifier, error: e });
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
async changePassword(identifier, oldPassword, newPassword) {
|
|
112
|
+
try {
|
|
113
|
+
if (!this.config.login.verifyUserCredentials) {
|
|
114
|
+
throw new Error("Credential verification is not configured.");
|
|
115
|
+
}
|
|
116
|
+
const isAuthenticated = await this.config.login.verifyUserCredentials(identifier, oldPassword);
|
|
117
|
+
if (!isAuthenticated) {
|
|
118
|
+
throw new errors_1.InvalidCredentialsError();
|
|
119
|
+
}
|
|
120
|
+
await this.config.passwordReset?.updatePassword?.(identifier, newPassword);
|
|
121
|
+
await this.config.passwordReset?.onSuccess?.({ identifier });
|
|
122
|
+
this.logger.info(`Password changed successfully for identifier: ${identifier}`);
|
|
123
|
+
}
|
|
124
|
+
catch (e) {
|
|
125
|
+
const error = new errors_1.AuthError(e, "Change password error");
|
|
126
|
+
this.logger.error("Change password error:", e);
|
|
127
|
+
await this.config.passwordReset?.onFailure?.({ identifier, error: e });
|
|
128
|
+
throw error;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
async auditLoginAttempt(identifier, success, context) {
|
|
132
|
+
await this.config.audit?.logAttempt?.(identifier, success, context);
|
|
133
|
+
}
|
|
134
|
+
async auditPasswordChange(identifier, context) {
|
|
135
|
+
await this.config.audit?.logPasswordChange?.(identifier, context);
|
|
136
|
+
}
|
|
137
|
+
validatePasswordPolicy(password) {
|
|
138
|
+
return this.config.passwordPolicy.validatePassword?.(password) ?? true;
|
|
139
|
+
}
|
|
140
|
+
async checkFailedAttempts(identifier) {
|
|
141
|
+
try {
|
|
142
|
+
if (this.config.security?.maxFailedLoginAttempts) {
|
|
143
|
+
const failedAttempts = (await this.config.login.getFailedAttempts?.(identifier)) || 0;
|
|
144
|
+
if (failedAttempts >= this.config.security.maxFailedLoginAttempts) {
|
|
145
|
+
this.logger.warn(`User ${identifier} is temporarily locked out.`);
|
|
146
|
+
throw new errors_1.AccountLockedError();
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
catch (e) {
|
|
151
|
+
this.logger.error("Check failed attempts:", e);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
async isAccountLocked(account, ...args) {
|
|
155
|
+
if (await this.config.isAccountLocked?.(account, ...args)) {
|
|
156
|
+
throw new errors_1.AccountLockedError();
|
|
157
|
+
}
|
|
158
|
+
if (typeof account === "string" && this.config.security?.lockoutDuration) {
|
|
159
|
+
const lockoutKey = `lockout:${account}`;
|
|
160
|
+
const lockoutSession = await this.session?.get(lockoutKey);
|
|
161
|
+
if (lockoutSession) {
|
|
162
|
+
const elapsed = Date.now() - Number(lockoutSession.date);
|
|
163
|
+
if (elapsed < this.config.security.lockoutDuration * 60 * 1000) {
|
|
164
|
+
this.logger.warn(`Account ${account} is temporarily locked out.`);
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
await this.session?.destroy(lockoutKey);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
async incrementFailedAttempts(account) {
|
|
175
|
+
if (this.config.login.incrementFailedAttempts) {
|
|
176
|
+
await this.config.login.incrementFailedAttempts(account);
|
|
177
|
+
const failedAttempts = (await this.config.login.getFailedAttempts?.(account)) || 0;
|
|
178
|
+
if (this.config.security?.maxFailedLoginAttempts &&
|
|
179
|
+
failedAttempts >= this.config.security.maxFailedLoginAttempts) {
|
|
180
|
+
const lockoutKey = `lockout:${account}`;
|
|
181
|
+
await this.session?.set(lockoutKey, {
|
|
182
|
+
date: Date.now(),
|
|
183
|
+
});
|
|
184
|
+
this.logger.warn(`Account ${account} has been locked due to failed attempts.`);
|
|
185
|
+
this.notifyAccountLocked(account);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
async notifyAccountLocked(identifier) {
|
|
190
|
+
if (this.config.security?.notifyOnLockout) {
|
|
191
|
+
await this.config.security.notifyOnLockout(identifier);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
async checkPasswordExpiry(identifier) {
|
|
195
|
+
if (this.config.passwordPolicy?.passwordExpirationDays) {
|
|
196
|
+
const lastChanged = await this.config.passwordPolicy.getLastPasswordChange?.(identifier);
|
|
197
|
+
if (lastChanged &&
|
|
198
|
+
Date.now() - Number(lastChanged) >
|
|
199
|
+
this.config.passwordPolicy.passwordExpirationDays * 86400000) {
|
|
200
|
+
throw new Error("Password expired, please reset your password.");
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
exports.CredentialBasedAuthStrategy = CredentialBasedAuthStrategy;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as Soap from "@soapjs/soap";
|
|
2
|
+
import { TokenBasedAuthStrategy } from "../token-based-auth.strategy";
|
|
3
|
+
import { JwtConfig } from "./jwt.types";
|
|
4
|
+
import { SessionHandler } from "../../session/session-handler";
|
|
5
|
+
export declare class JwtStrategy<TContext = unknown, TUser = unknown> extends TokenBasedAuthStrategy<TContext, TUser> {
|
|
6
|
+
protected config: JwtConfig<TContext, TUser>;
|
|
7
|
+
protected session?: SessionHandler;
|
|
8
|
+
protected logger?: Soap.Logger;
|
|
9
|
+
constructor(config: JwtConfig<TContext, TUser>, session?: SessionHandler, logger?: Soap.Logger);
|
|
10
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.JwtStrategy = void 0;
|
|
7
|
+
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
|
8
|
+
const util_1 = require("util");
|
|
9
|
+
const token_based_auth_strategy_1 = require("../token-based-auth.strategy");
|
|
10
|
+
const errors_1 = require("../../errors");
|
|
11
|
+
const jwt_tools_1 = require("./jwt.tools");
|
|
12
|
+
const verifyAsync = (0, util_1.promisify)(jsonwebtoken_1.default.verify);
|
|
13
|
+
class JwtStrategy extends token_based_auth_strategy_1.TokenBasedAuthStrategy {
|
|
14
|
+
config;
|
|
15
|
+
session;
|
|
16
|
+
logger;
|
|
17
|
+
constructor(config, session, logger) {
|
|
18
|
+
if (!config.access.secretKey) {
|
|
19
|
+
throw new errors_1.UndefinedTokenSecretError("Access");
|
|
20
|
+
}
|
|
21
|
+
if (config.refresh && !config.refresh.secretKey) {
|
|
22
|
+
throw new errors_1.UndefinedTokenSecretError("Refresh");
|
|
23
|
+
}
|
|
24
|
+
const accessTokenConfig = (0, jwt_tools_1.prepareAccessTokenConfig)(config);
|
|
25
|
+
const refreshTokenConfig = (0, jwt_tools_1.prepareRefreshTokenConfig)(config);
|
|
26
|
+
super(config, {
|
|
27
|
+
...accessTokenConfig,
|
|
28
|
+
generate(payload) {
|
|
29
|
+
return jsonwebtoken_1.default.sign(payload, accessTokenConfig.secretKey, accessTokenConfig.signOptions);
|
|
30
|
+
},
|
|
31
|
+
verify(token) {
|
|
32
|
+
try {
|
|
33
|
+
if (!token)
|
|
34
|
+
throw new errors_1.UndefinedTokenError("Access");
|
|
35
|
+
if (!accessTokenConfig.secretKey)
|
|
36
|
+
throw new errors_1.UndefinedTokenSecretError("Access");
|
|
37
|
+
return verifyAsync(token, accessTokenConfig.secretKey, accessTokenConfig.verifyOptions);
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
this.logger?.error("JWT verification failed:", error);
|
|
41
|
+
throw new errors_1.InvalidTokenError("Access");
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
}, {
|
|
45
|
+
...refreshTokenConfig,
|
|
46
|
+
generate(payload) {
|
|
47
|
+
return jsonwebtoken_1.default.sign(payload, refreshTokenConfig.secretKey, refreshTokenConfig.signOptions);
|
|
48
|
+
},
|
|
49
|
+
verify(token) {
|
|
50
|
+
try {
|
|
51
|
+
if (!token)
|
|
52
|
+
throw new errors_1.UndefinedTokenError("Refresh");
|
|
53
|
+
if (!refreshTokenConfig.secretKey)
|
|
54
|
+
throw new errors_1.UndefinedTokenSecretError("Refresh");
|
|
55
|
+
return verifyAsync(token, refreshTokenConfig.secretKey, refreshTokenConfig.verifyOptions);
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
this.logger?.error("JWT verification failed:", error);
|
|
59
|
+
throw new errors_1.InvalidTokenError("Refresh");
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
this.config = config;
|
|
64
|
+
this.session = session;
|
|
65
|
+
this.logger = logger;
|
|
66
|
+
this.logger?.info("JWTStrategy initialized with provided configurations.");
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
exports.JwtStrategy = JwtStrategy;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { JwtAccessTokenHandlerConfig, JwtConfig, JwtRefreshTokenHandlerConfig } from "./jwt.types";
|
|
2
|
+
export declare const prepareAccessTokenConfig: (config: JwtConfig) => JwtAccessTokenHandlerConfig;
|
|
3
|
+
export declare const prepareRefreshTokenConfig: (config: JwtConfig) => JwtRefreshTokenHandlerConfig;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.prepareRefreshTokenConfig = exports.prepareAccessTokenConfig = void 0;
|
|
27
|
+
const Soap = __importStar(require("@soapjs/soap"));
|
|
28
|
+
const prepareAccessTokenConfig = (config) => {
|
|
29
|
+
return Soap.removeUndefinedProperties({
|
|
30
|
+
...config.access,
|
|
31
|
+
tokenType: "Access",
|
|
32
|
+
expiresIn: config.access.expiresIn || "1h",
|
|
33
|
+
signOptions: {
|
|
34
|
+
...config.access.signOptions,
|
|
35
|
+
algorithm: config.access.signOptions.algorithm || "HS256",
|
|
36
|
+
expiresIn: config.access.expiresIn || "1h",
|
|
37
|
+
audience: config.access.audience,
|
|
38
|
+
issuer: config.access.issuer,
|
|
39
|
+
subject: config.access.subject,
|
|
40
|
+
},
|
|
41
|
+
verifyOptions: config.access.verifyOptions
|
|
42
|
+
? {
|
|
43
|
+
...config.access.verifyOptions,
|
|
44
|
+
algorithms: config.access.verifyOptions.algorithms || ["HS256"],
|
|
45
|
+
expiresIn: config.access.expiresIn || "1h",
|
|
46
|
+
audience: config.access.audience,
|
|
47
|
+
issuer: config.access.issuer,
|
|
48
|
+
subject: config.access.subject,
|
|
49
|
+
}
|
|
50
|
+
: {},
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
exports.prepareAccessTokenConfig = prepareAccessTokenConfig;
|
|
54
|
+
const prepareRefreshTokenConfig = (config) => {
|
|
55
|
+
return Soap.removeUndefinedProperties({
|
|
56
|
+
...config.refresh,
|
|
57
|
+
secretKey: config.refresh.secretKey,
|
|
58
|
+
tokenType: "Refresh",
|
|
59
|
+
signOptions: {
|
|
60
|
+
...config.refresh.signOptions,
|
|
61
|
+
algorithm: config.refresh.signOptions.algorithm || "HS256",
|
|
62
|
+
expiresIn: config.refresh.expiresIn || "7d",
|
|
63
|
+
audience: config.refresh.audience,
|
|
64
|
+
issuer: config.refresh.issuer,
|
|
65
|
+
subject: config.refresh.subject,
|
|
66
|
+
},
|
|
67
|
+
verifyOptions: config.refresh.verifyOptions
|
|
68
|
+
? {
|
|
69
|
+
...config.refresh.verifyOptions,
|
|
70
|
+
algorithm: config.refresh.verifyOptions.algorithms || ["HS256"],
|
|
71
|
+
expiresIn: config.refresh.expiresIn || "7d",
|
|
72
|
+
audience: config.refresh.audience,
|
|
73
|
+
issuer: config.refresh.issuer,
|
|
74
|
+
subject: config.refresh.subject,
|
|
75
|
+
}
|
|
76
|
+
: {},
|
|
77
|
+
});
|
|
78
|
+
};
|
|
79
|
+
exports.prepareRefreshTokenConfig = prepareRefreshTokenConfig;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { TokenBasedAuthStrategyConfig, TokenHandlerConfig } from "../../types";
|
|
2
|
+
export interface JwtVerifyOptions {
|
|
3
|
+
algorithms?: string[];
|
|
4
|
+
notBefore?: string | number;
|
|
5
|
+
clockTolerance?: number;
|
|
6
|
+
ignoreExpiration?: boolean;
|
|
7
|
+
ignoreNotBefore?: boolean;
|
|
8
|
+
complete?: boolean;
|
|
9
|
+
clockTimestamp?: number;
|
|
10
|
+
[key: string]: any;
|
|
11
|
+
}
|
|
12
|
+
export interface JwtSignOptions {
|
|
13
|
+
algorithm?: "HS256" | "HS384" | "HS512" | "RS256" | "RS384" | "RS512" | "ES256" | "ES384" | "ES512" | "PS256" | "PS384" | "PS512" | "none";
|
|
14
|
+
notBefore?: string | number;
|
|
15
|
+
jwtid?: string;
|
|
16
|
+
issuedAt?: number;
|
|
17
|
+
mutatePayload?: (payload: Record<string, any>) => Record<string, any>;
|
|
18
|
+
noTimestamp?: boolean;
|
|
19
|
+
keyid?: string;
|
|
20
|
+
allowUnsafe?: boolean;
|
|
21
|
+
}
|
|
22
|
+
export type JwtAccessTokenHandlerConfig = {
|
|
23
|
+
verifyOptions?: JwtVerifyOptions;
|
|
24
|
+
signOptions: JwtSignOptions;
|
|
25
|
+
} & TokenHandlerConfig;
|
|
26
|
+
export type JwtRefreshTokenHandlerConfig = {
|
|
27
|
+
verifyOptions?: JwtVerifyOptions;
|
|
28
|
+
signOptions: JwtSignOptions;
|
|
29
|
+
} & TokenHandlerConfig;
|
|
30
|
+
export type JwtConfig<TContext = unknown, TUser = unknown> = {
|
|
31
|
+
access: JwtAccessTokenHandlerConfig;
|
|
32
|
+
refresh?: JwtRefreshTokenHandlerConfig;
|
|
33
|
+
} & TokenBasedAuthStrategyConfig<TContext, TUser>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as Soap from "@soapjs/soap";
|
|
2
|
+
import { CredentialBasedAuthStrategy } from "../credential-based-auth.strategy";
|
|
3
|
+
import { LocalStrategyConfig } from "./local.types";
|
|
4
|
+
import { SessionHandler } from "../../session/session-handler";
|
|
5
|
+
export declare class LocalStrategy<TContext = unknown, TUser = unknown> extends CredentialBasedAuthStrategy<TContext, TUser> {
|
|
6
|
+
protected config: LocalStrategyConfig<TContext, TUser>;
|
|
7
|
+
protected session?: SessionHandler;
|
|
8
|
+
protected logger?: Soap.Logger;
|
|
9
|
+
constructor(config: LocalStrategyConfig<TContext, TUser>, session?: SessionHandler, logger?: Soap.Logger);
|
|
10
|
+
protected extractCredentials(context?: TContext): Promise<{
|
|
11
|
+
identifier: string;
|
|
12
|
+
password: string;
|
|
13
|
+
}>;
|
|
14
|
+
protected verifyCredentials(credentials: {
|
|
15
|
+
identifier: string;
|
|
16
|
+
password: string;
|
|
17
|
+
}): Promise<boolean>;
|
|
18
|
+
protected retrieveUser(credentials: {
|
|
19
|
+
identifier: string;
|
|
20
|
+
password: string;
|
|
21
|
+
}): Promise<TUser | null>;
|
|
22
|
+
requestPasswordReset(email: string): Promise<void>;
|
|
23
|
+
resetPassword(email: string, token: string, newPassword: string): Promise<void>;
|
|
24
|
+
changePassword(email: string, oldPassword: string, newPassword: string): Promise<void>;
|
|
25
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LocalStrategy = void 0;
|
|
4
|
+
const errors_1 = require("../../errors");
|
|
5
|
+
const credential_based_auth_strategy_1 = require("../credential-based-auth.strategy");
|
|
6
|
+
class LocalStrategy extends credential_based_auth_strategy_1.CredentialBasedAuthStrategy {
|
|
7
|
+
config;
|
|
8
|
+
session;
|
|
9
|
+
logger;
|
|
10
|
+
constructor(config, session, logger) {
|
|
11
|
+
super(config, session, logger);
|
|
12
|
+
this.config = config;
|
|
13
|
+
this.session = session;
|
|
14
|
+
this.logger = logger;
|
|
15
|
+
}
|
|
16
|
+
async extractCredentials(context) {
|
|
17
|
+
return this.config.login.extractCredentials(context);
|
|
18
|
+
}
|
|
19
|
+
async verifyCredentials(credentials) {
|
|
20
|
+
return this.config.login.verifyUserCredentials(credentials.identifier, credentials.password);
|
|
21
|
+
}
|
|
22
|
+
async retrieveUser(credentials) {
|
|
23
|
+
return this.config.login.retrieveUserData(credentials.identifier);
|
|
24
|
+
}
|
|
25
|
+
async requestPasswordReset(email) {
|
|
26
|
+
try {
|
|
27
|
+
if (!this.config.passwordReset?.generateResetToken) {
|
|
28
|
+
throw new Error("Password reset token generation is not configured.");
|
|
29
|
+
}
|
|
30
|
+
const token = await this.config.passwordReset.generateResetToken(email);
|
|
31
|
+
await this.config.passwordReset.sendResetEmail?.(email, token);
|
|
32
|
+
this.logger?.info(`Password reset requested for email: ${email}`);
|
|
33
|
+
await this.config.passwordReset.onSuccess?.({ email });
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
this.logger?.error("Password reset request error:", error);
|
|
37
|
+
await this.config.passwordReset.onFailure?.({ email, error });
|
|
38
|
+
throw new errors_1.AuthError(error, "Password reset request failed.");
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
async resetPassword(email, token, newPassword) {
|
|
42
|
+
try {
|
|
43
|
+
if (!this.config.passwordReset?.validateResetToken) {
|
|
44
|
+
throw new Error("Password reset token validation is not configured.");
|
|
45
|
+
}
|
|
46
|
+
const isValid = await this.config.passwordReset.validateResetToken(token);
|
|
47
|
+
if (!isValid)
|
|
48
|
+
throw new Error("Invalid or expired reset token.");
|
|
49
|
+
await this.config.passwordReset.updatePassword(email, newPassword);
|
|
50
|
+
this.logger?.info(`Password reset successful for email: ${email}`);
|
|
51
|
+
await this.config.passwordReset.onSuccess?.({ email });
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
this.logger?.error("Password reset error:", error);
|
|
55
|
+
await this.config.passwordReset.onFailure?.({ email, error });
|
|
56
|
+
throw new errors_1.AuthError(error, "Password reset failed.");
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async changePassword(email, oldPassword, newPassword) {
|
|
60
|
+
try {
|
|
61
|
+
const isAuthenticated = await this.config.login.verifyUserCredentials(email, oldPassword);
|
|
62
|
+
if (!isAuthenticated) {
|
|
63
|
+
throw new errors_1.InvalidCredentialsError();
|
|
64
|
+
}
|
|
65
|
+
await this.config.passwordReset?.updatePassword?.(email, newPassword);
|
|
66
|
+
this.logger?.info(`Password changed successfully for email: ${email}`);
|
|
67
|
+
await this.config.passwordReset?.onSuccess?.({ email });
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
this.logger?.error("Change password error:", error);
|
|
71
|
+
await this.config.passwordReset?.onFailure?.({ email, error });
|
|
72
|
+
throw new errors_1.AuthError(error, "Change password failed.");
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
exports.LocalStrategy = LocalStrategy;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import * as Soap from "@soapjs/soap";
|
|
2
|
+
import { TokenHandlerConfig, AuthResult } from "../../types";
|
|
3
|
+
import { TokenBasedAuthStrategy } from "../token-based-auth.strategy";
|
|
4
|
+
import { OAuth2StrategyConfig } from "./oauth2.types";
|
|
5
|
+
import { SessionHandler } from "../../session/session-handler";
|
|
6
|
+
export declare class OAuth2Strategy<TContext = unknown, TUser = unknown> extends TokenBasedAuthStrategy<TContext, TUser> {
|
|
7
|
+
protected config: OAuth2StrategyConfig<TContext, TUser>;
|
|
8
|
+
protected accessTokenHandler: TokenHandlerConfig;
|
|
9
|
+
protected refreshTokenHandler?: TokenHandlerConfig;
|
|
10
|
+
protected session?: SessionHandler;
|
|
11
|
+
protected logger?: Soap.Logger;
|
|
12
|
+
constructor(config: OAuth2StrategyConfig<TContext, TUser>, accessTokenHandler: TokenHandlerConfig, refreshTokenHandler?: TokenHandlerConfig, session?: SessionHandler, logger?: Soap.Logger);
|
|
13
|
+
authenticate(context: TContext): Promise<AuthResult<TUser>>;
|
|
14
|
+
protected verifyAuthorizationCode(context: TContext, code: string): void;
|
|
15
|
+
protected extractAuthorizationCode(context: TContext): string | null;
|
|
16
|
+
protected redirectUser(context: TContext, authUrl: string): void;
|
|
17
|
+
protected buildAuthorizationUrl(context: TContext): string;
|
|
18
|
+
protected exchangeCodeForToken(context: TContext, code: string): Promise<{
|
|
19
|
+
accessToken: string;
|
|
20
|
+
refreshToken?: string;
|
|
21
|
+
}>;
|
|
22
|
+
handleAuthorizationRedirect(context: TContext): void;
|
|
23
|
+
protected retrieveUser(accessToken: string): Promise<TUser | null>;
|
|
24
|
+
protected exchangeClientCredentials(): Promise<{
|
|
25
|
+
accessToken: string;
|
|
26
|
+
}>;
|
|
27
|
+
protected exchangePasswordGrant(): Promise<{
|
|
28
|
+
accessToken: string;
|
|
29
|
+
}>;
|
|
30
|
+
refreshAccessToken(context: TContext): Promise<{
|
|
31
|
+
accessToken: string;
|
|
32
|
+
refreshToken?: string;
|
|
33
|
+
}>;
|
|
34
|
+
revokeToken(token: string): Promise<void>;
|
|
35
|
+
}
|