@soapjs/soap-auth 0.3.3 → 0.4.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/.claude/settings.local.json +20 -0
- package/build/__tests__/soap-auth.test.js +94 -0
- package/build/errors.d.ts +1 -1
- package/build/errors.js +2 -2
- package/build/index.d.ts +1 -0
- package/build/index.js +1 -0
- package/build/services/__tests__/password.service.test.js +25 -18
- package/build/services/__tests__/totp.service.test.d.ts +1 -0
- package/build/services/__tests__/totp.service.test.js +120 -0
- package/build/services/auth-throttle.service.d.ts +2 -2
- package/build/services/auth-throttle.service.js +2 -2
- package/build/services/index.d.ts +1 -0
- package/build/services/index.js +1 -0
- package/build/services/password.service.d.ts +7 -5
- package/build/services/password.service.js +76 -18
- package/build/services/totp.service.d.ts +16 -0
- package/build/services/totp.service.js +96 -0
- package/build/session/__tests__/session-handler.test.js +22 -14
- package/build/session/session-handler.d.ts +1 -0
- package/build/session/session-handler.js +39 -6
- package/build/soap-auth.d.ts +13 -6
- package/build/soap-auth.js +132 -5
- package/build/strategies/__tests__/base-auth.strategy.test.d.ts +3 -2
- package/build/strategies/__tests__/base-auth.strategy.test.js +1 -0
- package/build/strategies/__tests__/credential-auth.strategy.test.d.ts +1 -0
- package/build/strategies/__tests__/credential-auth.strategy.test.js +18 -17
- package/build/strategies/__tests__/token-auth.strategy.test.d.ts +1 -0
- package/build/strategies/__tests__/token-auth.strategy.test.js +1 -0
- package/build/strategies/api-key/api-key.strategy.d.ts +4 -4
- package/build/strategies/api-key/api-key.strategy.js +3 -2
- package/build/strategies/base-auth.strategy.d.ts +5 -4
- package/build/strategies/basic/basic.strategy.d.ts +2 -1
- package/build/strategies/basic/basic.strategy.js +1 -0
- package/build/strategies/credential-auth.strategy.d.ts +7 -7
- package/build/strategies/credential-auth.strategy.js +9 -14
- package/build/strategies/index.d.ts +1 -0
- package/build/strategies/index.js +1 -0
- package/build/strategies/jwt/__tests__/jwt.strategy.test.js +2 -2
- package/build/strategies/jwt/jwt.strategy.d.ts +3 -1
- package/build/strategies/jwt/jwt.strategy.js +35 -6
- package/build/strategies/jwt/jwt.tools.js +8 -8
- package/build/strategies/local/__tests__/local.strategy.test.js +8 -2
- package/build/strategies/local/local.strategy.d.ts +4 -1
- package/build/strategies/local/local.strategy.js +81 -0
- package/build/strategies/oauth2/__tests__/oauth2.strategy.test.d.ts +1 -0
- package/build/strategies/oauth2/__tests__/oauth2.strategy.test.js +239 -0
- package/build/strategies/oauth2/hybrid.oauth2.strategy.d.ts +3 -3
- package/build/strategies/oauth2/hybrid.oauth2.strategy.js +1 -6
- package/build/strategies/oauth2/oauth2.errors.d.ts +1 -0
- package/build/strategies/oauth2/oauth2.errors.js +4 -0
- package/build/strategies/oauth2/oauth2.strategy.d.ts +6 -4
- package/build/strategies/oauth2/oauth2.strategy.js +114 -46
- package/build/strategies/oauth2/oauth2.tools.js +2 -2
- package/build/strategies/oauth2/oauth2.types.d.ts +2 -2
- package/build/strategies/oauth2/providers/__tests__/social-providers.test.d.ts +1 -0
- package/build/strategies/oauth2/providers/__tests__/social-providers.test.js +201 -0
- package/build/strategies/oauth2/providers/facebook.strategy.d.ts +11 -0
- package/build/strategies/oauth2/providers/facebook.strategy.js +58 -0
- package/build/strategies/oauth2/providers/github.strategy.d.ts +11 -0
- package/build/strategies/oauth2/providers/github.strategy.js +56 -0
- package/build/strategies/oauth2/providers/google.strategy.d.ts +11 -0
- package/build/strategies/oauth2/providers/google.strategy.js +52 -0
- package/build/strategies/oauth2/providers/http-oauth2.strategy.d.ts +16 -0
- package/build/strategies/oauth2/providers/http-oauth2.strategy.js +49 -0
- package/build/strategies/oauth2/providers/index.d.ts +5 -0
- package/build/strategies/oauth2/providers/index.js +21 -0
- package/build/strategies/oauth2/providers/provider.types.d.ts +7 -0
- package/build/strategies/oauth2/providers/provider.types.js +2 -0
- package/build/strategies/token-auth.strategy.d.ts +4 -4
- package/build/strategies/token-auth.strategy.js +2 -3
- package/build/tools/tools.js +1 -2
- package/build/types.d.ts +31 -32
- package/build/utils/__tests__/validation.test.d.ts +1 -0
- package/build/utils/__tests__/validation.test.js +181 -0
- package/build/utils/validation.d.ts +23 -0
- package/build/utils/validation.js +139 -0
- package/package.json +8 -7
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TotpService = void 0;
|
|
4
|
+
const crypto_1 = require("crypto");
|
|
5
|
+
const BASE32_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
|
6
|
+
function base32Encode(buf) {
|
|
7
|
+
let bits = 0;
|
|
8
|
+
let value = 0;
|
|
9
|
+
let output = "";
|
|
10
|
+
for (const byte of buf) {
|
|
11
|
+
value = (value << 8) | byte;
|
|
12
|
+
bits += 8;
|
|
13
|
+
while (bits >= 5) {
|
|
14
|
+
output += BASE32_CHARS[(value >>> (bits - 5)) & 31];
|
|
15
|
+
bits -= 5;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
if (bits > 0) {
|
|
19
|
+
output += BASE32_CHARS[(value << (5 - bits)) & 31];
|
|
20
|
+
}
|
|
21
|
+
return output;
|
|
22
|
+
}
|
|
23
|
+
function base32Decode(str) {
|
|
24
|
+
const s = str.replace(/=+$/, "").toUpperCase();
|
|
25
|
+
const bytes = [];
|
|
26
|
+
let bits = 0;
|
|
27
|
+
let value = 0;
|
|
28
|
+
for (const ch of s) {
|
|
29
|
+
const idx = BASE32_CHARS.indexOf(ch);
|
|
30
|
+
if (idx === -1)
|
|
31
|
+
throw new Error(`Invalid base32 character: ${ch}`);
|
|
32
|
+
value = (value << 5) | idx;
|
|
33
|
+
bits += 5;
|
|
34
|
+
if (bits >= 8) {
|
|
35
|
+
bytes.push((value >>> (bits - 8)) & 255);
|
|
36
|
+
bits -= 8;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return Buffer.from(bytes);
|
|
40
|
+
}
|
|
41
|
+
function hotp(key, counter, digits) {
|
|
42
|
+
const buf = Buffer.alloc(8);
|
|
43
|
+
buf.writeBigUInt64BE(counter);
|
|
44
|
+
const hmac = (0, crypto_1.createHmac)("sha1", key).update(buf).digest();
|
|
45
|
+
const offset = hmac[hmac.length - 1] & 0x0f;
|
|
46
|
+
const code = ((hmac[offset] & 0x7f) << 24) |
|
|
47
|
+
((hmac[offset + 1] & 0xff) << 16) |
|
|
48
|
+
((hmac[offset + 2] & 0xff) << 8) |
|
|
49
|
+
(hmac[offset + 3] & 0xff);
|
|
50
|
+
return String(code % 10 ** digits).padStart(digits, "0");
|
|
51
|
+
}
|
|
52
|
+
class TotpService {
|
|
53
|
+
step;
|
|
54
|
+
digits;
|
|
55
|
+
window;
|
|
56
|
+
constructor(options = {}) {
|
|
57
|
+
this.step = options.step ?? 30;
|
|
58
|
+
this.digits = options.digits ?? 6;
|
|
59
|
+
this.window = options.window ?? 1;
|
|
60
|
+
}
|
|
61
|
+
generateSecret(byteLength = 20) {
|
|
62
|
+
return base32Encode((0, crypto_1.randomBytes)(byteLength));
|
|
63
|
+
}
|
|
64
|
+
generateOTP(secret, timestamp = Date.now()) {
|
|
65
|
+
const key = base32Decode(secret);
|
|
66
|
+
const counter = BigInt(Math.floor(timestamp / 1000 / this.step));
|
|
67
|
+
return hotp(key, counter, this.digits);
|
|
68
|
+
}
|
|
69
|
+
verifyOTP(secret, code, timestamp = Date.now()) {
|
|
70
|
+
if (!/^\d+$/.test(code) || code.length !== this.digits)
|
|
71
|
+
return false;
|
|
72
|
+
const key = base32Decode(secret);
|
|
73
|
+
const counter = BigInt(Math.floor(timestamp / 1000 / this.step));
|
|
74
|
+
for (let delta = -this.window; delta <= this.window; delta++) {
|
|
75
|
+
const expected = hotp(key, counter + BigInt(delta), this.digits);
|
|
76
|
+
if (expected === code)
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
generateQRUri(secret, issuer, account) {
|
|
82
|
+
const params = new URLSearchParams({
|
|
83
|
+
secret,
|
|
84
|
+
issuer,
|
|
85
|
+
algorithm: "SHA1",
|
|
86
|
+
digits: String(this.digits),
|
|
87
|
+
period: String(this.step),
|
|
88
|
+
});
|
|
89
|
+
const label = `${encodeURIComponent(issuer)}:${encodeURIComponent(account)}`;
|
|
90
|
+
return `otpauth://totp/${label}?${params.toString()}`;
|
|
91
|
+
}
|
|
92
|
+
generateBackupCodes(count = 8) {
|
|
93
|
+
return Array.from({ length: count }, () => (0, crypto_1.randomBytes)(6).toString("hex").toUpperCase());
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
exports.TotpService = TotpService;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const session_handler_1 = require("../session-handler");
|
|
4
4
|
const session_errors_1 = require("../session.errors");
|
|
5
|
+
const validation_1 = require("../../utils/validation");
|
|
5
6
|
describe("SessionHandler", () => {
|
|
6
7
|
let mockStore;
|
|
7
8
|
let mockLogger;
|
|
@@ -21,6 +22,7 @@ describe("SessionHandler", () => {
|
|
|
21
22
|
warn: jest.fn(),
|
|
22
23
|
};
|
|
23
24
|
config = {
|
|
25
|
+
secret: "test-secret-key",
|
|
24
26
|
store: mockStore,
|
|
25
27
|
sessionKey: "CUSTOMSESSION",
|
|
26
28
|
sessionHeader: "x-custom-session",
|
|
@@ -29,7 +31,7 @@ describe("SessionHandler", () => {
|
|
|
29
31
|
maxAge: 3600,
|
|
30
32
|
},
|
|
31
33
|
generateSessionId: jest.fn(),
|
|
32
|
-
getSessionId:
|
|
34
|
+
getSessionId: jest.fn(),
|
|
33
35
|
embedSessionId: undefined,
|
|
34
36
|
createSessionData: undefined,
|
|
35
37
|
};
|
|
@@ -39,10 +41,10 @@ describe("SessionHandler", () => {
|
|
|
39
41
|
it("should throw if store is not provided", () => {
|
|
40
42
|
expect(() => {
|
|
41
43
|
new session_handler_1.SessionHandler({});
|
|
42
|
-
}).toThrowError("
|
|
44
|
+
}).toThrowError("config.secret is required");
|
|
43
45
|
});
|
|
44
46
|
it("should set default sessionKey and headerKey if not provided", () => {
|
|
45
|
-
const newHandler = new session_handler_1.SessionHandler({ store: mockStore });
|
|
47
|
+
const newHandler = new session_handler_1.SessionHandler({ secret: "test-secret", store: mockStore });
|
|
46
48
|
expect(newHandler.sessionKey).toBe("SESSIONID");
|
|
47
49
|
expect(newHandler.headerKey).toBe("x-session-id");
|
|
48
50
|
});
|
|
@@ -94,7 +96,7 @@ describe("SessionHandler", () => {
|
|
|
94
96
|
throw new Error("Test error");
|
|
95
97
|
});
|
|
96
98
|
const handler = new session_handler_1.SessionHandler(config, mockLogger);
|
|
97
|
-
handler.setSessionId({}, "boom");
|
|
99
|
+
expect(() => handler.setSessionId({}, "boom")).toThrow("Test error");
|
|
98
100
|
expect(mockLogger.error).toHaveBeenCalledWith("Error setting session ID:", expect.any(Error));
|
|
99
101
|
});
|
|
100
102
|
});
|
|
@@ -107,23 +109,31 @@ describe("SessionHandler", () => {
|
|
|
107
109
|
expect(config.getSessionId).toHaveBeenCalled();
|
|
108
110
|
});
|
|
109
111
|
it("should return cookie session if present", () => {
|
|
112
|
+
config.getSessionId = undefined;
|
|
113
|
+
const handler = new session_handler_1.SessionHandler(config, mockLogger);
|
|
110
114
|
const context = { cookies: { CUSTOMSESSION: "cookieValue" } };
|
|
111
|
-
const sid =
|
|
115
|
+
const sid = handler.getSessionId(context);
|
|
112
116
|
expect(sid).toBe("cookieValue");
|
|
113
117
|
});
|
|
114
118
|
it("should return header session if present", () => {
|
|
119
|
+
config.getSessionId = undefined;
|
|
120
|
+
const handler = new session_handler_1.SessionHandler(config, mockLogger);
|
|
115
121
|
const context = { headers: { "x-custom-session": "headerValue" } };
|
|
116
|
-
const sid =
|
|
122
|
+
const sid = handler.getSessionId(context);
|
|
117
123
|
expect(sid).toBe("headerValue");
|
|
118
124
|
});
|
|
119
125
|
it("should return sessionId if set directly on context", () => {
|
|
126
|
+
config.getSessionId = undefined;
|
|
127
|
+
const handler = new session_handler_1.SessionHandler(config, mockLogger);
|
|
120
128
|
const context = { sessionId: "directValue" };
|
|
121
|
-
const sid =
|
|
129
|
+
const sid = handler.getSessionId(context);
|
|
122
130
|
expect(sid).toBe("directValue");
|
|
123
131
|
});
|
|
124
132
|
it("should return null if not found", () => {
|
|
133
|
+
config.getSessionId = undefined;
|
|
134
|
+
const handler = new session_handler_1.SessionHandler(config, mockLogger);
|
|
125
135
|
const context = {};
|
|
126
|
-
const sid =
|
|
136
|
+
const sid = handler.getSessionId(context);
|
|
127
137
|
expect(sid).toBeNull();
|
|
128
138
|
});
|
|
129
139
|
it("should log error and return null if something goes wrong", () => {
|
|
@@ -180,10 +190,8 @@ describe("SessionHandler", () => {
|
|
|
180
190
|
});
|
|
181
191
|
});
|
|
182
192
|
describe("setSessionData", () => {
|
|
183
|
-
it("should
|
|
184
|
-
await sessionHandler.setSessionData("", { user: {} });
|
|
185
|
-
expect(mockLogger.warn).toHaveBeenCalledWith("No session ID found, unable to set session.");
|
|
186
|
-
expect(mockStore.setSession).not.toHaveBeenCalled();
|
|
193
|
+
it("should throw ValidationError if sessionId is falsy", async () => {
|
|
194
|
+
await expect(sessionHandler.setSessionData("", { user: {} })).rejects.toThrow(validation_1.ValidationError);
|
|
187
195
|
});
|
|
188
196
|
it("should call store.setSession with provided data", async () => {
|
|
189
197
|
await sessionHandler.setSessionData("sessionXYZ", {
|
|
@@ -196,9 +204,9 @@ describe("SessionHandler", () => {
|
|
|
196
204
|
});
|
|
197
205
|
it("should log error if store.setSession fails", async () => {
|
|
198
206
|
mockStore.setSession.mockRejectedValue(new Error("Store error"));
|
|
199
|
-
await sessionHandler.setSessionData("session123", {
|
|
207
|
+
await expect(sessionHandler.setSessionData("session123", {
|
|
200
208
|
user: { id: "xyz" },
|
|
201
|
-
});
|
|
209
|
+
})).rejects.toThrow("Store error");
|
|
202
210
|
expect(mockLogger.error).toHaveBeenCalledWith("Error storing session data:", expect.any(Error));
|
|
203
211
|
});
|
|
204
212
|
});
|
|
@@ -7,6 +7,7 @@ export declare class SessionHandler<TContext = unknown, TUser = unknown, TData =
|
|
|
7
7
|
private sessionKey;
|
|
8
8
|
private headerKey;
|
|
9
9
|
constructor(config: SessionConfig, logger?: Soap.Logger);
|
|
10
|
+
private validateConfig;
|
|
10
11
|
setSessionId(context: TContext, sessionId: string): void;
|
|
11
12
|
getSessionId(context: TContext): string | null;
|
|
12
13
|
generateSessionId(): string;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SessionHandler = void 0;
|
|
4
|
-
const
|
|
4
|
+
const crypto_1 = require("crypto");
|
|
5
5
|
const session_errors_1 = require("./session.errors");
|
|
6
|
+
const validation_1 = require("../utils/validation");
|
|
6
7
|
class SessionHandler {
|
|
7
8
|
config;
|
|
8
9
|
logger;
|
|
@@ -12,6 +13,7 @@ class SessionHandler {
|
|
|
12
13
|
constructor(config, logger) {
|
|
13
14
|
this.config = config;
|
|
14
15
|
this.logger = logger;
|
|
16
|
+
this.validateConfig(config);
|
|
15
17
|
if (!config.store) {
|
|
16
18
|
throw new Error("Session store is required.");
|
|
17
19
|
}
|
|
@@ -19,8 +21,37 @@ class SessionHandler {
|
|
|
19
21
|
this.sessionKey = config.sessionKey || "SESSIONID";
|
|
20
22
|
this.headerKey = config.sessionHeader || "x-session-id";
|
|
21
23
|
}
|
|
24
|
+
validateConfig(config) {
|
|
25
|
+
try {
|
|
26
|
+
validation_1.ValidationUtils.required(config, "config");
|
|
27
|
+
validation_1.ValidationUtils.required(config.secret, "config.secret");
|
|
28
|
+
validation_1.ValidationUtils.nonEmptyString(config.secret, "config.secret");
|
|
29
|
+
validation_1.ValidationUtils.required(config.store, "config.store");
|
|
30
|
+
validation_1.ValidationUtils.object(config.store, "config.store");
|
|
31
|
+
if (config.sessionKey) {
|
|
32
|
+
validation_1.ValidationUtils.nonEmptyString(config.sessionKey, "config.sessionKey");
|
|
33
|
+
}
|
|
34
|
+
if (config.sessionHeader) {
|
|
35
|
+
validation_1.ValidationUtils.nonEmptyString(config.sessionHeader, "config.sessionHeader");
|
|
36
|
+
}
|
|
37
|
+
const requiredMethods = ['getSession', 'setSession', 'destroySession', 'touchSession', 'getSessionIds'];
|
|
38
|
+
for (const method of requiredMethods) {
|
|
39
|
+
if (typeof config.store[method] !== 'function') {
|
|
40
|
+
throw new validation_1.ValidationError(`Session store must implement ${method} method`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
if (error instanceof validation_1.ValidationError) {
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
throw new validation_1.ValidationError(`Invalid SessionHandler configuration: ${error.message}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
22
51
|
setSessionId(context, sessionId) {
|
|
23
52
|
try {
|
|
53
|
+
validation_1.ValidationUtils.required(context, "context");
|
|
54
|
+
validation_1.ValidationUtils.nonEmptyString(sessionId, "sessionId");
|
|
24
55
|
if (this.config.embedSessionId) {
|
|
25
56
|
this.config.embedSessionId(context, sessionId);
|
|
26
57
|
return;
|
|
@@ -48,10 +79,12 @@ class SessionHandler {
|
|
|
48
79
|
}
|
|
49
80
|
catch (error) {
|
|
50
81
|
this.config.logger?.error("Error setting session ID:", error);
|
|
82
|
+
throw error;
|
|
51
83
|
}
|
|
52
84
|
}
|
|
53
85
|
getSessionId(context) {
|
|
54
86
|
try {
|
|
87
|
+
validation_1.ValidationUtils.required(context, "context");
|
|
55
88
|
if (this.config.getSessionId) {
|
|
56
89
|
return this.config.getSessionId(context);
|
|
57
90
|
}
|
|
@@ -74,7 +107,7 @@ class SessionHandler {
|
|
|
74
107
|
}
|
|
75
108
|
}
|
|
76
109
|
generateSessionId() {
|
|
77
|
-
return this.config.generateSessionId?.() || (0,
|
|
110
|
+
return this.config.generateSessionId?.() || (0, crypto_1.randomUUID)();
|
|
78
111
|
}
|
|
79
112
|
buildSessionData(data, context) {
|
|
80
113
|
return this.config.createSessionData
|
|
@@ -83,6 +116,7 @@ class SessionHandler {
|
|
|
83
116
|
}
|
|
84
117
|
async getSessionData(sessionId) {
|
|
85
118
|
try {
|
|
119
|
+
validation_1.ValidationUtils.nonEmptyString(sessionId, "sessionId");
|
|
86
120
|
const data = await this.store.getSession(sessionId);
|
|
87
121
|
return data;
|
|
88
122
|
}
|
|
@@ -93,15 +127,14 @@ class SessionHandler {
|
|
|
93
127
|
}
|
|
94
128
|
async setSessionData(sessionId, data) {
|
|
95
129
|
try {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
130
|
+
validation_1.ValidationUtils.nonEmptyString(sessionId, "sessionId");
|
|
131
|
+
validation_1.ValidationUtils.required(data, "data");
|
|
100
132
|
await this.store.setSession(sessionId, data);
|
|
101
133
|
this.config.logger?.info(`Session set for ID: ${sessionId}`);
|
|
102
134
|
}
|
|
103
135
|
catch (error) {
|
|
104
136
|
this.config.logger?.error("Error storing session data:", error);
|
|
137
|
+
throw error;
|
|
105
138
|
}
|
|
106
139
|
}
|
|
107
140
|
async touch(sessionId, data) {
|
package/build/soap-auth.d.ts
CHANGED
|
@@ -1,17 +1,24 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as Soap from "@soapjs/soap";
|
|
2
|
+
import { AuthCategories, SoapAuthConfig } from "./types";
|
|
2
3
|
export declare class SoapAuth {
|
|
3
4
|
private requiredStrategyMethods;
|
|
4
5
|
private strategies;
|
|
5
6
|
private logger?;
|
|
6
7
|
constructor(config: SoapAuthConfig);
|
|
8
|
+
private validateConfig;
|
|
9
|
+
private validateSessionConfig;
|
|
10
|
+
private validateJwtConfig;
|
|
11
|
+
private validateHttpStrategies;
|
|
12
|
+
private validateSocketStrategies;
|
|
7
13
|
private isAuthStrategy;
|
|
8
|
-
addStrategy(strategyInstance: AuthStrategy | undefined, name: string, type: AuthCategories): void;
|
|
14
|
+
addStrategy(strategyInstance: Soap.AuthStrategy | undefined, name: string, type: AuthCategories): void;
|
|
9
15
|
removeStrategy(name: string | string[], type: AuthCategories): void;
|
|
10
16
|
hasStrategy(name: string, type: AuthCategories): boolean;
|
|
11
|
-
getStrategy<T extends AuthStrategy>(name: string, type: AuthCategories): T;
|
|
12
|
-
getHttpStrategy<T extends AuthStrategy>(name: string): T;
|
|
13
|
-
getSocketStrategy<T extends AuthStrategy>(name: string): T;
|
|
14
|
-
getEventStrategy<T extends AuthStrategy>(name: string): T;
|
|
17
|
+
getStrategy<T extends Soap.AuthStrategy>(name: string, type: AuthCategories): T;
|
|
18
|
+
getHttpStrategy<T extends Soap.AuthStrategy>(name: string): T;
|
|
19
|
+
getSocketStrategy<T extends Soap.AuthStrategy>(name: string): T;
|
|
20
|
+
getEventStrategy<T extends Soap.AuthStrategy>(name: string): T;
|
|
15
21
|
listStrategies(type: AuthCategories): string[];
|
|
16
22
|
init(sequential?: boolean): Promise<void>;
|
|
23
|
+
static create(config: SoapAuthConfig): Promise<SoapAuth>;
|
|
17
24
|
}
|
package/build/soap-auth.js
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SoapAuth = void 0;
|
|
4
|
+
const validation_1 = require("./utils/validation");
|
|
5
|
+
const session_handler_1 = require("./session/session-handler");
|
|
6
|
+
const jwt_strategy_1 = require("./strategies/jwt/jwt.strategy");
|
|
7
|
+
const local_strategy_1 = require("./strategies/local/local.strategy");
|
|
8
|
+
const basic_strategy_1 = require("./strategies/basic/basic.strategy");
|
|
9
|
+
const api_key_strategy_1 = require("./strategies/api-key/api-key.strategy");
|
|
4
10
|
class SoapAuth {
|
|
5
|
-
requiredStrategyMethods = ["authenticate"
|
|
11
|
+
requiredStrategyMethods = ["authenticate"];
|
|
6
12
|
strategies = new Map();
|
|
7
13
|
logger;
|
|
8
14
|
constructor(config) {
|
|
15
|
+
this.validateConfig(config);
|
|
9
16
|
this.strategies.set("http", new Map());
|
|
10
17
|
this.strategies.set("socket", new Map());
|
|
11
18
|
this.strategies.set("event", new Map());
|
|
@@ -15,12 +22,83 @@ class SoapAuth {
|
|
|
15
22
|
this.strategies.set("edge", new Map());
|
|
16
23
|
this.logger = config.logger;
|
|
17
24
|
}
|
|
25
|
+
validateConfig(config) {
|
|
26
|
+
try {
|
|
27
|
+
validation_1.ValidationUtils.required(config, "config");
|
|
28
|
+
if (config.logger) {
|
|
29
|
+
validation_1.ValidationUtils.object(config.logger, "config.logger");
|
|
30
|
+
}
|
|
31
|
+
if (config.session) {
|
|
32
|
+
this.validateSessionConfig(config.session);
|
|
33
|
+
}
|
|
34
|
+
if (config.jwt) {
|
|
35
|
+
this.validateJwtConfig(config.jwt);
|
|
36
|
+
}
|
|
37
|
+
if (config.http) {
|
|
38
|
+
this.validateHttpStrategies(config.http);
|
|
39
|
+
}
|
|
40
|
+
if (config.socket) {
|
|
41
|
+
this.validateSocketStrategies(config.socket);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
if (error instanceof validation_1.ValidationError) {
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
throw new validation_1.ValidationError(`Invalid configuration: ${error.message}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
validateSessionConfig(session) {
|
|
52
|
+
validation_1.ValidationUtils.required(session.secret, "session.secret");
|
|
53
|
+
validation_1.ValidationUtils.nonEmptyString(session.secret, "session.secret");
|
|
54
|
+
if (session.sessionKey) {
|
|
55
|
+
validation_1.ValidationUtils.nonEmptyString(session.sessionKey, "session.sessionKey");
|
|
56
|
+
}
|
|
57
|
+
if (session.sessionHeader) {
|
|
58
|
+
validation_1.ValidationUtils.nonEmptyString(session.sessionHeader, "session.sessionHeader");
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
validateJwtConfig(jwt) {
|
|
62
|
+
if (jwt.accessToken) {
|
|
63
|
+
validation_1.ValidationUtils.required(jwt.accessToken.issuer, "jwt.accessToken.issuer");
|
|
64
|
+
validation_1.ValidationUtils.required(jwt.accessToken.issuer.secretKey, "jwt.accessToken.issuer.secretKey");
|
|
65
|
+
validation_1.ValidationUtils.nonEmptyString(jwt.accessToken.issuer.secretKey, "jwt.accessToken.issuer.secretKey");
|
|
66
|
+
}
|
|
67
|
+
if (jwt.refreshToken) {
|
|
68
|
+
validation_1.ValidationUtils.required(jwt.refreshToken.issuer, "jwt.refreshToken.issuer");
|
|
69
|
+
validation_1.ValidationUtils.required(jwt.refreshToken.issuer.secretKey, "jwt.refreshToken.issuer.secretKey");
|
|
70
|
+
validation_1.ValidationUtils.nonEmptyString(jwt.refreshToken.issuer.secretKey, "jwt.refreshToken.issuer.secretKey");
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
validateHttpStrategies(http) {
|
|
74
|
+
validation_1.ValidationUtils.object(http, "http");
|
|
75
|
+
if (http.custom) {
|
|
76
|
+
validation_1.ValidationUtils.object(http.custom, "http.custom");
|
|
77
|
+
for (const [name, strategy] of Object.entries(http.custom)) {
|
|
78
|
+
validation_1.ValidationUtils.required(strategy, `http.custom.${name}`);
|
|
79
|
+
validation_1.ValidationUtils.object(strategy, `http.custom.${name}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
validateSocketStrategies(socket) {
|
|
84
|
+
validation_1.ValidationUtils.object(socket, "socket");
|
|
85
|
+
if (socket.custom) {
|
|
86
|
+
validation_1.ValidationUtils.object(socket.custom, "socket.custom");
|
|
87
|
+
for (const [name, strategy] of Object.entries(socket.custom)) {
|
|
88
|
+
validation_1.ValidationUtils.required(strategy, `socket.custom.${name}`);
|
|
89
|
+
validation_1.ValidationUtils.object(strategy, `socket.custom.${name}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
18
93
|
isAuthStrategy(strategy) {
|
|
19
94
|
return (typeof strategy === "object" &&
|
|
20
95
|
strategy !== null &&
|
|
21
96
|
this.requiredStrategyMethods.every((method) => typeof strategy[method] === "function"));
|
|
22
97
|
}
|
|
23
98
|
addStrategy(strategyInstance, name, type) {
|
|
99
|
+
validation_1.ValidationUtils.required(strategyInstance, "strategyInstance");
|
|
100
|
+
validation_1.ValidationUtils.nonEmptyString(name, "name");
|
|
101
|
+
validation_1.ValidationUtils.oneOf(type, "type", ["http", "socket", "event", "isa", "webhook", "grpc", "edge"]);
|
|
24
102
|
if (!this.strategies.has(type)) {
|
|
25
103
|
throw new Error(`Invalid strategy type "${type}".`);
|
|
26
104
|
}
|
|
@@ -33,21 +111,28 @@ class SoapAuth {
|
|
|
33
111
|
}
|
|
34
112
|
}
|
|
35
113
|
removeStrategy(name, type) {
|
|
114
|
+
validation_1.ValidationUtils.required(name, "name");
|
|
115
|
+
validation_1.ValidationUtils.oneOf(type, "type", ["http", "socket", "event", "isa", "webhook", "grpc", "edge"]);
|
|
36
116
|
if (!this.strategies.has(type)) {
|
|
37
117
|
throw new Error(`Invalid strategy type "${type}".`);
|
|
38
118
|
}
|
|
39
119
|
const names = Array.isArray(name) ? name : [name];
|
|
40
120
|
names.forEach((n) => {
|
|
121
|
+
validation_1.ValidationUtils.nonEmptyString(n, "strategy name");
|
|
41
122
|
this.strategies.get(type).delete(n);
|
|
42
123
|
});
|
|
43
124
|
}
|
|
44
125
|
hasStrategy(name, type) {
|
|
126
|
+
validation_1.ValidationUtils.nonEmptyString(name, "name");
|
|
127
|
+
validation_1.ValidationUtils.oneOf(type, "type", ["http", "socket", "event", "isa", "webhook", "grpc", "edge"]);
|
|
45
128
|
if (!this.strategies.has(type)) {
|
|
46
129
|
throw new Error(`Invalid strategy type "${type}".`);
|
|
47
130
|
}
|
|
48
131
|
return this.strategies.get(type).has(name);
|
|
49
132
|
}
|
|
50
133
|
getStrategy(name, type) {
|
|
134
|
+
validation_1.ValidationUtils.nonEmptyString(name, "name");
|
|
135
|
+
validation_1.ValidationUtils.oneOf(type, "type", ["http", "socket", "event", "isa", "webhook", "grpc", "edge"]);
|
|
51
136
|
if (!this.strategies.has(type)) {
|
|
52
137
|
throw new Error(`Invalid strategy type "${type}".`);
|
|
53
138
|
}
|
|
@@ -101,7 +186,7 @@ class SoapAuth {
|
|
|
101
186
|
if (sequential) {
|
|
102
187
|
for (const strategy of strategies) {
|
|
103
188
|
try {
|
|
104
|
-
await strategy.init();
|
|
189
|
+
await strategy.init?.();
|
|
105
190
|
}
|
|
106
191
|
catch (error) {
|
|
107
192
|
this.logger?.error(`Failed to initialize strategy: ${error.message}`);
|
|
@@ -109,10 +194,52 @@ class SoapAuth {
|
|
|
109
194
|
}
|
|
110
195
|
}
|
|
111
196
|
else {
|
|
112
|
-
await Promise.all(strategies.map((strategy) => strategy
|
|
113
|
-
|
|
114
|
-
|
|
197
|
+
await Promise.all(strategies.map((strategy) => Promise.resolve(strategy.init?.()).catch((error) => this.logger?.error(`Failed to initialize strategy: ${error.message}`))));
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
static async create(config) {
|
|
201
|
+
const auth = new SoapAuth(config);
|
|
202
|
+
const logger = config.logger;
|
|
203
|
+
const sessionHandler = config.session
|
|
204
|
+
? new session_handler_1.SessionHandler(config.session, logger)
|
|
205
|
+
: undefined;
|
|
206
|
+
if (config.http) {
|
|
207
|
+
const sharedJwt = config.http.jwt
|
|
208
|
+
? new jwt_strategy_1.JwtStrategy(config.http.jwt, logger)
|
|
209
|
+
: undefined;
|
|
210
|
+
if (config.http.local) {
|
|
211
|
+
auth.addStrategy(new local_strategy_1.LocalStrategy(config.http.local, sessionHandler, sharedJwt, logger), "local", "http");
|
|
212
|
+
}
|
|
213
|
+
if (config.http.basic) {
|
|
214
|
+
auth.addStrategy(new basic_strategy_1.BasicStrategy(config.http.basic, sessionHandler, sharedJwt, logger), "basic", "http");
|
|
215
|
+
}
|
|
216
|
+
if (config.http.apiKey) {
|
|
217
|
+
auth.addStrategy(new api_key_strategy_1.ApiKeyStrategy(config.http.apiKey, logger), "api-key", "http");
|
|
218
|
+
}
|
|
219
|
+
if (sharedJwt) {
|
|
220
|
+
auth.addStrategy(sharedJwt, "jwt", "http");
|
|
221
|
+
}
|
|
222
|
+
if (config.http.custom) {
|
|
223
|
+
for (const [name, strategy] of Object.entries(config.http.custom)) {
|
|
224
|
+
auth.addStrategy(strategy, name, "http");
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
if (config.socket) {
|
|
229
|
+
if (config.socket.jwt) {
|
|
230
|
+
auth.addStrategy(new jwt_strategy_1.JwtStrategy(config.socket.jwt, logger), "jwt", "socket");
|
|
231
|
+
}
|
|
232
|
+
if (config.socket.apiKey) {
|
|
233
|
+
auth.addStrategy(new api_key_strategy_1.ApiKeyStrategy(config.socket.apiKey, logger), "api-key", "socket");
|
|
234
|
+
}
|
|
235
|
+
if (config.socket.custom) {
|
|
236
|
+
for (const [name, strategy] of Object.entries(config.socket.custom)) {
|
|
237
|
+
auth.addStrategy(strategy, name, "socket");
|
|
238
|
+
}
|
|
239
|
+
}
|
|
115
240
|
}
|
|
241
|
+
await auth.init();
|
|
242
|
+
return auth;
|
|
116
243
|
}
|
|
117
244
|
}
|
|
118
245
|
exports.SoapAuth = SoapAuth;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BaseAuthStrategyConfig } from "../../../src/types";
|
|
2
2
|
import { SessionHandler } from "../../../src/session/session-handler";
|
|
3
3
|
import * as Soap from "@soapjs/soap";
|
|
4
4
|
import { BaseAuthStrategy } from "../base-auth.strategy";
|
|
@@ -9,6 +9,7 @@ export interface MockUser {
|
|
|
9
9
|
export interface MockContext {
|
|
10
10
|
}
|
|
11
11
|
export declare class TestBaseAuthStrategy extends BaseAuthStrategy<MockContext, MockUser> {
|
|
12
|
+
readonly name = "test";
|
|
12
13
|
constructor(config: BaseAuthStrategyConfig<MockContext, MockUser>, session?: SessionHandler, logger?: Soap.Logger);
|
|
13
|
-
authenticate(context?: MockContext): Promise<AuthResult<MockUser
|
|
14
|
+
authenticate(context?: MockContext): Promise<Soap.AuthResult<MockUser> | null>;
|
|
14
15
|
}
|
|
@@ -4,6 +4,7 @@ exports.TestBaseAuthStrategy = void 0;
|
|
|
4
4
|
const session_errors_1 = require("../../../src/session/session.errors");
|
|
5
5
|
const base_auth_strategy_1 = require("../base-auth.strategy");
|
|
6
6
|
class TestBaseAuthStrategy extends base_auth_strategy_1.BaseAuthStrategy {
|
|
7
|
+
name = "test";
|
|
7
8
|
constructor(config, session, logger) {
|
|
8
9
|
super(config, session, logger);
|
|
9
10
|
}
|
|
@@ -4,6 +4,7 @@ import { JwtStrategy } from "../../../src/strategies/jwt/jwt.strategy";
|
|
|
4
4
|
import * as Soap from "@soapjs/soap";
|
|
5
5
|
import { CredentialAuthStrategy } from "../credential-auth.strategy";
|
|
6
6
|
export declare class TestCredentialAuthStrategy extends CredentialAuthStrategy<any, any> {
|
|
7
|
+
readonly name = "test-credential";
|
|
7
8
|
constructor(config: CredentialAuthStrategyConfig<any, any>, session?: SessionHandler, jwt?: JwtStrategy<any, any>, logger?: Soap.Logger);
|
|
8
9
|
protected verifyCredentials(identifier: string, password: string): Promise<boolean>;
|
|
9
10
|
protected extractCredentials(context: any): Promise<{
|