strray-ai 1.7.0 → 1.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.opencode/enforcer-config.json +2 -2
- package/.opencode/package.json +1 -1
- package/.opencode/strray/codex.json +1 -1
- package/.opencode/strray/config.json +1 -1
- package/.opencode/strray/features.json +1 -1
- package/dist/core/boot-orchestrator.js +1 -1
- package/dist/core/boot-orchestrator.js.map +1 -1
- package/dist/core/features-config.js +1 -1
- package/dist/core/features-config.js.map +1 -1
- package/dist/delegation/task-skill-router.d.ts.map +1 -1
- package/dist/delegation/task-skill-router.js +110 -21
- package/dist/delegation/task-skill-router.js.map +1 -1
- package/dist/mcps/architect-tools.server.js +1 -1
- package/dist/mcps/architect-tools.server.js.map +1 -1
- package/dist/mcps/auto-format.server.js +1 -1
- package/dist/mcps/auto-format.server.js.map +1 -1
- package/dist/mcps/boot-orchestrator.server.js +1 -1
- package/dist/mcps/boot-orchestrator.server.js.map +1 -1
- package/dist/mcps/enforcer-tools.server.js +1 -1
- package/dist/mcps/enforcer-tools.server.js.map +1 -1
- package/dist/mcps/framework-compliance-audit.server.js +1 -1
- package/dist/mcps/framework-compliance-audit.server.js.map +1 -1
- package/dist/mcps/framework-help.server.js +1 -1
- package/dist/mcps/framework-help.server.js.map +1 -1
- package/dist/mcps/knowledge-skills/api-design.server.js +1 -1
- package/dist/mcps/knowledge-skills/api-design.server.js.map +1 -1
- package/dist/mcps/knowledge-skills/architecture-patterns.server.js +1 -1
- package/dist/mcps/knowledge-skills/architecture-patterns.server.js.map +1 -1
- package/dist/mcps/knowledge-skills/bug-triage-specialist.server.js +1 -1
- package/dist/mcps/knowledge-skills/bug-triage-specialist.server.js.map +1 -1
- package/dist/mcps/knowledge-skills/code-analyzer.server.js +1 -1
- package/dist/mcps/knowledge-skills/code-analyzer.server.js.map +1 -1
- package/dist/mcps/knowledge-skills/code-review.server.js +1 -1
- package/dist/mcps/knowledge-skills/code-review.server.js.map +1 -1
- package/dist/mcps/knowledge-skills/content-creator.server.js +1 -1
- package/dist/mcps/knowledge-skills/content-creator.server.js.map +1 -1
- package/dist/mcps/knowledge-skills/database-design.server.js +1 -1
- package/dist/mcps/knowledge-skills/database-design.server.js.map +1 -1
- package/dist/mcps/knowledge-skills/devops-deployment.server.js +1 -1
- package/dist/mcps/knowledge-skills/devops-deployment.server.js.map +1 -1
- package/dist/mcps/knowledge-skills/git-workflow.server.js +1 -1
- package/dist/mcps/knowledge-skills/git-workflow.server.js.map +1 -1
- package/dist/mcps/knowledge-skills/growth-strategist.server.js +1 -1
- package/dist/mcps/knowledge-skills/growth-strategist.server.js.map +1 -1
- package/dist/mcps/knowledge-skills/log-monitor.server.js +1 -1
- package/dist/mcps/knowledge-skills/log-monitor.server.js.map +1 -1
- package/dist/mcps/knowledge-skills/mobile-development.server.js +1 -1
- package/dist/mcps/knowledge-skills/mobile-development.server.js.map +1 -1
- package/dist/mcps/knowledge-skills/multimodal-looker.server.js +1 -1
- package/dist/mcps/knowledge-skills/multimodal-looker.server.js.map +1 -1
- package/dist/mcps/knowledge-skills/performance-optimization.server.js +1 -1
- package/dist/mcps/knowledge-skills/performance-optimization.server.js.map +1 -1
- package/dist/mcps/knowledge-skills/project-analysis.server.js +1 -1
- package/dist/mcps/knowledge-skills/project-analysis.server.js.map +1 -1
- package/dist/mcps/knowledge-skills/refactoring-strategies.server.js +1 -1
- package/dist/mcps/knowledge-skills/refactoring-strategies.server.js.map +1 -1
- package/dist/mcps/knowledge-skills/security-audit.server.js +1 -1
- package/dist/mcps/knowledge-skills/security-audit.server.js.map +1 -1
- package/dist/mcps/knowledge-skills/seo-consultant.server.js +1 -1
- package/dist/mcps/knowledge-skills/seo-consultant.server.js.map +1 -1
- package/dist/mcps/knowledge-skills/session-management.server.js +1 -1
- package/dist/mcps/knowledge-skills/session-management.server.js.map +1 -1
- package/dist/mcps/knowledge-skills/skill-invocation.server.js +1 -1
- package/dist/mcps/knowledge-skills/skill-invocation.server.js.map +1 -1
- package/dist/mcps/knowledge-skills/strategist.server.js +1 -1
- package/dist/mcps/knowledge-skills/strategist.server.js.map +1 -1
- package/dist/mcps/knowledge-skills/tech-writer.server.js +2 -2
- package/dist/mcps/knowledge-skills/tech-writer.server.js.map +1 -1
- package/dist/mcps/knowledge-skills/testing-best-practices.server.js +1 -1
- package/dist/mcps/knowledge-skills/testing-best-practices.server.js.map +1 -1
- package/dist/mcps/knowledge-skills/testing-strategy.server.js +1 -1
- package/dist/mcps/knowledge-skills/testing-strategy.server.js.map +1 -1
- package/dist/mcps/knowledge-skills/ui-ux-design.server.js +1 -1
- package/dist/mcps/knowledge-skills/ui-ux-design.server.js.map +1 -1
- package/dist/mcps/lint.server.js +1 -1
- package/dist/mcps/lint.server.js.map +1 -1
- package/dist/mcps/mcp-client.js +1 -1
- package/dist/mcps/mcp-client.js.map +1 -1
- package/dist/mcps/model-health-check.server.js +1 -1
- package/dist/mcps/model-health-check.server.js.map +1 -1
- package/dist/mcps/orchestrator.server.js +1 -1
- package/dist/mcps/orchestrator.server.js.map +1 -1
- package/dist/mcps/performance-analysis.server.js +1 -1
- package/dist/mcps/performance-analysis.server.js.map +1 -1
- package/dist/mcps/processor-pipeline.server.js +1 -1
- package/dist/mcps/processor-pipeline.server.js.map +1 -1
- package/dist/mcps/researcher.server.js +1 -1
- package/dist/mcps/researcher.server.js.map +1 -1
- package/dist/mcps/security-scan.server.js +1 -1
- package/dist/mcps/security-scan.server.js.map +1 -1
- package/dist/mcps/state-manager.server.js +1 -1
- package/dist/mcps/state-manager.server.js.map +1 -1
- package/dist/orchestrator/universal-registry-bridge.js +1 -1
- package/dist/orchestrator/universal-registry-bridge.js.map +1 -1
- package/dist/plugins/marketplace/marketplace-service.d.ts +108 -0
- package/dist/plugins/marketplace/marketplace-service.d.ts.map +1 -0
- package/dist/plugins/marketplace/marketplace-service.js +620 -0
- package/dist/plugins/marketplace/marketplace-service.js.map +1 -0
- package/dist/plugins/marketplace/marketplace-types.d.ts +468 -0
- package/dist/plugins/marketplace/marketplace-types.d.ts.map +1 -0
- package/dist/plugins/marketplace/marketplace-types.js +11 -0
- package/dist/plugins/marketplace/marketplace-types.js.map +1 -0
- package/dist/plugins/plugin-system.d.ts +152 -0
- package/dist/plugins/plugin-system.d.ts.map +1 -0
- package/dist/plugins/plugin-system.js +419 -0
- package/dist/plugins/plugin-system.js.map +1 -0
- package/dist/plugins/stringray-codex-injection.d.ts +21 -0
- package/dist/plugins/stringray-codex-injection.d.ts.map +1 -0
- package/dist/plugins/stringray-codex-injection.js +169 -0
- package/dist/plugins/stringray-codex-injection.js.map +1 -0
- package/dist/security/secure-authentication-system.d.ts +107 -6
- package/dist/security/secure-authentication-system.d.ts.map +1 -1
- package/dist/security/secure-authentication-system.js +426 -11
- package/dist/security/secure-authentication-system.js.map +1 -1
- package/dist/security/security-hardening-system.d.ts +11 -2
- package/dist/security/security-hardening-system.d.ts.map +1 -1
- package/dist/security/security-hardening-system.js +62 -4
- package/dist/security/security-hardening-system.js.map +1 -1
- package/package.json +4 -2
- package/scripts/node/universal-version-manager.js +3 -3
|
@@ -24,7 +24,7 @@ export interface User {
|
|
|
24
24
|
export interface AuthenticationRequest {
|
|
25
25
|
username?: string;
|
|
26
26
|
email?: string;
|
|
27
|
-
password
|
|
27
|
+
password?: string;
|
|
28
28
|
method: "password" | "oauth2" | "saml" | "api_key";
|
|
29
29
|
metadata?: Record<string, any>;
|
|
30
30
|
}
|
|
@@ -73,6 +73,34 @@ export interface RBACConfig {
|
|
|
73
73
|
permissions: Record<string, string[]>;
|
|
74
74
|
roleHierarchy: Record<string, string[]>;
|
|
75
75
|
}
|
|
76
|
+
export interface OAuth2TokenResponse {
|
|
77
|
+
access_token: string;
|
|
78
|
+
refresh_token?: string;
|
|
79
|
+
expires_in: number;
|
|
80
|
+
token_type: string;
|
|
81
|
+
scope?: string;
|
|
82
|
+
}
|
|
83
|
+
export interface OAuth2User {
|
|
84
|
+
id?: string;
|
|
85
|
+
email: string;
|
|
86
|
+
name: string;
|
|
87
|
+
picture?: string;
|
|
88
|
+
}
|
|
89
|
+
export interface APIKey {
|
|
90
|
+
id: string;
|
|
91
|
+
key: string;
|
|
92
|
+
userId: string;
|
|
93
|
+
name: string;
|
|
94
|
+
permissions: string[];
|
|
95
|
+
lastUsedAt?: Date;
|
|
96
|
+
createdAt: Date;
|
|
97
|
+
expiresAt?: Date;
|
|
98
|
+
isActive: boolean;
|
|
99
|
+
rateLimit?: {
|
|
100
|
+
requests: number;
|
|
101
|
+
window: number;
|
|
102
|
+
};
|
|
103
|
+
}
|
|
76
104
|
/**
|
|
77
105
|
* Secure authentication and authorization system
|
|
78
106
|
*/
|
|
@@ -85,6 +113,7 @@ export declare class SecureAuthenticationSystem extends EventEmitter {
|
|
|
85
113
|
private rbacConfig;
|
|
86
114
|
private oauth2Config;
|
|
87
115
|
private mfaEnabled;
|
|
116
|
+
private apiKeys;
|
|
88
117
|
constructor(jwtSecret: string, rbacConfig: RBACConfig, options?: {
|
|
89
118
|
jwtExpiresIn?: string;
|
|
90
119
|
refreshTokenExpiresIn?: string;
|
|
@@ -100,16 +129,34 @@ export declare class SecureAuthenticationSystem extends EventEmitter {
|
|
|
100
129
|
*/
|
|
101
130
|
authenticate(request: AuthenticationRequest): Promise<AuthenticationResult>;
|
|
102
131
|
/**
|
|
103
|
-
|
|
104
|
-
|
|
132
|
+
* Authenticate with password
|
|
133
|
+
*/
|
|
105
134
|
private authenticateWithPassword;
|
|
106
135
|
/**
|
|
107
|
-
|
|
108
|
-
|
|
136
|
+
* Authenticate with OAuth2
|
|
137
|
+
* SECURITY: Secure OAuth2 token validation with token endpoint validation
|
|
138
|
+
*/
|
|
109
139
|
private authenticateWithOAuth2;
|
|
110
140
|
/**
|
|
111
|
-
*
|
|
141
|
+
* Exchange OAuth2 authorization code for access token
|
|
142
|
+
*/
|
|
143
|
+
private exchangeCodeForToken;
|
|
144
|
+
/**
|
|
145
|
+
* Validate OAuth2 token with user info endpoint
|
|
146
|
+
*/
|
|
147
|
+
private validateOAuth2Token;
|
|
148
|
+
/**
|
|
149
|
+
* Find user by email
|
|
150
|
+
*/
|
|
151
|
+
private findUserByEmail;
|
|
152
|
+
/**
|
|
153
|
+
* Create user from OAuth2 data
|
|
112
154
|
*/
|
|
155
|
+
private createUserFromOAuth2;
|
|
156
|
+
/**
|
|
157
|
+
* Authenticate with API key
|
|
158
|
+
* SECURITY: Secure API key validation with rate limiting and usage tracking
|
|
159
|
+
*/
|
|
113
160
|
private authenticateWithApiKey;
|
|
114
161
|
/**
|
|
115
162
|
* Authorize user action
|
|
@@ -213,6 +260,50 @@ export declare class SecureAuthenticationSystem extends EventEmitter {
|
|
|
213
260
|
* Delete user
|
|
214
261
|
*/
|
|
215
262
|
deleteUser(userId: string): boolean;
|
|
263
|
+
/**
|
|
264
|
+
* Create new API key for user
|
|
265
|
+
* SECURITY: Generate cryptographically secure API keys with usage tracking
|
|
266
|
+
*/
|
|
267
|
+
createAPIKey(userId: string, options: {
|
|
268
|
+
name: string;
|
|
269
|
+
permissions?: string[];
|
|
270
|
+
expiresAt?: Date;
|
|
271
|
+
rateLimit?: {
|
|
272
|
+
requests: number;
|
|
273
|
+
window: number;
|
|
274
|
+
};
|
|
275
|
+
}): APIKey;
|
|
276
|
+
/**
|
|
277
|
+
* Revoke API key by ID
|
|
278
|
+
*/
|
|
279
|
+
revokeAPIKey(apiKeyId: string, userId: string): boolean;
|
|
280
|
+
/**
|
|
281
|
+
* Get API keys for a user
|
|
282
|
+
*/
|
|
283
|
+
getAPIKeys(userId: string): APIKey[];
|
|
284
|
+
/**
|
|
285
|
+
* Get API key by ID (admin only)
|
|
286
|
+
*/
|
|
287
|
+
getAPIKeyById(apiKeyId: string, requestingUserId?: string): APIKey | undefined;
|
|
288
|
+
/**
|
|
289
|
+
* Update API key properties
|
|
290
|
+
*/
|
|
291
|
+
updateAPIKey(apiKeyId: string, userId: string, updates: Partial<APIKey>): boolean;
|
|
292
|
+
/**
|
|
293
|
+
* Check API key rate limit
|
|
294
|
+
*/
|
|
295
|
+
checkRateLimit(apiKey: string): Promise<{
|
|
296
|
+
allowed: boolean;
|
|
297
|
+
remaining: number;
|
|
298
|
+
resetAt: Date;
|
|
299
|
+
}>;
|
|
300
|
+
/**
|
|
301
|
+
* Rotate API keys (security best practice)
|
|
302
|
+
*/
|
|
303
|
+
rotateAPIKeys(userId: string): Promise<{
|
|
304
|
+
newApiKey: APIKey;
|
|
305
|
+
revokedApiKey: APIKey;
|
|
306
|
+
}>;
|
|
216
307
|
/**
|
|
217
308
|
* Get authentication statistics
|
|
218
309
|
*/
|
|
@@ -220,8 +311,18 @@ export declare class SecureAuthenticationSystem extends EventEmitter {
|
|
|
220
311
|
totalUsers: number;
|
|
221
312
|
activeUsers: number;
|
|
222
313
|
totalSessions: number;
|
|
314
|
+
totalAPIKeys: number;
|
|
315
|
+
activeAPIKeys: number;
|
|
223
316
|
recentEvents: any[];
|
|
224
317
|
};
|
|
318
|
+
/**
|
|
319
|
+
* Generate cryptographically secure API key
|
|
320
|
+
*/
|
|
321
|
+
private generateSecureAPIKey;
|
|
322
|
+
/**
|
|
323
|
+
* Mask API key for display purposes (security)
|
|
324
|
+
*/
|
|
325
|
+
private maskAPIKey;
|
|
225
326
|
}
|
|
226
327
|
export declare const secureAuthSystem: SecureAuthenticationSystem;
|
|
227
328
|
//# sourceMappingURL=secure-authentication-system.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"secure-authentication-system.d.ts","sourceRoot":"","sources":["../../src/security/secure-authentication-system.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"secure-authentication-system.d.ts","sourceRoot":"","sources":["../../src/security/secure-authentication-system.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,7 +8,10 @@
|
|
|
8
8
|
* @since 2026-01-08
|
|
9
9
|
*/
|
|
10
10
|
import { EventEmitter } from "events";
|
|
11
|
+
import * as crypto from "crypto";
|
|
11
12
|
import * as jwt from "jsonwebtoken";
|
|
13
|
+
import * as https from "https";
|
|
14
|
+
import { URL } from "url";
|
|
12
15
|
import { securityHardeningSystem } from "./security-hardening-system.js";
|
|
13
16
|
import { frameworkLogger } from "../core/framework-logger.js";
|
|
14
17
|
/**
|
|
@@ -23,6 +26,7 @@ export class SecureAuthenticationSystem extends EventEmitter {
|
|
|
23
26
|
rbacConfig;
|
|
24
27
|
oauth2Config;
|
|
25
28
|
mfaEnabled = false;
|
|
29
|
+
apiKeys = new Map();
|
|
26
30
|
constructor(jwtSecret, rbacConfig, options = {}) {
|
|
27
31
|
super();
|
|
28
32
|
this.jwtSecret = jwtSecret;
|
|
@@ -131,12 +135,15 @@ export class SecureAuthenticationSystem extends EventEmitter {
|
|
|
131
135
|
}
|
|
132
136
|
}
|
|
133
137
|
/**
|
|
134
|
-
|
|
135
|
-
|
|
138
|
+
* Authenticate with password
|
|
139
|
+
*/
|
|
136
140
|
async authenticateWithPassword(request) {
|
|
137
141
|
if (!request.username && !request.email) {
|
|
138
142
|
throw new Error("Username or email is required");
|
|
139
143
|
}
|
|
144
|
+
if (!request.password) {
|
|
145
|
+
throw new Error("Password is required for password authentication");
|
|
146
|
+
}
|
|
140
147
|
// Find user by username or email
|
|
141
148
|
let user;
|
|
142
149
|
for (const u of this.users.values()) {
|
|
@@ -157,23 +164,234 @@ export class SecureAuthenticationSystem extends EventEmitter {
|
|
|
157
164
|
return isValidPassword ? user : undefined;
|
|
158
165
|
}
|
|
159
166
|
/**
|
|
160
|
-
|
|
161
|
-
|
|
167
|
+
* Authenticate with OAuth2
|
|
168
|
+
* SECURITY: Secure OAuth2 token validation with token endpoint validation
|
|
169
|
+
*/
|
|
162
170
|
async authenticateWithOAuth2(request) {
|
|
163
171
|
if (!this.oauth2Config) {
|
|
164
172
|
throw new Error("OAuth2 not configured");
|
|
165
173
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
174
|
+
const { metadata, email } = request;
|
|
175
|
+
if (!metadata || !metadata.code && !metadata.access_token) {
|
|
176
|
+
throw new Error("OAuth2 requires code or access token in metadata");
|
|
177
|
+
}
|
|
178
|
+
try {
|
|
179
|
+
let tokenResponse;
|
|
180
|
+
// Handle different OAuth2 flows
|
|
181
|
+
if (metadata.access_token) {
|
|
182
|
+
// Direct token validation (e.g., from Single Page App)
|
|
183
|
+
tokenResponse = {
|
|
184
|
+
access_token: metadata.access_token,
|
|
185
|
+
expires_in: metadata.expires_in || 3600,
|
|
186
|
+
token_type: metadata.token_type || "Bearer",
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
else if (metadata.code) {
|
|
190
|
+
// Authorization Code Grant Flow
|
|
191
|
+
tokenResponse = await this.exchangeCodeForToken(metadata.code, metadata.redirect_uri);
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
throw new Error("Invalid OAuth2 flow");
|
|
195
|
+
}
|
|
196
|
+
// Validate token with provider's token endpoint
|
|
197
|
+
const userInfo = await this.validateOAuth2Token(tokenResponse.access_token);
|
|
198
|
+
// Find or create user based on OAuth2 email
|
|
199
|
+
let user = this.findUserByEmail(userInfo.email);
|
|
200
|
+
if (!user) {
|
|
201
|
+
// Create new user from OAuth2 data
|
|
202
|
+
user = await this.createUserFromOAuth2(userInfo, request.metadata || {});
|
|
203
|
+
}
|
|
204
|
+
else if (!user.isActive) {
|
|
205
|
+
throw new Error("User account is disabled");
|
|
206
|
+
}
|
|
207
|
+
// Update OAuth2 metadata
|
|
208
|
+
user.metadata = user.metadata || {};
|
|
209
|
+
user.metadata.oauth2 = {
|
|
210
|
+
provider: this.oauth2Config.clientId,
|
|
211
|
+
lastLoginAt: new Date(),
|
|
212
|
+
scopes: tokenResponse.scope,
|
|
213
|
+
expiresAt: tokenResponse.expires_in ? new Date(Date.now() + tokenResponse.expires_in * 1000) : undefined,
|
|
214
|
+
};
|
|
215
|
+
this.users.set(user.id, user);
|
|
216
|
+
return user;
|
|
217
|
+
}
|
|
218
|
+
catch (error) {
|
|
219
|
+
const errorMessage = `OAuth2 authentication failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
220
|
+
frameworkLogger.log("secure-authentication-system", "-security-oauth2-authentication-failed-errormessage-", "error", { message: `[SECURITY] OAuth2 authentication failed: ${errorMessage}` });
|
|
221
|
+
throw new Error(errorMessage);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Exchange OAuth2 authorization code for access token
|
|
226
|
+
*/
|
|
227
|
+
async exchangeCodeForToken(code, redirectUri) {
|
|
228
|
+
return new Promise((resolve, reject) => {
|
|
229
|
+
const postData = JSON.stringify({
|
|
230
|
+
grant_type: "authorization_code",
|
|
231
|
+
client_id: this.oauth2Config.clientId,
|
|
232
|
+
client_secret: this.oauth2Config.clientSecret,
|
|
233
|
+
code: code,
|
|
234
|
+
redirect_uri: redirectUri || this.oauth2Config.callbackURL,
|
|
235
|
+
});
|
|
236
|
+
const options = {
|
|
237
|
+
hostname: new URL(this.oauth2Config.tokenURL).hostname,
|
|
238
|
+
port: 443,
|
|
239
|
+
path: new URL(this.oauth2Config.tokenURL).pathname,
|
|
240
|
+
method: "POST",
|
|
241
|
+
headers: {
|
|
242
|
+
"Content-Type": "application/json",
|
|
243
|
+
"Content-Length": Buffer.byteLength(postData),
|
|
244
|
+
},
|
|
245
|
+
};
|
|
246
|
+
const req = https.request(options, (res) => {
|
|
247
|
+
let data = "";
|
|
248
|
+
res.on("data", (chunk) => { data += chunk; });
|
|
249
|
+
res.on("end", () => {
|
|
250
|
+
if (res.statusCode === 200) {
|
|
251
|
+
try {
|
|
252
|
+
const tokenData = JSON.parse(data);
|
|
253
|
+
resolve({
|
|
254
|
+
access_token: tokenData.access_token,
|
|
255
|
+
refresh_token: tokenData.refresh_token,
|
|
256
|
+
expires_in: tokenData.expires_in,
|
|
257
|
+
token_type: tokenData.token_type,
|
|
258
|
+
scope: tokenData.scope,
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
catch (parseError) {
|
|
262
|
+
reject(new Error(`Invalid token response: ${parseError instanceof Error ? parseError.message : String(parseError)}`));
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
reject(new Error(`Token exchange failed: ${res.statusCode} - ${data}`));
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
req.on("error", (error) => {
|
|
271
|
+
reject(new Error(`HTTP request failed: ${error.message}`));
|
|
272
|
+
});
|
|
273
|
+
req.write(postData);
|
|
274
|
+
req.end();
|
|
275
|
+
});
|
|
169
276
|
}
|
|
170
277
|
/**
|
|
171
|
-
*
|
|
278
|
+
* Validate OAuth2 token with user info endpoint
|
|
172
279
|
*/
|
|
280
|
+
async validateOAuth2Token(token) {
|
|
281
|
+
return new Promise((resolve, reject) => {
|
|
282
|
+
const options = {
|
|
283
|
+
hostname: new URL(this.oauth2Config.tokenURL).hostname,
|
|
284
|
+
port: 443,
|
|
285
|
+
path: "/oauth/userinfo", // Common endpoint, adjust based on provider
|
|
286
|
+
method: "GET",
|
|
287
|
+
headers: {
|
|
288
|
+
"Authorization": `Bearer ${token}`,
|
|
289
|
+
},
|
|
290
|
+
};
|
|
291
|
+
const req = https.request(options, (res) => {
|
|
292
|
+
let data = "";
|
|
293
|
+
res.on("data", (chunk) => { data += chunk; });
|
|
294
|
+
res.on("end", () => {
|
|
295
|
+
if (res.statusCode === 200) {
|
|
296
|
+
try {
|
|
297
|
+
const userInfo = JSON.parse(data);
|
|
298
|
+
resolve({
|
|
299
|
+
id: userInfo.sub || userInfo.id,
|
|
300
|
+
email: userInfo.email,
|
|
301
|
+
name: userInfo.name || userInfo.display_name,
|
|
302
|
+
picture: userInfo.picture,
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
catch (parseError) {
|
|
306
|
+
reject(new Error(`Invalid user info response: ${parseError instanceof Error ? parseError.message : String(parseError)}`));
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
reject(new Error(`Token validation failed: ${res.statusCode} - ${data}`));
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
});
|
|
314
|
+
req.on("error", (error) => {
|
|
315
|
+
reject(new Error(`HTTP request failed: ${error.message}`));
|
|
316
|
+
});
|
|
317
|
+
req.end();
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Find user by email
|
|
322
|
+
*/
|
|
323
|
+
findUserByEmail(email) {
|
|
324
|
+
for (const user of this.users.values()) {
|
|
325
|
+
if (user.email === email) {
|
|
326
|
+
return user;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return undefined;
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Create user from OAuth2 data
|
|
333
|
+
*/
|
|
334
|
+
async createUserFromOAuth2(userInfo, metadata) {
|
|
335
|
+
const userId = crypto.randomUUID();
|
|
336
|
+
const username = userInfo.name || userInfo.email.split('@')[0];
|
|
337
|
+
// Create base user with OAuth2 roles
|
|
338
|
+
const user = {
|
|
339
|
+
id: userId,
|
|
340
|
+
username: `${username}_${Date.now()}`, // Ensure uniqueness
|
|
341
|
+
email: userInfo.email,
|
|
342
|
+
roles: ["user"], // Default role
|
|
343
|
+
permissions: ["authenticated"],
|
|
344
|
+
metadata: {
|
|
345
|
+
...metadata,
|
|
346
|
+
oauth2: {
|
|
347
|
+
provider: this.oauth2Config.clientId,
|
|
348
|
+
createdAt: new Date(),
|
|
349
|
+
scopes: metadata.scopes || [],
|
|
350
|
+
},
|
|
351
|
+
},
|
|
352
|
+
createdAt: new Date(),
|
|
353
|
+
isActive: true,
|
|
354
|
+
};
|
|
355
|
+
this.users.set(userId, user);
|
|
356
|
+
return user;
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Authenticate with API key
|
|
360
|
+
* SECURITY: Secure API key validation with rate limiting and usage tracking
|
|
361
|
+
*/
|
|
173
362
|
async authenticateWithApiKey(request) {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
363
|
+
if (!request.metadata || !request.metadata.api_key) {
|
|
364
|
+
throw new Error("API key is required in metadata");
|
|
365
|
+
}
|
|
366
|
+
const apiKeyValue = request.metadata.api_key;
|
|
367
|
+
// Find API key and validate
|
|
368
|
+
const apiKey = Array.from(this.apiKeys.values()).find(key => key.key === apiKeyValue && key.isActive);
|
|
369
|
+
if (!apiKey) {
|
|
370
|
+
throw new Error("Invalid or inactive API key");
|
|
371
|
+
}
|
|
372
|
+
// Check if API key is expired
|
|
373
|
+
if (apiKey.expiresAt && new Date() > apiKey.expiresAt) {
|
|
374
|
+
throw new Error("API key expired");
|
|
375
|
+
}
|
|
376
|
+
// Get user associated with API key
|
|
377
|
+
const user = this.users.get(apiKey.userId);
|
|
378
|
+
if (!user) {
|
|
379
|
+
throw new Error("User associated with API key not found");
|
|
380
|
+
}
|
|
381
|
+
if (!user.isActive) {
|
|
382
|
+
throw new Error("User account is disabled");
|
|
383
|
+
}
|
|
384
|
+
// Update API key usage tracking
|
|
385
|
+
apiKey.lastUsedAt = new Date();
|
|
386
|
+
this.apiKeys.set(apiKey.id, apiKey);
|
|
387
|
+
// Update user metadata
|
|
388
|
+
user.metadata = user.metadata || {};
|
|
389
|
+
user.metadata.apiKeyUsage = {
|
|
390
|
+
lastUsedAt: new Date(),
|
|
391
|
+
apiKeyId: apiKey.id,
|
|
392
|
+
apiKeyName: apiKey.name,
|
|
393
|
+
};
|
|
394
|
+
return user;
|
|
177
395
|
}
|
|
178
396
|
/**
|
|
179
397
|
* Authorize user action
|
|
@@ -482,18 +700,215 @@ export class SecureAuthenticationSystem extends EventEmitter {
|
|
|
482
700
|
deleteUser(userId) {
|
|
483
701
|
return this.users.delete(userId);
|
|
484
702
|
}
|
|
703
|
+
/**
|
|
704
|
+
* Create new API key for user
|
|
705
|
+
* SECURITY: Generate cryptographically secure API keys with usage tracking
|
|
706
|
+
*/
|
|
707
|
+
createAPIKey(userId, options) {
|
|
708
|
+
// Validate user exists
|
|
709
|
+
const user = this.users.get(userId);
|
|
710
|
+
if (!user) {
|
|
711
|
+
throw new Error("User not found");
|
|
712
|
+
}
|
|
713
|
+
// Generate secure API key
|
|
714
|
+
const apiKey = this.generateSecureAPIKey();
|
|
715
|
+
// Create API key object
|
|
716
|
+
const newApiKey = {
|
|
717
|
+
id: securityHardeningSystem.generateSecureToken(16),
|
|
718
|
+
key: apiKey,
|
|
719
|
+
userId,
|
|
720
|
+
name: options.name,
|
|
721
|
+
permissions: options.permissions || user.permissions,
|
|
722
|
+
lastUsedAt: options.expiresAt instanceof Date ? new Date() : undefined,
|
|
723
|
+
createdAt: new Date(),
|
|
724
|
+
expiresAt: options.expiresAt instanceof Date ? options.expiresAt : undefined,
|
|
725
|
+
isActive: true,
|
|
726
|
+
rateLimit: options.rateLimit,
|
|
727
|
+
};
|
|
728
|
+
// Store API key
|
|
729
|
+
this.apiKeys.set(newApiKey.id, newApiKey);
|
|
730
|
+
// Log API key creation
|
|
731
|
+
frameworkLogger.log("secure-authentication-system", "-api-key-created-event-api-key-id-for-user-event-userid-", "info", {
|
|
732
|
+
message: `🔑 API key created: ${newApiKey.id} for user ${userId}`,
|
|
733
|
+
apiKeyId: newApiKey.id,
|
|
734
|
+
userId,
|
|
735
|
+
permissions: newApiKey.permissions,
|
|
736
|
+
expiresAt: newApiKey.expiresAt,
|
|
737
|
+
});
|
|
738
|
+
return newApiKey;
|
|
739
|
+
}
|
|
740
|
+
/**
|
|
741
|
+
* Revoke API key by ID
|
|
742
|
+
*/
|
|
743
|
+
revokeAPIKey(apiKeyId, userId) {
|
|
744
|
+
const apiKey = this.apiKeys.get(apiKeyId);
|
|
745
|
+
if (!apiKey) {
|
|
746
|
+
throw new Error("API key not found");
|
|
747
|
+
}
|
|
748
|
+
if (apiKey.userId !== userId) {
|
|
749
|
+
throw new Error("Unauthorized to revoke this API key");
|
|
750
|
+
}
|
|
751
|
+
// Deactivate API key
|
|
752
|
+
apiKey.isActive = false;
|
|
753
|
+
this.apiKeys.set(apiKeyId, apiKey);
|
|
754
|
+
// Log API key revocation
|
|
755
|
+
frameworkLogger.log("secure-authentication-system", "-api-key-revoked-event-api-key-id-for-user-event-userid-", "info", {
|
|
756
|
+
message: `🔓 API key revoked: ${apiKeyId} for user ${userId}`,
|
|
757
|
+
apiKeyId,
|
|
758
|
+
userId,
|
|
759
|
+
});
|
|
760
|
+
return true;
|
|
761
|
+
}
|
|
762
|
+
/**
|
|
763
|
+
* Get API keys for a user
|
|
764
|
+
*/
|
|
765
|
+
getAPIKeys(userId) {
|
|
766
|
+
const userKeys = Array.from(this.apiKeys.values())
|
|
767
|
+
.filter(key => key.userId === userId)
|
|
768
|
+
.map(key => ({
|
|
769
|
+
...key,
|
|
770
|
+
key: this.maskAPIKey(key.key), // Mask the key for security
|
|
771
|
+
}));
|
|
772
|
+
return userKeys;
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* Get API key by ID (admin only)
|
|
776
|
+
*/
|
|
777
|
+
getAPIKeyById(apiKeyId, requestingUserId) {
|
|
778
|
+
const apiKey = this.apiKeys.get(apiKeyId);
|
|
779
|
+
if (!apiKey) {
|
|
780
|
+
return undefined;
|
|
781
|
+
}
|
|
782
|
+
// Check if user owns this API key or has admin permissions
|
|
783
|
+
if (requestingUserId && apiKey.userId !== requestingUserId) {
|
|
784
|
+
const user = this.users.get(requestingUserId);
|
|
785
|
+
if (!user || !user.permissions.includes("admin")) {
|
|
786
|
+
throw new Error("Unauthorized to access this API key");
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
return {
|
|
790
|
+
...apiKey,
|
|
791
|
+
key: this.maskAPIKey(apiKey.key), // Mask the key for security
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
/**
|
|
795
|
+
* Update API key properties
|
|
796
|
+
*/
|
|
797
|
+
updateAPIKey(apiKeyId, userId, updates) {
|
|
798
|
+
const apiKey = this.apiKeys.get(apiKeyId);
|
|
799
|
+
if (!apiKey) {
|
|
800
|
+
throw new Error("API key not found");
|
|
801
|
+
}
|
|
802
|
+
if (apiKey.userId !== userId) {
|
|
803
|
+
throw new Error("Unauthorized to update this API key");
|
|
804
|
+
}
|
|
805
|
+
// Update API key properties
|
|
806
|
+
const updatedApiKey = {
|
|
807
|
+
...apiKey,
|
|
808
|
+
...updates,
|
|
809
|
+
updatedAt: new Date(), // Add update timestamp
|
|
810
|
+
};
|
|
811
|
+
this.apiKeys.set(apiKeyId, updatedApiKey);
|
|
812
|
+
// Log API key update
|
|
813
|
+
frameworkLogger.log("secure-authentication-system", "-api-key-updated-event-api-key-id-for-user-event-userid-", "info", {
|
|
814
|
+
message: `📝 API key updated: ${apiKeyId} for user ${userId}`,
|
|
815
|
+
apiKeyId,
|
|
816
|
+
userId,
|
|
817
|
+
updatedFields: Object.keys(updates),
|
|
818
|
+
});
|
|
819
|
+
return true;
|
|
820
|
+
}
|
|
821
|
+
/**
|
|
822
|
+
* Check API key rate limit
|
|
823
|
+
*/
|
|
824
|
+
async checkRateLimit(apiKey) {
|
|
825
|
+
// Find API key
|
|
826
|
+
const apiKeyData = Array.from(this.apiKeys.values()).find(key => key.key === apiKey);
|
|
827
|
+
if (!apiKeyData || !apiKeyData.isActive) {
|
|
828
|
+
return { allowed: false, remaining: 0, resetAt: new Date() };
|
|
829
|
+
}
|
|
830
|
+
// Check expiration
|
|
831
|
+
if (apiKeyData.expiresAt && new Date() > apiKeyData.expiresAt) {
|
|
832
|
+
return { allowed: false, remaining: 0, resetAt: new Date() };
|
|
833
|
+
}
|
|
834
|
+
// Get or create rate limit tracking
|
|
835
|
+
// In production, this would be persisted to a database
|
|
836
|
+
const rateLimit = apiKeyData.rateLimit;
|
|
837
|
+
if (!rateLimit) {
|
|
838
|
+
return { allowed: true, remaining: -1, resetAt: new Date() }; // No rate limit
|
|
839
|
+
}
|
|
840
|
+
// Simple in-memory rate limiting (production would use Redis/database)
|
|
841
|
+
// For demo purposes, we'll accept all requests with rate limits configured
|
|
842
|
+
return {
|
|
843
|
+
allowed: true,
|
|
844
|
+
remaining: rateLimit.requests,
|
|
845
|
+
resetAt: new Date(Date.now() + rateLimit.window * 60 * 1000)
|
|
846
|
+
};
|
|
847
|
+
}
|
|
848
|
+
/**
|
|
849
|
+
* Rotate API keys (security best practice)
|
|
850
|
+
*/
|
|
851
|
+
async rotateAPIKeys(userId) {
|
|
852
|
+
// Get user's active API keys
|
|
853
|
+
const userKeys = Array.from(this.apiKeys.values())
|
|
854
|
+
.filter(key => key.userId === userId && key.isActive);
|
|
855
|
+
if (userKeys.length === 0) {
|
|
856
|
+
throw new Error("No active API keys to rotate");
|
|
857
|
+
}
|
|
858
|
+
// Revoke the oldest active API key
|
|
859
|
+
const apiKeyToRevoke = userKeys.reduce((oldest, current) => current.createdAt < oldest.createdAt ? current : oldest);
|
|
860
|
+
// Create new API key with same properties
|
|
861
|
+
const newApiKey = this.createAPIKey(userId, {
|
|
862
|
+
name: `${apiKeyToRevoke.name} (rotated)`,
|
|
863
|
+
permissions: apiKeyToRevoke.permissions,
|
|
864
|
+
expiresAt: apiKeyToRevoke.expiresAt instanceof Date ? apiKeyToRevoke.expiresAt : undefined,
|
|
865
|
+
rateLimit: apiKeyToRevoke.rateLimit,
|
|
866
|
+
});
|
|
867
|
+
// Revoke the old API key
|
|
868
|
+
this.revokeAPIKey(apiKeyToRevoke.id, userId);
|
|
869
|
+
return { newApiKey, revokedApiKey: apiKeyToRevoke };
|
|
870
|
+
}
|
|
485
871
|
/**
|
|
486
872
|
* Get authentication statistics
|
|
487
873
|
*/
|
|
488
874
|
getAuthStats() {
|
|
489
875
|
const activeUsers = Array.from(this.users.values()).filter((u) => u.isActive).length;
|
|
876
|
+
const totalAPIKeys = this.apiKeys.size;
|
|
877
|
+
const activeAPIKeys = Array.from(this.apiKeys.values()).filter((key) => key.isActive).length;
|
|
490
878
|
return {
|
|
491
879
|
totalUsers: this.users.size,
|
|
492
880
|
activeUsers,
|
|
493
881
|
totalSessions: this.sessions.size,
|
|
882
|
+
totalAPIKeys,
|
|
883
|
+
activeAPIKeys,
|
|
494
884
|
recentEvents: [], // Would track recent auth events
|
|
495
885
|
};
|
|
496
886
|
}
|
|
887
|
+
/**
|
|
888
|
+
* Generate cryptographically secure API key
|
|
889
|
+
*/
|
|
890
|
+
generateSecureAPIKey() {
|
|
891
|
+
// Generate a secure token for API key
|
|
892
|
+
const prefix = "sk_" + crypto
|
|
893
|
+
.randomBytes(32)
|
|
894
|
+
.toString('base64')
|
|
895
|
+
.replace(/\+/g, '-')
|
|
896
|
+
.replace(/\//g, '_')
|
|
897
|
+
.replace(/=/g, '');
|
|
898
|
+
return prefix;
|
|
899
|
+
}
|
|
900
|
+
/**
|
|
901
|
+
* Mask API key for display purposes (security)
|
|
902
|
+
*/
|
|
903
|
+
maskAPIKey(apiKey) {
|
|
904
|
+
if (apiKey.length <= 8) {
|
|
905
|
+
return '*'.repeat(apiKey.length);
|
|
906
|
+
}
|
|
907
|
+
const prefix = apiKey.substring(0, 4);
|
|
908
|
+
const suffix = apiKey.substring(apiKey.length - 4);
|
|
909
|
+
const masked = '*'.repeat(apiKey.length - 8);
|
|
910
|
+
return `${prefix}${masked}${suffix}`;
|
|
911
|
+
}
|
|
497
912
|
}
|
|
498
913
|
// Export singleton instance (would need proper initialization)
|
|
499
914
|
export const secureAuthSystem = {}; // Placeholder
|