@shin1ohno/sage 0.8.8 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -0
- package/dist/cli/http-server-with-config.d.ts +2 -0
- package/dist/cli/http-server-with-config.d.ts.map +1 -1
- package/dist/cli/http-server-with-config.js +70 -0
- package/dist/cli/http-server-with-config.js.map +1 -1
- package/dist/cli/main-entry.d.ts.map +1 -1
- package/dist/cli/main-entry.js +1 -0
- package/dist/cli/main-entry.js.map +1 -1
- package/dist/cli/parser.d.ts +2 -0
- package/dist/cli/parser.d.ts.map +1 -1
- package/dist/cli/parser.js +7 -0
- package/dist/cli/parser.js.map +1 -1
- package/dist/config/validation.d.ts +721 -0
- package/dist/config/validation.d.ts.map +1 -1
- package/dist/config/validation.js +203 -0
- package/dist/config/validation.js.map +1 -1
- package/dist/integrations/calendar-service.d.ts +14 -0
- package/dist/integrations/calendar-service.d.ts.map +1 -1
- package/dist/integrations/calendar-service.js.map +1 -1
- package/dist/integrations/calendar-source-manager.d.ts +86 -3
- package/dist/integrations/calendar-source-manager.d.ts.map +1 -1
- package/dist/integrations/calendar-source-manager.js +146 -15
- package/dist/integrations/calendar-source-manager.js.map +1 -1
- package/dist/integrations/google-calendar-service.d.ts +55 -3
- package/dist/integrations/google-calendar-service.d.ts.map +1 -1
- package/dist/integrations/google-calendar-service.js +213 -4
- package/dist/integrations/google-calendar-service.js.map +1 -1
- package/dist/oauth/encryption-service.d.ts +104 -0
- package/dist/oauth/encryption-service.d.ts.map +1 -0
- package/dist/oauth/encryption-service.js +271 -0
- package/dist/oauth/encryption-service.js.map +1 -0
- package/dist/oauth/file-mutex.d.ts +68 -0
- package/dist/oauth/file-mutex.d.ts.map +1 -0
- package/dist/oauth/file-mutex.js +140 -0
- package/dist/oauth/file-mutex.js.map +1 -0
- package/dist/oauth/google-oauth-handler.d.ts +8 -9
- package/dist/oauth/google-oauth-handler.d.ts.map +1 -1
- package/dist/oauth/google-oauth-handler.js +30 -65
- package/dist/oauth/google-oauth-handler.js.map +1 -1
- package/dist/oauth/index.d.ts +1 -0
- package/dist/oauth/index.d.ts.map +1 -1
- package/dist/oauth/index.js +2 -0
- package/dist/oauth/index.js.map +1 -1
- package/dist/oauth/oauth-server.d.ts +61 -1
- package/dist/oauth/oauth-server.d.ts.map +1 -1
- package/dist/oauth/oauth-server.js +181 -40
- package/dist/oauth/oauth-server.js.map +1 -1
- package/dist/oauth/persistent-client-store.d.ts +58 -0
- package/dist/oauth/persistent-client-store.d.ts.map +1 -0
- package/dist/oauth/persistent-client-store.js +187 -0
- package/dist/oauth/persistent-client-store.js.map +1 -0
- package/dist/oauth/persistent-refresh-token-store.d.ts +77 -0
- package/dist/oauth/persistent-refresh-token-store.d.ts.map +1 -0
- package/dist/oauth/persistent-refresh-token-store.js +225 -0
- package/dist/oauth/persistent-refresh-token-store.js.map +1 -0
- package/dist/oauth/persistent-session-store.d.ts +69 -0
- package/dist/oauth/persistent-session-store.d.ts.map +1 -0
- package/dist/oauth/persistent-session-store.js +154 -0
- package/dist/oauth/persistent-session-store.js.map +1 -0
- package/dist/oauth/session-store.d.ts +31 -0
- package/dist/oauth/session-store.d.ts.map +1 -0
- package/dist/oauth/session-store.js +47 -0
- package/dist/oauth/session-store.js.map +1 -0
- package/dist/services/working-cadence.d.ts +37 -1
- package/dist/services/working-cadence.d.ts.map +1 -1
- package/dist/services/working-cadence.js +151 -13
- package/dist/services/working-cadence.js.map +1 -1
- package/dist/tools/calendar/handlers.d.ts +82 -3
- package/dist/tools/calendar/handlers.d.ts.map +1 -1
- package/dist/tools/calendar/handlers.js +200 -16
- package/dist/tools/calendar/handlers.js.map +1 -1
- package/dist/types/google-calendar-types.d.ts +150 -3
- package/dist/types/google-calendar-types.d.ts.map +1 -1
- package/dist/types/google-calendar-types.js +79 -2
- package/dist/types/google-calendar-types.js.map +1 -1
- package/dist/types/task.d.ts +14 -0
- package/dist/types/task.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -7,12 +7,9 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { google } from 'googleapis';
|
|
9
9
|
import { generateCodeVerifier, generateCodeChallenge } from './pkce.js';
|
|
10
|
-
import {
|
|
11
|
-
import { promisify } from 'util';
|
|
12
|
-
import { readFile, writeFile, mkdir } from 'fs/promises';
|
|
10
|
+
import { EncryptionService } from './encryption-service.js';
|
|
13
11
|
import { join } from 'path';
|
|
14
12
|
import { homedir } from 'os';
|
|
15
|
-
const scryptAsync = promisify(scrypt);
|
|
16
13
|
/**
|
|
17
14
|
* Google Calendar API Scopes
|
|
18
15
|
*/
|
|
@@ -29,65 +26,36 @@ export const GOOGLE_CALENDAR_SCOPES = [
|
|
|
29
26
|
export class GoogleOAuthHandler {
|
|
30
27
|
codeVerifier = null;
|
|
31
28
|
config;
|
|
32
|
-
|
|
29
|
+
encryptionService;
|
|
33
30
|
tokensStoragePath;
|
|
31
|
+
initialized = false;
|
|
34
32
|
constructor(config, encryptionKey, userId) {
|
|
35
33
|
this.config = config;
|
|
36
|
-
//
|
|
37
|
-
this.
|
|
34
|
+
// Initialize EncryptionService with provided key
|
|
35
|
+
this.encryptionService = new EncryptionService({
|
|
36
|
+
encryptionKey: encryptionKey || process.env.SAGE_ENCRYPTION_KEY,
|
|
37
|
+
});
|
|
38
38
|
// Store tokens at ~/.sage/google_oauth_tokens_{userId}.enc
|
|
39
39
|
const sageDir = join(homedir(), '.sage');
|
|
40
40
|
const userIdSuffix = userId ? `_${userId}` : '';
|
|
41
41
|
this.tokensStoragePath = join(sageDir, `google_oauth_tokens${userIdSuffix}.enc`);
|
|
42
42
|
}
|
|
43
43
|
/**
|
|
44
|
-
*
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
return new google.auth.OAuth2(this.config.clientId, this.config.clientSecret, redirectUri || this.config.redirectUri);
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Encrypt data using AES-256-GCM
|
|
44
|
+
* Initialize encryption service
|
|
45
|
+
*
|
|
46
|
+
* Must be called before any token storage operations.
|
|
51
47
|
*/
|
|
52
|
-
async
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const iv = randomBytes(16);
|
|
58
|
-
// Create cipher
|
|
59
|
-
const cipher = createCipheriv('aes-256-gcm', key, iv);
|
|
60
|
-
// Encrypt data
|
|
61
|
-
let encrypted = cipher.update(data, 'utf8', 'hex');
|
|
62
|
-
encrypted += cipher.final('hex');
|
|
63
|
-
// Get auth tag
|
|
64
|
-
const authTag = cipher.getAuthTag();
|
|
65
|
-
// Combine: salt:iv:authTag:encrypted
|
|
66
|
-
return `${salt.toString('hex')}:${iv.toString('hex')}:${authTag.toString('hex')}:${encrypted}`;
|
|
48
|
+
async ensureInitialized() {
|
|
49
|
+
if (!this.initialized) {
|
|
50
|
+
await this.encryptionService.initialize();
|
|
51
|
+
this.initialized = true;
|
|
52
|
+
}
|
|
67
53
|
}
|
|
68
54
|
/**
|
|
69
|
-
*
|
|
55
|
+
* Create OAuth2Client instance
|
|
70
56
|
*/
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const parts = encryptedData.split(':');
|
|
74
|
-
if (parts.length !== 4) {
|
|
75
|
-
throw new Error('Invalid encrypted data format');
|
|
76
|
-
}
|
|
77
|
-
const [saltHex, ivHex, authTagHex, encrypted] = parts;
|
|
78
|
-
// Convert from hex
|
|
79
|
-
const salt = Buffer.from(saltHex, 'hex');
|
|
80
|
-
const iv = Buffer.from(ivHex, 'hex');
|
|
81
|
-
const authTag = Buffer.from(authTagHex, 'hex');
|
|
82
|
-
// Derive key from encryption key using scrypt
|
|
83
|
-
const key = (await scryptAsync(this.encryptionKey, salt, 32));
|
|
84
|
-
// Create decipher
|
|
85
|
-
const decipher = createDecipheriv('aes-256-gcm', key, iv);
|
|
86
|
-
decipher.setAuthTag(authTag);
|
|
87
|
-
// Decrypt data
|
|
88
|
-
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
|
|
89
|
-
decrypted += decipher.final('utf8');
|
|
90
|
-
return decrypted;
|
|
57
|
+
createOAuth2Client(redirectUri) {
|
|
58
|
+
return new google.auth.OAuth2(this.config.clientId, this.config.clientSecret, redirectUri || this.config.redirectUri);
|
|
91
59
|
}
|
|
92
60
|
/**
|
|
93
61
|
* Generate authorization URL with PKCE code_challenge
|
|
@@ -211,6 +179,8 @@ export class GoogleOAuthHandler {
|
|
|
211
179
|
*/
|
|
212
180
|
async storeTokens(tokens) {
|
|
213
181
|
try {
|
|
182
|
+
// Ensure encryption service is initialized
|
|
183
|
+
await this.ensureInitialized();
|
|
214
184
|
// Convert to stored format
|
|
215
185
|
const storedTokens = {
|
|
216
186
|
accessToken: tokens.accessToken,
|
|
@@ -218,14 +188,9 @@ export class GoogleOAuthHandler {
|
|
|
218
188
|
expiresAt: new Date(tokens.expiresAt).toISOString(),
|
|
219
189
|
scope: tokens.scope,
|
|
220
190
|
};
|
|
221
|
-
// Encrypt tokens
|
|
191
|
+
// Encrypt and store tokens using EncryptionService
|
|
222
192
|
const tokensJson = JSON.stringify(storedTokens);
|
|
223
|
-
|
|
224
|
-
// Ensure ~/.sage directory exists
|
|
225
|
-
const sageDir = join(homedir(), '.sage');
|
|
226
|
-
await mkdir(sageDir, { recursive: true });
|
|
227
|
-
// Write encrypted tokens to file
|
|
228
|
-
await writeFile(this.tokensStoragePath, encrypted, 'utf8');
|
|
193
|
+
await this.encryptionService.encryptToFile(tokensJson, this.tokensStoragePath);
|
|
229
194
|
}
|
|
230
195
|
catch (error) {
|
|
231
196
|
throw new Error(`Failed to store tokens: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
@@ -241,10 +206,14 @@ export class GoogleOAuthHandler {
|
|
|
241
206
|
*/
|
|
242
207
|
async getTokens() {
|
|
243
208
|
try {
|
|
244
|
-
//
|
|
245
|
-
|
|
246
|
-
// Decrypt tokens
|
|
247
|
-
const tokensJson = await this.
|
|
209
|
+
// Ensure encryption service is initialized
|
|
210
|
+
await this.ensureInitialized();
|
|
211
|
+
// Decrypt tokens from file using EncryptionService
|
|
212
|
+
const tokensJson = await this.encryptionService.decryptFromFile(this.tokensStoragePath);
|
|
213
|
+
// Return null if file not found
|
|
214
|
+
if (tokensJson === null) {
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
248
217
|
const storedTokens = JSON.parse(tokensJson);
|
|
249
218
|
// Convert to GoogleOAuthTokens format
|
|
250
219
|
return {
|
|
@@ -255,10 +224,6 @@ export class GoogleOAuthHandler {
|
|
|
255
224
|
};
|
|
256
225
|
}
|
|
257
226
|
catch (error) {
|
|
258
|
-
// Return null if file not found (user hasn't authenticated yet)
|
|
259
|
-
if (error.code === 'ENOENT') {
|
|
260
|
-
return null;
|
|
261
|
-
}
|
|
262
227
|
throw new Error(`Failed to get tokens: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
263
228
|
}
|
|
264
229
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"google-oauth-handler.js","sourceRoot":"","sources":["../../src/oauth/google-oauth-handler.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEpC,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AACxE,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"google-oauth-handler.js","sourceRoot":"","sources":["../../src/oauth/google-oauth-handler.ts"],"names":[],"mappings":"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"}
|
package/dist/oauth/index.d.ts
CHANGED
|
@@ -10,4 +10,5 @@ export { createRefreshTokenStore, RefreshTokenStore } from './refresh-token-stor
|
|
|
10
10
|
export { createClientStore, ClientStore } from './client-store.js';
|
|
11
11
|
export { OAuthServer, createOAuthServer, OAuthServerConfig } from './oauth-server.js';
|
|
12
12
|
export { OAuthHandler, createOAuthHandler, OAuthHandlerConfig } from './oauth-handler.js';
|
|
13
|
+
export { FileMutex, FileMutexMetrics } from './file-mutex.js';
|
|
13
14
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/oauth/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,cAAc,YAAY,CAAC;AAG3B,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAGxI,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGvF,OAAO,EAAE,4BAA4B,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAGvF,OAAO,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAGtF,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGnE,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAGtF,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/oauth/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,cAAc,YAAY,CAAC;AAG3B,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAGxI,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGvF,OAAO,EAAE,4BAA4B,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAGvF,OAAO,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAGtF,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGnE,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAGtF,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAG1F,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC"}
|
package/dist/oauth/index.js
CHANGED
|
@@ -18,4 +18,6 @@ export { createClientStore } from './client-store.js';
|
|
|
18
18
|
export { OAuthServer, createOAuthServer } from './oauth-server.js';
|
|
19
19
|
// OAuth Handler
|
|
20
20
|
export { OAuthHandler, createOAuthHandler } from './oauth-handler.js';
|
|
21
|
+
// File Mutex (for serializing file operations)
|
|
22
|
+
export { FileMutex } from './file-mutex.js';
|
|
21
23
|
//# sourceMappingURL=index.js.map
|
package/dist/oauth/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/oauth/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,QAAQ;AACR,cAAc,YAAY,CAAC;AAE3B,OAAO;AACP,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAExI,gBAAgB;AAChB,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAgB,MAAM,oBAAoB,CAAC;AAEvF,2BAA2B;AAC3B,OAAO,EAAE,4BAA4B,EAA0B,MAAM,iBAAiB,CAAC;AAEvF,sBAAsB;AACtB,OAAO,EAAE,uBAAuB,EAAqB,MAAM,0BAA0B,CAAC;AAEtF,eAAe;AACf,OAAO,EAAE,iBAAiB,EAAe,MAAM,mBAAmB,CAAC;AAEnE,eAAe;AACf,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAqB,MAAM,mBAAmB,CAAC;AAEtF,gBAAgB;AAChB,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAsB,MAAM,oBAAoB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/oauth/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,QAAQ;AACR,cAAc,YAAY,CAAC;AAE3B,OAAO;AACP,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAExI,gBAAgB;AAChB,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAgB,MAAM,oBAAoB,CAAC;AAEvF,2BAA2B;AAC3B,OAAO,EAAE,4BAA4B,EAA0B,MAAM,iBAAiB,CAAC;AAEvF,sBAAsB;AACtB,OAAO,EAAE,uBAAuB,EAAqB,MAAM,0BAA0B,CAAC;AAEtF,eAAe;AACf,OAAO,EAAE,iBAAiB,EAAe,MAAM,mBAAmB,CAAC;AAEnE,eAAe;AACf,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAqB,MAAM,mBAAmB,CAAC;AAEtF,gBAAgB;AAChB,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAsB,MAAM,oBAAoB,CAAC;AAE1F,+CAA+C;AAC/C,OAAO,EAAE,SAAS,EAAoB,MAAM,iBAAiB,CAAC"}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { AuthorizationServerMetadata, ProtectedResourceMetadata, AuthorizationRequest, TokenResponse, OAuthError, OAuthClient, ClientRegistrationRequest, VerifyTokenResult, OAuthUser, UserSession } from './types.js';
|
|
8
8
|
import { ClientRegistrationResult } from './client-store.js';
|
|
9
|
+
import { EncryptionService } from './encryption-service.js';
|
|
9
10
|
/**
|
|
10
11
|
* OAuth Server Configuration
|
|
11
12
|
*/
|
|
@@ -18,6 +19,9 @@ export interface OAuthServerConfig {
|
|
|
18
19
|
users?: OAuthUser[];
|
|
19
20
|
privateKey?: string;
|
|
20
21
|
publicKey?: string;
|
|
22
|
+
enablePersistence?: boolean;
|
|
23
|
+
encryptionService?: EncryptionService;
|
|
24
|
+
storageBasePath?: string;
|
|
21
25
|
}
|
|
22
26
|
/**
|
|
23
27
|
* Authorization Pending Request (stored during consent flow)
|
|
@@ -40,6 +44,7 @@ export declare class OAuthServer {
|
|
|
40
44
|
private users;
|
|
41
45
|
private pendingAuthRequests;
|
|
42
46
|
private loginAttempts;
|
|
47
|
+
private encryptionService?;
|
|
43
48
|
private privateKey;
|
|
44
49
|
private publicKey;
|
|
45
50
|
constructor(config: OAuthServerConfig, keys?: {
|
|
@@ -47,9 +52,17 @@ export declare class OAuthServer {
|
|
|
47
52
|
publicKey: string;
|
|
48
53
|
});
|
|
49
54
|
/**
|
|
50
|
-
* Initialize the server with generated keys
|
|
55
|
+
* Initialize the server with generated keys and load persisted data
|
|
51
56
|
*/
|
|
52
57
|
initialize(): Promise<void>;
|
|
58
|
+
/**
|
|
59
|
+
* Log startup metrics
|
|
60
|
+
*/
|
|
61
|
+
private logStartupMetrics;
|
|
62
|
+
/**
|
|
63
|
+
* Type guard to check if object has a method
|
|
64
|
+
*/
|
|
65
|
+
private hasMethod;
|
|
53
66
|
private parseExpiryToSeconds;
|
|
54
67
|
/**
|
|
55
68
|
* Get Protected Resource Metadata (RFC 9728)
|
|
@@ -156,6 +169,53 @@ export declare class OAuthServer {
|
|
|
156
169
|
scope: string;
|
|
157
170
|
description: string;
|
|
158
171
|
}>;
|
|
172
|
+
/**
|
|
173
|
+
* Shutdown the server and flush all pending data
|
|
174
|
+
*
|
|
175
|
+
* Call this method before server shutdown to ensure all data is persisted.
|
|
176
|
+
*/
|
|
177
|
+
shutdown(): Promise<void>;
|
|
178
|
+
/**
|
|
179
|
+
* Get metrics for monitoring
|
|
180
|
+
*/
|
|
181
|
+
getMetrics(): {
|
|
182
|
+
refreshTokens: {
|
|
183
|
+
count: number;
|
|
184
|
+
expiredCount: number;
|
|
185
|
+
rotatedCount: number;
|
|
186
|
+
};
|
|
187
|
+
clients: {
|
|
188
|
+
count: number;
|
|
189
|
+
};
|
|
190
|
+
sessions: {
|
|
191
|
+
count: number;
|
|
192
|
+
expiredCount: number;
|
|
193
|
+
};
|
|
194
|
+
storage?: {
|
|
195
|
+
tokensSize: number;
|
|
196
|
+
clientsSize: number;
|
|
197
|
+
sessionsSize: number;
|
|
198
|
+
};
|
|
199
|
+
};
|
|
200
|
+
/**
|
|
201
|
+
* Get health status for monitoring
|
|
202
|
+
*/
|
|
203
|
+
getHealthStatus(): {
|
|
204
|
+
healthy: boolean;
|
|
205
|
+
encryption: {
|
|
206
|
+
initialized: boolean;
|
|
207
|
+
keySource: string;
|
|
208
|
+
};
|
|
209
|
+
storage: {
|
|
210
|
+
accessible: boolean;
|
|
211
|
+
lastSave?: string;
|
|
212
|
+
};
|
|
213
|
+
issues: string[];
|
|
214
|
+
};
|
|
215
|
+
/**
|
|
216
|
+
* Get file size safely
|
|
217
|
+
*/
|
|
218
|
+
private getFileSize;
|
|
159
219
|
}
|
|
160
220
|
/**
|
|
161
221
|
* Create an OAuth Server instance
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth-server.d.ts","sourceRoot":"","sources":["../../src/oauth/oauth-server.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"oauth-server.d.ts","sourceRoot":"","sources":["../../src/oauth/oauth-server.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,45 +4,19 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Main OAuth server that coordinates all OAuth components and handles requests.
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
7
|
+
import { createHash } from 'crypto';
|
|
8
|
+
import { join } from 'path';
|
|
8
9
|
import { SCOPE_DEFINITIONS, DEFAULT_TOKEN_EXPIRY, CLAUDE_CALLBACK_URLS, } from './types.js';
|
|
9
10
|
import { createTokenService, generateKeyPair } from './token-service.js';
|
|
10
11
|
import { createAuthorizationCodeStore } from './code-store.js';
|
|
11
12
|
import { createRefreshTokenStore } from './refresh-token-store.js';
|
|
12
13
|
import { createClientStore } from './client-store.js';
|
|
14
|
+
import { createSessionStore } from './session-store.js';
|
|
13
15
|
import { verifyCodeChallenge } from './pkce.js';
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
sessions = new Map();
|
|
19
|
-
sessionExpiryMs = 24 * 60 * 60 * 1000; // 24 hours
|
|
20
|
-
createSession(userId) {
|
|
21
|
-
const sessionId = randomBytes(32).toString('hex');
|
|
22
|
-
const now = Date.now();
|
|
23
|
-
const session = {
|
|
24
|
-
sessionId,
|
|
25
|
-
userId,
|
|
26
|
-
createdAt: now,
|
|
27
|
-
expiresAt: now + this.sessionExpiryMs,
|
|
28
|
-
};
|
|
29
|
-
this.sessions.set(sessionId, session);
|
|
30
|
-
return session;
|
|
31
|
-
}
|
|
32
|
-
getSession(sessionId) {
|
|
33
|
-
const session = this.sessions.get(sessionId);
|
|
34
|
-
if (!session)
|
|
35
|
-
return null;
|
|
36
|
-
if (Date.now() > session.expiresAt) {
|
|
37
|
-
this.sessions.delete(sessionId);
|
|
38
|
-
return null;
|
|
39
|
-
}
|
|
40
|
-
return session;
|
|
41
|
-
}
|
|
42
|
-
deleteSession(sessionId) {
|
|
43
|
-
this.sessions.delete(sessionId);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
16
|
+
import { EncryptionService } from './encryption-service.js';
|
|
17
|
+
import { PersistentRefreshTokenStore } from './persistent-refresh-token-store.js';
|
|
18
|
+
import { PersistentClientStore } from './persistent-client-store.js';
|
|
19
|
+
import { PersistentSessionStore } from './persistent-session-store.js';
|
|
46
20
|
/**
|
|
47
21
|
* OAuth Server Class
|
|
48
22
|
*/
|
|
@@ -56,6 +30,7 @@ export class OAuthServer {
|
|
|
56
30
|
users;
|
|
57
31
|
pendingAuthRequests = new Map();
|
|
58
32
|
loginAttempts = new Map();
|
|
33
|
+
encryptionService;
|
|
59
34
|
privateKey;
|
|
60
35
|
publicKey;
|
|
61
36
|
constructor(config, keys) {
|
|
@@ -65,13 +40,32 @@ export class OAuthServer {
|
|
|
65
40
|
// Parse expiry durations to seconds
|
|
66
41
|
const refreshTokenExpirySec = this.parseExpiryToSeconds(config.refreshTokenExpiry || DEFAULT_TOKEN_EXPIRY.refreshToken);
|
|
67
42
|
const authCodeExpirySec = this.parseExpiryToSeconds(config.authorizationCodeExpiry || DEFAULT_TOKEN_EXPIRY.authorizationCode);
|
|
68
|
-
//
|
|
43
|
+
// Determine if persistence is enabled (default: true)
|
|
44
|
+
const enablePersistence = config.enablePersistence !== false;
|
|
45
|
+
// Initialize stores based on persistence setting
|
|
46
|
+
if (enablePersistence) {
|
|
47
|
+
// Create EncryptionService if not provided
|
|
48
|
+
this.encryptionService = config.encryptionService || new EncryptionService();
|
|
49
|
+
// Determine storage paths (use custom base path if provided, for testing)
|
|
50
|
+
const basePath = config.storageBasePath;
|
|
51
|
+
const refreshTokenPath = basePath ? join(basePath, 'oauth_refresh_tokens.enc') : undefined;
|
|
52
|
+
const clientPath = basePath ? join(basePath, 'oauth_clients.enc') : undefined;
|
|
53
|
+
const sessionPath = basePath ? join(basePath, 'oauth_sessions.enc') : undefined;
|
|
54
|
+
// Create persistent stores
|
|
55
|
+
this.refreshTokenStore = new PersistentRefreshTokenStore({ expirySeconds: refreshTokenExpirySec }, this.encryptionService, refreshTokenPath);
|
|
56
|
+
this.clientStore = new PersistentClientStore({ allowedRedirectUris: [...(config.allowedRedirectUris || []), ...CLAUDE_CALLBACK_URLS] }, this.encryptionService, clientPath);
|
|
57
|
+
this.sessionStore = new PersistentSessionStore(this.encryptionService, sessionPath);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
// Create in-memory stores (for testing)
|
|
61
|
+
this.refreshTokenStore = createRefreshTokenStore({ expirySeconds: refreshTokenExpirySec });
|
|
62
|
+
this.clientStore = createClientStore({
|
|
63
|
+
allowedRedirectUris: [...(config.allowedRedirectUris || []), ...CLAUDE_CALLBACK_URLS],
|
|
64
|
+
});
|
|
65
|
+
this.sessionStore = createSessionStore();
|
|
66
|
+
}
|
|
67
|
+
// Authorization code store always uses in-memory (short-lived, not needed for persistence)
|
|
69
68
|
this.codeStore = createAuthorizationCodeStore({ expirySeconds: authCodeExpirySec });
|
|
70
|
-
this.refreshTokenStore = createRefreshTokenStore({ expirySeconds: refreshTokenExpirySec });
|
|
71
|
-
this.clientStore = createClientStore({
|
|
72
|
-
allowedRedirectUris: [...(config.allowedRedirectUris || []), ...CLAUDE_CALLBACK_URLS],
|
|
73
|
-
});
|
|
74
|
-
this.sessionStore = new InMemorySessionStore();
|
|
75
69
|
// Initialize token service (will be updated when keys are available)
|
|
76
70
|
this.tokenService = createTokenService({
|
|
77
71
|
issuer: config.issuer,
|
|
@@ -86,9 +80,10 @@ export class OAuthServer {
|
|
|
86
80
|
}
|
|
87
81
|
}
|
|
88
82
|
/**
|
|
89
|
-
* Initialize the server with generated keys
|
|
83
|
+
* Initialize the server with generated keys and load persisted data
|
|
90
84
|
*/
|
|
91
85
|
async initialize() {
|
|
86
|
+
// Generate keys if not provided
|
|
92
87
|
if (!this.privateKey || !this.publicKey) {
|
|
93
88
|
const keys = await generateKeyPair();
|
|
94
89
|
this.privateKey = keys.privateKey;
|
|
@@ -101,6 +96,44 @@ export class OAuthServer {
|
|
|
101
96
|
accessTokenExpiry: this.config.accessTokenExpiry || DEFAULT_TOKEN_EXPIRY.accessToken,
|
|
102
97
|
});
|
|
103
98
|
}
|
|
99
|
+
// Initialize encryption service and load persisted data if persistence is enabled
|
|
100
|
+
if (this.encryptionService) {
|
|
101
|
+
await this.encryptionService.initialize();
|
|
102
|
+
// Load persisted data in parallel
|
|
103
|
+
await Promise.all([
|
|
104
|
+
this.hasMethod(this.refreshTokenStore, 'loadFromStorage')
|
|
105
|
+
? this.refreshTokenStore.loadFromStorage()
|
|
106
|
+
: Promise.resolve(),
|
|
107
|
+
this.hasMethod(this.clientStore, 'loadFromStorage')
|
|
108
|
+
? this.clientStore.loadFromStorage()
|
|
109
|
+
: Promise.resolve(),
|
|
110
|
+
this.hasMethod(this.sessionStore, 'loadFromStorage')
|
|
111
|
+
? this.sessionStore.loadFromStorage()
|
|
112
|
+
: Promise.resolve(),
|
|
113
|
+
]);
|
|
114
|
+
console.log('[OAuth] Persistent storage initialized');
|
|
115
|
+
// Log startup metrics
|
|
116
|
+
this.logStartupMetrics();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Log startup metrics
|
|
121
|
+
*/
|
|
122
|
+
logStartupMetrics() {
|
|
123
|
+
const metrics = this.getMetrics();
|
|
124
|
+
console.log('[OAuth] Startup Metrics:');
|
|
125
|
+
console.log(` - Refresh Tokens: ${metrics.refreshTokens.count} (${metrics.refreshTokens.expiredCount} expired, ${metrics.refreshTokens.rotatedCount} rotated)`);
|
|
126
|
+
console.log(` - OAuth Clients: ${metrics.clients.count}`);
|
|
127
|
+
console.log(` - Sessions: ${metrics.sessions.count} (${metrics.sessions.expiredCount} expired)`);
|
|
128
|
+
if (metrics.storage) {
|
|
129
|
+
console.log(` - Storage: ${metrics.storage.tokensSize} bytes (tokens), ${metrics.storage.clientsSize} bytes (clients), ${metrics.storage.sessionsSize} bytes (sessions)`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Type guard to check if object has a method
|
|
134
|
+
*/
|
|
135
|
+
hasMethod(obj, methodName) {
|
|
136
|
+
return obj && typeof obj[methodName] === 'function';
|
|
104
137
|
}
|
|
105
138
|
parseExpiryToSeconds(expiry) {
|
|
106
139
|
const match = expiry.match(/^(\d+)([smhdw])$/);
|
|
@@ -481,6 +514,114 @@ export class OAuthServer {
|
|
|
481
514
|
description: SCOPE_DEFINITIONS[scope],
|
|
482
515
|
}));
|
|
483
516
|
}
|
|
517
|
+
/**
|
|
518
|
+
* Shutdown the server and flush all pending data
|
|
519
|
+
*
|
|
520
|
+
* Call this method before server shutdown to ensure all data is persisted.
|
|
521
|
+
*/
|
|
522
|
+
async shutdown() {
|
|
523
|
+
if (this.encryptionService) {
|
|
524
|
+
await Promise.all([
|
|
525
|
+
this.hasMethod(this.refreshTokenStore, 'flush')
|
|
526
|
+
? this.refreshTokenStore.flush()
|
|
527
|
+
: Promise.resolve(),
|
|
528
|
+
this.hasMethod(this.clientStore, 'flush')
|
|
529
|
+
? this.clientStore.flush()
|
|
530
|
+
: Promise.resolve(),
|
|
531
|
+
this.hasMethod(this.sessionStore, 'flush')
|
|
532
|
+
? this.sessionStore.flush()
|
|
533
|
+
: Promise.resolve(),
|
|
534
|
+
]);
|
|
535
|
+
console.log('[OAuth] All data flushed to storage');
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Get metrics for monitoring
|
|
540
|
+
*/
|
|
541
|
+
getMetrics() {
|
|
542
|
+
const refreshTokens = this.hasMethod(this.refreshTokenStore, 'getMetrics')
|
|
543
|
+
? this.refreshTokenStore.getMetrics()
|
|
544
|
+
: { count: 0, expiredCount: 0, rotatedCount: 0 };
|
|
545
|
+
const clients = this.hasMethod(this.clientStore, 'getMetrics')
|
|
546
|
+
? this.clientStore.getMetrics()
|
|
547
|
+
: { count: 0 };
|
|
548
|
+
const sessions = this.hasMethod(this.sessionStore, 'getMetrics')
|
|
549
|
+
? this.sessionStore.getMetrics()
|
|
550
|
+
: { count: 0, expiredCount: 0 };
|
|
551
|
+
// Get storage file sizes if persistence is enabled
|
|
552
|
+
let storage;
|
|
553
|
+
if (this.encryptionService) {
|
|
554
|
+
const { homedir } = require('os');
|
|
555
|
+
const { join } = require('path');
|
|
556
|
+
try {
|
|
557
|
+
const tokensPath = join(homedir(), '.sage', 'oauth_refresh_tokens.enc');
|
|
558
|
+
const clientsPath = join(homedir(), '.sage', 'oauth_clients.enc');
|
|
559
|
+
const sessionsPath = join(homedir(), '.sage', 'oauth_sessions.enc');
|
|
560
|
+
storage = {
|
|
561
|
+
tokensSize: this.getFileSize(tokensPath),
|
|
562
|
+
clientsSize: this.getFileSize(clientsPath),
|
|
563
|
+
sessionsSize: this.getFileSize(sessionsPath),
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
catch {
|
|
567
|
+
// Storage files may not exist yet
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
return {
|
|
571
|
+
refreshTokens,
|
|
572
|
+
clients,
|
|
573
|
+
sessions,
|
|
574
|
+
storage,
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* Get health status for monitoring
|
|
579
|
+
*/
|
|
580
|
+
getHealthStatus() {
|
|
581
|
+
const issues = [];
|
|
582
|
+
let storageAccessible = true;
|
|
583
|
+
// Check encryption service
|
|
584
|
+
const encryptionStatus = this.encryptionService
|
|
585
|
+
? this.encryptionService.getHealthStatus()
|
|
586
|
+
: { initialized: false, keySource: 'none', keyStoragePath: '' };
|
|
587
|
+
if (!encryptionStatus.initialized && this.encryptionService) {
|
|
588
|
+
issues.push('Encryption service not initialized');
|
|
589
|
+
}
|
|
590
|
+
// Check storage accessibility
|
|
591
|
+
if (this.encryptionService) {
|
|
592
|
+
const { existsSync } = require('fs');
|
|
593
|
+
const { homedir } = require('os');
|
|
594
|
+
const { join } = require('path');
|
|
595
|
+
const storageDir = join(homedir(), '.sage');
|
|
596
|
+
if (!existsSync(storageDir)) {
|
|
597
|
+
issues.push('Storage directory does not exist');
|
|
598
|
+
storageAccessible = false;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
return {
|
|
602
|
+
healthy: issues.length === 0,
|
|
603
|
+
encryption: {
|
|
604
|
+
initialized: encryptionStatus.initialized,
|
|
605
|
+
keySource: encryptionStatus.keySource,
|
|
606
|
+
},
|
|
607
|
+
storage: {
|
|
608
|
+
accessible: storageAccessible,
|
|
609
|
+
},
|
|
610
|
+
issues,
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
/**
|
|
614
|
+
* Get file size safely
|
|
615
|
+
*/
|
|
616
|
+
getFileSize(path) {
|
|
617
|
+
try {
|
|
618
|
+
const { statSync } = require('fs');
|
|
619
|
+
return statSync(path).size;
|
|
620
|
+
}
|
|
621
|
+
catch {
|
|
622
|
+
return 0;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
484
625
|
}
|
|
485
626
|
/**
|
|
486
627
|
* Create an OAuth Server instance
|