@anmiles/google-api-wrapper 7.0.2 → 7.0.4
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.d.ts +1 -1
- package/dist/lib/secrets.js +16 -4
- package/dist/lib/secrets.js.map +1 -1
- package/package.json +1 -1
- package/src/lib/__tests__/secrets.test.ts +103 -28
- package/src/lib/jsonLib.ts +1 -1
- package/src/lib/secrets.ts +20 -6
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.4](../../tags/v7.0.4) - 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.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ declare function getScopes(): string[];
|
|
|
16
16
|
declare function getSecrets(profile: string): Secrets;
|
|
17
17
|
declare function getCredentials(profile: string, auth: GoogleApis.Common.OAuth2Client, options?: AuthOptions): Promise<GoogleApis.Auth.Credentials>;
|
|
18
18
|
declare function validateCredentials(credentials: GoogleApis.Auth.Credentials): Promise<boolean>;
|
|
19
|
-
declare function createCredentials(profile: string, auth: GoogleApis.Auth.OAuth2Client, options?: AuthOptions): Promise<GoogleApis.Auth.Credentials>;
|
|
19
|
+
declare function createCredentials(profile: string, auth: GoogleApis.Auth.OAuth2Client, options?: AuthOptions, prompt?: GoogleApis.Auth.GenerateAuthUrlOpts['prompt']): Promise<GoogleApis.Auth.Credentials>;
|
|
20
20
|
declare function checkSecrets(profile: string, secretsObject: Secrets, secretsFile: string): true | void;
|
|
21
21
|
declare function getScopesError(scopesFile: string): string;
|
|
22
22
|
declare function getSecretsError(profile: string, secretsFile: string): string;
|
package/dist/lib/secrets.js
CHANGED
|
@@ -30,26 +30,38 @@ 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
|
+
// eslint-disable-next-line camelcase
|
|
40
|
+
const credentials = await secrets_1.default.createCredentials(profile, auth, options, refresh_token ? undefined : 'consent');
|
|
41
|
+
// eslint-disable-next-line camelcase
|
|
42
|
+
return { refresh_token, ...credentials };
|
|
43
|
+
}, secrets_1.default.validateCredentials);
|
|
36
44
|
}
|
|
37
45
|
exports.getCredentials = getCredentials;
|
|
38
46
|
async function validateCredentials(credentials) {
|
|
39
47
|
if (!credentials.access_token) {
|
|
40
48
|
return false;
|
|
41
49
|
}
|
|
50
|
+
if (!credentials.refresh_token) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
42
53
|
if (!credentials.expiry_date) {
|
|
43
54
|
return true;
|
|
44
55
|
}
|
|
45
56
|
return new Date().getTime() - credentials.expiry_date < tokenExpiration;
|
|
46
57
|
}
|
|
47
|
-
async function createCredentials(profile, auth, options) {
|
|
58
|
+
async function createCredentials(profile, auth, options, prompt) {
|
|
48
59
|
const scope = (options === null || options === void 0 ? void 0 : options.scopes) || secrets_1.default.getScopes();
|
|
49
60
|
return new Promise((resolve) => {
|
|
50
61
|
const authUrl = auth.generateAuthUrl({
|
|
51
62
|
// eslint-disable-next-line camelcase
|
|
52
63
|
access_type: 'offline',
|
|
64
|
+
prompt,
|
|
53
65
|
scope,
|
|
54
66
|
});
|
|
55
67
|
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,46 +239,85 @@ describe('src/lib/secrets', () => {
|
|
|
226
239
|
expect(getJSONAsyncSpy).not.toBeCalled();
|
|
227
240
|
});
|
|
228
241
|
|
|
229
|
-
it('should fallback
|
|
242
|
+
it('should call createCredentials with consent in fallback if no existing credentials', async () => {
|
|
243
|
+
fileExists = false;
|
|
244
|
+
|
|
230
245
|
await original.getCredentials(profile, auth);
|
|
231
246
|
|
|
247
|
+
expect(secrets.createCredentials).not.toBeCalled();
|
|
248
|
+
|
|
232
249
|
const fallback = getJSONAsyncSpy.mock.calls[0][1];
|
|
233
|
-
await fallback();
|
|
250
|
+
const result = await fallback();
|
|
234
251
|
|
|
235
|
-
expect(
|
|
252
|
+
expect(jsonLib.readJSON).not.toBeCalled();
|
|
253
|
+
expect(secrets.createCredentials).toBeCalledWith(profile, auth, undefined, 'consent');
|
|
254
|
+
expect(result).toEqual(credentialsJSON);
|
|
236
255
|
});
|
|
237
256
|
|
|
238
|
-
it('should call createCredentials
|
|
257
|
+
it('should call createCredentials with consent in fallback if no existing credentials and pass temporariness', async () => {
|
|
258
|
+
fileExists = false;
|
|
259
|
+
|
|
239
260
|
await original.getCredentials(profile, auth, { temporary : false });
|
|
240
261
|
|
|
262
|
+
expect(secrets.createCredentials).not.toBeCalled();
|
|
263
|
+
|
|
241
264
|
const fallback = getJSONAsyncSpy.mock.calls[0][1];
|
|
242
|
-
await fallback();
|
|
265
|
+
const result = await fallback();
|
|
243
266
|
|
|
244
|
-
expect(
|
|
267
|
+
expect(jsonLib.readJSON).not.toBeCalled();
|
|
268
|
+
expect(secrets.createCredentials).toBeCalledWith(profile, auth, { temporary : false }, 'consent');
|
|
269
|
+
expect(result).toEqual(credentialsJSON);
|
|
245
270
|
});
|
|
246
271
|
|
|
247
|
-
it('should call createCredentials
|
|
248
|
-
|
|
272
|
+
it('should call createCredentials with consent in fallback if existing credentials do not have refresh token', async () => {
|
|
273
|
+
fileExists = true;
|
|
249
274
|
|
|
250
|
-
|
|
251
|
-
});
|
|
275
|
+
await original.getCredentials(profile, auth);
|
|
252
276
|
|
|
253
|
-
|
|
254
|
-
const result = await original.getCredentials(profile, auth);
|
|
277
|
+
expect(secrets.createCredentials).not.toBeCalled();
|
|
255
278
|
|
|
279
|
+
const fallback = getJSONAsyncSpy.mock.calls[0][1];
|
|
280
|
+
const result = await fallback();
|
|
281
|
+
|
|
282
|
+
expect(jsonLib.readJSON).toBeCalledWith(credentialsFile);
|
|
283
|
+
expect(secrets.createCredentials).toBeCalledWith(profile, auth, undefined, 'consent');
|
|
256
284
|
expect(result).toEqual(credentialsJSON);
|
|
257
285
|
});
|
|
258
286
|
|
|
259
|
-
it('should
|
|
260
|
-
|
|
287
|
+
it('should call createCredentials without consent in fallback and replace refresh_token if existing credentials have refresh token', async () => {
|
|
288
|
+
fileExists = true;
|
|
289
|
+
// eslint-disable-next-line camelcase
|
|
290
|
+
jest.spyOn(jsonLib, 'readJSON').mockReturnValueOnce({ ...credentialsJSON, refresh_token : 'refresh_token' });
|
|
291
|
+
|
|
292
|
+
await original.getCredentials(profile, auth);
|
|
261
293
|
|
|
262
|
-
expect(
|
|
294
|
+
expect(secrets.createCredentials).not.toBeCalled();
|
|
295
|
+
|
|
296
|
+
const fallback = getJSONAsyncSpy.mock.calls[0][1];
|
|
297
|
+
const result = await fallback();
|
|
298
|
+
|
|
299
|
+
expect(jsonLib.readJSON).toBeCalledWith(credentialsFile);
|
|
300
|
+
expect(secrets.createCredentials).toBeCalledWith(profile, auth, undefined, undefined);
|
|
301
|
+
// eslint-disable-next-line camelcase
|
|
302
|
+
expect(result).toEqual({ ... credentialsJSON, refresh_token : 'refresh_token' });
|
|
263
303
|
});
|
|
264
304
|
|
|
265
|
-
it('should
|
|
266
|
-
|
|
305
|
+
it('should call createCredentials without consent in fallback and leave refresh token if existing credentials have refresh token', async () => {
|
|
306
|
+
fileExists = true;
|
|
307
|
+
// eslint-disable-next-line camelcase
|
|
308
|
+
jest.spyOn(jsonLib, 'readJSON').mockReturnValueOnce({ ...credentialsJSON, refresh_token : 'refresh_token' });
|
|
309
|
+
// eslint-disable-next-line camelcase
|
|
310
|
+
jest.spyOn(secrets, 'createCredentials').mockResolvedValueOnce({ ...credentialsJSON, refresh_token : 'refresh_token_exists' });
|
|
311
|
+
|
|
312
|
+
await original.getCredentials(profile, auth);
|
|
267
313
|
|
|
268
|
-
|
|
314
|
+
const fallback = getJSONAsyncSpy.mock.calls[0][1];
|
|
315
|
+
const result = await fallback();
|
|
316
|
+
|
|
317
|
+
expect(jsonLib.readJSON).toBeCalledWith(credentialsFile);
|
|
318
|
+
expect(secrets.createCredentials).toBeCalledWith(profile, auth, undefined, undefined);
|
|
319
|
+
// eslint-disable-next-line camelcase
|
|
320
|
+
expect(result).toEqual({ ...credentialsJSON, refresh_token : 'refresh_token_exists' });
|
|
269
321
|
});
|
|
270
322
|
});
|
|
271
323
|
|
|
@@ -274,23 +326,28 @@ describe('src/lib/secrets', () => {
|
|
|
274
326
|
expect(await original.validateCredentials({})).toEqual(false);
|
|
275
327
|
});
|
|
276
328
|
|
|
329
|
+
it('should return true if no refresh token', async () => {
|
|
330
|
+
// eslint-disable-next-line camelcase
|
|
331
|
+
expect(await original.validateCredentials({ access_token : 'token' })).toEqual(false);
|
|
332
|
+
});
|
|
333
|
+
|
|
277
334
|
it('should return true if no expiration', async () => {
|
|
278
335
|
// eslint-disable-next-line camelcase
|
|
279
|
-
expect(await original.validateCredentials({ access_token : 'token' })).toEqual(true);
|
|
336
|
+
expect(await original.validateCredentials({ access_token : 'token', refresh_token : 'token' })).toEqual(true);
|
|
280
337
|
});
|
|
281
338
|
|
|
282
339
|
it('should return true if credentials are not more than 1 week ago', async () => {
|
|
283
340
|
const expiryDate = new Date();
|
|
284
341
|
expiryDate.setDate(expiryDate.getDate() - 6);
|
|
285
342
|
// eslint-disable-next-line camelcase
|
|
286
|
-
expect(await original.validateCredentials({ access_token : 'token', expiry_date : expiryDate.getTime() })).toEqual(true);
|
|
343
|
+
expect(await original.validateCredentials({ access_token : 'token', refresh_token : 'token', expiry_date : expiryDate.getTime() })).toEqual(true);
|
|
287
344
|
});
|
|
288
345
|
|
|
289
346
|
it('should return true if credentials are more than 1 week ago', async () => {
|
|
290
347
|
const expiryDate = new Date();
|
|
291
348
|
expiryDate.setDate(expiryDate.getDate() - 8);
|
|
292
349
|
// eslint-disable-next-line camelcase
|
|
293
|
-
expect(await original.validateCredentials({ access_token : 'token', expiry_date : expiryDate.getTime() })).toEqual(false);
|
|
350
|
+
expect(await original.validateCredentials({ access_token : 'token', refresh_token : 'token', expiry_date : expiryDate.getTime() })).toEqual(false);
|
|
294
351
|
});
|
|
295
352
|
});
|
|
296
353
|
|
|
@@ -305,6 +362,23 @@ describe('src/lib/secrets', () => {
|
|
|
305
362
|
expect(auth.generateAuthUrl).toBeCalledWith({
|
|
306
363
|
// eslint-disable-next-line camelcase
|
|
307
364
|
access_type : 'offline',
|
|
365
|
+
prompt : undefined,
|
|
366
|
+
scope : [
|
|
367
|
+
'https://www.googleapis.com/auth/calendar.calendars.readonly',
|
|
368
|
+
'https://www.googleapis.com/auth/calendar.events.readonly',
|
|
369
|
+
],
|
|
370
|
+
});
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
it('should generate authUrl and require consent if explicitly asked', async () => {
|
|
374
|
+
willOpen(tokenUrl, 100);
|
|
375
|
+
|
|
376
|
+
await original.createCredentials(profile, auth, { temporary : true }, 'consent');
|
|
377
|
+
|
|
378
|
+
expect(auth.generateAuthUrl).toBeCalledWith({
|
|
379
|
+
// eslint-disable-next-line camelcase
|
|
380
|
+
access_type : 'offline',
|
|
381
|
+
prompt : 'consent',
|
|
308
382
|
scope : [
|
|
309
383
|
'https://www.googleapis.com/auth/calendar.calendars.readonly',
|
|
310
384
|
'https://www.googleapis.com/auth/calendar.events.readonly',
|
|
@@ -320,6 +394,7 @@ describe('src/lib/secrets', () => {
|
|
|
320
394
|
expect(auth.generateAuthUrl).toBeCalledWith({
|
|
321
395
|
// eslint-disable-next-line camelcase
|
|
322
396
|
access_type : 'offline',
|
|
397
|
+
prompt : undefined,
|
|
323
398
|
scope : [ 'scope1', 'scope2' ],
|
|
324
399
|
});
|
|
325
400
|
});
|
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,18 @@ 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
|
+
// eslint-disable-next-line camelcase
|
|
44
|
+
const credentials = await secrets.createCredentials(profile, auth, options, refresh_token ? undefined : 'consent');
|
|
45
|
+
// eslint-disable-next-line camelcase
|
|
46
|
+
return { refresh_token, ...credentials };
|
|
47
|
+
}, secrets.validateCredentials);
|
|
39
48
|
}
|
|
40
49
|
|
|
41
50
|
async function validateCredentials(credentials: GoogleApis.Auth.Credentials): Promise<boolean> {
|
|
@@ -43,6 +52,10 @@ async function validateCredentials(credentials: GoogleApis.Auth.Credentials): Pr
|
|
|
43
52
|
return false;
|
|
44
53
|
}
|
|
45
54
|
|
|
55
|
+
if (!credentials.refresh_token) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
|
|
46
59
|
if (!credentials.expiry_date) {
|
|
47
60
|
return true;
|
|
48
61
|
}
|
|
@@ -50,13 +63,14 @@ async function validateCredentials(credentials: GoogleApis.Auth.Credentials): Pr
|
|
|
50
63
|
return new Date().getTime() - credentials.expiry_date < tokenExpiration;
|
|
51
64
|
}
|
|
52
65
|
|
|
53
|
-
async function createCredentials(profile: string, auth: GoogleApis.Auth.OAuth2Client, options?: AuthOptions): Promise<GoogleApis.Auth.Credentials> {
|
|
66
|
+
async function createCredentials(profile: string, auth: GoogleApis.Auth.OAuth2Client, options?: AuthOptions, prompt?: GoogleApis.Auth.GenerateAuthUrlOpts['prompt']): Promise<GoogleApis.Auth.Credentials> {
|
|
54
67
|
const scope = options?.scopes || secrets.getScopes();
|
|
55
68
|
|
|
56
69
|
return new Promise((resolve) => {
|
|
57
70
|
const authUrl = auth.generateAuthUrl({
|
|
58
71
|
// eslint-disable-next-line camelcase
|
|
59
72
|
access_type : 'offline',
|
|
73
|
+
prompt,
|
|
60
74
|
scope,
|
|
61
75
|
});
|
|
62
76
|
|