@brainwavesio/google-docs-mcp 1.0.0 → 1.0.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/src/auth.ts DELETED
@@ -1,228 +0,0 @@
1
- // src/auth.ts
2
- import { google } from 'googleapis';
3
- import { OAuth2Client } from 'google-auth-library';
4
- import { JWT } from 'google-auth-library';
5
- import * as fs from 'fs/promises';
6
- import * as path from 'path';
7
- import * as readline from 'readline/promises';
8
- import { fileURLToPath } from 'url';
9
-
10
- // --- Calculate paths relative to this script file (ESM way) ---
11
- const __filename = fileURLToPath(import.meta.url);
12
- const __dirname = path.dirname(__filename);
13
- const projectRootDir = path.resolve(__dirname, '..');
14
-
15
- const TOKEN_PATH = path.join(projectRootDir, 'token.json');
16
- const CREDENTIALS_PATH = path.join(projectRootDir, 'credentials.json');
17
- // --- End of path calculation ---
18
-
19
- const SCOPES = [
20
- 'https://www.googleapis.com/auth/documents',
21
- 'https://www.googleapis.com/auth/drive',
22
- 'https://www.googleapis.com/auth/spreadsheets'
23
- ];
24
-
25
- // --- Environment variable authentication ---
26
- // Supports OAuth credentials via env vars for containerized/npx usage:
27
- // - GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, GOOGLE_REFRESH_TOKEN
28
- // - Or GOOGLE_CREDENTIALS_JSON (full credentials.json content as string)
29
- function hasEnvCredentials(): boolean {
30
- return !!(
31
- (process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET && process.env.GOOGLE_REFRESH_TOKEN) ||
32
- process.env.GOOGLE_CREDENTIALS_JSON
33
- );
34
- }
35
-
36
- async function authorizeWithEnvCredentials(): Promise<OAuth2Client> {
37
- let clientId: string;
38
- let clientSecret: string;
39
- let refreshToken: string | undefined;
40
-
41
- if (process.env.GOOGLE_CREDENTIALS_JSON) {
42
- // Parse the full credentials.json from env var
43
- const keys = JSON.parse(process.env.GOOGLE_CREDENTIALS_JSON);
44
- const key = keys.installed || keys.web;
45
- if (!key) {
46
- throw new Error('GOOGLE_CREDENTIALS_JSON must contain "installed" or "web" key');
47
- }
48
- clientId = key.client_id;
49
- clientSecret = key.client_secret;
50
- refreshToken = process.env.GOOGLE_REFRESH_TOKEN;
51
- } else {
52
- clientId = process.env.GOOGLE_CLIENT_ID!;
53
- clientSecret = process.env.GOOGLE_CLIENT_SECRET!;
54
- refreshToken = process.env.GOOGLE_REFRESH_TOKEN;
55
- }
56
-
57
- const client = new google.auth.OAuth2(clientId, clientSecret);
58
-
59
- if (refreshToken) {
60
- client.setCredentials({ refresh_token: refreshToken });
61
- console.error('Using OAuth credentials from environment variables.');
62
- return client;
63
- }
64
-
65
- // No refresh token - need to do interactive auth flow
66
- console.error('No GOOGLE_REFRESH_TOKEN found. Starting interactive OAuth flow...');
67
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
68
-
69
- const authorizeUrl = client.generateAuthUrl({
70
- access_type: 'offline',
71
- scope: SCOPES.join(' '),
72
- });
73
-
74
- console.error('Authorize this app by visiting this url:', authorizeUrl);
75
- const code = await rl.question('Enter the code from that page here: ');
76
- rl.close();
77
-
78
- const { tokens } = await client.getToken(code);
79
- client.setCredentials(tokens);
80
-
81
- if (tokens.refresh_token) {
82
- console.error('\n=== SAVE THIS REFRESH TOKEN ===');
83
- console.error('Add this to your environment:');
84
- console.error(`GOOGLE_REFRESH_TOKEN=${tokens.refresh_token}`);
85
- console.error('===============================\n');
86
- }
87
-
88
- console.error('Authentication successful!');
89
- return client;
90
- }
91
- // --- End of environment variable authentication ---
92
-
93
- // --- Service Account Authentication ---
94
- async function authorizeWithServiceAccount(): Promise<JWT> {
95
- const serviceAccountPath = process.env.SERVICE_ACCOUNT_PATH!;
96
- const impersonateUser = process.env.GOOGLE_IMPERSONATE_USER;
97
- try {
98
- const keyFileContent = await fs.readFile(serviceAccountPath, 'utf8');
99
- const serviceAccountKey = JSON.parse(keyFileContent);
100
-
101
- const auth = new JWT({
102
- email: serviceAccountKey.client_email,
103
- key: serviceAccountKey.private_key,
104
- scopes: SCOPES,
105
- subject: impersonateUser,
106
- });
107
- await auth.authorize();
108
- if (impersonateUser) {
109
- console.error(`Service Account authentication successful, impersonating: ${impersonateUser}`);
110
- } else {
111
- console.error('Service Account authentication successful!');
112
- }
113
- return auth;
114
- } catch (error: any) {
115
- if (error.code === 'ENOENT') {
116
- console.error(`FATAL: Service account key file not found at path: ${serviceAccountPath}`);
117
- throw new Error(`Service account key file not found. Please check the path in SERVICE_ACCOUNT_PATH.`);
118
- }
119
- console.error('FATAL: Error loading or authorizing the service account key:', error.message);
120
- throw new Error('Failed to authorize using the service account. Ensure the key file is valid and the path is correct.');
121
- }
122
- }
123
- // --- End of Service Account Authentication ---
124
-
125
- // --- File-based OAuth (original behavior) ---
126
- async function loadSavedCredentialsIfExist(): Promise<OAuth2Client | null> {
127
- try {
128
- const content = await fs.readFile(TOKEN_PATH);
129
- const credentials = JSON.parse(content.toString());
130
- const { client_secret, client_id, redirect_uris } = await loadClientSecrets();
131
- const client = new google.auth.OAuth2(client_id, client_secret, redirect_uris?.[0]);
132
- client.setCredentials(credentials);
133
- return client;
134
- } catch (err) {
135
- return null;
136
- }
137
- }
138
-
139
- async function loadClientSecrets() {
140
- const content = await fs.readFile(CREDENTIALS_PATH);
141
- const keys = JSON.parse(content.toString());
142
- const key = keys.installed || keys.web;
143
- if (!key) throw new Error("Could not find client secrets in credentials.json.");
144
- return {
145
- client_id: key.client_id,
146
- client_secret: key.client_secret,
147
- redirect_uris: key.redirect_uris || ['http://localhost:3000/'],
148
- client_type: keys.web ? 'web' : 'installed'
149
- };
150
- }
151
-
152
- async function saveCredentials(client: OAuth2Client): Promise<void> {
153
- const { client_secret, client_id } = await loadClientSecrets();
154
- const payload = JSON.stringify({
155
- type: 'authorized_user',
156
- client_id: client_id,
157
- client_secret: client_secret,
158
- refresh_token: client.credentials.refresh_token,
159
- });
160
- await fs.writeFile(TOKEN_PATH, payload);
161
- console.error('Token stored to', TOKEN_PATH);
162
- }
163
-
164
- async function authenticate(): Promise<OAuth2Client> {
165
- const { client_secret, client_id, redirect_uris, client_type } = await loadClientSecrets();
166
- const redirectUri = client_type === 'web' ? redirect_uris[0] : 'urn:ietf:wg:oauth:2.0:oob';
167
- console.error(`DEBUG: Using redirect URI: ${redirectUri}`);
168
- console.error(`DEBUG: Client type: ${client_type}`);
169
- const oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirectUri);
170
-
171
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
172
-
173
- const authorizeUrl = oAuth2Client.generateAuthUrl({
174
- access_type: 'offline',
175
- scope: SCOPES.join(' '),
176
- });
177
-
178
- console.error('DEBUG: Generated auth URL:', authorizeUrl);
179
- console.error('Authorize this app by visiting this url:', authorizeUrl);
180
- const code = await rl.question('Enter the code from that page here: ');
181
- rl.close();
182
-
183
- try {
184
- const { tokens } = await oAuth2Client.getToken(code);
185
- oAuth2Client.setCredentials(tokens);
186
- if (tokens.refresh_token) {
187
- await saveCredentials(oAuth2Client);
188
- } else {
189
- console.error("Did not receive refresh token. Token might expire.");
190
- }
191
- console.error('Authentication successful!');
192
- return oAuth2Client;
193
- } catch (err) {
194
- console.error('Error retrieving access token', err);
195
- throw new Error('Authentication failed');
196
- }
197
- }
198
- // --- End of file-based OAuth ---
199
-
200
- // --- Main exported function ---
201
- // Priority order:
202
- // 1. Service account (SERVICE_ACCOUNT_PATH)
203
- // 2. Environment variables (GOOGLE_CLIENT_ID + GOOGLE_CLIENT_SECRET + GOOGLE_REFRESH_TOKEN, or GOOGLE_CREDENTIALS_JSON)
204
- // 3. File-based OAuth (credentials.json + token.json)
205
- export async function authorize(): Promise<OAuth2Client | JWT> {
206
- // 1. Check for service account
207
- if (process.env.SERVICE_ACCOUNT_PATH) {
208
- console.error('Service account path detected. Attempting service account authentication...');
209
- return authorizeWithServiceAccount();
210
- }
211
-
212
- // 2. Check for env var credentials
213
- if (hasEnvCredentials()) {
214
- console.error('Environment variable credentials detected. Using env-based authentication...');
215
- return authorizeWithEnvCredentials();
216
- }
217
-
218
- // 3. Fall back to file-based OAuth
219
- console.error('Using file-based OAuth flow...');
220
- let client = await loadSavedCredentialsIfExist();
221
- if (client) {
222
- console.error('Using saved credentials.');
223
- return client;
224
- }
225
- console.error('Starting authentication flow...');
226
- client = await authenticate();
227
- return client;
228
- }
@@ -1,101 +0,0 @@
1
- // src/auth.ts
2
- import { google } from 'googleapis';
3
- import { OAuth2Client } from 'google-auth-library';
4
- import * as fs from 'fs/promises';
5
- import * as path from 'path';
6
- import * as readline from 'readline/promises';
7
- import { fileURLToPath } from 'url';
8
-
9
- // --- Calculate paths relative to this script file (ESM way) ---
10
- const __filename = fileURLToPath(import.meta.url);
11
- const __dirname = path.dirname(__filename);
12
- const projectRootDir = path.resolve(__dirname, '..');
13
-
14
- const TOKEN_PATH = path.join(projectRootDir, 'token.json');
15
- const CREDENTIALS_PATH = path.join(projectRootDir, 'credentials.json');
16
- // --- End of path calculation ---
17
-
18
- const SCOPES = [
19
- 'https://www.googleapis.com/auth/documents',
20
- 'https://www.googleapis.com/auth/drive.file' // Or scope used in Step 1
21
- ];
22
-
23
- async function loadSavedCredentialsIfExist(): Promise<OAuth2Client | null> {
24
- try {
25
- const content = await fs.readFile(TOKEN_PATH);
26
- const credentials = JSON.parse(content.toString());
27
- const { client_secret, client_id, redirect_uris } = await loadClientSecrets();
28
- const client = new google.auth.OAuth2(client_id, client_secret, redirect_uris?.[0]);
29
- client.setCredentials(credentials);
30
- return client;
31
- } catch (err) {
32
- return null;
33
- }
34
- }
35
-
36
- async function loadClientSecrets() {
37
- const content = await fs.readFile(CREDENTIALS_PATH);
38
- const keys = JSON.parse(content.toString());
39
- const key = keys.installed || keys.web;
40
- if (!key) throw new Error("Could not find client secrets in credentials.json.");
41
- return {
42
- client_id: key.client_id,
43
- client_secret: key.client_secret,
44
- redirect_uris: key.redirect_uris
45
- };
46
- }
47
-
48
- async function saveCredentials(client: OAuth2Client): Promise<void> {
49
- const { client_secret, client_id } = await loadClientSecrets();
50
- const payload = JSON.stringify({
51
- type: 'authorized_user',
52
- client_id: client_id,
53
- client_secret: client_secret,
54
- refresh_token: client.credentials.refresh_token,
55
- });
56
- await fs.writeFile(TOKEN_PATH, payload);
57
- console.error('Token stored to', TOKEN_PATH);
58
- }
59
-
60
- async function authenticate(): Promise<OAuth2Client> {
61
- const { client_secret, client_id, redirect_uris } = await loadClientSecrets();
62
- const oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirect_uris?.[0]);
63
-
64
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
65
-
66
- const authorizeUrl = oAuth2Client.generateAuthUrl({
67
- access_type: 'offline',
68
- scope: SCOPES.join(' '),
69
- });
70
-
71
- console.error('Authorize this app by visiting this url:', authorizeUrl);
72
- const code = await rl.question('Enter the code from that page here: ');
73
- rl.close();
74
-
75
- try {
76
- const { tokens } = await oAuth2Client.getToken(code);
77
- oAuth2Client.setCredentials(tokens);
78
- if (tokens.refresh_token) { // Save only if we got a refresh token
79
- await saveCredentials(oAuth2Client);
80
- } else {
81
- console.error("Did not receive refresh token. Token might expire.");
82
- }
83
- console.error('Authentication successful!');
84
- return oAuth2Client;
85
- } catch (err) {
86
- console.error('Error retrieving access token', err);
87
- throw new Error('Authentication failed');
88
- }
89
- }
90
-
91
- export async function authorize(): Promise<OAuth2Client> {
92
- let client = await loadSavedCredentialsIfExist();
93
- if (client) {
94
- // Optional: Add token refresh logic here if needed, though library often handles it.
95
- console.error('Using saved credentials.');
96
- return client;
97
- }
98
- console.error('Starting authentication flow...');
99
- client = await authenticate();
100
- return client;
101
- }