Skip to content

Commit 0640b7a

Browse files
committed
Implement oauth2 client
1 parent 6ecc2f4 commit 0640b7a

File tree

7 files changed

+79
-14
lines changed

7 files changed

+79
-14
lines changed

src/google/auth/base.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import * as google from 'googleapis';
1+
import * as google from 'googleapis'
22

33
export interface AuthClient {
4-
getAuth(): google.Auth.GoogleAuth
4+
getAuthHeadersClient(): google.Auth.GoogleAuth | google.Auth.OAuth2Client
55
}

src/google/auth/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export * from './base'
22
export * from './models'
3+
export * from './oauth2'
34
export * from './service_account'
45
export * from './models'

src/google/auth/oauth2.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import * as google from 'googleapis';
2+
import { authenticate } from '@google-cloud/local-auth'
3+
4+
import * as fs from 'fs';
5+
import { AuthClient } from './base';
6+
7+
export class Oauth2GoogleAuthClient implements AuthClient {
8+
private auth!: google.Auth.OAuth2Client;
9+
10+
private constructor(auth: google.Auth.OAuth2Client) {
11+
this.auth = auth;
12+
}
13+
14+
public static async fromFile(
15+
secretFilePath: string,
16+
credsFilePath: string,
17+
scopes: string[],
18+
): Promise<Oauth2GoogleAuthClient> {
19+
const storedClient = await this.loadCredsIfExists(credsFilePath);
20+
if (storedClient) {
21+
return new Oauth2GoogleAuthClient(storedClient);
22+
}
23+
24+
const newClient = await authenticate({
25+
scopes: scopes,
26+
keyfilePath: secretFilePath,
27+
});
28+
29+
if (newClient.credentials) {
30+
await this.storeCredentials(secretFilePath, credsFilePath, newClient);
31+
}
32+
return new Oauth2GoogleAuthClient(newClient);
33+
}
34+
35+
private static async loadCredsIfExists(credsFilePath: string): Promise<google.Auth.OAuth2Client | null> {
36+
try {
37+
const content = await fs.promises.readFile(credsFilePath);
38+
const credentials = JSON.parse(content.toString());
39+
return google.google.auth.fromJSON(credentials) as google.Auth.OAuth2Client;
40+
} catch (err) {
41+
return null;
42+
}
43+
}
44+
45+
private static async storeCredentials(
46+
secretFilePath: string,
47+
credsFilePath: string,
48+
client: google.Auth.OAuth2Client,
49+
) {
50+
const content = await fs.promises.readFile(secretFilePath);
51+
const keys = JSON.parse(content.toString());
52+
const key = keys.installed || keys.web;
53+
const payload = JSON.stringify({
54+
type: 'authorized_user',
55+
client_id: key.client_id,
56+
client_secret: key.client_secret,
57+
refresh_token: client.credentials.refresh_token,
58+
});
59+
await fs.promises.writeFile(credsFilePath, payload);
60+
}
61+
62+
public getAuthHeadersClient(): google.Auth.GoogleAuth | google.Auth.OAuth2Client {
63+
return this.auth!;
64+
}
65+
}

src/google/auth/service_account.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export class ServiceAccountGoogleAuthClient implements AuthClient {
2525
return new ServiceAccountGoogleAuthClient(authClient);
2626
}
2727

28-
public getAuth(): google.Auth.GoogleAuth {
28+
public getAuthHeadersClient(): google.Auth.GoogleAuth | google.Auth.OAuth2Client {
2929
return this.auth!;
3030
}
3131
}

src/google/sheets/wrapper.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import { google, sheets_v4 } from 'googleapis';
1+
import * as google from 'googleapis';
22
import axios, { AxiosInstance } from 'axios';
3-
import { GoogleAuth } from 'google-auth-library';
43

54
import { AuthClient } from '../auth/base';
65
import {
@@ -20,13 +19,13 @@ import {
2019
} from './models';
2120

2221
export class Wrapper {
23-
private googleAuth: GoogleAuth;
24-
private service: sheets_v4.Sheets;
22+
private authClient: google.Auth.GoogleAuth | google.Auth.OAuth2Client;
23+
private service: google.sheets_v4.Sheets;
2524
private rawClient: AxiosInstance;
2625

2726
constructor(auth: AuthClient) {
28-
this.googleAuth = auth.getAuth();
29-
this.service = google.sheets({ version: 'v4', auth: this.googleAuth });
27+
this.authClient = auth.getAuthHeadersClient();
28+
this.service = google.google.sheets({ version: 'v4', auth: this.authClient });
3029
this.rawClient = axios.create({ validateStatus: () => true });
3130
}
3231

@@ -284,7 +283,7 @@ export class Wrapper {
284283
// This ensures the latest access token is used (and refreshed if needed).
285284
const response = await this.rawClient.get(
286285
url,
287-
{ headers: await this.googleAuth.getRequestHeaders() },
286+
{ headers: await this.authClient.getRequestHeaders() },
288287
);
289288
if (response.status !== 200) {
290289
throw new Error(`Failed to query rows, status: ${response.status}`);

tests/google/sheets/models.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ describe('RawQueryRowsResult', () => {
7373
};
7474

7575
const expected: QueryRowsResult = { rows: [] };
76-
const mockAuth = { getAuth: jest.fn() };
76+
const mockAuth = { getAuthHeadersClient: jest.fn() };
7777
const wrapper = new Wrapper(mockAuth);
7878
const result = wrapper['toQueryRowsResult'](rawResult);
7979
expect(result).toEqual(expected);
@@ -121,7 +121,7 @@ describe('RawQueryRowsResult', () => {
121121
],
122122
};
123123

124-
const mockAuth = { getAuth: jest.fn() };
124+
const mockAuth = { getAuthHeadersClient: jest.fn() };
125125
const wrapper = new Wrapper(mockAuth);
126126
const result = wrapper['toQueryRowsResult'](rawResult);
127127
expect(result).toEqual(expected);
@@ -147,7 +147,7 @@ describe('RawQueryRowsResult', () => {
147147
},
148148
};
149149

150-
const mockAuth = { getAuth: jest.fn() };
150+
const mockAuth = { getAuthHeadersClient: jest.fn() };
151151
const wrapper = new Wrapper(mockAuth);
152152
expect(() => wrapper['toQueryRowsResult'](rawResult)).toThrow('Unsupported cell value type: something');
153153
});

tests/google/sheets/wrapper.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ describe('Wrapper', () => {
2626
let mockSheetsService: jest.Mocked<sheets_v4.Sheets>;
2727

2828
beforeEach(() => {
29-
mockAuth = { getAuth: jest.fn() };
29+
mockAuth = { getAuthHeadersClient: jest.fn() };
3030
wrapper = new Wrapper(mockAuth);
3131
mockSheetsService = wrapper['service'] as unknown as jest.Mocked<sheets_v4.Sheets>;
3232
});

0 commit comments

Comments
 (0)