purmemo-mcp 14.2.0 → 15.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/oauth-manager.d.ts +67 -0
- package/dist/auth/oauth-manager.d.ts.map +1 -0
- package/dist/auth/oauth-manager.js +367 -0
- package/dist/auth/oauth-manager.js.map +1 -0
- package/dist/auth/token-store.d.ts +31 -0
- package/dist/auth/token-store.d.ts.map +1 -0
- package/dist/auth/token-store.js +114 -0
- package/dist/auth/token-store.js.map +1 -0
- package/dist/auth/universal-auth.d.ts +73 -0
- package/dist/auth/universal-auth.d.ts.map +1 -0
- package/dist/auth/universal-auth.js +483 -0
- package/dist/auth/universal-auth.js.map +1 -0
- package/dist/hooks/hook-utils.d.ts +21 -0
- package/dist/hooks/hook-utils.d.ts.map +1 -0
- package/dist/hooks/hook-utils.js +106 -0
- package/dist/hooks/hook-utils.js.map +1 -0
- package/dist/hooks/purmemo_heartbeat.d.ts +9 -0
- package/dist/hooks/purmemo_heartbeat.d.ts.map +1 -0
- package/dist/hooks/purmemo_heartbeat.js +79 -0
- package/dist/hooks/purmemo_heartbeat.js.map +1 -0
- package/dist/hooks/purmemo_precompact.d.ts +9 -0
- package/dist/hooks/purmemo_precompact.d.ts.map +1 -0
- package/dist/hooks/purmemo_precompact.js +78 -0
- package/dist/hooks/purmemo_precompact.js.map +1 -0
- package/dist/hooks/purmemo_save.d.ts +9 -0
- package/dist/hooks/purmemo_save.d.ts.map +1 -0
- package/dist/hooks/purmemo_save.js +77 -0
- package/dist/hooks/purmemo_save.js.map +1 -0
- package/dist/hooks/purmemo_session_start.d.ts +9 -0
- package/dist/hooks/purmemo_session_start.d.ts.map +1 -0
- package/dist/hooks/purmemo_session_start.js +152 -0
- package/dist/hooks/purmemo_session_start.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/intelligent-memory.d.ts +64 -0
- package/dist/intelligent-memory.d.ts.map +1 -0
- package/dist/intelligent-memory.js +760 -0
- package/dist/intelligent-memory.js.map +1 -0
- package/dist/remote/connection-monitor.d.ts +32 -0
- package/dist/remote/connection-monitor.d.ts.map +1 -0
- package/dist/remote/connection-monitor.js +172 -0
- package/dist/remote/connection-monitor.js.map +1 -0
- package/dist/remote/favicon.ico +0 -0
- package/dist/remote/icon.png +0 -0
- package/dist/remote/login.html +295 -0
- package/dist/remote/oauth-simple.d.ts +21 -0
- package/dist/remote/oauth-simple.d.ts.map +1 -0
- package/dist/remote/oauth-simple.js +82 -0
- package/dist/remote/oauth-simple.js.map +1 -0
- package/dist/remote/success.html +91 -0
- package/dist/remote/widgets/context.html +216 -0
- package/dist/remote/widgets/discover.html +215 -0
- package/dist/remote/widgets/memory-detail.html +181 -0
- package/dist/remote/widgets/recall.html +369 -0
- package/dist/remote/widgets/save.html +170 -0
- package/dist/server.d.ts +39 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +4438 -0
- package/dist/server.js.map +1 -0
- package/dist/setup.d.ts +12 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +408 -0
- package/dist/setup.js.map +1 -0
- package/dist/types.d.ts +390 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/package.json +27 -15
- package/src/remote/favicon.ico +0 -0
- package/src/remote/icon.png +0 -0
- package/src/.mcpbignore +0 -10
- package/src/auth/oauth-manager.js +0 -408
- package/src/auth/token-store.js +0 -155
- package/src/auth/universal-auth.js +0 -551
- package/src/hooks/purmemo_heartbeat.js +0 -170
- package/src/hooks/purmemo_precompact.js +0 -166
- package/src/hooks/purmemo_save.js +0 -164
- package/src/hooks/purmemo_session_start.js +0 -200
- package/src/index.js +0 -5
- package/src/intelligent-memory.js +0 -866
- package/src/manifest.json +0 -87
- package/src/remote/connection-monitor.js +0 -170
- package/src/remote/oauth-simple.js +0 -84
- package/src/server.js +0 -4095
- package/src/setup.js +0 -433
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth Manager for Purmemo MCP
|
|
3
|
+
* Handles OAuth 2.1 + PKCE flow for seamless authentication
|
|
4
|
+
*/
|
|
5
|
+
interface OAuthConfig {
|
|
6
|
+
apiUrl?: string;
|
|
7
|
+
clientId?: string;
|
|
8
|
+
redirectUri?: string;
|
|
9
|
+
}
|
|
10
|
+
declare class OAuthManager {
|
|
11
|
+
private apiUrl;
|
|
12
|
+
private clientId;
|
|
13
|
+
private redirectUri;
|
|
14
|
+
private tokenStore;
|
|
15
|
+
private server;
|
|
16
|
+
private pendingAuth;
|
|
17
|
+
private platform;
|
|
18
|
+
constructor(config?: OAuthConfig);
|
|
19
|
+
/**
|
|
20
|
+
* Get current authentication token
|
|
21
|
+
* @returns {Promise<string|null>} Access token or null if not authenticated
|
|
22
|
+
*/
|
|
23
|
+
getToken(): Promise<any>;
|
|
24
|
+
/**
|
|
25
|
+
* Check if token is expired or about to expire
|
|
26
|
+
*/
|
|
27
|
+
isTokenExpired(token: any): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Start OAuth flow
|
|
30
|
+
* @returns {Promise<string>} Access token
|
|
31
|
+
*/
|
|
32
|
+
authenticate(): Promise<string>;
|
|
33
|
+
/**
|
|
34
|
+
* Perform the actual OAuth flow
|
|
35
|
+
*/
|
|
36
|
+
performOAuthFlow(): Promise<any>;
|
|
37
|
+
/**
|
|
38
|
+
* Start local server to handle OAuth callback
|
|
39
|
+
*/
|
|
40
|
+
startCallbackServer(expectedState: any): Promise<unknown>;
|
|
41
|
+
/**
|
|
42
|
+
* Stop the callback server
|
|
43
|
+
*/
|
|
44
|
+
stopCallbackServer(): void;
|
|
45
|
+
/**
|
|
46
|
+
* Exchange authorization code for access token
|
|
47
|
+
*/
|
|
48
|
+
exchangeCodeForToken(code: any, codeVerifier: any): Promise<any>;
|
|
49
|
+
/**
|
|
50
|
+
* Refresh access token
|
|
51
|
+
*/
|
|
52
|
+
refreshToken(refreshToken: any): Promise<any>;
|
|
53
|
+
/**
|
|
54
|
+
* Clear stored authentication
|
|
55
|
+
*/
|
|
56
|
+
logout(): Promise<void>;
|
|
57
|
+
/**
|
|
58
|
+
* Generate PKCE code verifier
|
|
59
|
+
*/
|
|
60
|
+
generateCodeVerifier(): string;
|
|
61
|
+
/**
|
|
62
|
+
* Generate PKCE code challenge from verifier
|
|
63
|
+
*/
|
|
64
|
+
generateCodeChallenge(verifier: any): string;
|
|
65
|
+
}
|
|
66
|
+
export default OAuthManager;
|
|
67
|
+
//# sourceMappingURL=oauth-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth-manager.d.ts","sourceRoot":"","sources":["../../src/auth/oauth-manager.ts"],"names":[],"mappings":"AACA;;;GAGG;AAWH,UAAU,WAAW;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,cAAM,YAAY;IAChB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,WAAW,CAAyB;IAC5C,OAAO,CAAC,QAAQ,CAAS;gBAEb,MAAM,GAAE,WAAgB;IAapC;;;OAGG;IACG,QAAQ;IA4Bd;;OAEG;IACH,cAAc,CAAC,KAAK,KAAA;IAUpB;;;OAGG;IACG,YAAY;IAelB;;OAEG;IACG,gBAAgB;IA6EtB;;OAEG;IACH,mBAAmB,CAAC,aAAa,KAAA;IAsHjC;;OAEG;IACH,kBAAkB;IAOlB;;OAEG;IACG,oBAAoB,CAAC,IAAI,KAAA,EAAE,YAAY,KAAA;IAqC7C;;OAEG;IACG,YAAY,CAAC,YAAY,KAAA;IAsC/B;;OAEG;IACG,MAAM;IAKZ;;OAEG;IACH,oBAAoB;IAIpB;;OAEG;IACH,qBAAqB,CAAC,QAAQ,KAAA;CAG/B;AAED,eAAe,YAAY,CAAC"}
|
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
// @ts-nocheck — OAuth CLI utility, full typing in follow-up
|
|
2
|
+
/**
|
|
3
|
+
* OAuth Manager for Purmemo MCP
|
|
4
|
+
* Handles OAuth 2.1 + PKCE flow for seamless authentication
|
|
5
|
+
*/
|
|
6
|
+
import * as crypto from 'crypto';
|
|
7
|
+
import express from 'express';
|
|
8
|
+
import open from 'open';
|
|
9
|
+
import { execFile } from 'child_process';
|
|
10
|
+
import * as os from 'os';
|
|
11
|
+
import TokenStore from './token-store.js';
|
|
12
|
+
class OAuthManager {
|
|
13
|
+
apiUrl;
|
|
14
|
+
clientId;
|
|
15
|
+
redirectUri;
|
|
16
|
+
tokenStore;
|
|
17
|
+
server;
|
|
18
|
+
pendingAuth;
|
|
19
|
+
platform;
|
|
20
|
+
constructor(config = {}) {
|
|
21
|
+
this.apiUrl = config.apiUrl || process.env.PURMEMO_API_URL || 'https://api.purmemo.ai';
|
|
22
|
+
this.clientId = config.clientId || 'chatgpt-purmemo';
|
|
23
|
+
this.redirectUri = config.redirectUri || 'http://localhost:3456/callback';
|
|
24
|
+
this.tokenStore = new TokenStore();
|
|
25
|
+
this.server = null;
|
|
26
|
+
this.pendingAuth = null;
|
|
27
|
+
this.platform = os.platform();
|
|
28
|
+
}
|
|
29
|
+
// Removed complex browser opening strategies - going manual-first
|
|
30
|
+
// The performOAuthFlow method now handles everything clearly
|
|
31
|
+
/**
|
|
32
|
+
* Get current authentication token
|
|
33
|
+
* @returns {Promise<string|null>} Access token or null if not authenticated
|
|
34
|
+
*/
|
|
35
|
+
async getToken() {
|
|
36
|
+
// First check if we have a valid token
|
|
37
|
+
const storedToken = await this.tokenStore.getToken();
|
|
38
|
+
if (storedToken && storedToken.access_token) {
|
|
39
|
+
// Check if token needs refresh (expired or close to expiry)
|
|
40
|
+
if (this.isTokenExpired(storedToken)) {
|
|
41
|
+
try {
|
|
42
|
+
return await this.refreshToken(storedToken.refresh_token);
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
console.error('Token refresh failed:', error.message);
|
|
46
|
+
// If refresh fails, start new OAuth flow
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return storedToken.access_token;
|
|
51
|
+
}
|
|
52
|
+
// Fallback to environment variable for backwards compatibility
|
|
53
|
+
const envApiKey = process.env.PURMEMO_API_KEY;
|
|
54
|
+
if (envApiKey) {
|
|
55
|
+
console.log('📔 Using API key from environment variable');
|
|
56
|
+
return envApiKey;
|
|
57
|
+
}
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Check if token is expired or about to expire
|
|
62
|
+
*/
|
|
63
|
+
isTokenExpired(token) {
|
|
64
|
+
if (!token.expires_at)
|
|
65
|
+
return false;
|
|
66
|
+
const now = Date.now();
|
|
67
|
+
const expiresAt = new Date(token.expires_at).getTime();
|
|
68
|
+
const bufferTime = 5 * 60 * 1000; // 5 minutes buffer
|
|
69
|
+
return now >= (expiresAt - bufferTime);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Start OAuth flow
|
|
73
|
+
* @returns {Promise<string>} Access token
|
|
74
|
+
*/
|
|
75
|
+
async authenticate() {
|
|
76
|
+
if (this.pendingAuth) {
|
|
77
|
+
return this.pendingAuth;
|
|
78
|
+
}
|
|
79
|
+
this.pendingAuth = this.performOAuthFlow();
|
|
80
|
+
try {
|
|
81
|
+
const token = await this.pendingAuth;
|
|
82
|
+
return token;
|
|
83
|
+
}
|
|
84
|
+
finally {
|
|
85
|
+
this.pendingAuth = null;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Perform the actual OAuth flow
|
|
90
|
+
*/
|
|
91
|
+
async performOAuthFlow() {
|
|
92
|
+
// Generate PKCE challenge
|
|
93
|
+
const codeVerifier = this.generateCodeVerifier();
|
|
94
|
+
const codeChallenge = this.generateCodeChallenge(codeVerifier);
|
|
95
|
+
const state = crypto.randomBytes(16).toString('hex');
|
|
96
|
+
// Build OAuth URL
|
|
97
|
+
const authUrl = new URL(`${this.apiUrl}/api/oauth/authorize`);
|
|
98
|
+
authUrl.searchParams.append('client_id', this.clientId);
|
|
99
|
+
authUrl.searchParams.append('redirect_uri', this.redirectUri);
|
|
100
|
+
authUrl.searchParams.append('response_type', 'code');
|
|
101
|
+
authUrl.searchParams.append('scope', 'memories.read memories.write entities.read');
|
|
102
|
+
authUrl.searchParams.append('state', state);
|
|
103
|
+
authUrl.searchParams.append('code_challenge', codeChallenge);
|
|
104
|
+
authUrl.searchParams.append('code_challenge_method', 'S256');
|
|
105
|
+
// MANUAL-FIRST: Show the URL BEFORE starting the server
|
|
106
|
+
console.log('\n' + '═'.repeat(70));
|
|
107
|
+
console.log('🔐 AUTHENTICATION REQUIRED');
|
|
108
|
+
console.log('═'.repeat(70));
|
|
109
|
+
console.log('');
|
|
110
|
+
console.log('Please complete authentication in your browser:');
|
|
111
|
+
console.log('');
|
|
112
|
+
console.log('📋 COPY THIS URL:');
|
|
113
|
+
console.log('─'.repeat(70));
|
|
114
|
+
console.log(authUrl.toString());
|
|
115
|
+
console.log('─'.repeat(70));
|
|
116
|
+
console.log('');
|
|
117
|
+
console.log('📱 STEPS:');
|
|
118
|
+
console.log(' 1. Copy the URL above');
|
|
119
|
+
console.log(' 2. Open your browser');
|
|
120
|
+
console.log(' 3. Paste and press Enter');
|
|
121
|
+
console.log(' 4. Sign in with Google, GitHub, or Email');
|
|
122
|
+
console.log(' 5. You\'ll be redirected back automatically');
|
|
123
|
+
console.log('');
|
|
124
|
+
console.log('⏳ Waiting for you to sign in...');
|
|
125
|
+
console.log('═'.repeat(70));
|
|
126
|
+
console.log('');
|
|
127
|
+
// NOW start the callback server after showing instructions
|
|
128
|
+
const authCode = await this.startCallbackServer(state);
|
|
129
|
+
// Try to open browser quietly in background (might work, might not)
|
|
130
|
+
try {
|
|
131
|
+
if (this.platform === 'darwin') {
|
|
132
|
+
// On macOS, use execFile (not exec) to avoid shell injection vulnerabilities
|
|
133
|
+
// execFile doesn't spawn a shell, so the URL is passed as a safe argument
|
|
134
|
+
execFile('open', [authUrl.toString()], (error) => {
|
|
135
|
+
if (!error) {
|
|
136
|
+
console.log('✨ Browser opened automatically - check your browser tabs');
|
|
137
|
+
}
|
|
138
|
+
// If it fails, that's fine - user has manual instructions
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
// Try other platforms
|
|
143
|
+
await open(authUrl.toString(), { wait: false }).catch(() => {
|
|
144
|
+
// Silently fail - manual instructions are already shown
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
// Silent fail - manual instructions are primary
|
|
150
|
+
}
|
|
151
|
+
// Wait for callback with auth code
|
|
152
|
+
const code = await authCode;
|
|
153
|
+
// Exchange code for token
|
|
154
|
+
const tokenResponse = await this.exchangeCodeForToken(code, codeVerifier);
|
|
155
|
+
// Store token securely
|
|
156
|
+
await this.tokenStore.saveToken(tokenResponse);
|
|
157
|
+
console.log('✅ Authentication successful!\n');
|
|
158
|
+
return tokenResponse.access_token;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Start local server to handle OAuth callback
|
|
162
|
+
*/
|
|
163
|
+
startCallbackServer(expectedState) {
|
|
164
|
+
return new Promise((resolve, reject) => {
|
|
165
|
+
const app = express();
|
|
166
|
+
let resolved = false;
|
|
167
|
+
app.get('/callback', async (req, res) => {
|
|
168
|
+
const { code, state, error, error_description } = req.query;
|
|
169
|
+
if (error) {
|
|
170
|
+
res.send(`
|
|
171
|
+
<html>
|
|
172
|
+
<head>
|
|
173
|
+
<title>Authentication Failed</title>
|
|
174
|
+
<style>
|
|
175
|
+
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
176
|
+
display: flex; justify-content: center; align-items: center;
|
|
177
|
+
height: 100vh; margin: 0; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); }
|
|
178
|
+
.container { background: white; padding: 40px; border-radius: 10px;
|
|
179
|
+
box-shadow: 0 20px 60px rgba(0,0,0,0.3); text-align: center; max-width: 400px; }
|
|
180
|
+
h1 { color: #e53e3e; margin: 0 0 10px 0; }
|
|
181
|
+
p { color: #718096; margin: 10px 0; }
|
|
182
|
+
.error { background: #fed7d7; padding: 10px; border-radius: 5px;
|
|
183
|
+
color: #c53030; margin-top: 20px; }
|
|
184
|
+
</style>
|
|
185
|
+
</head>
|
|
186
|
+
<body>
|
|
187
|
+
<div class="container">
|
|
188
|
+
<h1>❌ Authentication Failed</h1>
|
|
189
|
+
<p>Unable to complete authentication</p>
|
|
190
|
+
<div class="error">${error}: ${error_description || 'Unknown error'}</div>
|
|
191
|
+
<p style="margin-top: 20px; font-size: 14px;">You can close this window</p>
|
|
192
|
+
</div>
|
|
193
|
+
</body>
|
|
194
|
+
</html>
|
|
195
|
+
`);
|
|
196
|
+
if (!resolved) {
|
|
197
|
+
resolved = true;
|
|
198
|
+
this.stopCallbackServer();
|
|
199
|
+
reject(new Error(`OAuth error: ${error} - ${error_description}`));
|
|
200
|
+
}
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
if (state !== expectedState) {
|
|
204
|
+
res.send(`
|
|
205
|
+
<html>
|
|
206
|
+
<head><title>Security Error</title></head>
|
|
207
|
+
<body style="font-family: sans-serif; text-align: center; padding: 50px;">
|
|
208
|
+
<h1 style="color: red;">Security Error</h1>
|
|
209
|
+
<p>State mismatch - possible CSRF attack</p>
|
|
210
|
+
</body>
|
|
211
|
+
</html>
|
|
212
|
+
`);
|
|
213
|
+
if (!resolved) {
|
|
214
|
+
resolved = true;
|
|
215
|
+
this.stopCallbackServer();
|
|
216
|
+
reject(new Error('OAuth state mismatch'));
|
|
217
|
+
}
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
// Success response
|
|
221
|
+
res.send(`
|
|
222
|
+
<html>
|
|
223
|
+
<head>
|
|
224
|
+
<title>Authentication Successful</title>
|
|
225
|
+
<style>
|
|
226
|
+
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
227
|
+
display: flex; justify-content: center; align-items: center;
|
|
228
|
+
height: 100vh; margin: 0; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); }
|
|
229
|
+
.container { background: white; padding: 40px; border-radius: 10px;
|
|
230
|
+
box-shadow: 0 20px 60px rgba(0,0,0,0.3); text-align: center; max-width: 400px; }
|
|
231
|
+
h1 { color: #48bb78; margin: 0 0 10px 0; }
|
|
232
|
+
p { color: #718096; margin: 10px 0; }
|
|
233
|
+
.success { background: #c6f6d5; padding: 15px; border-radius: 5px;
|
|
234
|
+
color: #22543d; margin-top: 20px; }
|
|
235
|
+
.logo { font-size: 48px; margin-bottom: 20px; }
|
|
236
|
+
</style>
|
|
237
|
+
</head>
|
|
238
|
+
<body>
|
|
239
|
+
<div class="container">
|
|
240
|
+
<div class="logo">🧠</div>
|
|
241
|
+
<h1>✅ Authentication Successful!</h1>
|
|
242
|
+
<p>You're now connected to Purmemo</p>
|
|
243
|
+
<div class="success">
|
|
244
|
+
You can now close this window and return to Claude Desktop
|
|
245
|
+
</div>
|
|
246
|
+
<script>setTimeout(() => window.close(), 3000);</script>
|
|
247
|
+
</div>
|
|
248
|
+
</body>
|
|
249
|
+
</html>
|
|
250
|
+
`);
|
|
251
|
+
if (!resolved) {
|
|
252
|
+
resolved = true;
|
|
253
|
+
this.stopCallbackServer();
|
|
254
|
+
resolve(code);
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
// Start server
|
|
258
|
+
this.server = app.listen(3456, () => {
|
|
259
|
+
console.log('🌐 Waiting for authentication callback...\n');
|
|
260
|
+
});
|
|
261
|
+
// Timeout after 5 minutes
|
|
262
|
+
setTimeout(() => {
|
|
263
|
+
if (!resolved) {
|
|
264
|
+
resolved = true;
|
|
265
|
+
this.stopCallbackServer();
|
|
266
|
+
reject(new Error('Authentication timeout'));
|
|
267
|
+
}
|
|
268
|
+
}, 5 * 60 * 1000);
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Stop the callback server
|
|
273
|
+
*/
|
|
274
|
+
stopCallbackServer() {
|
|
275
|
+
if (this.server) {
|
|
276
|
+
this.server.close();
|
|
277
|
+
this.server = null;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Exchange authorization code for access token
|
|
282
|
+
*/
|
|
283
|
+
async exchangeCodeForToken(code, codeVerifier) {
|
|
284
|
+
const response = await fetch(`${this.apiUrl}/api/oauth/token`, {
|
|
285
|
+
method: 'POST',
|
|
286
|
+
headers: {
|
|
287
|
+
'Content-Type': 'application/json',
|
|
288
|
+
'User-Agent': 'purmemo-mcp/2.0.0'
|
|
289
|
+
},
|
|
290
|
+
body: JSON.stringify({
|
|
291
|
+
grant_type: 'authorization_code',
|
|
292
|
+
code,
|
|
293
|
+
client_id: this.clientId,
|
|
294
|
+
redirect_uri: this.redirectUri,
|
|
295
|
+
code_verifier: codeVerifier
|
|
296
|
+
})
|
|
297
|
+
});
|
|
298
|
+
if (!response.ok) {
|
|
299
|
+
const error = await response.text();
|
|
300
|
+
throw new Error(`Token exchange failed: ${error}`);
|
|
301
|
+
}
|
|
302
|
+
const tokenData = await response.json();
|
|
303
|
+
// Add expiry time
|
|
304
|
+
if (tokenData.expires_in) {
|
|
305
|
+
tokenData.expires_at = new Date(Date.now() + tokenData.expires_in * 1000).toISOString();
|
|
306
|
+
}
|
|
307
|
+
// Store user tier info
|
|
308
|
+
if (tokenData.user) {
|
|
309
|
+
tokenData.user_tier = tokenData.user.tier || 'free';
|
|
310
|
+
tokenData.memory_limit = tokenData.user.tier === 'pro' ? null : 100;
|
|
311
|
+
}
|
|
312
|
+
return tokenData;
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Refresh access token
|
|
316
|
+
*/
|
|
317
|
+
async refreshToken(refreshToken) {
|
|
318
|
+
if (!refreshToken) {
|
|
319
|
+
throw new Error('No refresh token available');
|
|
320
|
+
}
|
|
321
|
+
console.log('🔄 Refreshing authentication token...');
|
|
322
|
+
const response = await fetch(`${this.apiUrl}/api/auth/refresh`, {
|
|
323
|
+
method: 'POST',
|
|
324
|
+
headers: {
|
|
325
|
+
'Content-Type': 'application/json',
|
|
326
|
+
'User-Agent': 'purmemo-mcp/2.0.0'
|
|
327
|
+
},
|
|
328
|
+
body: JSON.stringify({
|
|
329
|
+
refresh_token: refreshToken
|
|
330
|
+
})
|
|
331
|
+
});
|
|
332
|
+
if (!response.ok) {
|
|
333
|
+
const error = await response.text();
|
|
334
|
+
throw new Error(`Token refresh failed: ${error}`);
|
|
335
|
+
}
|
|
336
|
+
const tokenData = await response.json();
|
|
337
|
+
// Add expiry time
|
|
338
|
+
if (tokenData.expires_in) {
|
|
339
|
+
tokenData.expires_at = new Date(Date.now() + tokenData.expires_in * 1000).toISOString();
|
|
340
|
+
}
|
|
341
|
+
// Store refreshed token
|
|
342
|
+
await this.tokenStore.saveToken(tokenData);
|
|
343
|
+
console.log('✅ Token refreshed successfully');
|
|
344
|
+
return tokenData.access_token;
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Clear stored authentication
|
|
348
|
+
*/
|
|
349
|
+
async logout() {
|
|
350
|
+
await this.tokenStore.clearToken();
|
|
351
|
+
console.log('👋 Logged out successfully');
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Generate PKCE code verifier
|
|
355
|
+
*/
|
|
356
|
+
generateCodeVerifier() {
|
|
357
|
+
return crypto.randomBytes(32).toString('base64url');
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Generate PKCE code challenge from verifier
|
|
361
|
+
*/
|
|
362
|
+
generateCodeChallenge(verifier) {
|
|
363
|
+
return crypto.createHash('sha256').update(verifier).digest('base64url');
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
export default OAuthManager;
|
|
367
|
+
//# sourceMappingURL=oauth-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth-manager.js","sourceRoot":"","sources":["../../src/auth/oauth-manager.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D;;;GAGG;AAEH,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AACjC,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,UAAU,MAAM,kBAAkB,CAAC;AAU1C,MAAM,YAAY;IACR,MAAM,CAAS;IACf,QAAQ,CAAS;IACjB,WAAW,CAAS;IACpB,UAAU,CAAa;IACvB,MAAM,CAAgB;IACtB,WAAW,CAAyB;IACpC,QAAQ,CAAS;IAEzB,YAAY,SAAsB,EAAE;QAClC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,wBAAwB,CAAC;QACvF,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,IAAI,iBAAiB,CAAC;QACrD,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,WAAW,IAAI,gCAAgC,CAAC;QAC1E,IAAI,CAAC,UAAU,GAAG,IAAI,UAAU,EAAE,CAAC;QACnC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QACxB,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC;IAChC,CAAC;IAED,kEAAkE;IAClE,6DAA6D;IAE7D;;;OAGG;IACH,KAAK,CAAC,QAAQ;QACZ,uCAAuC;QACvC,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC;QAErD,IAAI,WAAW,IAAI,WAAW,CAAC,YAAY,EAAE,CAAC;YAC5C,4DAA4D;YAC5D,IAAI,IAAI,CAAC,cAAc,CAAC,WAAW,CAAC,EAAE,CAAC;gBACrC,IAAI,CAAC;oBACH,OAAO,MAAM,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC;gBAC5D,CAAC;gBAAC,OAAO,KAAc,EAAE,CAAC;oBACxB,OAAO,CAAC,KAAK,CAAC,uBAAuB,EAAG,KAAe,CAAC,OAAO,CAAC,CAAC;oBACjE,yCAAyC;oBACzC,OAAO,IAAI,CAAC;gBACd,CAAC;YACH,CAAC;YACD,OAAO,WAAW,CAAC,YAAY,CAAC;QAClC,CAAC;QAED,+DAA+D;QAC/D,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;QAC9C,IAAI,SAAS,EAAE,CAAC;YACd,OAAO,CAAC,GAAG,CAAC,4CAA4C,CAAC,CAAC;YAC1D,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACH,cAAc,CAAC,KAAK;QAClB,IAAI,CAAC,KAAK,CAAC,UAAU;YAAE,OAAO,KAAK,CAAC;QAEpC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE,CAAC;QACvD,MAAM,UAAU,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,mBAAmB;QAErD,OAAO,GAAG,IAAI,CAAC,SAAS,GAAG,UAAU,CAAC,CAAC;IACzC,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,YAAY;QAChB,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,OAAO,IAAI,CAAC,WAAW,CAAC;QAC1B,CAAC;QAED,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAE3C,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC;YACrC,OAAO,KAAK,CAAC;QACf,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QAC1B,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,gBAAgB;QACpB,0BAA0B;QAC1B,MAAM,YAAY,GAAG,IAAI,CAAC,oBAAoB,EAAE,CAAC;QACjD,MAAM,aAAa,GAAG,IAAI,CAAC,qBAAqB,CAAC,YAAY,CAAC,CAAC;QAC/D,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QAErD,kBAAkB;QAClB,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,sBAAsB,CAAC,CAAC;QAC9D,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,WAAW,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QACxD,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,cAAc,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;QAC9D,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;QACrD,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,OAAO,EAAE,4CAA4C,CAAC,CAAC;QACnF,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAC5C,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,gBAAgB,EAAE,aAAa,CAAC,CAAC;QAC7D,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,uBAAuB,EAAE,MAAM,CAAC,CAAC;QAE7D,wDAAwD;QACxD,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;QACnC,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;QAC1C,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;QAC5B,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,iDAAiD,CAAC,CAAC;QAC/D,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;QACjC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;QAC5B,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;QAChC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;QAC5B,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QACzB,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC;QACvC,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;QACtC,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;QAC1C,OAAO,CAAC,GAAG,CAAC,4CAA4C,CAAC,CAAC;QAC1D,OAAO,CAAC,GAAG,CAAC,+CAA+C,CAAC,CAAC;QAC7D,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAC;QAC/C,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;QAC5B,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAEhB,2DAA2D;QAC3D,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC;QAEvD,oEAAoE;QACpE,IAAI,CAAC;YACH,IAAI,IAAI,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;gBAC/B,6EAA6E;gBAC7E,0EAA0E;gBAC1E,QAAQ,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,EAAE,CAAC,KAAK,EAAE,EAAE;oBAC/C,IAAI,CAAC,KAAK,EAAE,CAAC;wBACX,OAAO,CAAC,GAAG,CAAC,0DAA0D,CAAC,CAAC;oBAC1E,CAAC;oBACD,0DAA0D;gBAC5D,CAAC,CAAC,CAAC;YACL,CAAC;iBAAM,CAAC;gBACN,sBAAsB;gBACtB,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;oBACzD,wDAAwD;gBAC1D,CAAC,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,gDAAgD;QAClD,CAAC;QAED,mCAAmC;QACnC,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC;QAE5B,0BAA0B;QAC1B,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;QAE1E,uBAAuB;QACvB,MAAM,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;QAE/C,OAAO,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAC;QAE9C,OAAO,aAAa,CAAC,YAAY,CAAC;IACpC,CAAC;IAED;;OAEG;IACH,mBAAmB,CAAC,aAAa;QAC/B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;YACtB,IAAI,QAAQ,GAAG,KAAK,CAAC;YAErB,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;gBACtC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,iBAAiB,EAAE,GAAG,GAAG,CAAC,KAAK,CAAC;gBAE5D,IAAI,KAAK,EAAE,CAAC;oBACV,GAAG,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;uCAoBoB,KAAK,KAAK,iBAAiB,IAAI,eAAe;;;;;WAK1E,CAAC,CAAC;oBAEH,IAAI,CAAC,QAAQ,EAAE,CAAC;wBACd,QAAQ,GAAG,IAAI,CAAC;wBAChB,IAAI,CAAC,kBAAkB,EAAE,CAAC;wBAC1B,MAAM,CAAC,IAAI,KAAK,CAAC,gBAAgB,KAAK,MAAM,iBAAiB,EAAE,CAAC,CAAC,CAAC;oBACpE,CAAC;oBACD,OAAO;gBACT,CAAC;gBAED,IAAI,KAAK,KAAK,aAAa,EAAE,CAAC;oBAC5B,GAAG,CAAC,IAAI,CAAC;;;;;;;;WAQR,CAAC,CAAC;oBAEH,IAAI,CAAC,QAAQ,EAAE,CAAC;wBACd,QAAQ,GAAG,IAAI,CAAC;wBAChB,IAAI,CAAC,kBAAkB,EAAE,CAAC;wBAC1B,MAAM,CAAC,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC,CAAC;oBAC5C,CAAC;oBACD,OAAO;gBACT,CAAC;gBAED,mBAAmB;gBACnB,GAAG,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;SA6BR,CAAC,CAAC;gBAEH,IAAI,CAAC,QAAQ,EAAE,CAAC;oBACd,QAAQ,GAAG,IAAI,CAAC;oBAChB,IAAI,CAAC,kBAAkB,EAAE,CAAC;oBAC1B,OAAO,CAAC,IAAI,CAAC,CAAC;gBAChB,CAAC;YACH,CAAC,CAAC,CAAC;YAEH,eAAe;YACf,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE;gBAClC,OAAO,CAAC,GAAG,CAAC,6CAA6C,CAAC,CAAC;YAC7D,CAAC,CAAC,CAAC;YAEH,0BAA0B;YAC1B,UAAU,CAAC,GAAG,EAAE;gBACd,IAAI,CAAC,QAAQ,EAAE,CAAC;oBACd,QAAQ,GAAG,IAAI,CAAC;oBAChB,IAAI,CAAC,kBAAkB,EAAE,CAAC;oBAC1B,MAAM,CAAC,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC,CAAC;gBAC9C,CAAC;YACH,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;QACpB,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,kBAAkB;QAChB,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YACpB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACrB,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,oBAAoB,CAAC,IAAI,EAAE,YAAY;QAC3C,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,MAAM,kBAAkB,EAAE;YAC7D,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,YAAY,EAAE,mBAAmB;aAClC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,UAAU,EAAE,oBAAoB;gBAChC,IAAI;gBACJ,SAAS,EAAE,IAAI,CAAC,QAAQ;gBACxB,YAAY,EAAE,IAAI,CAAC,WAAW;gBAC9B,aAAa,EAAE,YAAY;aAC5B,CAAC;SACH,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YACpC,MAAM,IAAI,KAAK,CAAC,0BAA0B,KAAK,EAAE,CAAC,CAAC;QACrD,CAAC;QAED,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QAExC,kBAAkB;QAClB,IAAI,SAAS,CAAC,UAAU,EAAE,CAAC;YACzB,SAAS,CAAC,UAAU,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;QAC1F,CAAC;QAED,uBAAuB;QACvB,IAAI,SAAS,CAAC,IAAI,EAAE,CAAC;YACnB,SAAS,CAAC,SAAS,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,IAAI,MAAM,CAAC;YACpD,SAAS,CAAC,YAAY,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC;QACtE,CAAC;QAED,OAAO,SAAS,CAAC;IACnB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,YAAY,CAAC,YAAY;QAC7B,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;QAChD,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,uCAAuC,CAAC,CAAC;QAErD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,MAAM,mBAAmB,EAAE;YAC9D,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,YAAY,EAAE,mBAAmB;aAClC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,aAAa,EAAE,YAAY;aAC5B,CAAC;SACH,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YACpC,MAAM,IAAI,KAAK,CAAC,yBAAyB,KAAK,EAAE,CAAC,CAAC;QACpD,CAAC;QAED,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QAExC,kBAAkB;QAClB,IAAI,SAAS,CAAC,UAAU,EAAE,CAAC;YACzB,SAAS,CAAC,UAAU,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;QAC1F,CAAC;QAED,wBAAwB;QACxB,MAAM,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QAE3C,OAAO,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAC;QAE9C,OAAO,SAAS,CAAC,YAAY,CAAC;IAChC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,MAAM;QACV,MAAM,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC;QACnC,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;IAC5C,CAAC;IAED;;OAEG;IACH,oBAAoB;QAClB,OAAO,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IACtD,CAAC;IAED;;OAEG;IACH,qBAAqB,CAAC,QAAQ;QAC5B,OAAO,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IAC1E,CAAC;CACF;AAED,eAAe,YAAY,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secure Token Storage for Purmemo MCP
|
|
3
|
+
* Stores OAuth tokens securely in user's home directory
|
|
4
|
+
*/
|
|
5
|
+
import type { TokenData, UserInfo, EncryptedPayload } from '../types.js';
|
|
6
|
+
declare class TokenStore {
|
|
7
|
+
private configDir;
|
|
8
|
+
private tokenFile;
|
|
9
|
+
private encryptionKey;
|
|
10
|
+
constructor();
|
|
11
|
+
/** Get or generate encryption key for token storage */
|
|
12
|
+
private getEncryptionKey;
|
|
13
|
+
/** Ensure config directory exists */
|
|
14
|
+
ensureConfigDir(): Promise<void>;
|
|
15
|
+
/** Encrypt data */
|
|
16
|
+
encrypt(data: TokenData): EncryptedPayload;
|
|
17
|
+
/** Decrypt data */
|
|
18
|
+
decrypt(encryptedData: EncryptedPayload): TokenData;
|
|
19
|
+
/** Save token to disk */
|
|
20
|
+
saveToken(tokenData: TokenData): Promise<void>;
|
|
21
|
+
/** Get stored token */
|
|
22
|
+
getToken(): Promise<TokenData | null>;
|
|
23
|
+
/** Clear stored token */
|
|
24
|
+
clearToken(): Promise<void>;
|
|
25
|
+
/** Check if token exists */
|
|
26
|
+
hasToken(): Promise<boolean>;
|
|
27
|
+
/** Get user info from stored token */
|
|
28
|
+
getUserInfo(): Promise<UserInfo | null>;
|
|
29
|
+
}
|
|
30
|
+
export default TokenStore;
|
|
31
|
+
//# sourceMappingURL=token-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-store.d.ts","sourceRoot":"","sources":["../../src/auth/token-store.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEzE,cAAM,UAAU;IACd,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,aAAa,CAAS;;IAQ9B,uDAAuD;IACvD,OAAO,CAAC,gBAAgB;IAKxB,qCAAqC;IAC/B,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAWtC,mBAAmB;IACnB,OAAO,CAAC,IAAI,EAAE,SAAS,GAAG,gBAAgB;IAa1C,mBAAmB;IACnB,OAAO,CAAC,aAAa,EAAE,gBAAgB,GAAG,SAAS;IAUnD,yBAAyB;IACnB,SAAS,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAepD,uBAAuB;IACjB,QAAQ,IAAI,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAc3C,yBAAyB;IACnB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAUjC,4BAA4B;IACtB,QAAQ,IAAI,OAAO,CAAC,OAAO,CAAC;IASlC,sCAAsC;IAChC,WAAW,IAAI,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;CAY9C;AAED,eAAe,UAAU,CAAC"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secure Token Storage for Purmemo MCP
|
|
3
|
+
* Stores OAuth tokens securely in user's home directory
|
|
4
|
+
*/
|
|
5
|
+
import * as fs from 'fs/promises';
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
import * as crypto from 'crypto';
|
|
8
|
+
import * as os from 'os';
|
|
9
|
+
class TokenStore {
|
|
10
|
+
configDir;
|
|
11
|
+
tokenFile;
|
|
12
|
+
encryptionKey;
|
|
13
|
+
constructor() {
|
|
14
|
+
this.configDir = path.join(os.homedir(), '.purmemo');
|
|
15
|
+
this.tokenFile = path.join(this.configDir, 'auth.json');
|
|
16
|
+
this.encryptionKey = this.getEncryptionKey();
|
|
17
|
+
}
|
|
18
|
+
/** Get or generate encryption key for token storage */
|
|
19
|
+
getEncryptionKey() {
|
|
20
|
+
const machineId = os.hostname() + os.userInfo().username;
|
|
21
|
+
return crypto.createHash('sha256').update(machineId).digest();
|
|
22
|
+
}
|
|
23
|
+
/** Ensure config directory exists */
|
|
24
|
+
async ensureConfigDir() {
|
|
25
|
+
try {
|
|
26
|
+
await fs.mkdir(this.configDir, { recursive: true });
|
|
27
|
+
if (process.platform !== 'win32') {
|
|
28
|
+
await fs.chmod(this.configDir, 0o700);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
console.error('Failed to create config directory:', error);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/** Encrypt data */
|
|
36
|
+
encrypt(data) {
|
|
37
|
+
const iv = crypto.randomBytes(16);
|
|
38
|
+
const cipher = crypto.createCipheriv('aes-256-cbc', this.encryptionKey, iv);
|
|
39
|
+
let encrypted = cipher.update(JSON.stringify(data), 'utf8', 'hex');
|
|
40
|
+
encrypted += cipher.final('hex');
|
|
41
|
+
return {
|
|
42
|
+
iv: iv.toString('hex'),
|
|
43
|
+
data: encrypted
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/** Decrypt data */
|
|
47
|
+
decrypt(encryptedData) {
|
|
48
|
+
const iv = Buffer.from(encryptedData.iv, 'hex');
|
|
49
|
+
const decipher = crypto.createDecipheriv('aes-256-cbc', this.encryptionKey, iv);
|
|
50
|
+
let decrypted = decipher.update(encryptedData.data, 'hex', 'utf8');
|
|
51
|
+
decrypted += decipher.final('utf8');
|
|
52
|
+
return JSON.parse(decrypted);
|
|
53
|
+
}
|
|
54
|
+
/** Save token to disk */
|
|
55
|
+
async saveToken(tokenData) {
|
|
56
|
+
await this.ensureConfigDir();
|
|
57
|
+
const encrypted = this.encrypt(tokenData);
|
|
58
|
+
await fs.writeFile(this.tokenFile, JSON.stringify(encrypted, null, 2), 'utf8');
|
|
59
|
+
if (process.platform !== 'win32') {
|
|
60
|
+
await fs.chmod(this.tokenFile, 0o600);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/** Get stored token */
|
|
64
|
+
async getToken() {
|
|
65
|
+
try {
|
|
66
|
+
const data = await fs.readFile(this.tokenFile, 'utf8');
|
|
67
|
+
const encrypted = JSON.parse(data);
|
|
68
|
+
return this.decrypt(encrypted);
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
if (error.code === 'ENOENT') {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
console.error('Failed to read token:', error.message);
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/** Clear stored token */
|
|
79
|
+
async clearToken() {
|
|
80
|
+
try {
|
|
81
|
+
await fs.unlink(this.tokenFile);
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
if (error.code !== 'ENOENT') {
|
|
85
|
+
console.error('Failed to clear token:', error);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/** Check if token exists */
|
|
90
|
+
async hasToken() {
|
|
91
|
+
try {
|
|
92
|
+
await fs.access(this.tokenFile);
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/** Get user info from stored token */
|
|
100
|
+
async getUserInfo() {
|
|
101
|
+
const token = await this.getToken();
|
|
102
|
+
if (!token)
|
|
103
|
+
return null;
|
|
104
|
+
return {
|
|
105
|
+
user_id: token.user?.id,
|
|
106
|
+
email: token.user?.email,
|
|
107
|
+
tier: token.user_tier || 'free',
|
|
108
|
+
memory_limit: token.memory_limit,
|
|
109
|
+
expires_at: token.expires_at
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
export default TokenStore;
|
|
114
|
+
//# sourceMappingURL=token-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-store.js","sourceRoot":"","sources":["../../src/auth/token-store.ts"],"names":[],"mappings":"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"}
|