@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.
Files changed (64) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +223 -0
  3. package/build/errors.d.ts +53 -0
  4. package/build/errors.js +117 -0
  5. package/build/factories/auth-strategy.factory.d.ts +9 -0
  6. package/build/factories/auth-strategy.factory.js +16 -0
  7. package/build/factories/http-auth-strategy.factory.d.ts +5 -0
  8. package/build/factories/http-auth-strategy.factory.js +42 -0
  9. package/build/factories/socket-auth-strategy.factory.d.ts +5 -0
  10. package/build/factories/socket-auth-strategy.factory.js +27 -0
  11. package/build/index.d.ts +28 -0
  12. package/build/index.js +44 -0
  13. package/build/session/file.session-store.d.ts +10 -0
  14. package/build/session/file.session-store.js +59 -0
  15. package/build/session/memory.session-store.d.ts +7 -0
  16. package/build/session/memory.session-store.js +19 -0
  17. package/build/session/session-handler.d.ts +17 -0
  18. package/build/session/session-handler.js +145 -0
  19. package/build/soap-auth.d.ts +16 -0
  20. package/build/soap-auth.js +75 -0
  21. package/build/strategies/api-key/api-key.errors.d.ts +9 -0
  22. package/build/strategies/api-key/api-key.errors.js +24 -0
  23. package/build/strategies/api-key/api-key.strategy.d.ts +14 -0
  24. package/build/strategies/api-key/api-key.strategy.js +95 -0
  25. package/build/strategies/api-key/api-key.types.d.ts +13 -0
  26. package/build/strategies/api-key/api-key.types.js +2 -0
  27. package/build/strategies/base-auth.strategy.d.ts +17 -0
  28. package/build/strategies/base-auth.strategy.js +69 -0
  29. package/build/strategies/basic/basic.strategy.d.ts +25 -0
  30. package/build/strategies/basic/basic.strategy.js +53 -0
  31. package/build/strategies/basic/basic.types.d.ts +10 -0
  32. package/build/strategies/basic/basic.types.js +2 -0
  33. package/build/strategies/credential-based-auth.strategy.d.ts +30 -0
  34. package/build/strategies/credential-based-auth.strategy.js +205 -0
  35. package/build/strategies/jwt/jwt.strategy.d.ts +10 -0
  36. package/build/strategies/jwt/jwt.strategy.js +69 -0
  37. package/build/strategies/jwt/jwt.tools.d.ts +3 -0
  38. package/build/strategies/jwt/jwt.tools.js +79 -0
  39. package/build/strategies/jwt/jwt.types.d.ts +33 -0
  40. package/build/strategies/jwt/jwt.types.js +2 -0
  41. package/build/strategies/local/local.strategy.d.ts +25 -0
  42. package/build/strategies/local/local.strategy.js +76 -0
  43. package/build/strategies/local/local.types.d.ts +3 -0
  44. package/build/strategies/local/local.types.js +2 -0
  45. package/build/strategies/oauth2/oauth2.strategy.d.ts +35 -0
  46. package/build/strategies/oauth2/oauth2.strategy.js +259 -0
  47. package/build/strategies/oauth2/oauth2.tools.d.ts +4 -0
  48. package/build/strategies/oauth2/oauth2.tools.js +22 -0
  49. package/build/strategies/oauth2/oauth2.types.d.ts +29 -0
  50. package/build/strategies/oauth2/oauth2.types.js +2 -0
  51. package/build/strategies/token-based-auth.strategy.d.ts +25 -0
  52. package/build/strategies/token-based-auth.strategy.js +124 -0
  53. package/build/tools/session.tools.d.ts +6 -0
  54. package/build/tools/session.tools.js +15 -0
  55. package/build/tools/token.tools.d.ts +7 -0
  56. package/build/tools/token.tools.js +32 -0
  57. package/build/tools/tools.d.ts +3 -0
  58. package/build/tools/tools.js +23 -0
  59. package/build/types.d.ts +251 -0
  60. package/build/types.js +2 -0
  61. package/jest.config.unit.json +10 -0
  62. package/ldap.md +62 -0
  63. package/package.json +33 -0
  64. package/saml.md +244 -0
@@ -0,0 +1,7 @@
1
+ import { SessionStore } from "../types";
2
+ export declare class MemorySessionStore implements SessionStore {
3
+ getSession<SessionData>(sessionId: string): Promise<SessionData | null>;
4
+ setSession<SessionData>(sessionId: string, sessionData: SessionData): Promise<void>;
5
+ destroySession(sessionId: string): Promise<void>;
6
+ touchSession<SessionData>(sessionId: string, session: SessionData): Promise<void>;
7
+ }
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MemorySessionStore = void 0;
4
+ const sessions = {};
5
+ class MemorySessionStore {
6
+ async getSession(sessionId) {
7
+ return sessions[sessionId] || null;
8
+ }
9
+ async setSession(sessionId, sessionData) {
10
+ sessions[sessionId] = sessionData;
11
+ }
12
+ async destroySession(sessionId) {
13
+ delete sessions[sessionId];
14
+ }
15
+ async touchSession(sessionId, session) {
16
+ sessions[sessionId] = session;
17
+ }
18
+ }
19
+ exports.MemorySessionStore = MemorySessionStore;
@@ -0,0 +1,17 @@
1
+ import { SessionConfig, SessionData } from "../types";
2
+ export declare class SessionHandler<TContext = unknown, TData = SessionData> {
3
+ private config;
4
+ private store;
5
+ private sessionKey;
6
+ private headerKey;
7
+ constructor(config: SessionConfig);
8
+ setSessionId(context: TContext, sessionId: string): void;
9
+ getSessionId(context: TContext): string | null;
10
+ generateSessionId(): string;
11
+ buildSessionData(data: unknown, context?: TContext): TData;
12
+ get(context: TContext): Promise<TData | null>;
13
+ set(context: TContext, data: TData): Promise<void>;
14
+ touch(context: TContext, data?: Partial<TData>): Promise<void>;
15
+ destroy(context: TContext): Promise<void>;
16
+ isSessionExpired(sessionData: TData): boolean;
17
+ }
@@ -0,0 +1,145 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SessionHandler = void 0;
4
+ const uuid_1 = require("uuid");
5
+ class SessionHandler {
6
+ config;
7
+ store;
8
+ sessionKey;
9
+ headerKey;
10
+ constructor(config) {
11
+ this.config = config;
12
+ if (!config.store) {
13
+ throw new Error("Session store is required.");
14
+ }
15
+ this.store = config.store;
16
+ this.sessionKey = config.sessionKey || "SESSIONID";
17
+ this.headerKey = config.sessionHeader || "x-session-id";
18
+ }
19
+ setSessionId(context, sessionId) {
20
+ try {
21
+ if (this.config.embedSessionId) {
22
+ this.config.embedSessionId(context, sessionId);
23
+ return;
24
+ }
25
+ if (context && typeof context === "object") {
26
+ if ("res" in context && context.res) {
27
+ if ("cookie" in context.res) {
28
+ context.res.cookie(this.sessionKey, sessionId, {
29
+ httpOnly: true,
30
+ secure: true,
31
+ sameSite: "strict",
32
+ });
33
+ this.config.logger?.info(`Session ID set in cookie: ${this.sessionKey}`);
34
+ }
35
+ else if ("setHeader" in context.res) {
36
+ context.res.setHeader(this.headerKey, sessionId);
37
+ this.config.logger?.info(`Session ID set in header: ${this.headerKey}`);
38
+ }
39
+ }
40
+ else {
41
+ context.sessionId = sessionId;
42
+ this.config.logger?.info(`Session ID set in context object`);
43
+ }
44
+ }
45
+ }
46
+ catch (error) {
47
+ this.config.logger?.error("Error setting session ID:", error);
48
+ }
49
+ }
50
+ getSessionId(context) {
51
+ try {
52
+ if (this.config.getSessionId) {
53
+ return this.config.getSessionId(context);
54
+ }
55
+ if (context && typeof context === "object") {
56
+ if ("cookies" in context && context.cookies[this.sessionKey]) {
57
+ return context.cookies[this.sessionKey];
58
+ }
59
+ if ("headers" in context && context.headers[this.headerKey]) {
60
+ return context.headers[this.headerKey];
61
+ }
62
+ if ("sessionId" in context) {
63
+ return context.sessionId;
64
+ }
65
+ }
66
+ return null;
67
+ }
68
+ catch (error) {
69
+ this.config.logger?.error("Error retrieving session ID:", error);
70
+ return null;
71
+ }
72
+ }
73
+ generateSessionId() {
74
+ return this.config.generateSessionId?.() || (0, uuid_1.v4)();
75
+ }
76
+ buildSessionData(data, context) {
77
+ return this.config.createSessionData
78
+ ? this.config.createSessionData(data, context)
79
+ : { user: data };
80
+ }
81
+ async get(context) {
82
+ try {
83
+ const sessionId = this.getSessionId(context);
84
+ if (!sessionId)
85
+ return null;
86
+ return await this.store.getSession(sessionId);
87
+ }
88
+ catch (error) {
89
+ this.config.logger?.error("Error retrieving session data:", error);
90
+ return null;
91
+ }
92
+ }
93
+ async set(context, data) {
94
+ try {
95
+ const sessionId = this.getSessionId(context) || this.generateSessionId();
96
+ this.config.embedSessionId?.(context, sessionId);
97
+ await this.store.setSession(sessionId, data);
98
+ this.config.logger?.info(`Session set for ID: ${sessionId}`);
99
+ }
100
+ catch (error) {
101
+ this.config.logger?.error("Error storing session data:", error);
102
+ }
103
+ }
104
+ async touch(context, data) {
105
+ try {
106
+ const sessionId = this.getSessionId(context);
107
+ if (!sessionId) {
108
+ this.config.logger?.warn("No session ID found, unable to touch session.");
109
+ return;
110
+ }
111
+ const currentSession = await this.store.getSession(sessionId);
112
+ if (!currentSession) {
113
+ this.config.logger?.warn(`Session not found for ID: ${sessionId}`);
114
+ return;
115
+ }
116
+ const updatedSession = { ...currentSession, ...data };
117
+ await this.store.touchSession(sessionId, updatedSession);
118
+ this.config.logger?.info(`Session touched for ID: ${sessionId}`);
119
+ }
120
+ catch (error) {
121
+ this.config.logger?.error("Error touching session:", error);
122
+ }
123
+ }
124
+ async destroy(context) {
125
+ try {
126
+ const sessionId = this.getSessionId(context);
127
+ if (sessionId) {
128
+ await this.store.destroySession(sessionId);
129
+ this.config.logger?.info(`Session destroyed for ID: ${sessionId}`);
130
+ }
131
+ }
132
+ catch (error) {
133
+ this.config.logger?.error("Error destroying session:", error);
134
+ }
135
+ }
136
+ isSessionExpired(sessionData) {
137
+ if (!sessionData || !sessionData.createdAt) {
138
+ return false;
139
+ }
140
+ const now = Date.now();
141
+ const sessionExpiryMs = this.config.cookie?.maxAge || 3600000;
142
+ return now - sessionData.createdAt > sessionExpiryMs;
143
+ }
144
+ }
145
+ exports.SessionHandler = SessionHandler;
@@ -0,0 +1,16 @@
1
+ import { AuthResult, AuthStrategy, SoapAuthConfig } from "./types";
2
+ export declare class SoapAuth {
3
+ private requiredStrategyMethods;
4
+ private httpStrategies;
5
+ private socketStrategies;
6
+ constructor(config: SoapAuthConfig);
7
+ private isAuthStrategy;
8
+ addStrategy(strategiesMap: Map<string, AuthStrategy>, type: string, strategyInstance: AuthStrategy | undefined): void;
9
+ removeStrategy(type: string): boolean;
10
+ hasStrategy(type: string): boolean;
11
+ getStrategy(strategyName: string, layer?: "http" | "socket"): AuthStrategy | undefined;
12
+ listStrategies(): string[];
13
+ init(sequential?: boolean): Promise<void>;
14
+ authenticate(strategyName: string, context: any): Promise<AuthResult<any>>;
15
+ logout(strategyName: string, context: any): Promise<void>;
16
+ }
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SoapAuth = void 0;
4
+ const http_auth_strategy_factory_1 = require("./factories/http-auth-strategy.factory");
5
+ const socket_auth_strategy_factory_1 = require("./factories/socket-auth-strategy.factory");
6
+ class SoapAuth {
7
+ requiredStrategyMethods = ["authenticate", "init"];
8
+ httpStrategies = new Map();
9
+ socketStrategies = new Map();
10
+ constructor(config) {
11
+ const httpFactory = new http_auth_strategy_factory_1.HttpAuthStrategyFactory(config.logger);
12
+ this.httpStrategies = httpFactory.createStrategies(config);
13
+ const socketFactory = new socket_auth_strategy_factory_1.SocketAuthStrategyFactory(config.logger);
14
+ this.socketStrategies = socketFactory.createStrategies(config);
15
+ }
16
+ isAuthStrategy(strategy) {
17
+ const isValidStrategy = this.requiredStrategyMethods.every((method) => typeof strategy[method] === "function");
18
+ return isValidStrategy;
19
+ }
20
+ addStrategy(strategiesMap, type, strategyInstance) {
21
+ if (this.isAuthStrategy(strategyInstance)) {
22
+ strategiesMap.set(type, strategyInstance);
23
+ }
24
+ }
25
+ removeStrategy(type) {
26
+ if (this.httpStrategies.has(type)) {
27
+ this.httpStrategies.delete(type);
28
+ return true;
29
+ }
30
+ if (this.socketStrategies.has(type)) {
31
+ this.socketStrategies.delete(type);
32
+ return true;
33
+ }
34
+ return false;
35
+ }
36
+ hasStrategy(type) {
37
+ return this.httpStrategies.has(type) || this.socketStrategies.has(type);
38
+ }
39
+ getStrategy(strategyName, layer = "http") {
40
+ return layer === "http"
41
+ ? this.httpStrategies.get(strategyName)
42
+ : this.socketStrategies.get(strategyName);
43
+ }
44
+ listStrategies() {
45
+ return [...this.httpStrategies.keys(), ...this.socketStrategies.keys()];
46
+ }
47
+ async init(sequential = false) {
48
+ const strategies = [
49
+ ...this.httpStrategies.values(),
50
+ ...this.socketStrategies.values(),
51
+ ];
52
+ if (sequential) {
53
+ for (const strategy of strategies) {
54
+ await strategy.init();
55
+ }
56
+ }
57
+ else {
58
+ await Promise.all(strategies.map((strategy) => strategy.init()));
59
+ }
60
+ }
61
+ async authenticate(strategyName, context) {
62
+ const strategy = this.getStrategy(strategyName);
63
+ if (!strategy) {
64
+ throw new Error(`Authentication strategy "${strategyName}" not found.`);
65
+ }
66
+ return strategy.authenticate(context);
67
+ }
68
+ async logout(strategyName, context) {
69
+ const strategy = this.getStrategy(strategyName);
70
+ if (strategy?.logout) {
71
+ await strategy.logout(context);
72
+ }
73
+ }
74
+ }
75
+ exports.SoapAuth = SoapAuth;
@@ -0,0 +1,9 @@
1
+ export declare class MissingApiKeyError extends Error {
2
+ constructor();
3
+ }
4
+ export declare class InvalidApiKeyError extends Error {
5
+ constructor();
6
+ }
7
+ export declare class ExpiredApiKeyError extends Error {
8
+ constructor();
9
+ }
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ExpiredApiKeyError = exports.InvalidApiKeyError = exports.MissingApiKeyError = void 0;
4
+ class MissingApiKeyError extends Error {
5
+ constructor() {
6
+ super("API Key is missing in the request context.");
7
+ this.name = "MissingApiKeyError";
8
+ }
9
+ }
10
+ exports.MissingApiKeyError = MissingApiKeyError;
11
+ class InvalidApiKeyError extends Error {
12
+ constructor() {
13
+ super("Invalid API Key provided.");
14
+ this.name = "InvalidApiKeyError";
15
+ }
16
+ }
17
+ exports.InvalidApiKeyError = InvalidApiKeyError;
18
+ class ExpiredApiKeyError extends Error {
19
+ constructor() {
20
+ super("Expired API Key.");
21
+ this.name = "InvalidApiKeyError";
22
+ }
23
+ }
24
+ exports.ExpiredApiKeyError = ExpiredApiKeyError;
@@ -0,0 +1,14 @@
1
+ import * as Soap from "@soapjs/soap";
2
+ import { AuthResult, AuthStrategy } from "../../types";
3
+ import { ApiKeyStrategyConfig } from "./api-key.types";
4
+ export declare class ApiKeyStrategy<TContext = unknown, TUser = unknown> implements AuthStrategy<TContext, TUser> {
5
+ private config;
6
+ private logger;
7
+ constructor(config: ApiKeyStrategyConfig<TContext, TUser>, logger: Soap.Logger);
8
+ init(): Promise<void>;
9
+ authenticate(context?: TContext): Promise<AuthResult<TUser>>;
10
+ authorize(user: TUser, action: string, resource?: string): Promise<boolean>;
11
+ revoke(apiKey: string): Promise<void>;
12
+ private trackApiKeyUsage;
13
+ private incrementRequestCount;
14
+ }
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ApiKeyStrategy = void 0;
4
+ const api_key_errors_1 = require("./api-key.errors");
5
+ const errors_1 = require("../../errors");
6
+ class ApiKeyStrategy {
7
+ config;
8
+ logger;
9
+ constructor(config, logger) {
10
+ this.config = config;
11
+ this.logger = logger;
12
+ if (!this.config.extractApiKey || !this.config.retrieveUserByApiKey) {
13
+ throw new Error("ApiKeyStrategy requires extractApiKey and retrieveUserByApiKey functions.");
14
+ }
15
+ }
16
+ async init() {
17
+ return Promise.resolve();
18
+ }
19
+ async authenticate(context) {
20
+ try {
21
+ const apiKey = this.config.extractApiKey(context);
22
+ if (!apiKey) {
23
+ throw new api_key_errors_1.MissingApiKeyError();
24
+ }
25
+ if (await this.config.isAccountLocked?.(apiKey, context)) {
26
+ throw new errors_1.AccountLockedError();
27
+ }
28
+ if (await this.config.isApiKeyExpired?.(apiKey)) {
29
+ throw new api_key_errors_1.ExpiredApiKeyError();
30
+ }
31
+ if (this.config.checkRateLimit &&
32
+ (await this.config.checkRateLimit(apiKey))) {
33
+ throw new errors_1.RateLimitExceededError();
34
+ }
35
+ const user = await this.config.retrieveUserByApiKey(apiKey);
36
+ if (!user) {
37
+ await this.config.logFailedAttempt?.(apiKey, context);
38
+ throw new api_key_errors_1.InvalidApiKeyError();
39
+ }
40
+ if (this.config.authorizeByRoles) {
41
+ const hasAccess = await this.config.authorizeByRoles(user, this.config.roles || []);
42
+ if (!hasAccess) {
43
+ throw new errors_1.UnauthorizedRoleError();
44
+ }
45
+ }
46
+ await this.trackApiKeyUsage(apiKey);
47
+ await this.incrementRequestCount(apiKey);
48
+ await this.config.onSuccess?.({ user, context });
49
+ return { user };
50
+ }
51
+ catch (error) {
52
+ this.logger.error("API Key authentication error:", error);
53
+ try {
54
+ await this.config.onFailure?.({ error, context });
55
+ }
56
+ catch (callbackError) {
57
+ this.logger.error("onFailure callback error during authentication:", callbackError);
58
+ }
59
+ throw error;
60
+ }
61
+ }
62
+ async authorize(user, action, resource) {
63
+ if (this.config.authorize) {
64
+ return this.config.authorize(user, action, resource);
65
+ }
66
+ throw new Error("Authorization logic is not implemented.");
67
+ }
68
+ async revoke(apiKey) {
69
+ if (this.config.revokeApiKey) {
70
+ await this.config.revokeApiKey(apiKey);
71
+ }
72
+ else {
73
+ throw new Error("Revoke API key functionality is not configured.");
74
+ }
75
+ }
76
+ async trackApiKeyUsage(apiKey) {
77
+ try {
78
+ if (this.config.trackApiKeyUsage) {
79
+ await this.config.trackApiKeyUsage(apiKey);
80
+ }
81
+ }
82
+ catch (error) {
83
+ this.logger.warn("Failed to track API key usage:", error);
84
+ }
85
+ }
86
+ async incrementRequestCount(apiKey) {
87
+ try {
88
+ await this.config.incrementRequestCount?.(apiKey);
89
+ }
90
+ catch (error) {
91
+ this.logger.warn("Failed to increment request count:", error);
92
+ }
93
+ }
94
+ }
95
+ exports.ApiKeyStrategy = ApiKeyStrategy;
@@ -0,0 +1,13 @@
1
+ import { RateLimitConfig, RoleAuthorizationConfig, AuthFailureContext, AuthSuccessContext, AccountLockConfig } from "../../types";
2
+ export interface ApiKeyStrategyConfig<TContext = unknown, TUser = unknown> extends ApiKeyTrackingConfig, RateLimitConfig, RoleAuthorizationConfig<TUser>, AccountLockConfig<TContext> {
3
+ extractApiKey: (context: TContext) => string | null;
4
+ retrieveUserByApiKey: (apiKey: string) => Promise<TUser | null>;
5
+ authorize?: (user: TUser, action: string, resource?: string) => Promise<boolean>;
6
+ revokeApiKey?: (apiKey: string) => Promise<void>;
7
+ onSuccess?: (context: AuthSuccessContext<TUser, TContext>) => Promise<void> | void;
8
+ onFailure?: (context: AuthFailureContext<TContext>) => Promise<void> | void;
9
+ }
10
+ export interface ApiKeyTrackingConfig {
11
+ trackApiKeyUsage?: (apiKey: string) => Promise<void>;
12
+ isApiKeyExpired?: (apiKey: string) => Promise<boolean>;
13
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,17 @@
1
+ import * as Soap from "@soapjs/soap";
2
+ import { AuthResult, AuthStrategy, BaseAuthStrategyConfig } from "../types";
3
+ import { SessionHandler } from "../session/session-handler";
4
+ export declare abstract class BaseAuthStrategy<TContext = unknown, TUser = unknown> implements AuthStrategy<TContext, TUser> {
5
+ protected config: BaseAuthStrategyConfig<TContext, TUser>;
6
+ protected session?: SessionHandler;
7
+ protected logger?: Soap.Logger;
8
+ abstract authenticate(context?: TContext): Promise<AuthResult<TUser>>;
9
+ protected abstract retrieveUser(context: TContext): Promise<TUser | null>;
10
+ abstract logout(context: TContext): Promise<void>;
11
+ constructor(config: BaseAuthStrategyConfig<TContext, TUser>, session?: SessionHandler, logger?: Soap.Logger);
12
+ init(): Promise<void>;
13
+ protected isAccountLocked(account: any, ...args: unknown[]): Promise<boolean>;
14
+ protected isAuthorized(user: TUser): Promise<boolean>;
15
+ protected checkRateLimit(data: unknown): Promise<void>;
16
+ protected checkMfa(user: TUser, context: TContext): Promise<void>;
17
+ }
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BaseAuthStrategy = void 0;
4
+ const errors_1 = require("../errors");
5
+ class BaseAuthStrategy {
6
+ config;
7
+ session;
8
+ logger;
9
+ constructor(config, session, logger) {
10
+ this.config = config;
11
+ this.session = session;
12
+ this.logger = logger;
13
+ }
14
+ async init() {
15
+ return Promise.resolve();
16
+ }
17
+ async isAccountLocked(account, ...args) {
18
+ if (await this.config.isAccountLocked?.(account, ...args)) {
19
+ throw new errors_1.AccountLockedError();
20
+ }
21
+ return false;
22
+ }
23
+ async isAuthorized(user) {
24
+ if (this.config.authorizeByRoles && this.config.roles) {
25
+ const hasAccess = await this.config.authorizeByRoles(user, this.config.roles);
26
+ if (!hasAccess) {
27
+ throw new errors_1.UnauthorizedRoleError();
28
+ }
29
+ }
30
+ return true;
31
+ }
32
+ async checkRateLimit(data) {
33
+ if (this.config.checkRateLimit &&
34
+ (await this.config.checkRateLimit(data))) {
35
+ throw new errors_1.RateLimitExceededError();
36
+ }
37
+ }
38
+ async checkMfa(user, context) {
39
+ try {
40
+ if (this.config.mfa?.isMfaRequired?.(user)) {
41
+ const mfaCode = this.config.mfa?.extractMfaCode?.(context);
42
+ if (!mfaCode) {
43
+ await this.config.mfa.sendMfaCode?.(user, context);
44
+ throw new Error("2FA required. A verification code has been sent.");
45
+ }
46
+ const attempts = (await this.config.mfa.getMfaAttempts?.(user)) || 0;
47
+ if (this.config.mfa.maxMfaAttempts &&
48
+ attempts >= this.config.mfa.maxMfaAttempts) {
49
+ this.logger?.warn(`User ${user} exceeded maximum MFA attempts.`);
50
+ await this.config.mfa.lockMfaOnFailure?.(user);
51
+ throw new Error("Your account has been temporarily locked due to too many failed 2FA attempts.");
52
+ }
53
+ const isValidMfa = await this.config.mfa.validateMfaCode?.(user, mfaCode);
54
+ if (!isValidMfa) {
55
+ this.logger?.warn(`Invalid MFA code attempt for user: ${user}`);
56
+ await this.config.mfa.incrementMfaAttempts?.(user);
57
+ throw new Error("Invalid 2FA code provided.");
58
+ }
59
+ await this.config.mfa.resetMfaAttempts?.(user);
60
+ this.logger?.info(`2FA successfully validated for user: ${user}`);
61
+ }
62
+ }
63
+ catch (error) {
64
+ this.logger?.error(`2FA validation error for user: ${user}`, error);
65
+ throw error;
66
+ }
67
+ }
68
+ }
69
+ exports.BaseAuthStrategy = BaseAuthStrategy;
@@ -0,0 +1,25 @@
1
+ import * as Soap from "@soapjs/soap";
2
+ import { CredentialBasedAuthStrategy } from "../credential-based-auth.strategy";
3
+ import { BasicContext, BasicStrategyConfig } from "./basic.types";
4
+ import { SessionHandler } from "../../session/session-handler";
5
+ export declare class BasicStrategy<TContext extends BasicContext = BasicContext, TUser = unknown> extends CredentialBasedAuthStrategy<TContext, TUser> {
6
+ protected config: BasicStrategyConfig<TContext, TUser>;
7
+ protected session?: SessionHandler;
8
+ protected logger?: Soap.Logger;
9
+ constructor(config: BasicStrategyConfig<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,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BasicStrategy = void 0;
4
+ const credential_based_auth_strategy_1 = require("../credential-based-auth.strategy");
5
+ const errors_1 = require("../../errors");
6
+ class BasicStrategy 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
+ const authHeader = context?.headers?.authorization || context?.headers?.["x-custom-auth"];
18
+ if (!authHeader) {
19
+ throw new errors_1.MissingCredentialsError();
20
+ }
21
+ const parts = authHeader.split(" ");
22
+ if (parts.length !== 2 || parts[0] !== "Basic") {
23
+ throw new errors_1.InvalidCredentialsError();
24
+ }
25
+ try {
26
+ const decoded = Buffer.from(parts[1], "base64").toString("utf-8");
27
+ const [username, password] = decoded.split(":");
28
+ if (!username || !password) {
29
+ throw new errors_1.InvalidCredentialsError();
30
+ }
31
+ return { identifier: username, password };
32
+ }
33
+ catch {
34
+ throw new errors_1.InvalidCredentialsError();
35
+ }
36
+ }
37
+ async verifyCredentials(credentials) {
38
+ return this.config.login.verifyUserCredentials(credentials.identifier, credentials.password);
39
+ }
40
+ async retrieveUser(credentials) {
41
+ return this.config.login.retrieveUserData(credentials.identifier);
42
+ }
43
+ async requestPasswordReset(email) {
44
+ await super.requestPasswordReset(email);
45
+ }
46
+ async resetPassword(email, token, newPassword) {
47
+ await super.resetPassword(email, token, newPassword);
48
+ }
49
+ async changePassword(email, oldPassword, newPassword) {
50
+ await super.changePassword(email, oldPassword, newPassword);
51
+ }
52
+ }
53
+ exports.BasicStrategy = BasicStrategy;
@@ -0,0 +1,10 @@
1
+ import { CredentialBasedAuthStrategyConfig } from "../../types";
2
+ export interface BasicStrategyConfig<TContext = unknown, TUser = unknown> extends CredentialBasedAuthStrategyConfig<TContext, TUser> {
3
+ }
4
+ export type BasicContext = {
5
+ headers: {
6
+ authorization: string;
7
+ [key: string]: string;
8
+ };
9
+ [key: string]: unknown;
10
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,30 @@
1
+ import * as Soap from "@soapjs/soap";
2
+ import { AuthResult, CredentialBasedAuthStrategyConfig } from "../types";
3
+ import { BaseAuthStrategy } from "./base-auth.strategy";
4
+ import { SessionHandler } from "../session/session-handler";
5
+ export declare abstract class CredentialBasedAuthStrategy<TContext = unknown, TUser = unknown> extends BaseAuthStrategy<TContext, TUser> {
6
+ protected config: CredentialBasedAuthStrategyConfig<TContext, TUser>;
7
+ protected session?: SessionHandler;
8
+ protected logger?: Soap.Logger;
9
+ protected abstract extractCredentials(context?: TContext): Promise<{
10
+ identifier: string;
11
+ password: string;
12
+ }>;
13
+ protected abstract verifyCredentials(credentials: any): Promise<boolean>;
14
+ protected abstract retrieveUser(credentials: any): Promise<TUser | null>;
15
+ constructor(config: CredentialBasedAuthStrategyConfig<TContext, TUser>, session?: SessionHandler, logger?: Soap.Logger);
16
+ authenticate(context?: TContext): Promise<AuthResult<TUser>>;
17
+ protected handleSession(user: TUser, context?: TContext): Promise<void>;
18
+ logout(context?: TContext): Promise<void>;
19
+ requestPasswordReset(identifier: string, email?: string): Promise<void>;
20
+ resetPassword(identifier: string, token: string, newPassword: string): Promise<void>;
21
+ changePassword(identifier: string, oldPassword: string, newPassword: string): Promise<void>;
22
+ protected auditLoginAttempt(identifier: string, success: boolean, context?: TContext): Promise<void>;
23
+ protected auditPasswordChange(identifier: string, context?: TContext): Promise<void>;
24
+ protected validatePasswordPolicy(password: string): boolean;
25
+ protected checkFailedAttempts(identifier: string): Promise<void>;
26
+ protected isAccountLocked(account: any, ...args: unknown[]): Promise<boolean>;
27
+ protected incrementFailedAttempts(account: any): Promise<void>;
28
+ protected notifyAccountLocked(identifier: string): Promise<void>;
29
+ protected checkPasswordExpiry(identifier: string): Promise<void>;
30
+ }