@anmiles/google-api-wrapper 7.0.2 → 7.0.3
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/CHANGELOG.md +4 -0
- package/dist/lib/jsonLib.d.ts +1 -1
- package/dist/lib/jsonLib.js +2 -1
- package/dist/lib/jsonLib.js.map +1 -1
- package/dist/lib/secrets.js +14 -3
- package/dist/lib/secrets.js.map +1 -1
- package/package.json +1 -1
- package/src/lib/__tests__/secrets.test.ts +96 -19
- package/src/lib/jsonLib.ts +1 -1
- package/src/lib/secrets.ts +18 -5
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [7.0.3](../../tags/v7.0.3) - 2023-04-22
|
|
9
|
+
### Changed
|
|
10
|
+
- Always require refresh token for permanent credentials
|
|
11
|
+
|
|
8
12
|
## [7.0.2](../../tags/v7.0.2) - 2023-04-22
|
|
9
13
|
### Changed
|
|
10
14
|
- Show instructions in the browser to prevent direct opening profile-oriented pages in wrong browsers
|
package/dist/lib/jsonLib.d.ts
CHANGED
package/dist/lib/jsonLib.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.writeJSON = exports.getJSONAsync = exports.getJSON = void 0;
|
|
6
|
+
exports.readJSON = exports.writeJSON = exports.getJSONAsync = exports.getJSON = void 0;
|
|
7
7
|
const fs_1 = __importDefault(require("fs"));
|
|
8
8
|
const logger_1 = require("./logger");
|
|
9
9
|
const paths_1 = require("./paths");
|
|
@@ -46,6 +46,7 @@ function readJSON(filename) {
|
|
|
46
46
|
const jsonString = fs_1.default.readFileSync(filename).toString();
|
|
47
47
|
return JSON.parse(jsonString);
|
|
48
48
|
}
|
|
49
|
+
exports.readJSON = readJSON;
|
|
49
50
|
function checkJSON(filename, json) {
|
|
50
51
|
if (json) {
|
|
51
52
|
return;
|
package/dist/lib/jsonLib.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jsonLib.js","sourceRoot":"","sources":["../../src/lib/jsonLib.ts"],"names":[],"mappings":";;;;;;AAAA,4CAAoB;AACpB,qCAAiC;AACjC,mCAAqC;AAErC,wDAAgC;AAGhC,kBAAe,EAAE,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;AAEzE,SAAS,OAAO,CAAI,QAAgB,EAAE,cAA8C,EAAE,YAAmC;IACxH,IAAI,YAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE;QAC5B,MAAM,IAAI,GAAG,iBAAO,CAAC,QAAQ,CAAI,QAAQ,CAAC,CAAC;QAE3C,IAAI,CAAC,YAAY,IAAI,YAAY,CAAC,IAAI,CAAC,EAAE;YACxC,OAAO,IAAI,CAAC;SACZ;KACD;IAED,MAAM,IAAI,GAAG,cAAc,EAAE,CAAC;IAC9B,iBAAO,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAClC,IAAA,kBAAU,EAAC,QAAQ,CAAC,CAAC;IACrB,iBAAO,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAClC,OAAO,IAAI,CAAC;AACb,CAAC;AAjBQ,0BAAO;AAmBhB,KAAK,UAAU,YAAY,CAAI,QAAgB,EAAE,mBAAqC,EAAE,iBAAiD;IACxI,IAAI,YAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE;QAC5B,MAAM,IAAI,GAAG,iBAAO,CAAC,QAAQ,CAAI,QAAQ,CAAC,CAAC;QAE3C,IAAI,CAAC,iBAAiB,IAAI,MAAM,iBAAiB,CAAC,IAAI,CAAC,EAAE;YACxD,OAAO,IAAI,CAAC;SACZ;KACD;IAED,MAAM,IAAI,GAAG,MAAM,mBAAmB,EAAE,CAAC;IACzC,iBAAO,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAClC,IAAA,kBAAU,EAAC,QAAQ,CAAC,CAAC;IACrB,iBAAO,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAClC,OAAO,IAAI,CAAC;AACb,CAAC;AAjCiB,oCAAY;AAmC9B,SAAS,SAAS,CAAI,QAAgB,EAAE,IAAO;IAC9C,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;IACtD,YAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;AACxC,CAAC;AAtC+B,8BAAS;AAwCzC,SAAS,QAAQ,CAAI,QAAgB;IACpC,MAAM,UAAU,GAAG,YAAE,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,QAAQ,EAAE,CAAC;IACxD,OAAO,IAAI,CAAC,KAAK,CAAC,UAAU,CAAM,CAAC;AACpC,CAAC;
|
|
1
|
+
{"version":3,"file":"jsonLib.js","sourceRoot":"","sources":["../../src/lib/jsonLib.ts"],"names":[],"mappings":";;;;;;AAAA,4CAAoB;AACpB,qCAAiC;AACjC,mCAAqC;AAErC,wDAAgC;AAGhC,kBAAe,EAAE,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;AAEzE,SAAS,OAAO,CAAI,QAAgB,EAAE,cAA8C,EAAE,YAAmC;IACxH,IAAI,YAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE;QAC5B,MAAM,IAAI,GAAG,iBAAO,CAAC,QAAQ,CAAI,QAAQ,CAAC,CAAC;QAE3C,IAAI,CAAC,YAAY,IAAI,YAAY,CAAC,IAAI,CAAC,EAAE;YACxC,OAAO,IAAI,CAAC;SACZ;KACD;IAED,MAAM,IAAI,GAAG,cAAc,EAAE,CAAC;IAC9B,iBAAO,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAClC,IAAA,kBAAU,EAAC,QAAQ,CAAC,CAAC;IACrB,iBAAO,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAClC,OAAO,IAAI,CAAC;AACb,CAAC;AAjBQ,0BAAO;AAmBhB,KAAK,UAAU,YAAY,CAAI,QAAgB,EAAE,mBAAqC,EAAE,iBAAiD;IACxI,IAAI,YAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE;QAC5B,MAAM,IAAI,GAAG,iBAAO,CAAC,QAAQ,CAAI,QAAQ,CAAC,CAAC;QAE3C,IAAI,CAAC,iBAAiB,IAAI,MAAM,iBAAiB,CAAC,IAAI,CAAC,EAAE;YACxD,OAAO,IAAI,CAAC;SACZ;KACD;IAED,MAAM,IAAI,GAAG,MAAM,mBAAmB,EAAE,CAAC;IACzC,iBAAO,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAClC,IAAA,kBAAU,EAAC,QAAQ,CAAC,CAAC;IACrB,iBAAO,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAClC,OAAO,IAAI,CAAC;AACb,CAAC;AAjCiB,oCAAY;AAmC9B,SAAS,SAAS,CAAI,QAAgB,EAAE,IAAO;IAC9C,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;IACtD,YAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;AACxC,CAAC;AAtC+B,8BAAS;AAwCzC,SAAS,QAAQ,CAAI,QAAgB;IACpC,MAAM,UAAU,GAAG,YAAE,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,QAAQ,EAAE,CAAC;IACxD,OAAO,IAAI,CAAC,KAAK,CAAC,UAAU,CAAM,CAAC;AACpC,CAAC;AA3C0C,4BAAQ;AA6CnD,SAAS,SAAS,CAAI,QAAgB,EAAE,IAAO;IAC9C,IAAI,IAAI,EAAE;QACT,OAAO;KACP;IACD,IAAA,cAAK,EAAC,QAAQ,QAAQ,sGAAsG,CAAC,CAAC;AAC/H,CAAC"}
|
package/dist/lib/secrets.js
CHANGED
|
@@ -30,15 +30,25 @@ function getSecrets(profile) {
|
|
|
30
30
|
exports.getSecrets = getSecrets;
|
|
31
31
|
async function getCredentials(profile, auth, options) {
|
|
32
32
|
const credentialsFile = (0, paths_1.getCredentialsFile)(profile);
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
if (options === null || options === void 0 ? void 0 : options.temporary) {
|
|
34
|
+
return secrets_1.default.createCredentials(profile, auth, options);
|
|
35
|
+
}
|
|
36
|
+
return (0, jsonLib_1.getJSONAsync)(credentialsFile, async () => {
|
|
37
|
+
// eslint-disable-next-line camelcase
|
|
38
|
+
const refresh_token = (0, paths_1.ensureFile)(credentialsFile) ? (0, jsonLib_1.readJSON)(credentialsFile).refresh_token : undefined;
|
|
39
|
+
const credentials = await secrets_1.default.createCredentials(profile, auth, options);
|
|
40
|
+
// eslint-disable-next-line camelcase
|
|
41
|
+
return { refresh_token, ...credentials };
|
|
42
|
+
}, secrets_1.default.validateCredentials);
|
|
36
43
|
}
|
|
37
44
|
exports.getCredentials = getCredentials;
|
|
38
45
|
async function validateCredentials(credentials) {
|
|
39
46
|
if (!credentials.access_token) {
|
|
40
47
|
return false;
|
|
41
48
|
}
|
|
49
|
+
if (!credentials.refresh_token) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
42
52
|
if (!credentials.expiry_date) {
|
|
43
53
|
return true;
|
|
44
54
|
}
|
|
@@ -50,6 +60,7 @@ async function createCredentials(profile, auth, options) {
|
|
|
50
60
|
const authUrl = auth.generateAuthUrl({
|
|
51
61
|
// eslint-disable-next-line camelcase
|
|
52
62
|
access_type: 'offline',
|
|
63
|
+
prompt: (options === null || options === void 0 ? void 0 : options.temporary) ? undefined : 'consent',
|
|
53
64
|
scope,
|
|
54
65
|
});
|
|
55
66
|
const server = http_1.default.createServer(async (request, response) => {
|
package/dist/lib/secrets.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"secrets.js","sourceRoot":"","sources":["../../src/lib/secrets.ts"],"names":[],"mappings":";;;;;;AAAA,gDAAwB;AACxB,oEAA2C;AAC3C,gDAAwB;AAGxB,
|
|
1
|
+
{"version":3,"file":"secrets.js","sourceRoot":"","sources":["../../src/lib/secrets.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -5,6 +5,7 @@ import open from 'open';
|
|
|
5
5
|
import type GoogleApis from 'googleapis';
|
|
6
6
|
import jsonLib from '../jsonLib';
|
|
7
7
|
import logger from '../logger';
|
|
8
|
+
import paths from '../paths';
|
|
8
9
|
import type { Secrets } from '../../types';
|
|
9
10
|
|
|
10
11
|
import secrets from '../secrets';
|
|
@@ -14,7 +15,7 @@ jest.mock<typeof secrets>('../secrets', () => ({
|
|
|
14
15
|
getSecrets : jest.fn().mockImplementation(() => secretsJSON),
|
|
15
16
|
getCredentials : jest.fn(),
|
|
16
17
|
validateCredentials : jest.fn(),
|
|
17
|
-
createCredentials : jest.fn(),
|
|
18
|
+
createCredentials : jest.fn().mockImplementation(() => credentialsJSON),
|
|
18
19
|
checkSecrets : jest.fn(),
|
|
19
20
|
getScopesError : jest.fn().mockImplementation(() => scopesError),
|
|
20
21
|
getSecretsError : jest.fn().mockImplementation(() => secretsError),
|
|
@@ -46,8 +47,9 @@ jest.mock('open', () => jest.fn().mockImplementation((url: string) => {
|
|
|
46
47
|
}));
|
|
47
48
|
|
|
48
49
|
jest.mock<Partial<typeof jsonLib>>('../jsonLib', () => ({
|
|
49
|
-
getJSON
|
|
50
|
+
getJSON : jest.fn().mockImplementation(() => json),
|
|
50
51
|
getJSONAsync : jest.fn().mockImplementation(async () => json),
|
|
52
|
+
readJSON : jest.fn().mockImplementation(async () => json),
|
|
51
53
|
}));
|
|
52
54
|
|
|
53
55
|
jest.mock<Partial<typeof logger>>('../logger', () => ({
|
|
@@ -57,6 +59,13 @@ jest.mock<Partial<typeof logger>>('../logger', () => ({
|
|
|
57
59
|
}) as jest.Mock<never, any>,
|
|
58
60
|
}));
|
|
59
61
|
|
|
62
|
+
jest.mock<Partial<typeof paths>>('../paths', () => ({
|
|
63
|
+
getScopesFile : jest.fn().mockImplementation(() => scopesFile),
|
|
64
|
+
getSecretsFile : jest.fn().mockImplementation(() => secretsFile),
|
|
65
|
+
getCredentialsFile : jest.fn().mockImplementation(() => credentialsFile),
|
|
66
|
+
ensureFile : jest.fn().mockImplementation(() => fileExists),
|
|
67
|
+
}));
|
|
68
|
+
|
|
60
69
|
const profile = 'username1';
|
|
61
70
|
const scopesFile = 'scopes.json';
|
|
62
71
|
const secretsFile = 'secrets/username1.json';
|
|
@@ -85,11 +94,12 @@ const secretsJSON: Secrets = {
|
|
|
85
94
|
},
|
|
86
95
|
};
|
|
87
96
|
|
|
88
|
-
const credentialsJSON = {
|
|
89
|
-
|
|
97
|
+
const credentialsJSON: GoogleApis.Auth.Credentials = {
|
|
98
|
+
// eslint-disable-next-line camelcase
|
|
99
|
+
access_token : 'access_token222',
|
|
90
100
|
};
|
|
91
101
|
|
|
92
|
-
let json:
|
|
102
|
+
let json: any;
|
|
93
103
|
|
|
94
104
|
const code = 'code';
|
|
95
105
|
const authUrl = 'https://authUrl';
|
|
@@ -138,6 +148,8 @@ const connections = [
|
|
|
138
148
|
{ remoteAddress : 'server', remotePort : '1003', on : jest.fn(), destroy : jest.fn() },
|
|
139
149
|
];
|
|
140
150
|
|
|
151
|
+
let fileExists: boolean;
|
|
152
|
+
|
|
141
153
|
describe('src/lib/secrets', () => {
|
|
142
154
|
describe('getScopes', () => {
|
|
143
155
|
const getJSONSpy = jest.spyOn(jsonLib, 'getJSON');
|
|
@@ -203,7 +215,8 @@ describe('src/lib/secrets', () => {
|
|
|
203
215
|
const getJSONAsyncSpy = jest.spyOn(jsonLib, 'getJSONAsync');
|
|
204
216
|
|
|
205
217
|
beforeEach(() => {
|
|
206
|
-
json
|
|
218
|
+
json = credentialsJSON;
|
|
219
|
+
fileExists = false;
|
|
207
220
|
});
|
|
208
221
|
|
|
209
222
|
it('should get json from credentials file by default', async () => {
|
|
@@ -213,7 +226,7 @@ describe('src/lib/secrets', () => {
|
|
|
213
226
|
expect(getJSONAsyncSpy.mock.calls[0][0]).toEqual(credentialsFile);
|
|
214
227
|
});
|
|
215
228
|
|
|
216
|
-
it('should get json from credentials file if temporariness
|
|
229
|
+
it('should get json from credentials file if temporariness not set', async () => {
|
|
217
230
|
await original.getCredentials(profile, auth, { temporary : false });
|
|
218
231
|
|
|
219
232
|
expect(getJSONAsyncSpy).toBeCalled();
|
|
@@ -226,18 +239,22 @@ describe('src/lib/secrets', () => {
|
|
|
226
239
|
expect(getJSONAsyncSpy).not.toBeCalled();
|
|
227
240
|
});
|
|
228
241
|
|
|
229
|
-
it('should
|
|
242
|
+
it('should call createCredentials in fallback', async () => {
|
|
230
243
|
await original.getCredentials(profile, auth);
|
|
231
244
|
|
|
245
|
+
expect(secrets.createCredentials).not.toBeCalled();
|
|
246
|
+
|
|
232
247
|
const fallback = getJSONAsyncSpy.mock.calls[0][1];
|
|
233
248
|
await fallback();
|
|
234
249
|
|
|
235
250
|
expect(secrets.createCredentials).toBeCalledWith(profile, auth, undefined);
|
|
236
251
|
});
|
|
237
252
|
|
|
238
|
-
it('should call createCredentials
|
|
253
|
+
it('should call createCredentials in fallback if temporariness not set', async () => {
|
|
239
254
|
await original.getCredentials(profile, auth, { temporary : false });
|
|
240
255
|
|
|
256
|
+
expect(secrets.createCredentials).not.toBeCalled();
|
|
257
|
+
|
|
241
258
|
const fallback = getJSONAsyncSpy.mock.calls[0][1];
|
|
242
259
|
await fallback();
|
|
243
260
|
|
|
@@ -250,22 +267,60 @@ describe('src/lib/secrets', () => {
|
|
|
250
267
|
expect(secrets.createCredentials).toBeCalledWith(profile, auth, { temporary : true });
|
|
251
268
|
});
|
|
252
269
|
|
|
253
|
-
it('should return credentials
|
|
254
|
-
|
|
270
|
+
it('should return credentials in fallback', async () => {
|
|
271
|
+
await original.getCredentials(profile, auth);
|
|
272
|
+
|
|
273
|
+
const fallback = getJSONAsyncSpy.mock.calls[0][1];
|
|
274
|
+
const result = await fallback();
|
|
255
275
|
|
|
256
276
|
expect(result).toEqual(credentialsJSON);
|
|
257
277
|
});
|
|
258
278
|
|
|
259
|
-
it('should return credentials if
|
|
260
|
-
|
|
279
|
+
it('should return credentials in fallback and copy existing refresh token from existing file if created credentials do not have refresh token', async () => {
|
|
280
|
+
fileExists = true;
|
|
281
|
+
// eslint-disable-next-line camelcase
|
|
282
|
+
jest.spyOn(jsonLib, 'readJSON').mockReturnValueOnce({ ...credentialsJSON, refresh_token : 'refresh_token' });
|
|
283
|
+
|
|
284
|
+
await original.getCredentials(profile, auth);
|
|
261
285
|
|
|
286
|
+
const fallback = getJSONAsyncSpy.mock.calls[0][1];
|
|
287
|
+
const result = await fallback();
|
|
288
|
+
|
|
289
|
+
expect(jsonLib.readJSON).toBeCalledWith(credentialsFile);
|
|
290
|
+
// eslint-disable-next-line camelcase
|
|
291
|
+
expect(result).toEqual({ ...credentialsJSON, refresh_token : 'refresh_token' });
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
it('should return credentials in fallback and leave refresh_token undefined if there is no existing file', async () => {
|
|
295
|
+
fileExists = false;
|
|
296
|
+
// eslint-disable-next-line camelcase
|
|
297
|
+
jest.spyOn(jsonLib, 'readJSON').mockReturnValueOnce({ ...credentialsJSON, refresh_token : 'refresh_token' });
|
|
298
|
+
|
|
299
|
+
await original.getCredentials(profile, auth);
|
|
300
|
+
|
|
301
|
+
const fallback = getJSONAsyncSpy.mock.calls[0][1];
|
|
302
|
+
const result = await fallback();
|
|
303
|
+
|
|
304
|
+
expect(jsonLib.readJSON).not.toBeCalled();
|
|
305
|
+
// eslint-disable-next-line camelcase
|
|
262
306
|
expect(result).toEqual(credentialsJSON);
|
|
263
307
|
});
|
|
264
308
|
|
|
265
|
-
it('should return
|
|
266
|
-
|
|
309
|
+
it('should return credentials in fallback and leave refresh_token as is if it is set from createCredentials', async () => {
|
|
310
|
+
fileExists = true;
|
|
311
|
+
// eslint-disable-next-line camelcase
|
|
312
|
+
jest.spyOn(secrets, 'createCredentials').mockResolvedValueOnce({ ...credentialsJSON, refresh_token : 'refresh_token_1' });
|
|
313
|
+
// eslint-disable-next-line camelcase
|
|
314
|
+
jest.spyOn(jsonLib, 'readJSON').mockReturnValueOnce({ ...credentialsJSON, refresh_token : 'refresh_token_2' });
|
|
315
|
+
|
|
316
|
+
await original.getCredentials(profile, auth);
|
|
317
|
+
|
|
318
|
+
const fallback = getJSONAsyncSpy.mock.calls[0][1];
|
|
319
|
+
const result = await fallback();
|
|
267
320
|
|
|
268
|
-
expect(
|
|
321
|
+
expect(jsonLib.readJSON).toBeCalledWith(credentialsFile);
|
|
322
|
+
// eslint-disable-next-line camelcase
|
|
323
|
+
expect(result).toEqual({ ...credentialsJSON, refresh_token : 'refresh_token_1' });
|
|
269
324
|
});
|
|
270
325
|
});
|
|
271
326
|
|
|
@@ -274,23 +329,28 @@ describe('src/lib/secrets', () => {
|
|
|
274
329
|
expect(await original.validateCredentials({})).toEqual(false);
|
|
275
330
|
});
|
|
276
331
|
|
|
332
|
+
it('should return true if no refresh token', async () => {
|
|
333
|
+
// eslint-disable-next-line camelcase
|
|
334
|
+
expect(await original.validateCredentials({ access_token : 'token' })).toEqual(false);
|
|
335
|
+
});
|
|
336
|
+
|
|
277
337
|
it('should return true if no expiration', async () => {
|
|
278
338
|
// eslint-disable-next-line camelcase
|
|
279
|
-
expect(await original.validateCredentials({ access_token : 'token' })).toEqual(true);
|
|
339
|
+
expect(await original.validateCredentials({ access_token : 'token', refresh_token : 'token' })).toEqual(true);
|
|
280
340
|
});
|
|
281
341
|
|
|
282
342
|
it('should return true if credentials are not more than 1 week ago', async () => {
|
|
283
343
|
const expiryDate = new Date();
|
|
284
344
|
expiryDate.setDate(expiryDate.getDate() - 6);
|
|
285
345
|
// eslint-disable-next-line camelcase
|
|
286
|
-
expect(await original.validateCredentials({ access_token : 'token', expiry_date : expiryDate.getTime() })).toEqual(true);
|
|
346
|
+
expect(await original.validateCredentials({ access_token : 'token', refresh_token : 'token', expiry_date : expiryDate.getTime() })).toEqual(true);
|
|
287
347
|
});
|
|
288
348
|
|
|
289
349
|
it('should return true if credentials are more than 1 week ago', async () => {
|
|
290
350
|
const expiryDate = new Date();
|
|
291
351
|
expiryDate.setDate(expiryDate.getDate() - 8);
|
|
292
352
|
// eslint-disable-next-line camelcase
|
|
293
|
-
expect(await original.validateCredentials({ access_token : 'token', expiry_date : expiryDate.getTime() })).toEqual(false);
|
|
353
|
+
expect(await original.validateCredentials({ access_token : 'token', refresh_token : 'token', expiry_date : expiryDate.getTime() })).toEqual(false);
|
|
294
354
|
});
|
|
295
355
|
});
|
|
296
356
|
|
|
@@ -302,6 +362,22 @@ describe('src/lib/secrets', () => {
|
|
|
302
362
|
|
|
303
363
|
await original.createCredentials(profile, auth);
|
|
304
364
|
|
|
365
|
+
expect(auth.generateAuthUrl).toBeCalledWith({
|
|
366
|
+
// eslint-disable-next-line camelcase
|
|
367
|
+
access_type : 'offline',
|
|
368
|
+
prompt : 'consent',
|
|
369
|
+
scope : [
|
|
370
|
+
'https://www.googleapis.com/auth/calendar.calendars.readonly',
|
|
371
|
+
'https://www.googleapis.com/auth/calendar.events.readonly',
|
|
372
|
+
],
|
|
373
|
+
});
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
it('should generate authUrl and do not require consent if credentials are temporary because refresh token is not required in credentials JSON', async () => {
|
|
377
|
+
willOpen(tokenUrl, 100);
|
|
378
|
+
|
|
379
|
+
await original.createCredentials(profile, auth, { temporary : true });
|
|
380
|
+
|
|
305
381
|
expect(auth.generateAuthUrl).toBeCalledWith({
|
|
306
382
|
// eslint-disable-next-line camelcase
|
|
307
383
|
access_type : 'offline',
|
|
@@ -320,6 +396,7 @@ describe('src/lib/secrets', () => {
|
|
|
320
396
|
expect(auth.generateAuthUrl).toBeCalledWith({
|
|
321
397
|
// eslint-disable-next-line camelcase
|
|
322
398
|
access_type : 'offline',
|
|
399
|
+
prompt : 'consent',
|
|
323
400
|
scope : [ 'scope1', 'scope2' ],
|
|
324
401
|
});
|
|
325
402
|
});
|
package/src/lib/jsonLib.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { ensureFile } from './paths';
|
|
|
4
4
|
|
|
5
5
|
import jsonLib from './jsonLib';
|
|
6
6
|
|
|
7
|
-
export { getJSON, getJSONAsync, writeJSON };
|
|
7
|
+
export { getJSON, getJSONAsync, writeJSON, readJSON };
|
|
8
8
|
export default { getJSON, getJSONAsync, writeJSON, readJSON, checkJSON };
|
|
9
9
|
|
|
10
10
|
function getJSON<T>(filename: string, createCallback: () => Exclude<T, Promise<any>>, validateJSON?: (json: T) => boolean): T {
|
package/src/lib/secrets.ts
CHANGED
|
@@ -3,9 +3,9 @@ import enableDestroy from 'server-destroy';
|
|
|
3
3
|
import open from 'open';
|
|
4
4
|
import type GoogleApis from 'googleapis';
|
|
5
5
|
import type { Secrets, AuthOptions } from '../types';
|
|
6
|
-
import { getJSON, getJSONAsync } from './jsonLib';
|
|
6
|
+
import { getJSON, getJSONAsync, readJSON } from './jsonLib';
|
|
7
7
|
import { warn, error } from './logger';
|
|
8
|
-
import { getScopesFile, getSecretsFile, getCredentialsFile } from './paths';
|
|
8
|
+
import { getScopesFile, getSecretsFile, getCredentialsFile, ensureFile } from './paths';
|
|
9
9
|
|
|
10
10
|
import secrets from './secrets';
|
|
11
11
|
|
|
@@ -33,9 +33,17 @@ function getSecrets(profile: string): Secrets {
|
|
|
33
33
|
async function getCredentials(profile: string, auth: GoogleApis.Common.OAuth2Client, options?: AuthOptions): Promise<GoogleApis.Auth.Credentials> {
|
|
34
34
|
const credentialsFile = getCredentialsFile(profile);
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
if (options?.temporary) {
|
|
37
|
+
return secrets.createCredentials(profile, auth, options);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return getJSONAsync(credentialsFile, async () => {
|
|
41
|
+
// eslint-disable-next-line camelcase
|
|
42
|
+
const refresh_token = ensureFile(credentialsFile) ? readJSON<GoogleApis.Auth.Credentials>(credentialsFile).refresh_token : undefined;
|
|
43
|
+
const credentials = await secrets.createCredentials(profile, auth, options);
|
|
44
|
+
// eslint-disable-next-line camelcase
|
|
45
|
+
return { refresh_token, ...credentials };
|
|
46
|
+
}, secrets.validateCredentials);
|
|
39
47
|
}
|
|
40
48
|
|
|
41
49
|
async function validateCredentials(credentials: GoogleApis.Auth.Credentials): Promise<boolean> {
|
|
@@ -43,6 +51,10 @@ async function validateCredentials(credentials: GoogleApis.Auth.Credentials): Pr
|
|
|
43
51
|
return false;
|
|
44
52
|
}
|
|
45
53
|
|
|
54
|
+
if (!credentials.refresh_token) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
|
|
46
58
|
if (!credentials.expiry_date) {
|
|
47
59
|
return true;
|
|
48
60
|
}
|
|
@@ -57,6 +69,7 @@ async function createCredentials(profile: string, auth: GoogleApis.Auth.OAuth2Cl
|
|
|
57
69
|
const authUrl = auth.generateAuthUrl({
|
|
58
70
|
// eslint-disable-next-line camelcase
|
|
59
71
|
access_type : 'offline',
|
|
72
|
+
prompt : options?.temporary ? undefined : 'consent',
|
|
60
73
|
scope,
|
|
61
74
|
});
|
|
62
75
|
|