@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.
Files changed (52) hide show
  1. package/README.md +86 -9
  2. package/dist/auth/callback-server.d.ts +41 -0
  3. package/dist/auth/callback-server.d.ts.map +1 -0
  4. package/dist/auth/callback-server.js +247 -0
  5. package/dist/auth/callback-server.js.map +1 -0
  6. package/dist/auth/callback-server.test.d.ts +2 -0
  7. package/dist/auth/callback-server.test.d.ts.map +1 -0
  8. package/dist/auth/callback-server.test.js +233 -0
  9. package/dist/auth/callback-server.test.js.map +1 -0
  10. package/dist/auth/oauth-flow.d.ts +55 -0
  11. package/dist/auth/oauth-flow.d.ts.map +1 -0
  12. package/dist/auth/oauth-flow.js +120 -0
  13. package/dist/auth/oauth-flow.js.map +1 -0
  14. package/dist/auth/oauth-flow.test.d.ts +2 -0
  15. package/dist/auth/oauth-flow.test.d.ts.map +1 -0
  16. package/dist/auth/oauth-flow.test.js +154 -0
  17. package/dist/auth/oauth-flow.test.js.map +1 -0
  18. package/dist/auth/token-manager.d.ts +43 -0
  19. package/dist/auth/token-manager.d.ts.map +1 -0
  20. package/dist/auth/token-manager.js +105 -0
  21. package/dist/auth/token-manager.js.map +1 -0
  22. package/dist/auth/token-manager.test.d.ts +2 -0
  23. package/dist/auth/token-manager.test.d.ts.map +1 -0
  24. package/dist/auth/token-manager.test.js +190 -0
  25. package/dist/auth/token-manager.test.js.map +1 -0
  26. package/dist/auth/user-auth.d.ts +48 -0
  27. package/dist/auth/user-auth.d.ts.map +1 -0
  28. package/dist/auth/user-auth.js +186 -0
  29. package/dist/auth/user-auth.js.map +1 -0
  30. package/dist/auth/user-auth.test.d.ts +2 -0
  31. package/dist/auth/user-auth.test.d.ts.map +1 -0
  32. package/dist/auth/user-auth.test.js +366 -0
  33. package/dist/auth/user-auth.test.js.map +1 -0
  34. package/dist/client.d.ts +5 -0
  35. package/dist/client.d.ts.map +1 -1
  36. package/dist/client.js +42 -4
  37. package/dist/client.js.map +1 -1
  38. package/dist/client.test.js +2 -3
  39. package/dist/client.test.js.map +1 -1
  40. package/dist/index.js +109 -6
  41. package/dist/index.js.map +1 -1
  42. package/dist/index.test.js +5 -3
  43. package/dist/index.test.js.map +1 -1
  44. package/dist/scripts/authenticate.d.ts +12 -0
  45. package/dist/scripts/authenticate.d.ts.map +1 -0
  46. package/dist/scripts/authenticate.js +95 -0
  47. package/dist/scripts/authenticate.js.map +1 -0
  48. package/dist/types.d.ts +0 -1
  49. package/dist/types.d.ts.map +1 -1
  50. package/dist/types.test.js +0 -11
  51. package/dist/types.test.js.map +1 -1
  52. 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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=token-manager.test.d.ts.map
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=user-auth.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-auth.test.d.ts","sourceRoot":"","sources":["../../src/auth/user-auth.test.ts"],"names":[],"mappings":""}