1 | 'use strict';
|
2 |
|
3 | const Promise = require('bluebird');
|
4 | const httpRequest = require('./httpRequest');
|
5 | const config = require('./config');
|
6 | const ArgError = require('../errors').ArgError;
|
7 | const logger = require('./logger').getLogger('testim-custom-token');
|
8 | const localRunnerCache = require('./runnerFileCache');
|
9 |
|
10 | let _serverToken;
|
11 | let _serverTokenExp;
|
12 | let _refreshToken;
|
13 | let _ngrokToken;
|
14 |
|
15 | let _projectId = null;
|
16 | let _token = null;
|
17 |
|
18 | const FIVE_MIN_IN_MS = 5 * 60 * 1000;
|
19 |
|
20 |
|
21 | async function init(projectId, token) {
|
22 | _projectId = projectId;
|
23 | _token = token;
|
24 | const tokenValue = await generateToken();
|
25 | return tokenValue;
|
26 | }
|
27 |
|
28 |
|
29 | function initFromData(authData, projectId, token) {
|
30 | _serverToken = authData.token;
|
31 | _projectId = projectId;
|
32 | _token = token;
|
33 | _serverTokenExp = getTokenExp(_serverToken);
|
34 | _refreshToken = authData.refreshToken;
|
35 | _ngrokToken = authData.ngrokToken;
|
36 | }
|
37 |
|
38 | function getTokenV3(projectId = _projectId, token = _token) {
|
39 | return localRunnerCache.memoize(() => {
|
40 | logger.info('request to get cli token from server');
|
41 | return httpRequest.post({
|
42 | url: `${config.SERVICES_HOST}/auth/token`,
|
43 | body: { projectId, token },
|
44 | });
|
45 | }, 'getTokenV3', FIVE_MIN_IN_MS * 10, { projectId, token })();
|
46 | }
|
47 |
|
48 | async function refreshToken() {
|
49 | logger.info('request to refresh JWT cli token from server');
|
50 | const customToken = await httpRequest.post({
|
51 | url: `${config.SERVICES_HOST}/auth/refreshToken`,
|
52 | body: { token: _serverToken, refreshToken: _refreshToken },
|
53 | });
|
54 | _serverToken = customToken.token;
|
55 | _serverTokenExp = getTokenExp(_serverToken);
|
56 | return _serverToken;
|
57 | }
|
58 |
|
59 | function generateToken() {
|
60 | return getTokenV3()
|
61 | .then(customToken => {
|
62 | logger.info('successfully get cli token from server');
|
63 | _serverToken = customToken.token;
|
64 | _serverTokenExp = getTokenExp(_serverToken);
|
65 | _refreshToken = customToken.refreshToken;
|
66 | _ngrokToken = customToken.ngrokToken;
|
67 | return _serverToken;
|
68 | })
|
69 | .catch(error => error.message.includes('Unauthorized'), () => {
|
70 | throw new ArgError(
|
71 | 'Error trying to retrieve CLI token. ' +
|
72 | 'Your CLI token and project might not match. ' +
|
73 | 'Please make sure to pass `--project` and `--token` that' +
|
74 | ' match to each other or make sure they match in your ~/.testim file.');
|
75 | })
|
76 | .catch(error => {
|
77 | logger.error(`While trying to retrieve CLI token. caught error: ${error}`, { projectId: _projectId });
|
78 | throw new ArgError(`While trying to retrieve CLI token, caught error: ${error}`);
|
79 | });
|
80 | }
|
81 |
|
82 | function getTokenExp(token) {
|
83 | const jwtLib = require('jsonwebtoken');
|
84 | const jwt = jwtLib.decode(token);
|
85 | return jwt.exp * 1000;
|
86 | }
|
87 |
|
88 | function getCustomTokenV3() {
|
89 | if (!_serverTokenExp) {
|
90 | return generateToken();
|
91 | }
|
92 | if (_serverTokenExp < (Date.now() + FIVE_MIN_IN_MS)) {
|
93 | return refreshToken().catch(err => {
|
94 | logger.error('failed to refresh token, executing fallback', err);
|
95 | return generateToken();
|
96 | });
|
97 | }
|
98 | return Promise.resolve(_serverToken);
|
99 | }
|
100 |
|
101 | function getRefreshToken() {
|
102 | return _refreshToken;
|
103 | }
|
104 |
|
105 | function getTokenV3UserData() {
|
106 | if (_serverToken) {
|
107 | const jwtLib = require('jsonwebtoken');
|
108 | const data = jwtLib.decode(_serverToken);
|
109 | return { uid: data.id, ngrokToken: _ngrokToken };
|
110 | }
|
111 | return {};
|
112 | }
|
113 |
|
114 | module.exports = {
|
115 | init: Promise.method(init),
|
116 | initFromData,
|
117 | getCustomTokenV3,
|
118 | getTokenV3UserData,
|
119 | getRefreshToken,
|
120 | };
|