@sweepbright/api-client 0.31.7 → 0.31.8-pre-release.0
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/dist/common/envHosts.d.ts +2 -0
- package/dist/common/envHosts.js +10 -0
- package/dist/common/envHosts.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +42 -12
- package/dist/index.js.map +1 -1
- package/dist/oauth-manager/AuthenticationService_PrivateAPI.d.ts +20 -0
- package/dist/oauth-manager/AuthenticationService_PrivateAPI.js +54 -0
- package/dist/oauth-manager/AuthenticationService_PrivateAPI.js.map +1 -0
- package/dist/oauth-manager/OAuthManager_Impl.d.ts +10 -0
- package/dist/oauth-manager/OAuthManager_Impl.js +96 -0
- package/dist/oauth-manager/OAuthManager_Impl.js.map +1 -0
- package/dist/oauth-manager/SessionDataImpl.d.ts +18 -0
- package/dist/oauth-manager/SessionDataImpl.js +47 -0
- package/dist/oauth-manager/SessionDataImpl.js.map +1 -0
- package/dist/oauth-manager/TokensRepository_Memory.d.ts +8 -0
- package/dist/oauth-manager/TokensRepository_Memory.js +102 -0
- package/dist/oauth-manager/TokensRepository_Memory.js.map +1 -0
- package/dist/oauth-manager/TokensRepository_Redis.d.ts +13 -0
- package/dist/oauth-manager/TokensRepository_Redis.js +69 -0
- package/dist/oauth-manager/TokensRepository_Redis.js.map +1 -0
- package/dist/oauth-manager/factories/createOAuthManager.d.ts +16 -0
- package/dist/oauth-manager/factories/createOAuthManager.js +23 -0
- package/dist/oauth-manager/factories/createOAuthManager.js.map +1 -0
- package/dist/oauth-manager/interfaces.d.ts +56 -0
- package/dist/oauth-manager/interfaces.js +3 -0
- package/dist/oauth-manager/interfaces.js.map +1 -0
- package/dist/oauth-manager/playground/main.d.ts +1 -0
- package/dist/oauth-manager/playground/main.js +42 -0
- package/dist/oauth-manager/playground/main.js.map +1 -0
- package/dist/types.d.ts +2 -1
- package/dist/types.js.map +1 -1
- package/package.json +9 -2
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.envHosts = void 0;
|
|
4
|
+
const types_1 = require("../types");
|
|
5
|
+
exports.envHosts = {
|
|
6
|
+
[types_1.Env.PRODUCTION]: 'https://api.sweepbright.com',
|
|
7
|
+
[types_1.Env.STAGING]: 'https://api.staging.sweepbright.com',
|
|
8
|
+
[types_1.Env.DEV]: 'https://api.dev.sweepbright.com',
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=envHosts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"envHosts.js","sourceRoot":"","sources":["../../src/common/envHosts.ts"],"names":[],"mappings":";;;AAAA,oCAA+B;AAElB,QAAA,QAAQ,GAAwB;IAC3C,CAAC,WAAG,CAAC,UAAU,CAAC,EAAE,6BAA6B;IAC/C,CAAC,WAAG,CAAC,OAAO,CAAC,EAAE,qCAAqC;IACpD,CAAC,WAAG,CAAC,GAAG,CAAC,EAAE,iCAAiC;CAC7C,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createOAuthManager } from './oauth-manager/factories/createOAuthManager';
|
|
1
2
|
import channelAccounts from './resources/channel_accounts';
|
|
2
3
|
import channels from './resources/channels';
|
|
3
4
|
import companies from './resources/companies';
|
|
@@ -22,4 +23,4 @@ interface Client {
|
|
|
22
23
|
estates: ReturnType<typeof estates>;
|
|
23
24
|
}
|
|
24
25
|
declare function createClient(conf: ClientConf): Client;
|
|
25
|
-
export {
|
|
26
|
+
export { Client, Env, createClient, createOAuthManager };
|
package/dist/index.js
CHANGED
|
@@ -3,9 +3,13 @@ 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.
|
|
6
|
+
exports.createOAuthManager = exports.createClient = exports.Env = void 0;
|
|
7
7
|
const axios_1 = __importDefault(require("axios"));
|
|
8
8
|
const tiny_invariant_1 = __importDefault(require("tiny-invariant"));
|
|
9
|
+
const envHosts_1 = require("./common/envHosts");
|
|
10
|
+
const SessionDataImpl_1 = require("./oauth-manager/SessionDataImpl");
|
|
11
|
+
const createOAuthManager_1 = require("./oauth-manager/factories/createOAuthManager");
|
|
12
|
+
Object.defineProperty(exports, "createOAuthManager", { enumerable: true, get: function () { return createOAuthManager_1.createOAuthManager; } });
|
|
9
13
|
const channel_accounts_1 = __importDefault(require("./resources/channel_accounts"));
|
|
10
14
|
const channels_1 = __importDefault(require("./resources/channels"));
|
|
11
15
|
const companies_1 = __importDefault(require("./resources/companies"));
|
|
@@ -18,19 +22,21 @@ const offices_1 = __importDefault(require("./resources/offices"));
|
|
|
18
22
|
const types_1 = require("./types");
|
|
19
23
|
Object.defineProperty(exports, "Env", { enumerable: true, get: function () { return types_1.Env; } });
|
|
20
24
|
const utils_1 = require("./utils");
|
|
21
|
-
const envHosts = {
|
|
22
|
-
[types_1.Env.PRODUCTION]: 'https://api.sweepbright.com',
|
|
23
|
-
[types_1.Env.STAGING]: 'https://api.staging.sweepbright.com',
|
|
24
|
-
[types_1.Env.DEV]: 'https://api.dev.sweepbright.com',
|
|
25
|
-
};
|
|
26
25
|
function createClient(conf) {
|
|
27
|
-
const { clientId, clientSecret, env = types_1.Env.PRODUCTION, version } = conf;
|
|
26
|
+
const { clientId, clientSecret, oauthManager, env = types_1.Env.PRODUCTION, version, } = conf;
|
|
28
27
|
let _auth = null;
|
|
28
|
+
let _oAuthManagerInvalidateInterceptor;
|
|
29
29
|
const httpClient = axios_1.default.create({
|
|
30
|
-
baseURL: envHosts[env],
|
|
30
|
+
baseURL: envHosts_1.envHosts[env],
|
|
31
31
|
});
|
|
32
32
|
//TODO: getData here is an antipattern. Interceptor should not change a type of response
|
|
33
33
|
httpClient.interceptors.response.use(utils_1.getData, utils_1.handleRequestFailure);
|
|
34
|
+
httpClient.interceptors.response.use(undefined, (e) => {
|
|
35
|
+
if (_oAuthManagerInvalidateInterceptor) {
|
|
36
|
+
return _oAuthManagerInvalidateInterceptor(e);
|
|
37
|
+
}
|
|
38
|
+
return e;
|
|
39
|
+
});
|
|
34
40
|
if (version) {
|
|
35
41
|
httpClient.defaults.headers.common['Accept'] = `application/vnd.sweepbright.${version}+json`;
|
|
36
42
|
}
|
|
@@ -45,11 +51,37 @@ function createClient(conf) {
|
|
|
45
51
|
}
|
|
46
52
|
},
|
|
47
53
|
};
|
|
54
|
+
async function authorizeWithOAuthManager(oauthManager, clientId, clientSecret, scopes, options) {
|
|
55
|
+
const accessToken = await oauthManager.getAccessTokenBySessionData(new SessionDataImpl_1.SessionDataImpl({
|
|
56
|
+
clientId,
|
|
57
|
+
clientSecret,
|
|
58
|
+
scopes,
|
|
59
|
+
}), options);
|
|
60
|
+
if (!accessToken) {
|
|
61
|
+
throw new Error('Authentication failed');
|
|
62
|
+
}
|
|
63
|
+
_auth = {
|
|
64
|
+
token: accessToken,
|
|
65
|
+
type: types_1.TokenType.API_TOKEN,
|
|
66
|
+
};
|
|
67
|
+
httpClient.defaults.headers.common['Authorization'] = `Bearer ${accessToken}`;
|
|
68
|
+
}
|
|
48
69
|
const client = {
|
|
49
|
-
authorize(config = {}) {
|
|
70
|
+
async authorize(config = {}) {
|
|
50
71
|
const { scopes = ['companies:read', 'properties:download', 'lead_info:write'], } = config;
|
|
51
72
|
(0, tiny_invariant_1.default)(clientId, 'missing client id');
|
|
52
73
|
(0, tiny_invariant_1.default)(clientSecret, 'missing client secret');
|
|
74
|
+
if (oauthManager) {
|
|
75
|
+
await authorizeWithOAuthManager(oauthManager, clientId, clientSecret, scopes);
|
|
76
|
+
_oAuthManagerInvalidateInterceptor = async (e) => {
|
|
77
|
+
// If the token is invalid, try to re-authorize
|
|
78
|
+
if (e.status && e.status === 401) {
|
|
79
|
+
await authorizeWithOAuthManager(oauthManager, clientId, clientSecret, scopes, { forceInvalidate: true });
|
|
80
|
+
}
|
|
81
|
+
return e;
|
|
82
|
+
};
|
|
83
|
+
return client;
|
|
84
|
+
}
|
|
53
85
|
return httpClient
|
|
54
86
|
.post('/auth/access-token', {
|
|
55
87
|
client_id: clientId,
|
|
@@ -57,12 +89,10 @@ function createClient(conf) {
|
|
|
57
89
|
grant_type: 'client_credentials',
|
|
58
90
|
scope: scopes.join(','),
|
|
59
91
|
})
|
|
60
|
-
.then(({ access_token
|
|
92
|
+
.then(({ access_token }) => {
|
|
61
93
|
(0, tiny_invariant_1.default)(access_token, 'access token should be defined');
|
|
62
|
-
(0, tiny_invariant_1.default)(expires_in, 'expires_in should be defined');
|
|
63
94
|
_auth = {
|
|
64
95
|
token: access_token,
|
|
65
|
-
expiresIn: expires_in,
|
|
66
96
|
type: types_1.TokenType.API_TOKEN,
|
|
67
97
|
};
|
|
68
98
|
// https://github.com/axios/axios#custom-instance-defaults
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;AAAA,kDAA0B;AAC1B,oEAAuC;AACvC,oFAA2D;AAC3D,oEAA4C;AAC5C,sEAA8C;AAC9C,0FAAkE;AAClE,oEAA4C;AAC5C,kEAA0C;AAC1C,8DAAsC;AACtC,0EAAkD;AAClD,kEAA0C;AAC1C,mCAA4E;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { AuthenticationService, SessionData, Tokens } from './interfaces';
|
|
2
|
+
interface AuthenticationServicePrivateAPIOptions {
|
|
3
|
+
baseUrl: string;
|
|
4
|
+
apiVersion: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* AuthenticationService_PrivateAPI is used for authentication
|
|
8
|
+
* (exchanging the client credentials for an access token)
|
|
9
|
+
*
|
|
10
|
+
* This is the implementation of the {@link AuthenticationService} interface
|
|
11
|
+
* which uses Private API for authentication.
|
|
12
|
+
*
|
|
13
|
+
* Do not use this class directly.
|
|
14
|
+
*/
|
|
15
|
+
export declare class AuthenticationService_PrivateAPI implements AuthenticationService {
|
|
16
|
+
private httpClient;
|
|
17
|
+
constructor(options: AuthenticationServicePrivateAPIOptions);
|
|
18
|
+
authenticate(sessionData: SessionData): Promise<Tokens | null>;
|
|
19
|
+
}
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.AuthenticationService_PrivateAPI = void 0;
|
|
7
|
+
const assert_1 = require("assert");
|
|
8
|
+
const axios_1 = __importDefault(require("axios"));
|
|
9
|
+
/**
|
|
10
|
+
* AuthenticationService_PrivateAPI is used for authentication
|
|
11
|
+
* (exchanging the client credentials for an access token)
|
|
12
|
+
*
|
|
13
|
+
* This is the implementation of the {@link AuthenticationService} interface
|
|
14
|
+
* which uses Private API for authentication.
|
|
15
|
+
*
|
|
16
|
+
* Do not use this class directly.
|
|
17
|
+
*/
|
|
18
|
+
class AuthenticationService_PrivateAPI {
|
|
19
|
+
constructor(options) {
|
|
20
|
+
this.httpClient = axios_1.default.create({
|
|
21
|
+
baseURL: options.baseUrl,
|
|
22
|
+
headers: {
|
|
23
|
+
Accept: `application/vnd.sweepbright.${options.apiVersion}+json`,
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
async authenticate(sessionData) {
|
|
28
|
+
const request = {
|
|
29
|
+
client_id: sessionData.getCredentials().clientId,
|
|
30
|
+
client_secret: sessionData.getCredentials().clientSecret,
|
|
31
|
+
scope: sessionData.getScopes().join(','),
|
|
32
|
+
grant_type: 'client_credentials',
|
|
33
|
+
};
|
|
34
|
+
const response = await this.httpClient.post('/auth/access-token', request, {
|
|
35
|
+
validateStatus(status) {
|
|
36
|
+
return status === 200 || status === 401;
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
if (response.status === 401) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
const { data: { access_token, expires_in, token_type }, } = response;
|
|
43
|
+
(0, assert_1.ok)(access_token, 'access token should be defined');
|
|
44
|
+
(0, assert_1.ok)(expires_in, 'expires_in should be defined');
|
|
45
|
+
(0, assert_1.strictEqual)(token_type, 'Bearer', 'token_type should be Bearer');
|
|
46
|
+
const tokens = {
|
|
47
|
+
accessToken: access_token,
|
|
48
|
+
expiresIn: expires_in,
|
|
49
|
+
};
|
|
50
|
+
return tokens;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
exports.AuthenticationService_PrivateAPI = AuthenticationService_PrivateAPI;
|
|
54
|
+
//# sourceMappingURL=AuthenticationService_PrivateAPI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AuthenticationService_PrivateAPI.js","sourceRoot":"","sources":["../../src/oauth-manager/AuthenticationService_PrivateAPI.ts"],"names":[],"mappings":";;;;;;AAAA,mCAAyC;AACzC,kDAA6C;AAQ7C;;;;;;;;GAQG;AACH,MAAa,gCAAgC;IAG3C,YAAY,OAA+C;QACzD,IAAI,CAAC,UAAU,GAAG,eAAK,CAAC,MAAM,CAAC;YAC7B,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,OAAO,EAAE;gBACP,MAAM,EAAE,+BAA+B,OAAO,CAAC,UAAU,OAAO;aACjE;SACF,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,WAAwB;QACzC,MAAM,OAAO,GAAG;YACd,SAAS,EAAE,WAAW,CAAC,cAAc,EAAE,CAAC,QAAQ;YAChD,aAAa,EAAE,WAAW,CAAC,cAAc,EAAE,CAAC,YAAY;YACxD,KAAK,EAAE,WAAW,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC;YACxC,UAAU,EAAE,oBAAoB;SACjC,CAAC;QAEF,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,oBAAoB,EAAE,OAAO,EAAE;YACzE,cAAc,CAAC,MAAM;gBACnB,OAAO,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG,CAAC;YAC1C,CAAC;SACF,CAAC,CAAC;QACH,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE;YAC3B,OAAO,IAAI,CAAC;SACb;QACD,MAAM,EACJ,IAAI,EAAE,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,GAC/C,GAAG,QAAQ,CAAC;QACb,IAAA,WAAE,EAAC,YAAY,EAAE,gCAAgC,CAAC,CAAC;QACnD,IAAA,WAAE,EAAC,UAAU,EAAE,8BAA8B,CAAC,CAAC;QAC/C,IAAA,oBAAW,EAAC,UAAU,EAAE,QAAQ,EAAE,6BAA6B,CAAC,CAAC;QAEjE,MAAM,MAAM,GAAW;YACrB,WAAW,EAAE,YAAY;YACzB,SAAS,EAAE,UAAU;SACtB,CAAC;QAEF,OAAO,MAAM,CAAC;IAChB,CAAC;CACF;AA1CD,4EA0CC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { AuthenticationService, GetAccessTokenOptions, OAuthManager, SessionData, TokensRepository } from './interfaces';
|
|
2
|
+
export declare class OAuthManager_Impl implements OAuthManager {
|
|
3
|
+
private readonly tokensRepository;
|
|
4
|
+
private readonly authenticationService;
|
|
5
|
+
constructor(tokensRepository: TokensRepository, authenticationService: AuthenticationService);
|
|
6
|
+
private getLockTimeFromCurrentTime;
|
|
7
|
+
private sleep;
|
|
8
|
+
private expiresInToExpiresAt;
|
|
9
|
+
getAccessTokenBySessionData(sessionData: SessionData, options?: GetAccessTokenOptions): Promise<string | null>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OAuthManager_Impl = void 0;
|
|
4
|
+
const SECOND = 1000;
|
|
5
|
+
const LOCK_TTL = 10 * SECOND;
|
|
6
|
+
const MANAGER_POLLING_INTERVAL = 2 * SECOND;
|
|
7
|
+
const MANAGER_POLLING_TIMEOUT = 20 * SECOND;
|
|
8
|
+
class OAuthManager_Impl {
|
|
9
|
+
constructor(tokensRepository, authenticationService) {
|
|
10
|
+
this.tokensRepository = tokensRepository;
|
|
11
|
+
this.authenticationService = authenticationService;
|
|
12
|
+
}
|
|
13
|
+
getLockTimeFromCurrentTime(currentTime) {
|
|
14
|
+
return new Date(currentTime.getTime() + LOCK_TTL);
|
|
15
|
+
}
|
|
16
|
+
async sleep(ms) {
|
|
17
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
18
|
+
}
|
|
19
|
+
expiresInToExpiresAt(expiresIn, issuedAt) {
|
|
20
|
+
return new Date(issuedAt.getTime() + expiresIn * SECOND);
|
|
21
|
+
}
|
|
22
|
+
async getAccessTokenBySessionData(sessionData, options = {}) {
|
|
23
|
+
const { forceInvalidate } = options;
|
|
24
|
+
const sessionKey = sessionData.getSessionKey();
|
|
25
|
+
if (!forceInvalidate) {
|
|
26
|
+
const accessToken = await this.tokensRepository.getAccessTokenIfValid(sessionKey, new Date());
|
|
27
|
+
if (accessToken) {
|
|
28
|
+
console.log('access token reused');
|
|
29
|
+
return accessToken;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const lockAcquiredAt = new Date();
|
|
33
|
+
const lockId = await this.tokensRepository.acquireLock({
|
|
34
|
+
currentTime: lockAcquiredAt,
|
|
35
|
+
sessionKey,
|
|
36
|
+
lockedUntil: this.getLockTimeFromCurrentTime(lockAcquiredAt),
|
|
37
|
+
});
|
|
38
|
+
if (lockId) {
|
|
39
|
+
let tokens = null;
|
|
40
|
+
const cleanUpLock = async () => {
|
|
41
|
+
await this.tokensRepository.releaseLock(lockId, {
|
|
42
|
+
shouldUpdateTokens: false,
|
|
43
|
+
sessionKey,
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
try {
|
|
47
|
+
if (forceInvalidate) {
|
|
48
|
+
// Remove ASAP the token to be invalidated
|
|
49
|
+
await this.tokensRepository.deleteAccessToken(lockId, sessionKey);
|
|
50
|
+
}
|
|
51
|
+
console.log('authentication');
|
|
52
|
+
tokens = await this.authenticationService.authenticate(sessionData);
|
|
53
|
+
}
|
|
54
|
+
catch (e) {
|
|
55
|
+
console.log('authentication failed, error', e);
|
|
56
|
+
await cleanUpLock();
|
|
57
|
+
throw new Error('Failed to authenticate');
|
|
58
|
+
}
|
|
59
|
+
const issuedAt = new Date();
|
|
60
|
+
if (!tokens) {
|
|
61
|
+
console.log('authentication failed, wrong credentials');
|
|
62
|
+
await cleanUpLock();
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
console.log('authentication succeeded');
|
|
66
|
+
await this.tokensRepository.releaseLock(lockId, {
|
|
67
|
+
shouldUpdateTokens: true,
|
|
68
|
+
sessionKey,
|
|
69
|
+
tokens,
|
|
70
|
+
expiresAt: this.expiresInToExpiresAt(tokens.expiresIn, issuedAt),
|
|
71
|
+
currentTime: issuedAt,
|
|
72
|
+
});
|
|
73
|
+
return tokens.accessToken;
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
// Locked by another process. We need to wait for the token
|
|
77
|
+
// to become available (using polling).
|
|
78
|
+
// Or fail on timeout.
|
|
79
|
+
const startedAt = new Date();
|
|
80
|
+
for (;;) {
|
|
81
|
+
const accessToken = await this.tokensRepository.getAccessTokenIfValid(sessionKey, new Date());
|
|
82
|
+
if (accessToken) {
|
|
83
|
+
console.log('access token reused');
|
|
84
|
+
return accessToken;
|
|
85
|
+
}
|
|
86
|
+
if (new Date().getTime() >
|
|
87
|
+
startedAt.getTime() + MANAGER_POLLING_TIMEOUT) {
|
|
88
|
+
throw new Error('Timed out');
|
|
89
|
+
}
|
|
90
|
+
await this.sleep(MANAGER_POLLING_INTERVAL);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
exports.OAuthManager_Impl = OAuthManager_Impl;
|
|
96
|
+
//# sourceMappingURL=OAuthManager_Impl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OAuthManager_Impl.js","sourceRoot":"","sources":["../../src/oauth-manager/OAuthManager_Impl.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ClientCredentials, SessionData } from './interfaces';
|
|
2
|
+
interface SessionDataImplOptions {
|
|
3
|
+
readonly clientId: string;
|
|
4
|
+
readonly clientSecret: string;
|
|
5
|
+
readonly scopes: readonly string[];
|
|
6
|
+
}
|
|
7
|
+
export declare class SessionDataImpl implements SessionData {
|
|
8
|
+
private readonly credentials;
|
|
9
|
+
private readonly scopes;
|
|
10
|
+
private readonly sessionKey;
|
|
11
|
+
constructor(options: SessionDataImplOptions);
|
|
12
|
+
getCredentials(): ClientCredentials;
|
|
13
|
+
getSessionKey(): string;
|
|
14
|
+
getScopes(): readonly string[];
|
|
15
|
+
private normalizeScopes;
|
|
16
|
+
private createKey;
|
|
17
|
+
}
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SessionDataImpl = void 0;
|
|
4
|
+
const crypto_1 = require("crypto");
|
|
5
|
+
function shortHash(s) {
|
|
6
|
+
return (0, crypto_1.createHash)('sha256').update(s).digest('hex').substring(0, 8);
|
|
7
|
+
}
|
|
8
|
+
class SessionDataImpl {
|
|
9
|
+
constructor(options) {
|
|
10
|
+
this.credentials = {
|
|
11
|
+
clientId: options.clientId,
|
|
12
|
+
clientSecret: options.clientSecret,
|
|
13
|
+
};
|
|
14
|
+
this.scopes = options.scopes;
|
|
15
|
+
this.sessionKey = this.createKey(options);
|
|
16
|
+
}
|
|
17
|
+
getCredentials() {
|
|
18
|
+
return this.credentials;
|
|
19
|
+
}
|
|
20
|
+
getSessionKey() {
|
|
21
|
+
return this.sessionKey;
|
|
22
|
+
}
|
|
23
|
+
getScopes() {
|
|
24
|
+
return this.scopes;
|
|
25
|
+
}
|
|
26
|
+
normalizeScopes(scopes) {
|
|
27
|
+
const normalizedScopes = scopes.map((scope) => scope.toLowerCase());
|
|
28
|
+
normalizedScopes.sort();
|
|
29
|
+
return Array.from(new Set(normalizedScopes));
|
|
30
|
+
}
|
|
31
|
+
createKey(options) {
|
|
32
|
+
const clientId = options.clientId;
|
|
33
|
+
const normalizedScopes = this.normalizeScopes(options.scopes);
|
|
34
|
+
const secretHash = shortHash(this.credentials.clientSecret);
|
|
35
|
+
const scopesString = normalizedScopes.join(',');
|
|
36
|
+
// Session key uniquely identifies a session.
|
|
37
|
+
// Access token stored under the same session key can be reused globally by
|
|
38
|
+
// multiple processes, because it is guaranteed that the
|
|
39
|
+
// clientId, clientSecret and scopes are the same for the same key.
|
|
40
|
+
//
|
|
41
|
+
// Note: the clientSecret is hashed in order to avoid disclosure of sensitive data
|
|
42
|
+
// through logs or redis leaking.
|
|
43
|
+
return `${clientId}/${secretHash}/${scopesString}`;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
exports.SessionDataImpl = SessionDataImpl;
|
|
47
|
+
//# sourceMappingURL=SessionDataImpl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SessionDataImpl.js","sourceRoot":"","sources":["../../src/oauth-manager/SessionDataImpl.ts"],"names":[],"mappings":";;;AAAA,mCAAoC;AASpC,SAAS,SAAS,CAAC,CAAS;IAC1B,OAAO,IAAA,mBAAU,EAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AACtE,CAAC;AAED,MAAa,eAAe;IAK1B,YAAY,OAA+B;QACzC,IAAI,CAAC,WAAW,GAAG;YACjB,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,YAAY,EAAE,OAAO,CAAC,YAAY;SACnC,CAAC;QAEF,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAE7B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IAC5C,CAAC;IAED,cAAc;QACZ,OAAO,IAAI,CAAC,WAAW,CAAC;IAC1B,CAAC;IACD,aAAa;QACX,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IACD,SAAS;QACP,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAEO,eAAe,CAAC,MAAyB;QAC/C,MAAM,gBAAgB,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC;QACpE,gBAAgB,CAAC,IAAI,EAAE,CAAC;QACxB,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC;IAC/C,CAAC;IAEO,SAAS,CAAC,OAA+B;QAC/C,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;QAElC,MAAM,gBAAgB,GAAG,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAE9D,MAAM,UAAU,GAAG,SAAS,CAAC,IAAI,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC;QAE5D,MAAM,YAAY,GAAG,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAEhD,6CAA6C;QAC7C,2EAA2E;QAC3E,wDAAwD;QACxD,mEAAmE;QACnE,EAAE;QACF,kFAAkF;QAClF,iCAAiC;QACjC,OAAO,GAAG,QAAQ,IAAI,UAAU,IAAI,YAAY,EAAE,CAAC;IACrD,CAAC;CACF;AAlDD,0CAkDC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { LockAcquireOptions, LockId, LockReleaseOptions, TokensRepository } from './interfaces';
|
|
2
|
+
export declare class TokensRepository_Memory implements TokensRepository {
|
|
3
|
+
private records;
|
|
4
|
+
getAccessTokenIfValid(sessionKey: string, currentTime: Date): Promise<string | null>;
|
|
5
|
+
deleteAccessToken(lockId: LockId, sessionKey: string): Promise<void>;
|
|
6
|
+
acquireLock(options: LockAcquireOptions): Promise<LockId | null>;
|
|
7
|
+
releaseLock(lockId: LockId, options: LockReleaseOptions): Promise<void>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TokensRepository_Memory = void 0;
|
|
4
|
+
const uuid_1 = require("uuid");
|
|
5
|
+
const globalRecords = new Map();
|
|
6
|
+
class TokensRepository_Memory {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.records = globalRecords;
|
|
9
|
+
}
|
|
10
|
+
async getAccessTokenIfValid(sessionKey, currentTime) {
|
|
11
|
+
// "Valid" means the following:
|
|
12
|
+
// - A record exists for the given `channelAccountID`
|
|
13
|
+
// - The `accessToken` is not null
|
|
14
|
+
// - The `expires_at` is in the future
|
|
15
|
+
const record = this.records.get(sessionKey);
|
|
16
|
+
if (!record) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
const { accessToken, expiresAt } = record;
|
|
20
|
+
if (!accessToken || !expiresAt) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
if (expiresAt < currentTime) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
return accessToken;
|
|
27
|
+
}
|
|
28
|
+
async deleteAccessToken(lockId, sessionKey) {
|
|
29
|
+
const existingRecord = this.records.get(sessionKey);
|
|
30
|
+
if (!existingRecord) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (!existingRecord.lockId) {
|
|
34
|
+
throw new Error('Cannot delete access token, not locked');
|
|
35
|
+
}
|
|
36
|
+
if (existingRecord.lockId !== lockId) {
|
|
37
|
+
throw new Error('Cannot delete access token, locked by another process');
|
|
38
|
+
}
|
|
39
|
+
this.records.set(sessionKey, {
|
|
40
|
+
...existingRecord,
|
|
41
|
+
accessToken: null,
|
|
42
|
+
expiresAt: null,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
async acquireLock(options) {
|
|
46
|
+
//To acquire lock means to set the `locked_until` to the current time + lock TTL (10 sec)
|
|
47
|
+
//If the `locked_until` is already set and it's in the future, the lock is considered already acquired and we should return false
|
|
48
|
+
//If the `locked_until` is already set and it's in the past, the lock is expired and can be acquired again
|
|
49
|
+
const existingRecord = this.records.get(options.sessionKey);
|
|
50
|
+
if (!existingRecord) {
|
|
51
|
+
const lockId = (0, uuid_1.v4)();
|
|
52
|
+
// Create a new record
|
|
53
|
+
this.records.set(options.sessionKey, {
|
|
54
|
+
accessToken: null,
|
|
55
|
+
expiresAt: null,
|
|
56
|
+
lockedUntil: options.lockedUntil,
|
|
57
|
+
lockId,
|
|
58
|
+
});
|
|
59
|
+
return lockId;
|
|
60
|
+
}
|
|
61
|
+
const { lockedUntil } = existingRecord;
|
|
62
|
+
const isStillLocked = lockedUntil && lockedUntil > new Date();
|
|
63
|
+
if (isStillLocked) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
const lockId = (0, uuid_1.v4)();
|
|
67
|
+
// Update the record
|
|
68
|
+
this.records.set(options.sessionKey, {
|
|
69
|
+
...existingRecord,
|
|
70
|
+
lockedUntil: options.lockedUntil,
|
|
71
|
+
lockId,
|
|
72
|
+
});
|
|
73
|
+
return lockId;
|
|
74
|
+
}
|
|
75
|
+
async releaseLock(lockId, options) {
|
|
76
|
+
console.log('releasing lock');
|
|
77
|
+
const existingRecord = this.records.get(options.sessionKey);
|
|
78
|
+
if (!existingRecord) {
|
|
79
|
+
throw new Error('No record found');
|
|
80
|
+
}
|
|
81
|
+
if (existingRecord.lockId !== lockId) {
|
|
82
|
+
throw new Error('Cannot release lock, because it was set by another process');
|
|
83
|
+
}
|
|
84
|
+
if (!options.shouldUpdateTokens) {
|
|
85
|
+
this.records.set(options.sessionKey, {
|
|
86
|
+
...existingRecord,
|
|
87
|
+
lockedUntil: null,
|
|
88
|
+
lockId: null,
|
|
89
|
+
});
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
this.records.set(options.sessionKey, {
|
|
93
|
+
...existingRecord,
|
|
94
|
+
accessToken: options.tokens.accessToken,
|
|
95
|
+
expiresAt: options.expiresAt,
|
|
96
|
+
lockedUntil: null,
|
|
97
|
+
lockId: null,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
exports.TokensRepository_Memory = TokensRepository_Memory;
|
|
102
|
+
//# sourceMappingURL=TokensRepository_Memory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TokensRepository_Memory.js","sourceRoot":"","sources":["../../src/oauth-manager/TokensRepository_Memory.ts"],"names":[],"mappings":";;;AAAA,+BAA0B;AAe1B,MAAM,aAAa,GAA8B,IAAI,GAAG,EAAE,CAAC;AAE3D,MAAa,uBAAuB;IAApC;QACU,YAAO,GAA8B,aAAa,CAAC;IAoH7D,CAAC;IAlHC,KAAK,CAAC,qBAAqB,CACzB,UAAkB,EAClB,WAAiB;QAEjB,+BAA+B;QAC/B,qDAAqD;QACrD,kCAAkC;QAClC,sCAAsC;QAEtC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAC5C,IAAI,CAAC,MAAM,EAAE;YACX,OAAO,IAAI,CAAC;SACb;QAED,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,GAAG,MAAM,CAAC;QAE1C,IAAI,CAAC,WAAW,IAAI,CAAC,SAAS,EAAE;YAC9B,OAAO,IAAI,CAAC;SACb;QAED,IAAI,SAAS,GAAG,WAAW,EAAE;YAC3B,OAAO,IAAI,CAAC;SACb;QAED,OAAO,WAAW,CAAC;IACrB,CAAC;IAED,KAAK,CAAC,iBAAiB,CAAC,MAAc,EAAE,UAAkB;QACxD,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACpD,IAAI,CAAC,cAAc,EAAE;YACnB,OAAO;SACR;QACD,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE;YAC1B,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;SAC3D;QACD,IAAI,cAAc,CAAC,MAAM,KAAK,MAAM,EAAE;YACpC,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;SAC1E;QAED,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE;YAC3B,GAAG,cAAc;YACjB,WAAW,EAAE,IAAI;YACjB,SAAS,EAAE,IAAI;SAChB,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,OAA2B;QAC3C,yFAAyF;QACzF,iIAAiI;QACjI,0GAA0G;QAC1G,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QAC5D,IAAI,CAAC,cAAc,EAAE;YACnB,MAAM,MAAM,GAAG,IAAA,SAAE,GAAE,CAAC;YACpB,sBAAsB;YACtB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,EAAE;gBACnC,WAAW,EAAE,IAAI;gBACjB,SAAS,EAAE,IAAI;gBACf,WAAW,EAAE,OAAO,CAAC,WAAW;gBAChC,MAAM;aACP,CAAC,CAAC;YAEH,OAAO,MAAM,CAAC;SACf;QAED,MAAM,EAAE,WAAW,EAAE,GAAG,cAAc,CAAC;QACvC,MAAM,aAAa,GAAG,WAAW,IAAI,WAAW,GAAG,IAAI,IAAI,EAAE,CAAC;QAE9D,IAAI,aAAa,EAAE;YACjB,OAAO,IAAI,CAAC;SACb;QAED,MAAM,MAAM,GAAG,IAAA,SAAE,GAAE,CAAC;QACpB,oBAAoB;QACpB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,EAAE;YACnC,GAAG,cAAc;YACjB,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,MAAM;SACP,CAAC,CAAC;QAEH,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,KAAK,CAAC,WAAW,CACf,MAAc,EACd,OAA2B;QAE3B,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;QAC9B,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QAC5D,IAAI,CAAC,cAAc,EAAE;YACnB,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;SACpC;QAED,IAAI,cAAc,CAAC,MAAM,KAAK,MAAM,EAAE;YACpC,MAAM,IAAI,KAAK,CACb,4DAA4D,CAC7D,CAAC;SACH;QAED,IAAI,CAAC,OAAO,CAAC,kBAAkB,EAAE;YAC/B,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,EAAE;gBACnC,GAAG,cAAc;gBACjB,WAAW,EAAE,IAAI;gBACjB,MAAM,EAAE,IAAI;aACb,CAAC,CAAC;YACH,OAAO;SACR;QAED,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,EAAE;YACnC,GAAG,cAAc;YACjB,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,WAAW;YACvC,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,WAAW,EAAE,IAAI;YACjB,MAAM,EAAE,IAAI;SACb,CAAC,CAAC;IACL,CAAC;CACF;AArHD,0DAqHC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Redis } from 'ioredis';
|
|
2
|
+
import { LockAcquireOptions, LockId, LockReleaseOptions, TokensRepository } from './interfaces';
|
|
3
|
+
export declare class TokensRepository_Redis implements TokensRepository {
|
|
4
|
+
private readonly redis;
|
|
5
|
+
constructor(redis: Redis);
|
|
6
|
+
private readonly version;
|
|
7
|
+
private accessTokenKey;
|
|
8
|
+
private lockKey;
|
|
9
|
+
getAccessTokenIfValid(sessionKey: string, currentTime: Date): Promise<string | null>;
|
|
10
|
+
deleteAccessToken(lockId: LockId, sessionKey: string): Promise<void>;
|
|
11
|
+
acquireLock(options: LockAcquireOptions): Promise<LockId | null>;
|
|
12
|
+
releaseLock(lockId: LockId, options: LockReleaseOptions): Promise<void>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TokensRepository_Redis = void 0;
|
|
4
|
+
const uuid_1 = require("uuid");
|
|
5
|
+
class TokensRepository_Redis {
|
|
6
|
+
constructor(redis) {
|
|
7
|
+
this.redis = redis;
|
|
8
|
+
this.version = 'v1';
|
|
9
|
+
}
|
|
10
|
+
accessTokenKey(sessionKey) {
|
|
11
|
+
return `${this.version}/${sessionKey}/t`;
|
|
12
|
+
}
|
|
13
|
+
lockKey(sessionKey) {
|
|
14
|
+
return `${this.version}/${sessionKey}/l`;
|
|
15
|
+
}
|
|
16
|
+
getAccessTokenIfValid(sessionKey,
|
|
17
|
+
//eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
18
|
+
currentTime) {
|
|
19
|
+
// If a record exists, the record is valid.
|
|
20
|
+
// Expiration is provided automatically by Redis.
|
|
21
|
+
return this.redis.get(this.accessTokenKey(sessionKey));
|
|
22
|
+
}
|
|
23
|
+
async deleteAccessToken(lockId, sessionKey) {
|
|
24
|
+
const existingLock = await this.redis.get(this.lockKey(sessionKey));
|
|
25
|
+
if (!existingLock) {
|
|
26
|
+
throw new Error('Cannot delete access token, not locked');
|
|
27
|
+
}
|
|
28
|
+
if (existingLock !== lockId) {
|
|
29
|
+
throw new Error('Cannot delete access token, locked by another process');
|
|
30
|
+
}
|
|
31
|
+
await this.redis.del(this.accessTokenKey(sessionKey));
|
|
32
|
+
}
|
|
33
|
+
async acquireLock(options) {
|
|
34
|
+
const { sessionKey, lockedUntil, currentTime } = options;
|
|
35
|
+
const lockId = (0, uuid_1.v4)();
|
|
36
|
+
const result = await this.redis.set(this.lockKey(sessionKey), lockId, 'PX', // PX means "TTL in milliseconds"
|
|
37
|
+
lockedUntil.getTime() - currentTime.getTime(), 'NX' // NX means "only set record if not exists"
|
|
38
|
+
);
|
|
39
|
+
//Lock acquired
|
|
40
|
+
if (result === 'OK') {
|
|
41
|
+
return lockId;
|
|
42
|
+
}
|
|
43
|
+
//Lock not acquired, because another process has already acquired the lock
|
|
44
|
+
if (result === null) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
//Normally this should never happen
|
|
48
|
+
throw new Error(`Unknown response from Redis: ${result}`);
|
|
49
|
+
}
|
|
50
|
+
async releaseLock(lockId, options) {
|
|
51
|
+
const { sessionKey, shouldUpdateTokens } = options;
|
|
52
|
+
const existingLockId = await this.redis.get(this.lockKey(sessionKey));
|
|
53
|
+
if (existingLockId !== lockId) {
|
|
54
|
+
throw new Error('Cannot release lock, because it was set by another process');
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
if (shouldUpdateTokens) {
|
|
58
|
+
const { tokens: { accessToken }, expiresAt, currentTime, } = options;
|
|
59
|
+
await this.redis.set(this.accessTokenKey(sessionKey), accessToken, 'PX', // PX means "TTL in milliseconds"
|
|
60
|
+
expiresAt.getTime() - currentTime.getTime());
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
finally {
|
|
64
|
+
await this.redis.del(this.lockKey(sessionKey));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
exports.TokensRepository_Redis = TokensRepository_Redis;
|
|
69
|
+
//# sourceMappingURL=TokensRepository_Redis.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TokensRepository_Redis.js","sourceRoot":"","sources":["../../src/oauth-manager/TokensRepository_Redis.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Env } from '../../types';
|
|
2
|
+
import { OAuthManager } from '../interfaces';
|
|
3
|
+
interface CreateOAuthManagerOptionsRedis {
|
|
4
|
+
env: Env;
|
|
5
|
+
apiVersion: string;
|
|
6
|
+
storage: 'redis';
|
|
7
|
+
redisURL: string;
|
|
8
|
+
}
|
|
9
|
+
interface CreateOAuthManagerOptionsMemory {
|
|
10
|
+
env: Env;
|
|
11
|
+
apiVersion: string;
|
|
12
|
+
storage: 'memory';
|
|
13
|
+
}
|
|
14
|
+
type CreateOAuthManagerOptions = CreateOAuthManagerOptionsRedis | CreateOAuthManagerOptionsMemory;
|
|
15
|
+
export declare function createOAuthManager(opt: CreateOAuthManagerOptions): OAuthManager;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createOAuthManager = void 0;
|
|
4
|
+
const assert_1 = require("assert");
|
|
5
|
+
const ioredis_1 = require("ioredis");
|
|
6
|
+
const envHosts_1 = require("../../common/envHosts");
|
|
7
|
+
const AuthenticationService_PrivateAPI_1 = require("../AuthenticationService_PrivateAPI");
|
|
8
|
+
const OAuthManager_Impl_1 = require("../OAuthManager_Impl");
|
|
9
|
+
const TokensRepository_Memory_1 = require("../TokensRepository_Memory");
|
|
10
|
+
const TokensRepository_Redis_1 = require("../TokensRepository_Redis");
|
|
11
|
+
function createOAuthManager(opt) {
|
|
12
|
+
const { env, apiVersion, storage } = opt;
|
|
13
|
+
const baseUrl = envHosts_1.envHosts[env];
|
|
14
|
+
(0, assert_1.ok)(baseUrl, 'baseUrl should be defined');
|
|
15
|
+
const tokensRepository = storage === 'redis'
|
|
16
|
+
? new TokensRepository_Redis_1.TokensRepository_Redis(new ioredis_1.Redis(opt.redisURL))
|
|
17
|
+
: storage === 'memory'
|
|
18
|
+
? new TokensRepository_Memory_1.TokensRepository_Memory()
|
|
19
|
+
: (0, assert_1.fail)('unknown storage');
|
|
20
|
+
return new OAuthManager_Impl_1.OAuthManager_Impl(tokensRepository, new AuthenticationService_PrivateAPI_1.AuthenticationService_PrivateAPI({ baseUrl, apiVersion }));
|
|
21
|
+
}
|
|
22
|
+
exports.createOAuthManager = createOAuthManager;
|
|
23
|
+
//# sourceMappingURL=createOAuthManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createOAuthManager.js","sourceRoot":"","sources":["../../../src/oauth-manager/factories/createOAuthManager.ts"],"names":[],"mappings":";;;AAAA,mCAAkC;AAClC,qCAAgC;AAChC,oDAAiD;AAEjD,0FAAuF;AACvF,4DAAyD;AACzD,wEAAqE;AACrE,sEAAmE;AAkBnE,SAAgB,kBAAkB,CAChC,GAA8B;IAE9B,MAAM,EAAE,GAAG,EAAE,UAAU,EAAE,OAAO,EAAE,GAAG,GAAG,CAAC;IAEzC,MAAM,OAAO,GAAG,mBAAQ,CAAC,GAAG,CAAC,CAAC;IAC9B,IAAA,WAAE,EAAC,OAAO,EAAE,2BAA2B,CAAC,CAAC;IAEzC,MAAM,gBAAgB,GACpB,OAAO,KAAK,OAAO;QACjB,CAAC,CAAC,IAAI,+CAAsB,CAAC,IAAI,eAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACrD,CAAC,CAAC,OAAO,KAAK,QAAQ;YACtB,CAAC,CAAC,IAAI,iDAAuB,EAAE;YAC/B,CAAC,CAAC,IAAA,aAAI,EAAC,iBAAiB,CAAC,CAAC;IAE9B,OAAO,IAAI,qCAAiB,CAC1B,gBAAgB,EAChB,IAAI,mEAAgC,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,CAC9D,CAAC;AACJ,CAAC;AAnBD,gDAmBC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
type Scopes = readonly string[];
|
|
2
|
+
export interface ClientCredentials {
|
|
3
|
+
readonly clientId: string;
|
|
4
|
+
readonly clientSecret: string;
|
|
5
|
+
}
|
|
6
|
+
export interface SessionData {
|
|
7
|
+
getCredentials(): ClientCredentials;
|
|
8
|
+
getSessionKey(): string;
|
|
9
|
+
getScopes(): Scopes;
|
|
10
|
+
}
|
|
11
|
+
export interface Tokens {
|
|
12
|
+
accessToken: string;
|
|
13
|
+
expiresIn: number;
|
|
14
|
+
}
|
|
15
|
+
export interface LockAcquireOptions {
|
|
16
|
+
sessionKey: string;
|
|
17
|
+
lockedUntil: Date;
|
|
18
|
+
currentTime: Date;
|
|
19
|
+
}
|
|
20
|
+
interface LockReleaseOptionsWithTokens {
|
|
21
|
+
shouldUpdateTokens: true;
|
|
22
|
+
sessionKey: string;
|
|
23
|
+
tokens: Tokens;
|
|
24
|
+
expiresAt: Date;
|
|
25
|
+
currentTime: Date;
|
|
26
|
+
}
|
|
27
|
+
interface LockReleaseOptionsWithoutTokens {
|
|
28
|
+
shouldUpdateTokens: false;
|
|
29
|
+
sessionKey: string;
|
|
30
|
+
}
|
|
31
|
+
export type LockReleaseOptions = LockReleaseOptionsWithTokens | LockReleaseOptionsWithoutTokens;
|
|
32
|
+
export type LockId = string;
|
|
33
|
+
export interface TokensRepository {
|
|
34
|
+
getAccessTokenIfValid(sessionKey: string, currentTime: Date): Promise<string | null>;
|
|
35
|
+
deleteAccessToken(lockId: LockId, sessionKey: string): Promise<void>;
|
|
36
|
+
acquireLock(options: LockAcquireOptions): Promise<LockId | null>;
|
|
37
|
+
releaseLock(lockId: LockId, options: LockReleaseOptions): Promise<void>;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* AuthenticationService is used for authentication
|
|
41
|
+
* (exchanging the client credentials for an access token)
|
|
42
|
+
*/
|
|
43
|
+
export interface AuthenticationService {
|
|
44
|
+
authenticate(sessionData: SessionData): Promise<Tokens | null>;
|
|
45
|
+
}
|
|
46
|
+
export interface GetAccessTokenOptions {
|
|
47
|
+
/**
|
|
48
|
+
* Ignore and delete the existing token, if any.
|
|
49
|
+
* Only enable this option if your previous API request resulted with a 401.
|
|
50
|
+
*/
|
|
51
|
+
forceInvalidate?: boolean;
|
|
52
|
+
}
|
|
53
|
+
export interface OAuthManager {
|
|
54
|
+
getAccessTokenBySessionData(sessionData: SessionData, options?: GetAccessTokenOptions): Promise<string | null>;
|
|
55
|
+
}
|
|
56
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interfaces.js","sourceRoot":"","sources":["../../src/oauth-manager/interfaces.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const assert_1 = require("assert");
|
|
4
|
+
const types_1 = require("../../types");
|
|
5
|
+
const SessionDataImpl_1 = require("../SessionDataImpl");
|
|
6
|
+
const createOAuthManager_1 = require("../factories/createOAuthManager");
|
|
7
|
+
const REDIS = 'redis://default:redispw@localhost:49153';
|
|
8
|
+
async function main() {
|
|
9
|
+
const oauthManager = (0, createOAuthManager_1.createOAuthManager)({
|
|
10
|
+
env: types_1.Env.DEV,
|
|
11
|
+
apiVersion: 'v20230109',
|
|
12
|
+
storage: 'redis',
|
|
13
|
+
redisURL: REDIS,
|
|
14
|
+
});
|
|
15
|
+
const clientId = process.env.CLIENT_ID;
|
|
16
|
+
(0, assert_1.ok)(clientId, 'clientId should be defined');
|
|
17
|
+
const clientSecret = process.env.CLIENT_SECRET;
|
|
18
|
+
(0, assert_1.ok)(clientSecret, 'clientSecret should be defined');
|
|
19
|
+
const scopes = ['companies:read', 'properties:download', 'lead_info:write'];
|
|
20
|
+
const sessionData = new SessionDataImpl_1.SessionDataImpl({
|
|
21
|
+
clientId,
|
|
22
|
+
clientSecret,
|
|
23
|
+
scopes,
|
|
24
|
+
});
|
|
25
|
+
const promises = [];
|
|
26
|
+
for (let i = 0; i < 10; i++) {
|
|
27
|
+
promises.push((async () => {
|
|
28
|
+
const token = await oauthManager.getAccessTokenBySessionData(sessionData);
|
|
29
|
+
console.log(token?.length);
|
|
30
|
+
})());
|
|
31
|
+
}
|
|
32
|
+
await Promise.all(promises);
|
|
33
|
+
}
|
|
34
|
+
main()
|
|
35
|
+
.then(() => {
|
|
36
|
+
process.exit(0);
|
|
37
|
+
})
|
|
38
|
+
.catch((e) => {
|
|
39
|
+
console.error(e);
|
|
40
|
+
process.exit(1);
|
|
41
|
+
});
|
|
42
|
+
//# sourceMappingURL=main.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../../src/oauth-manager/playground/main.ts"],"names":[],"mappings":";;AAAA,mCAA4B;AAC5B,uCAAkC;AAClC,wDAAqD;AACrD,wEAAqE;AAErE,MAAM,KAAK,GAAG,yCAAyC,CAAC;AAExD,KAAK,UAAU,IAAI;IACjB,MAAM,YAAY,GAAG,IAAA,uCAAkB,EAAC;QACtC,GAAG,EAAE,WAAG,CAAC,GAAG;QACZ,UAAU,EAAE,WAAW;QACvB,OAAO,EAAE,OAAO;QAChB,QAAQ,EAAE,KAAK;KAChB,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC;IACvC,IAAA,WAAE,EAAC,QAAQ,EAAE,4BAA4B,CAAC,CAAC;IAE3C,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC;IAC/C,IAAA,WAAE,EAAC,YAAY,EAAE,gCAAgC,CAAC,CAAC;IAEnD,MAAM,MAAM,GAAG,CAAC,gBAAgB,EAAE,qBAAqB,EAAE,iBAAiB,CAAC,CAAC;IAC5E,MAAM,WAAW,GAAG,IAAI,iCAAe,CAAC;QACtC,QAAQ;QACR,YAAY;QACZ,MAAM;KACP,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAG,EAAE,CAAC;IAEpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE;QAC3B,QAAQ,CAAC,IAAI,CACX,CAAC,KAAK,IAAmB,EAAE;YACzB,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,2BAA2B,CAC1D,WAAW,CACZ,CAAC;YACF,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QAC7B,CAAC,CAAC,EAAE,CACL,CAAC;KACH;IAED,MAAM,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AAC9B,CAAC;AAED,IAAI,EAAE;KACH,IAAI,CAAC,GAAG,EAAE;IACT,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC;KACD,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE;IACX,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACjB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { AxiosInstance } from 'axios';
|
|
2
2
|
import { Property } from './entities/property';
|
|
3
|
+
import { OAuthManager } from './oauth-manager/interfaces';
|
|
3
4
|
export declare enum Env {
|
|
4
5
|
PRODUCTION = "production",
|
|
5
6
|
STAGING = "staging",
|
|
@@ -32,6 +33,7 @@ export declare enum EstateInclude {
|
|
|
32
33
|
export type ClientConf = {
|
|
33
34
|
clientId?: string;
|
|
34
35
|
clientSecret?: string;
|
|
36
|
+
oauthManager?: OAuthManager;
|
|
35
37
|
env?: Env;
|
|
36
38
|
version?: string;
|
|
37
39
|
};
|
|
@@ -44,7 +46,6 @@ export declare enum TokenType {
|
|
|
44
46
|
}
|
|
45
47
|
export type Auth = {
|
|
46
48
|
token: string;
|
|
47
|
-
expiresIn?: string;
|
|
48
49
|
type: TokenType;
|
|
49
50
|
};
|
|
50
51
|
export type Maybe<T> = T | null;
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";;;AAIA,IAAY,GAIX;AAJD,WAAY,GAAG;IACb,gCAAyB,CAAA;IACzB,0BAAmB,CAAA;IACnB,kBAAW,CAAA;AACb,CAAC,EAJW,GAAG,mBAAH,GAAG,QAId;AAED,IAAY,aAuBX;AAvBD,WAAY,aAAa;IACvB,sCAAqB,CAAA;IACrB,sCAAqB,CAAA;IACrB,0CAAyB,CAAA;IACzB,oDAAqC,CAAA;IACrC,sDAAuC,CAAA;IACvC,oEAAqD,CAAA;IACrD,0FAA2E,CAAA;IAC3E,4EAA6D,CAAA;IAC7D,4DAA6C,CAAA;IAC7C,0EAA2D,CAAA;IAC3D,gEAAiD,CAAA;IACjD,gDAAiC,CAAA;IACjC,8DAA+C,CAAA;IAC/C,gDAAiC,CAAA;IACjC,gEAAiD,CAAA;IACjD,8EAA+D,CAAA;IAC/D,oGAAqF,CAAA;IACrF,sFAAuE,CAAA;IACvE,sEAAuD,CAAA;IACvD,oFAAqE,CAAA;IACrE,oDAAqC,CAAA;IACrC,0EAA2D,CAAA;AAC7D,CAAC,EAvBW,aAAa,6BAAb,aAAa,QAuBxB;AAcD,IAAY,SAGX;AAHD,WAAY,SAAS;IACnB,qDAAU,CAAA;IACV,mDAAS,CAAA;AACX,CAAC,EAHW,SAAS,yBAAT,SAAS,QAGpB"}
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.31.
|
|
2
|
+
"version": "0.31.8-pre-release.0",
|
|
3
3
|
"license": "UNLICENCED",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"typings": "./dist/index.d.ts",
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
"build": "yarn tsc",
|
|
12
12
|
"test": "yarn jest --forceExit --detectOpenHandles",
|
|
13
13
|
"lint": "eslint --ext .ts ./src",
|
|
14
|
-
"format": "prettier --write \"**/*.{js,md,scss,css,ts,tsx}\""
|
|
14
|
+
"format": "prettier --write \"**/*.{js,md,scss,css,ts,tsx}\"",
|
|
15
|
+
"dev": "tsx ./src/oauth-manager/playground/main.ts"
|
|
15
16
|
},
|
|
16
17
|
"peerDependencies": {},
|
|
17
18
|
"husky": {
|
|
@@ -31,17 +32,23 @@
|
|
|
31
32
|
"devDependencies": {
|
|
32
33
|
"@sweepbright/eslint-config-shared": "^1.0.5",
|
|
33
34
|
"@sweepbright/shared-configs-tsconfig": "^1.0.1",
|
|
35
|
+
"@types/ioredis-mock": "^8.2.2",
|
|
34
36
|
"@types/jest": "^25.1.3",
|
|
37
|
+
"@types/uuid": "^9.0.2",
|
|
35
38
|
"husky": "^4.2.3",
|
|
39
|
+
"ioredis-mock": "^8.8.1",
|
|
36
40
|
"jest": "^29.5.0",
|
|
37
41
|
"msw": "^0.21.2",
|
|
38
42
|
"ts-jest": "^29.1.0",
|
|
39
43
|
"tslib": "^1.11.1",
|
|
44
|
+
"tsx": "^3.12.7",
|
|
40
45
|
"typescript": "^5.1.6"
|
|
41
46
|
},
|
|
42
47
|
"dependencies": {
|
|
43
48
|
"axios": "^0.21.2",
|
|
49
|
+
"ioredis": "^5.3.2",
|
|
44
50
|
"tiny-invariant": "^1.1.0",
|
|
51
|
+
"uuid": "^9.0.0",
|
|
45
52
|
"zod": "^3.21.4"
|
|
46
53
|
}
|
|
47
54
|
}
|