@efficy/tribecrm-mcp-server 0.4.2 ā 0.5.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/README.md +86 -9
- package/dist/auth/callback-server.d.ts +41 -0
- package/dist/auth/callback-server.d.ts.map +1 -0
- package/dist/auth/callback-server.js +247 -0
- package/dist/auth/callback-server.js.map +1 -0
- package/dist/auth/callback-server.test.d.ts +2 -0
- package/dist/auth/callback-server.test.d.ts.map +1 -0
- package/dist/auth/callback-server.test.js +233 -0
- package/dist/auth/callback-server.test.js.map +1 -0
- package/dist/auth/oauth-flow.d.ts +55 -0
- package/dist/auth/oauth-flow.d.ts.map +1 -0
- package/dist/auth/oauth-flow.js +120 -0
- package/dist/auth/oauth-flow.js.map +1 -0
- package/dist/auth/oauth-flow.test.d.ts +2 -0
- package/dist/auth/oauth-flow.test.d.ts.map +1 -0
- package/dist/auth/oauth-flow.test.js +154 -0
- package/dist/auth/oauth-flow.test.js.map +1 -0
- package/dist/auth/token-manager.d.ts +43 -0
- package/dist/auth/token-manager.d.ts.map +1 -0
- package/dist/auth/token-manager.js +105 -0
- package/dist/auth/token-manager.js.map +1 -0
- package/dist/auth/token-manager.test.d.ts +2 -0
- package/dist/auth/token-manager.test.d.ts.map +1 -0
- package/dist/auth/token-manager.test.js +190 -0
- package/dist/auth/token-manager.test.js.map +1 -0
- package/dist/auth/user-auth.d.ts +48 -0
- package/dist/auth/user-auth.d.ts.map +1 -0
- package/dist/auth/user-auth.js +186 -0
- package/dist/auth/user-auth.js.map +1 -0
- package/dist/auth/user-auth.test.d.ts +2 -0
- package/dist/auth/user-auth.test.d.ts.map +1 -0
- package/dist/auth/user-auth.test.js +366 -0
- package/dist/auth/user-auth.test.js.map +1 -0
- package/dist/client.d.ts +5 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +42 -4
- package/dist/client.js.map +1 -1
- package/dist/client.test.js +2 -3
- package/dist/client.test.js.map +1 -1
- package/dist/index.js +109 -6
- package/dist/index.js.map +1 -1
- package/dist/index.test.js +5 -3
- package/dist/index.test.js.map +1 -1
- package/dist/scripts/authenticate.d.ts +12 -0
- package/dist/scripts/authenticate.d.ts.map +1 -0
- package/dist/scripts/authenticate.js +95 -0
- package/dist/scripts/authenticate.js.map +1 -0
- package/dist/types.d.ts +0 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.test.js +0 -11
- package/dist/types.test.js.map +1 -1
- package/package.json +3 -2
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { AuthToken } from '../types.js';
|
|
2
|
+
export interface StoredTokens extends AuthToken {
|
|
3
|
+
expires_at: number;
|
|
4
|
+
refresh_token?: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Token Manager for user OAuth authentication
|
|
8
|
+
* Handles storage, loading, and validation of OAuth tokens
|
|
9
|
+
*/
|
|
10
|
+
export declare class TokenManager {
|
|
11
|
+
private tokenFilePath;
|
|
12
|
+
constructor(tokenFilePath?: string);
|
|
13
|
+
/**
|
|
14
|
+
* Get default token file path: ~/.tribecrm/tokens.json
|
|
15
|
+
*/
|
|
16
|
+
private getDefaultTokenPath;
|
|
17
|
+
/**
|
|
18
|
+
* Load tokens from disk
|
|
19
|
+
* @returns StoredTokens or null if not found/invalid
|
|
20
|
+
*/
|
|
21
|
+
loadTokens(): StoredTokens | null;
|
|
22
|
+
/**
|
|
23
|
+
* Save tokens to disk
|
|
24
|
+
* @param tokenResponse Token response from OAuth server
|
|
25
|
+
*/
|
|
26
|
+
saveTokens(tokenResponse: AuthToken & {
|
|
27
|
+
refresh_token?: string;
|
|
28
|
+
}): void;
|
|
29
|
+
/**
|
|
30
|
+
* Check if stored tokens are valid (not expired)
|
|
31
|
+
* @param bufferSeconds Buffer time before actual expiry (default: 60s)
|
|
32
|
+
*/
|
|
33
|
+
areTokensValid(bufferSeconds?: number): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Delete stored tokens
|
|
36
|
+
*/
|
|
37
|
+
deleteTokens(): void;
|
|
38
|
+
/**
|
|
39
|
+
* Get token file path
|
|
40
|
+
*/
|
|
41
|
+
getTokenPath(): string;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=token-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-manager.d.ts","sourceRoot":"","sources":["../../src/auth/token-manager.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,MAAM,WAAW,YAAa,SAAQ,SAAS;IAC7C,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;GAGG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,aAAa,CAAS;gBAElB,aAAa,CAAC,EAAE,MAAM;IAIlC;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAY3B;;;OAGG;IACH,UAAU,IAAI,YAAY,GAAG,IAAI;IAsBjC;;;OAGG;IACH,UAAU,CAAC,aAAa,EAAE,SAAS,GAAG;QAAE,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAyBvE;;;OAGG;IACH,cAAc,CAAC,aAAa,GAAE,MAAW,GAAG,OAAO;IAYnD;;OAEG;IACH,YAAY,IAAI,IAAI;IAWpB;;OAEG;IACH,YAAY,IAAI,MAAM;CAGvB"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
/**
|
|
5
|
+
* Token Manager for user OAuth authentication
|
|
6
|
+
* Handles storage, loading, and validation of OAuth tokens
|
|
7
|
+
*/
|
|
8
|
+
export class TokenManager {
|
|
9
|
+
tokenFilePath;
|
|
10
|
+
constructor(tokenFilePath) {
|
|
11
|
+
this.tokenFilePath = tokenFilePath || this.getDefaultTokenPath();
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Get default token file path: ~/.tribecrm/tokens.json
|
|
15
|
+
*/
|
|
16
|
+
getDefaultTokenPath() {
|
|
17
|
+
const homeDir = os.homedir();
|
|
18
|
+
const configDir = path.join(homeDir, '.tribecrm');
|
|
19
|
+
// Ensure directory exists
|
|
20
|
+
if (!fs.existsSync(configDir)) {
|
|
21
|
+
fs.mkdirSync(configDir, { recursive: true, mode: 0o700 }); // Private directory
|
|
22
|
+
}
|
|
23
|
+
return path.join(configDir, 'tokens.json');
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Load tokens from disk
|
|
27
|
+
* @returns StoredTokens or null if not found/invalid
|
|
28
|
+
*/
|
|
29
|
+
loadTokens() {
|
|
30
|
+
try {
|
|
31
|
+
if (!fs.existsSync(this.tokenFilePath)) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
const data = fs.readFileSync(this.tokenFilePath, 'utf-8');
|
|
35
|
+
const tokens = JSON.parse(data);
|
|
36
|
+
// Validate token structure
|
|
37
|
+
if (!tokens.access_token || !tokens.expires_at) {
|
|
38
|
+
console.error('Invalid token structure in stored file');
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
return tokens;
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
console.error('Error loading tokens:', error.message);
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Save tokens to disk
|
|
50
|
+
* @param tokenResponse Token response from OAuth server
|
|
51
|
+
*/
|
|
52
|
+
saveTokens(tokenResponse) {
|
|
53
|
+
try {
|
|
54
|
+
const now = Date.now();
|
|
55
|
+
const storedTokens = {
|
|
56
|
+
access_token: tokenResponse.access_token,
|
|
57
|
+
token_type: tokenResponse.token_type,
|
|
58
|
+
expires_in: tokenResponse.expires_in,
|
|
59
|
+
scope: tokenResponse.scope,
|
|
60
|
+
expires_at: now + (tokenResponse.expires_in * 1000),
|
|
61
|
+
refresh_token: tokenResponse.refresh_token,
|
|
62
|
+
};
|
|
63
|
+
// Write with restricted permissions (owner only)
|
|
64
|
+
fs.writeFileSync(this.tokenFilePath, JSON.stringify(storedTokens, null, 2), { mode: 0o600 });
|
|
65
|
+
console.error(`Tokens saved to ${this.tokenFilePath}`);
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
throw new Error(`Failed to save tokens: ${error.message}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Check if stored tokens are valid (not expired)
|
|
73
|
+
* @param bufferSeconds Buffer time before actual expiry (default: 60s)
|
|
74
|
+
*/
|
|
75
|
+
areTokensValid(bufferSeconds = 60) {
|
|
76
|
+
const tokens = this.loadTokens();
|
|
77
|
+
if (!tokens) {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
const now = Date.now();
|
|
81
|
+
const expiryWithBuffer = tokens.expires_at - (bufferSeconds * 1000);
|
|
82
|
+
return now < expiryWithBuffer;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Delete stored tokens
|
|
86
|
+
*/
|
|
87
|
+
deleteTokens() {
|
|
88
|
+
try {
|
|
89
|
+
if (fs.existsSync(this.tokenFilePath)) {
|
|
90
|
+
fs.unlinkSync(this.tokenFilePath);
|
|
91
|
+
console.error('Tokens deleted');
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
console.error('Error deleting tokens:', error.message);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Get token file path
|
|
100
|
+
*/
|
|
101
|
+
getTokenPath() {
|
|
102
|
+
return this.tokenFilePath;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=token-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-manager.js","sourceRoot":"","sources":["../../src/auth/token-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-manager.test.d.ts","sourceRoot":"","sources":["../../src/auth/token-manager.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import os from 'os';
|
|
5
|
+
import { TokenManager } from './token-manager.js';
|
|
6
|
+
// Mock fs module
|
|
7
|
+
vi.mock('fs');
|
|
8
|
+
const mockedFs = vi.mocked(fs, true);
|
|
9
|
+
describe('TokenManager', () => {
|
|
10
|
+
let tokenManager;
|
|
11
|
+
const testTokenPath = path.join(os.homedir(), '.tribecrm', 'tokens.json');
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
vi.clearAllMocks();
|
|
14
|
+
tokenManager = new TokenManager();
|
|
15
|
+
});
|
|
16
|
+
afterEach(() => {
|
|
17
|
+
vi.restoreAllMocks();
|
|
18
|
+
});
|
|
19
|
+
describe('loadTokens', () => {
|
|
20
|
+
it('should return null if token file does not exist', () => {
|
|
21
|
+
mockedFs.existsSync.mockReturnValue(false);
|
|
22
|
+
const result = tokenManager.loadTokens();
|
|
23
|
+
expect(result).toBeNull();
|
|
24
|
+
expect(mockedFs.existsSync).toHaveBeenCalledWith(testTokenPath);
|
|
25
|
+
});
|
|
26
|
+
it('should load and parse valid tokens from file', () => {
|
|
27
|
+
const mockTokens = {
|
|
28
|
+
access_token: 'test-token',
|
|
29
|
+
token_type: 'bearer',
|
|
30
|
+
expires_in: 86400,
|
|
31
|
+
scope: 'read write offline',
|
|
32
|
+
expires_at: Date.now() + 86400000,
|
|
33
|
+
refresh_token: 'test-refresh-token',
|
|
34
|
+
};
|
|
35
|
+
mockedFs.existsSync.mockReturnValue(true);
|
|
36
|
+
mockedFs.readFileSync.mockReturnValue(JSON.stringify(mockTokens));
|
|
37
|
+
const result = tokenManager.loadTokens();
|
|
38
|
+
expect(result).toEqual(mockTokens);
|
|
39
|
+
expect(mockedFs.readFileSync).toHaveBeenCalledWith(testTokenPath, 'utf-8');
|
|
40
|
+
});
|
|
41
|
+
it('should return null for invalid token structure', () => {
|
|
42
|
+
const invalidTokens = {
|
|
43
|
+
access_token: 'test-token',
|
|
44
|
+
// Missing expires_at
|
|
45
|
+
};
|
|
46
|
+
mockedFs.existsSync.mockReturnValue(true);
|
|
47
|
+
mockedFs.readFileSync.mockReturnValue(JSON.stringify(invalidTokens));
|
|
48
|
+
const result = tokenManager.loadTokens();
|
|
49
|
+
expect(result).toBeNull();
|
|
50
|
+
});
|
|
51
|
+
it('should handle JSON parse errors', () => {
|
|
52
|
+
mockedFs.existsSync.mockReturnValue(true);
|
|
53
|
+
mockedFs.readFileSync.mockReturnValue('invalid json');
|
|
54
|
+
const result = tokenManager.loadTokens();
|
|
55
|
+
expect(result).toBeNull();
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
describe('saveTokens', () => {
|
|
59
|
+
it('should save tokens with correct structure', () => {
|
|
60
|
+
const tokenResponse = {
|
|
61
|
+
access_token: 'new-token',
|
|
62
|
+
token_type: 'bearer',
|
|
63
|
+
expires_in: 86400,
|
|
64
|
+
scope: 'read write offline',
|
|
65
|
+
refresh_token: 'new-refresh-token',
|
|
66
|
+
};
|
|
67
|
+
mockedFs.mkdirSync.mockReturnValue(undefined);
|
|
68
|
+
mockedFs.writeFileSync.mockReturnValue(undefined);
|
|
69
|
+
tokenManager.saveTokens(tokenResponse);
|
|
70
|
+
expect(mockedFs.writeFileSync).toHaveBeenCalled();
|
|
71
|
+
const writeCall = mockedFs.writeFileSync.mock.calls[0];
|
|
72
|
+
expect(writeCall[0]).toBe(testTokenPath);
|
|
73
|
+
const savedTokens = JSON.parse(writeCall[1]);
|
|
74
|
+
expect(savedTokens).toHaveProperty('access_token', 'new-token');
|
|
75
|
+
expect(savedTokens).toHaveProperty('refresh_token', 'new-refresh-token');
|
|
76
|
+
expect(savedTokens).toHaveProperty('expires_at');
|
|
77
|
+
expect(savedTokens.expires_at).toBeGreaterThan(Date.now());
|
|
78
|
+
});
|
|
79
|
+
it('should save tokens with restricted permissions', () => {
|
|
80
|
+
const tokenResponse = {
|
|
81
|
+
access_token: 'token',
|
|
82
|
+
token_type: 'bearer',
|
|
83
|
+
expires_in: 86400,
|
|
84
|
+
scope: 'read',
|
|
85
|
+
};
|
|
86
|
+
mockedFs.mkdirSync.mockReturnValue(undefined);
|
|
87
|
+
mockedFs.writeFileSync.mockReturnValue(undefined);
|
|
88
|
+
tokenManager.saveTokens(tokenResponse);
|
|
89
|
+
const writeCall = mockedFs.writeFileSync.mock.calls[0];
|
|
90
|
+
expect(writeCall[2]).toEqual({ mode: 0o600 });
|
|
91
|
+
});
|
|
92
|
+
it('should throw error on write failure', () => {
|
|
93
|
+
const tokenResponse = {
|
|
94
|
+
access_token: 'token',
|
|
95
|
+
token_type: 'bearer',
|
|
96
|
+
expires_in: 86400,
|
|
97
|
+
scope: 'read',
|
|
98
|
+
};
|
|
99
|
+
mockedFs.mkdirSync.mockReturnValue(undefined);
|
|
100
|
+
mockedFs.writeFileSync.mockImplementation(() => {
|
|
101
|
+
throw new Error('Write failed');
|
|
102
|
+
});
|
|
103
|
+
expect(() => tokenManager.saveTokens(tokenResponse)).toThrow('Failed to save tokens');
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
describe('areTokensValid', () => {
|
|
107
|
+
it('should return false if no tokens exist', () => {
|
|
108
|
+
mockedFs.existsSync.mockReturnValue(false);
|
|
109
|
+
const result = tokenManager.areTokensValid();
|
|
110
|
+
expect(result).toBe(false);
|
|
111
|
+
});
|
|
112
|
+
it('should return true for valid unexpired tokens', () => {
|
|
113
|
+
const mockTokens = {
|
|
114
|
+
access_token: 'token',
|
|
115
|
+
token_type: 'bearer',
|
|
116
|
+
expires_in: 86400,
|
|
117
|
+
scope: 'read',
|
|
118
|
+
expires_at: Date.now() + 120000, // 2 minutes in future
|
|
119
|
+
};
|
|
120
|
+
mockedFs.existsSync.mockReturnValue(true);
|
|
121
|
+
mockedFs.readFileSync.mockReturnValue(JSON.stringify(mockTokens));
|
|
122
|
+
const result = tokenManager.areTokensValid();
|
|
123
|
+
expect(result).toBe(true);
|
|
124
|
+
});
|
|
125
|
+
it('should return false for expired tokens', () => {
|
|
126
|
+
const mockTokens = {
|
|
127
|
+
access_token: 'token',
|
|
128
|
+
token_type: 'bearer',
|
|
129
|
+
expires_in: 86400,
|
|
130
|
+
scope: 'read',
|
|
131
|
+
expires_at: Date.now() - 1000, // Expired
|
|
132
|
+
};
|
|
133
|
+
mockedFs.existsSync.mockReturnValue(true);
|
|
134
|
+
mockedFs.readFileSync.mockReturnValue(JSON.stringify(mockTokens));
|
|
135
|
+
const result = tokenManager.areTokensValid();
|
|
136
|
+
expect(result).toBe(false);
|
|
137
|
+
});
|
|
138
|
+
it('should respect buffer time', () => {
|
|
139
|
+
const mockTokens = {
|
|
140
|
+
access_token: 'token',
|
|
141
|
+
token_type: 'bearer',
|
|
142
|
+
expires_in: 86400,
|
|
143
|
+
scope: 'read',
|
|
144
|
+
expires_at: Date.now() + 30000, // 30 seconds in future
|
|
145
|
+
};
|
|
146
|
+
mockedFs.existsSync.mockReturnValue(true);
|
|
147
|
+
mockedFs.readFileSync.mockReturnValue(JSON.stringify(mockTokens));
|
|
148
|
+
// With 60s buffer, should be invalid
|
|
149
|
+
const resultWithBuffer = tokenManager.areTokensValid(60);
|
|
150
|
+
expect(resultWithBuffer).toBe(false);
|
|
151
|
+
// With 20s buffer, should be valid
|
|
152
|
+
const resultWithSmallBuffer = tokenManager.areTokensValid(20);
|
|
153
|
+
expect(resultWithSmallBuffer).toBe(true);
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
describe('deleteTokens', () => {
|
|
157
|
+
it('should delete token file if it exists', () => {
|
|
158
|
+
mockedFs.existsSync.mockReturnValue(true);
|
|
159
|
+
mockedFs.unlinkSync.mockReturnValue(undefined);
|
|
160
|
+
tokenManager.deleteTokens();
|
|
161
|
+
expect(mockedFs.unlinkSync).toHaveBeenCalledWith(testTokenPath);
|
|
162
|
+
});
|
|
163
|
+
it('should not throw if file does not exist', () => {
|
|
164
|
+
mockedFs.existsSync.mockReturnValue(false);
|
|
165
|
+
expect(() => tokenManager.deleteTokens()).not.toThrow();
|
|
166
|
+
expect(mockedFs.unlinkSync).not.toHaveBeenCalled();
|
|
167
|
+
});
|
|
168
|
+
it('should handle deletion errors gracefully', () => {
|
|
169
|
+
mockedFs.existsSync.mockReturnValue(true);
|
|
170
|
+
mockedFs.unlinkSync.mockImplementation(() => {
|
|
171
|
+
throw new Error('Delete failed');
|
|
172
|
+
});
|
|
173
|
+
expect(() => tokenManager.deleteTokens()).not.toThrow();
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
describe('getTokenPath', () => {
|
|
177
|
+
it('should return default token path', () => {
|
|
178
|
+
const path = tokenManager.getTokenPath();
|
|
179
|
+
expect(path).toContain('.tribecrm');
|
|
180
|
+
expect(path).toContain('tokens.json');
|
|
181
|
+
});
|
|
182
|
+
it('should return custom token path if provided', () => {
|
|
183
|
+
const customPath = '/custom/path/tokens.json';
|
|
184
|
+
const customTokenManager = new TokenManager(customPath);
|
|
185
|
+
const path = customTokenManager.getTokenPath();
|
|
186
|
+
expect(path).toBe(customPath);
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
//# sourceMappingURL=token-manager.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-manager.test.js","sourceRoot":"","sources":["../../src/auth/token-manager.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export interface UserAuthConfig {
|
|
2
|
+
authUrl: string;
|
|
3
|
+
clientId: string;
|
|
4
|
+
clientSecret?: string;
|
|
5
|
+
redirectUri: string;
|
|
6
|
+
callbackPort?: number;
|
|
7
|
+
tokenFilePath?: string;
|
|
8
|
+
usePKCE?: boolean;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* User Authentication Coordinator
|
|
12
|
+
* Manages the complete OAuth flow for user authentication
|
|
13
|
+
*/
|
|
14
|
+
export declare class UserAuth {
|
|
15
|
+
private tokenManager;
|
|
16
|
+
private oauthFlow;
|
|
17
|
+
private config;
|
|
18
|
+
private currentToken;
|
|
19
|
+
private tokenExpiry;
|
|
20
|
+
constructor(config: UserAuthConfig);
|
|
21
|
+
/**
|
|
22
|
+
* Get valid access token (load from disk, refresh if needed, or throw error)
|
|
23
|
+
* @returns Valid access token
|
|
24
|
+
*/
|
|
25
|
+
getAccessToken(): Promise<string>;
|
|
26
|
+
/**
|
|
27
|
+
* Perform interactive OAuth authentication flow
|
|
28
|
+
* Opens browser, waits for callback, exchanges code for tokens
|
|
29
|
+
*/
|
|
30
|
+
authenticate(): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Check if user is authenticated (has valid or refreshable tokens)
|
|
33
|
+
*/
|
|
34
|
+
isAuthenticated(): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Logout user (delete stored tokens)
|
|
37
|
+
*/
|
|
38
|
+
logout(): void;
|
|
39
|
+
/**
|
|
40
|
+
* Get token file path
|
|
41
|
+
*/
|
|
42
|
+
getTokenPath(): string;
|
|
43
|
+
/**
|
|
44
|
+
* Open URL in default browser (cross-platform)
|
|
45
|
+
*/
|
|
46
|
+
private openBrowser;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=user-auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user-auth.d.ts","sourceRoot":"","sources":["../../src/auth/user-auth.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;GAGG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,YAAY,CAAyD;IAC7E,OAAO,CAAC,WAAW,CAAa;gBAEpB,MAAM,EAAE,cAAc;IAYlC;;;OAGG;IACG,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC;IAwDvC;;;OAGG;IACG,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IA2DnC;;OAEG;IACH,eAAe,IAAI,OAAO;IAgB1B;;OAEG;IACH,MAAM,IAAI,IAAI;IAOd;;OAEG;IACH,YAAY,IAAI,MAAM;IAItB;;OAEG;YACW,WAAW;CAuB1B"}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { TokenManager } from './token-manager.js';
|
|
2
|
+
import { OAuthFlow } from './oauth-flow.js';
|
|
3
|
+
import { CallbackServer } from './callback-server.js';
|
|
4
|
+
/**
|
|
5
|
+
* User Authentication Coordinator
|
|
6
|
+
* Manages the complete OAuth flow for user authentication
|
|
7
|
+
*/
|
|
8
|
+
export class UserAuth {
|
|
9
|
+
tokenManager;
|
|
10
|
+
oauthFlow;
|
|
11
|
+
config;
|
|
12
|
+
currentToken = null;
|
|
13
|
+
tokenExpiry = 0;
|
|
14
|
+
constructor(config) {
|
|
15
|
+
this.config = config;
|
|
16
|
+
this.tokenManager = new TokenManager(config.tokenFilePath);
|
|
17
|
+
this.oauthFlow = new OAuthFlow({
|
|
18
|
+
authUrl: config.authUrl,
|
|
19
|
+
clientId: config.clientId,
|
|
20
|
+
clientSecret: config.clientSecret,
|
|
21
|
+
redirectUri: config.redirectUri,
|
|
22
|
+
usePKCE: config.usePKCE ?? false, // Default to false for TribeCRM compatibility
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Get valid access token (load from disk, refresh if needed, or throw error)
|
|
27
|
+
* @returns Valid access token
|
|
28
|
+
*/
|
|
29
|
+
async getAccessToken() {
|
|
30
|
+
// Try to use current token in memory
|
|
31
|
+
if (this.currentToken && this.tokenExpiry > Date.now() + 60000) {
|
|
32
|
+
return this.currentToken.access_token;
|
|
33
|
+
}
|
|
34
|
+
// Try to load from disk
|
|
35
|
+
const storedTokens = this.tokenManager.loadTokens();
|
|
36
|
+
if (storedTokens) {
|
|
37
|
+
// Check if still valid
|
|
38
|
+
if (this.tokenManager.areTokensValid()) {
|
|
39
|
+
this.currentToken = storedTokens;
|
|
40
|
+
this.tokenExpiry = storedTokens.expires_at;
|
|
41
|
+
return storedTokens.access_token;
|
|
42
|
+
}
|
|
43
|
+
// Try to refresh
|
|
44
|
+
if (storedTokens.refresh_token) {
|
|
45
|
+
try {
|
|
46
|
+
console.error('Access token expired, refreshing...');
|
|
47
|
+
const newTokens = await this.oauthFlow.refreshAccessToken(storedTokens.refresh_token);
|
|
48
|
+
this.tokenManager.saveTokens(newTokens);
|
|
49
|
+
this.currentToken = newTokens;
|
|
50
|
+
this.tokenExpiry = Date.now() + (newTokens.expires_in * 1000);
|
|
51
|
+
console.error('Access token refreshed successfully');
|
|
52
|
+
return newTokens.access_token;
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
console.error('Token refresh failed:', error.message);
|
|
56
|
+
// Fall through to error
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// No valid tokens - user needs to authenticate
|
|
61
|
+
const tokenPath = this.tokenManager.getTokenPath();
|
|
62
|
+
throw new Error('š User Authentication Required\n\n' +
|
|
63
|
+
'No valid authentication tokens found. You need to authenticate first.\n\n' +
|
|
64
|
+
'⨠EASY WAY - Authenticate directly from Claude:\n\n' +
|
|
65
|
+
' Ask me: "Can you authenticate with TribeCRM?"\n' +
|
|
66
|
+
' I\'ll use the tribecrm_authenticate tool to generate a login link for you!\n\n' +
|
|
67
|
+
'š Alternative - Manual authentication:\n\n' +
|
|
68
|
+
'1. Open your terminal/command prompt\n' +
|
|
69
|
+
'2. Navigate to your TribeCRM MCP server directory\n' +
|
|
70
|
+
'3. Run: npm run authenticate\n' +
|
|
71
|
+
'4. Your browser will open for TribeCRM login\n' +
|
|
72
|
+
'5. After successful login, tokens will be saved to:\n' +
|
|
73
|
+
` ${tokenPath}\n\n` +
|
|
74
|
+
'š” Or switch to app authentication by setting:\n' +
|
|
75
|
+
' TRIBECRM_AUTH=app (in your Claude Desktop config)\n\n' +
|
|
76
|
+
'For more help, see: https://github.com/efficy-sa/tribecrm-mcp-server#user-authentication');
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Perform interactive OAuth authentication flow
|
|
80
|
+
* Opens browser, waits for callback, exchanges code for tokens
|
|
81
|
+
*/
|
|
82
|
+
async authenticate() {
|
|
83
|
+
const callbackPort = this.config.callbackPort || 3000;
|
|
84
|
+
console.error('\nš Starting TribeCRM User Authentication...\n');
|
|
85
|
+
// Generate PKCE pair (only if usePKCE is enabled)
|
|
86
|
+
const pkce = this.config.usePKCE ? this.oauthFlow.generatePKCE() : null;
|
|
87
|
+
const state = OAuthFlow.generateState();
|
|
88
|
+
// Generate consent URL
|
|
89
|
+
const consentUrl = this.oauthFlow.generateConsentUrl(state, pkce?.challenge);
|
|
90
|
+
// Start callback server
|
|
91
|
+
const callbackServer = new CallbackServer(callbackPort);
|
|
92
|
+
console.error('š Step 1: Opening your browser for authentication...');
|
|
93
|
+
console.error('');
|
|
94
|
+
console.error(`If the browser doesn't open automatically, visit this URL:`);
|
|
95
|
+
console.error(` ${consentUrl}`);
|
|
96
|
+
console.error('');
|
|
97
|
+
// Open browser
|
|
98
|
+
await this.openBrowser(consentUrl);
|
|
99
|
+
console.error('ā³ Step 2: Waiting for authentication callback...');
|
|
100
|
+
console.error(' (Please log in to TribeCRM in your browser)');
|
|
101
|
+
console.error('');
|
|
102
|
+
try {
|
|
103
|
+
// Wait for OAuth callback
|
|
104
|
+
const result = await callbackServer.waitForCallback(state);
|
|
105
|
+
console.error('ā Step 3: Authorization code received');
|
|
106
|
+
console.error('');
|
|
107
|
+
console.error('š Step 4: Exchanging code for access token...');
|
|
108
|
+
// Exchange code for tokens
|
|
109
|
+
const tokens = await this.oauthFlow.exchangeCodeForTokens(result.code, pkce?.verifier);
|
|
110
|
+
// Save tokens
|
|
111
|
+
this.tokenManager.saveTokens(tokens);
|
|
112
|
+
// Update in-memory tokens
|
|
113
|
+
this.currentToken = tokens;
|
|
114
|
+
this.tokenExpiry = Date.now() + (tokens.expires_in * 1000);
|
|
115
|
+
console.error('');
|
|
116
|
+
console.error('ā
Authentication successful!');
|
|
117
|
+
console.error(` Tokens saved to: ${this.tokenManager.getTokenPath()}`);
|
|
118
|
+
console.error('');
|
|
119
|
+
console.error('You can now use the TribeCRM MCP server with user authentication.');
|
|
120
|
+
console.error('');
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
callbackServer.stop();
|
|
124
|
+
throw new Error(`Authentication failed: ${error.message}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Check if user is authenticated (has valid or refreshable tokens)
|
|
129
|
+
*/
|
|
130
|
+
isAuthenticated() {
|
|
131
|
+
// Check in-memory token
|
|
132
|
+
if (this.currentToken && this.tokenExpiry > Date.now() + 60000) {
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
// Check stored tokens
|
|
136
|
+
const tokens = this.tokenManager.loadTokens();
|
|
137
|
+
if (!tokens) {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
// Valid if not expired or has refresh token
|
|
141
|
+
return this.tokenManager.areTokensValid() || !!tokens.refresh_token;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Logout user (delete stored tokens)
|
|
145
|
+
*/
|
|
146
|
+
logout() {
|
|
147
|
+
this.tokenManager.deleteTokens();
|
|
148
|
+
this.currentToken = null;
|
|
149
|
+
this.tokenExpiry = 0;
|
|
150
|
+
console.error('Logged out successfully');
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Get token file path
|
|
154
|
+
*/
|
|
155
|
+
getTokenPath() {
|
|
156
|
+
return this.tokenManager.getTokenPath();
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Open URL in default browser (cross-platform)
|
|
160
|
+
*/
|
|
161
|
+
async openBrowser(url) {
|
|
162
|
+
const { exec } = await import('child_process');
|
|
163
|
+
const { promisify } = await import('util');
|
|
164
|
+
const execAsync = promisify(exec);
|
|
165
|
+
const platform = process.platform;
|
|
166
|
+
try {
|
|
167
|
+
if (platform === 'darwin') {
|
|
168
|
+
// macOS
|
|
169
|
+
await execAsync(`open "${url}"`);
|
|
170
|
+
}
|
|
171
|
+
else if (platform === 'win32') {
|
|
172
|
+
// Windows
|
|
173
|
+
await execAsync(`start "" "${url}"`);
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
// Linux/Unix
|
|
177
|
+
await execAsync(`xdg-open "${url}"`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
// If opening browser fails, user can still copy the URL
|
|
182
|
+
console.error('Note: Could not open browser automatically. Please open the URL manually.');
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
//# sourceMappingURL=user-auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user-auth.js","sourceRoot":"","sources":["../../src/auth/user-auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user-auth.test.d.ts","sourceRoot":"","sources":["../../src/auth/user-auth.test.ts"],"names":[],"mappings":""}
|