@or-sdk/authorizer 0.10.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/cjs/Basic/BasicAuth.js +157 -0
- package/dist/cjs/Basic/BasicAuth.js.map +1 -0
- package/dist/cjs/Basic/BasicCollection.js +107 -0
- package/dist/cjs/Basic/BasicCollection.js.map +1 -0
- package/dist/cjs/Basic/types.js +3 -0
- package/dist/cjs/Basic/types.js.map +1 -0
- package/dist/cjs/Basic/utils/createAuthKey.js +8 -0
- package/dist/cjs/Basic/utils/createAuthKey.js.map +1 -0
- package/dist/cjs/OAuth/OAuth.js +391 -0
- package/dist/cjs/OAuth/OAuth.js.map +1 -0
- package/dist/cjs/OAuth/OAuthCollection.js +138 -0
- package/dist/cjs/OAuth/OAuthCollection.js.map +1 -0
- package/dist/cjs/OAuth/types.js +11 -0
- package/dist/cjs/OAuth/types.js.map +1 -0
- package/dist/cjs/OAuth/utils/ServiceDefinition.js +175 -0
- package/dist/cjs/OAuth/utils/ServiceDefinition.js.map +1 -0
- package/dist/cjs/OAuth/utils/createAuthKey.js +8 -0
- package/dist/cjs/OAuth/utils/createAuthKey.js.map +1 -0
- package/dist/cjs/OAuth/utils/formatScope.js +20 -0
- package/dist/cjs/OAuth/utils/formatScope.js.map +1 -0
- package/dist/cjs/OAuth/utils/isExpired.js +12 -0
- package/dist/cjs/OAuth/utils/isExpired.js.map +1 -0
- package/dist/cjs/Token/TokenAuth.js +135 -0
- package/dist/cjs/Token/TokenAuth.js.map +1 -0
- package/dist/cjs/Token/TokenCollection.js +106 -0
- package/dist/cjs/Token/TokenCollection.js.map +1 -0
- package/dist/cjs/Token/types.js +3 -0
- package/dist/cjs/Token/types.js.map +1 -0
- package/dist/cjs/Token/utils/createAuthKey.js +8 -0
- package/dist/cjs/Token/utils/createAuthKey.js.map +1 -0
- package/dist/cjs/constants.js +16 -0
- package/dist/cjs/constants.js.map +1 -0
- package/dist/cjs/index.js +27 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/types.js +16 -0
- package/dist/cjs/types.js.map +1 -0
- package/dist/esm/Basic/BasicAuth.js +86 -0
- package/dist/esm/Basic/BasicAuth.js.map +1 -0
- package/dist/esm/Basic/BasicCollection.js +56 -0
- package/dist/esm/Basic/BasicCollection.js.map +1 -0
- package/dist/esm/Basic/types.js +2 -0
- package/dist/esm/Basic/types.js.map +1 -0
- package/dist/esm/Basic/utils/createAuthKey.js +4 -0
- package/dist/esm/Basic/utils/createAuthKey.js.map +1 -0
- package/dist/esm/OAuth/OAuth.js +258 -0
- package/dist/esm/OAuth/OAuth.js.map +1 -0
- package/dist/esm/OAuth/OAuthCollection.js +69 -0
- package/dist/esm/OAuth/OAuthCollection.js.map +1 -0
- package/dist/esm/OAuth/types.js +8 -0
- package/dist/esm/OAuth/types.js.map +1 -0
- package/dist/esm/OAuth/utils/ServiceDefinition.js +117 -0
- package/dist/esm/OAuth/utils/ServiceDefinition.js.map +1 -0
- package/dist/esm/OAuth/utils/createAuthKey.js +4 -0
- package/dist/esm/OAuth/utils/createAuthKey.js.map +1 -0
- package/dist/esm/OAuth/utils/formatScope.js +16 -0
- package/dist/esm/OAuth/utils/formatScope.js.map +1 -0
- package/dist/esm/OAuth/utils/isExpired.js +8 -0
- package/dist/esm/OAuth/utils/isExpired.js.map +1 -0
- package/dist/esm/Token/TokenAuth.js +64 -0
- package/dist/esm/Token/TokenAuth.js.map +1 -0
- package/dist/esm/Token/TokenCollection.js +55 -0
- package/dist/esm/Token/TokenCollection.js.map +1 -0
- package/dist/esm/Token/types.js +2 -0
- package/dist/esm/Token/types.js.map +1 -0
- package/dist/esm/Token/utils/createAuthKey.js +4 -0
- package/dist/esm/Token/utils/createAuthKey.js.map +1 -0
- package/dist/esm/constants.js +13 -0
- package/dist/esm/constants.js.map +1 -0
- package/dist/esm/index.js +8 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/types.js +4 -0
- package/dist/esm/types.js.map +1 -0
- package/dist/types/Basic/BasicAuth.d.ts +19 -0
- package/dist/types/Basic/BasicCollection.d.ts +12 -0
- package/dist/types/Basic/types.d.ts +26 -0
- package/dist/types/Basic/utils/createAuthKey.d.ts +1 -0
- package/dist/types/OAuth/OAuth.d.ts +18 -0
- package/dist/types/OAuth/OAuthCollection.d.ts +14 -0
- package/dist/types/OAuth/types.d.ts +81 -0
- package/dist/types/OAuth/utils/ServiceDefinition.d.ts +36 -0
- package/dist/types/OAuth/utils/createAuthKey.d.ts +1 -0
- package/dist/types/OAuth/utils/formatScope.d.ts +2 -0
- package/dist/types/OAuth/utils/isExpired.d.ts +1 -0
- package/dist/types/Token/TokenAuth.d.ts +11 -0
- package/dist/types/Token/TokenCollection.d.ts +12 -0
- package/dist/types/Token/types.d.ts +25 -0
- package/dist/types/Token/utils/createAuthKey.d.ts +1 -0
- package/dist/types/constants.d.ts +11 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/types.d.ts +3 -0
- package/package.json +33 -0
- package/src/Basic/BasicAuth.ts +147 -0
- package/src/Basic/BasicCollection.ts +97 -0
- package/src/Basic/types.ts +54 -0
- package/src/Basic/utils/createAuthKey.ts +3 -0
- package/src/OAuth/OAuth.ts +480 -0
- package/src/OAuth/OAuthCollection.ts +138 -0
- package/src/OAuth/types.ts +131 -0
- package/src/OAuth/utils/ServiceDefinition.ts +171 -0
- package/src/OAuth/utils/createAuthKey.ts +3 -0
- package/src/OAuth/utils/formatScope.ts +20 -0
- package/src/OAuth/utils/isExpired.ts +7 -0
- package/src/Token/TokenAuth.ts +120 -0
- package/src/Token/TokenCollection.ts +97 -0
- package/src/Token/types.ts +51 -0
- package/src/Token/utils/createAuthKey.ts +3 -0
- package/src/constants.ts +14 -0
- package/src/index.ts +10 -0
- package/src/types.ts +3 -0
- package/tsconfig.esm.json +9 -0
- package/tsconfig.json +7 -0
- package/tsconfig.types.json +9 -0
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
import { KeyValueStorage } from '@or-sdk/key-value-storage';
|
|
2
|
+
import { EventManager } from '@or-sdk/event-manager';
|
|
3
|
+
import { timeout } from '@or-sdk/base';
|
|
4
|
+
|
|
5
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
OAuthConfig,
|
|
9
|
+
OAuthData,
|
|
10
|
+
OAuthApp,
|
|
11
|
+
CreateOAuthConfig,
|
|
12
|
+
CreateOAuthResult,
|
|
13
|
+
ServiceDefinitionConfig,
|
|
14
|
+
} from './types';
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
SERVICE_PROVIDER_PATH,
|
|
18
|
+
OAUTH_REDIRECT_PROVIDER_PATH,
|
|
19
|
+
PREDEFINED_APP,
|
|
20
|
+
TEMPORARY_DATA_EXPIRATION_TIME,
|
|
21
|
+
AuthStatus,
|
|
22
|
+
} from '../constants';
|
|
23
|
+
|
|
24
|
+
import { formatScope } from './utils/formatScope';
|
|
25
|
+
import { isExpired } from './utils/isExpired';
|
|
26
|
+
import { ServiceDefinition } from './utils/ServiceDefinition';
|
|
27
|
+
import { createAuthKey } from './utils/createAuthKey';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* OneReach Authorizer service client
|
|
31
|
+
* ## Installation:
|
|
32
|
+
* ```
|
|
33
|
+
* $ npm i @or-sdk/authorizer
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export class OAuth {
|
|
37
|
+
private status = AuthStatus.READY;
|
|
38
|
+
|
|
39
|
+
private readonly authKey: string;
|
|
40
|
+
private readonly serviceName: string;
|
|
41
|
+
private readonly keyValueCollection: string;
|
|
42
|
+
private readonly keyValueStorage: KeyValueStorage;
|
|
43
|
+
private readonly eventManager: EventManager;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Connects to existing authorization
|
|
47
|
+
*
|
|
48
|
+
* ```typescript
|
|
49
|
+
* import { OAuth } from '@or-sdk/authorizer'
|
|
50
|
+
* const oAuthInstance = new OAuth({
|
|
51
|
+
* token: 'my-account-token-string',
|
|
52
|
+
* discoveryUrl: 'discovery.example.onereach.ai',
|
|
53
|
+
* serviceName: '__authorization_service_test_service',
|
|
54
|
+
* authKey: '637ac446-1021-475f-9992-3ce7f3ddb637::oauth::someAuth::__authorization_service_test_service::02bf4366-f987-49ea-90a6-0d346e13c3c1',
|
|
55
|
+
* keyValueCollection: 'custom_collection_name' // Pass this if you using custom name for key-value collection that differs from serviceName
|
|
56
|
+
* });
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
constructor(params: OAuthConfig) {
|
|
60
|
+
const { token, discoveryUrl, authKey, serviceName, keyValueCollection } =
|
|
61
|
+
params;
|
|
62
|
+
|
|
63
|
+
this.authKey = authKey;
|
|
64
|
+
this.serviceName = serviceName;
|
|
65
|
+
this.keyValueCollection = keyValueCollection || serviceName;
|
|
66
|
+
|
|
67
|
+
this.keyValueStorage = new KeyValueStorage({
|
|
68
|
+
token,
|
|
69
|
+
discoveryUrl,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
this.eventManager = new EventManager({
|
|
73
|
+
token,
|
|
74
|
+
discoveryUrl,
|
|
75
|
+
requestAccountId: true,
|
|
76
|
+
requestProvidersAccountId: true,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Creates a new auth record in specified collection with the given config.
|
|
82
|
+
* @returns Object with OAuth instance connected to created auth and authorizerUrl to redirect the user to for completing authorization
|
|
83
|
+
*
|
|
84
|
+
* ```typescript
|
|
85
|
+
* const {
|
|
86
|
+
* authorizeUrl, // redirect user to this url for completing authorization
|
|
87
|
+
* instance // new instance
|
|
88
|
+
* } = await OAuth.create({
|
|
89
|
+
* token: 'my-account-token-string',
|
|
90
|
+
* discoveryUrl: 'discovery.example.onereach.ai',
|
|
91
|
+
* serviceName: '__authorization_service_test_service',
|
|
92
|
+
* authName: 'my-auth-name',
|
|
93
|
+
* appId: 'my-app-id',
|
|
94
|
+
* keyValueCollection: 'custom_collection_name' // Pass this if you using custom name for key-value collection that differs from serviceName
|
|
95
|
+
* });
|
|
96
|
+
*
|
|
97
|
+
* // if you want to use returned instance, you must call a method that returns a promise that will be resolved when the user completes authorization process
|
|
98
|
+
* // if not, you can omit this step
|
|
99
|
+
* try {
|
|
100
|
+
* await instance.waitForCompletion()
|
|
101
|
+
* } catch (e) {
|
|
102
|
+
* // will be called if the user does not complete authorization within 5 minutes
|
|
103
|
+
* }
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
106
|
+
static async create(
|
|
107
|
+
params: CreateOAuthConfig
|
|
108
|
+
): Promise<CreateOAuthResult> {
|
|
109
|
+
if (
|
|
110
|
+
!(
|
|
111
|
+
params.serviceName &&
|
|
112
|
+
params.authName &&
|
|
113
|
+
params.discoveryUrl &&
|
|
114
|
+
params.token &&
|
|
115
|
+
params.appId
|
|
116
|
+
)
|
|
117
|
+
) {
|
|
118
|
+
throw new Error('Invalid config passed');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const { serviceName, authName, discoveryUrl, token, appId, scope } = params;
|
|
122
|
+
|
|
123
|
+
const keyValueCollection = params.keyValueCollection || serviceName;
|
|
124
|
+
|
|
125
|
+
const keyValueStorage = new KeyValueStorage({
|
|
126
|
+
token,
|
|
127
|
+
discoveryUrl,
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
const eventManager = new EventManager({
|
|
131
|
+
token,
|
|
132
|
+
discoveryUrl,
|
|
133
|
+
requestAccountId: true,
|
|
134
|
+
requestProvidersAccountId: true,
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
await eventManager.init();
|
|
138
|
+
|
|
139
|
+
const serviceDefinitionProviderRoute = `http/${eventManager.providersAccountId}${SERVICE_PROVIDER_PATH}`;
|
|
140
|
+
|
|
141
|
+
const services = await eventManager.makeRequest<{
|
|
142
|
+
[key: string]: ServiceDefinitionConfig;
|
|
143
|
+
}>({
|
|
144
|
+
method: 'GET',
|
|
145
|
+
route: serviceDefinitionProviderRoute,
|
|
146
|
+
params: {
|
|
147
|
+
type: 'list',
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
const currentServiceData = services[serviceName];
|
|
152
|
+
|
|
153
|
+
const apps = await keyValueStorage.getValueByKey(
|
|
154
|
+
keyValueCollection,
|
|
155
|
+
'__authorizer_apps'
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
const currentApp: OAuthApp = (apps.value as any).find(
|
|
159
|
+
(app: { label: string; value: OAuthApp; }) => app.value.appId === appId
|
|
160
|
+
).value;
|
|
161
|
+
|
|
162
|
+
const serviceDefinition = new ServiceDefinition(
|
|
163
|
+
currentServiceData,
|
|
164
|
+
currentApp.authLinkParams,
|
|
165
|
+
currentApp.environment
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
const id = uuidv4();
|
|
169
|
+
const authKey = createAuthKey(id, authName, keyValueCollection, eventManager.currentAccountId);
|
|
170
|
+
|
|
171
|
+
const additionalBodyData = {};
|
|
172
|
+
const additionalHeaders = {};
|
|
173
|
+
|
|
174
|
+
const emUrl = eventManager.serviceUrl;
|
|
175
|
+
const redirectProviderUrl = `${emUrl}/http/${eventManager.providersAccountId}${OAUTH_REDIRECT_PROVIDER_PATH}`;
|
|
176
|
+
|
|
177
|
+
const authConfigs: any = {
|
|
178
|
+
...additionalBodyData,
|
|
179
|
+
grant_type: 'authorization_code',
|
|
180
|
+
redirect_uri: redirectProviderUrl,
|
|
181
|
+
appId,
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
if (scope) {
|
|
185
|
+
const formattedScope = formatScope(scope, serviceDefinition.scopeType);
|
|
186
|
+
authConfigs.scope = formattedScope;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const configs = {
|
|
190
|
+
[serviceDefinition.requestDataType]: authConfigs,
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
const tempAuthData = {
|
|
194
|
+
expiresInDefaultValue: serviceDefinition.expiresInDefaultValue,
|
|
195
|
+
urlToExchangeToken: serviceDefinition.exchangeTokenUri,
|
|
196
|
+
refreshUri: serviceDefinition.refreshUri,
|
|
197
|
+
additionalHeaders,
|
|
198
|
+
configs,
|
|
199
|
+
isCustomApp: appId !== PREDEFINED_APP,
|
|
200
|
+
requestDataType: serviceDefinition.requestDataType,
|
|
201
|
+
service: keyValueCollection,
|
|
202
|
+
serviceConfigName: serviceName,
|
|
203
|
+
name: authName,
|
|
204
|
+
displayServiceName: serviceDefinition.displayServiceName,
|
|
205
|
+
accountId: eventManager.currentAccountId,
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
const authDataExpire = Date.now() + TEMPORARY_DATA_EXPIRATION_TIME;
|
|
209
|
+
await keyValueStorage.setValueByKey(
|
|
210
|
+
'__authorizer_temp-uuid',
|
|
211
|
+
id,
|
|
212
|
+
tempAuthData,
|
|
213
|
+
authDataExpire
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
const authUrl = new URL(serviceDefinition.authorizeUri);
|
|
217
|
+
const additionalParams: { [key: string]: string; } = JSON.parse(
|
|
218
|
+
serviceDefinition.authRequestAdditionalParams
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
Object.entries(additionalParams.queryParams).forEach(([key, value]) => {
|
|
222
|
+
authUrl.searchParams.append(key, value);
|
|
223
|
+
});
|
|
224
|
+
authUrl.searchParams.append('response_type', 'code');
|
|
225
|
+
authUrl.searchParams.append('client_id', currentApp.clientId);
|
|
226
|
+
authUrl.searchParams.append('redirect_uri', redirectProviderUrl);
|
|
227
|
+
authUrl.searchParams.append('state', authKey);
|
|
228
|
+
|
|
229
|
+
if (scope) {
|
|
230
|
+
const formattedScope = formatScope(scope, serviceDefinition.scopeType);
|
|
231
|
+
authUrl.searchParams.append('scope', formattedScope);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const authorizeUrl = authUrl.href;
|
|
235
|
+
|
|
236
|
+
const newOAuth = new OAuth({
|
|
237
|
+
serviceName,
|
|
238
|
+
authKey,
|
|
239
|
+
discoveryUrl,
|
|
240
|
+
token,
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
newOAuth.status = AuthStatus.PENDING;
|
|
244
|
+
|
|
245
|
+
return {
|
|
246
|
+
instance: newOAuth,
|
|
247
|
+
authorizeUrl,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
private async getOAuthAppById(appId: string): Promise<OAuthApp | undefined> {
|
|
252
|
+
const appsStorageRecord = await this.keyValueStorage.getValueByKey(
|
|
253
|
+
this.keyValueCollection,
|
|
254
|
+
'__authorizer_apps'
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
const appsRecords = appsStorageRecord.value as Array<{
|
|
258
|
+
label: string;
|
|
259
|
+
value: Omit<OAuthApp, 'name'>;
|
|
260
|
+
}>;
|
|
261
|
+
|
|
262
|
+
const currentApp = appsRecords.find(app => app.value.appId === appId);
|
|
263
|
+
|
|
264
|
+
if (!currentApp) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return {
|
|
269
|
+
name: currentApp.label,
|
|
270
|
+
...currentApp.value,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
private async getServiceDefinition(currentApp: OAuthApp): Promise<ServiceDefinition> {
|
|
275
|
+
await this.eventManager.init();
|
|
276
|
+
|
|
277
|
+
const serviceDefinitionProviderRoute = `http/${this.eventManager.providersAccountId}${SERVICE_PROVIDER_PATH}`;
|
|
278
|
+
|
|
279
|
+
const services = await this.eventManager.makeRequest<{
|
|
280
|
+
[key: string]: ServiceDefinitionConfig;
|
|
281
|
+
}>({
|
|
282
|
+
method: 'GET',
|
|
283
|
+
route: serviceDefinitionProviderRoute,
|
|
284
|
+
params: {
|
|
285
|
+
type: 'list',
|
|
286
|
+
},
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
const currentServiceData = services[this.serviceName];
|
|
290
|
+
|
|
291
|
+
return new ServiceDefinition(
|
|
292
|
+
currentServiceData,
|
|
293
|
+
currentApp.authLinkParams,
|
|
294
|
+
currentApp.environment
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Returns a promise that will be resolved after the user completes the authorization process
|
|
300
|
+
*
|
|
301
|
+
* ```typescript
|
|
302
|
+
* instance.waitForCompletion()
|
|
303
|
+
* .then(() => {
|
|
304
|
+
* // will be called after the user completes the authorization process
|
|
305
|
+
* })
|
|
306
|
+
* .catch(() => {
|
|
307
|
+
* // will be called if the user does not complete authorization within 5 minutes
|
|
308
|
+
* })
|
|
309
|
+
* ```
|
|
310
|
+
*/
|
|
311
|
+
public async waitForCompletion(): Promise<void> {
|
|
312
|
+
if (this.status === AuthStatus.PENDING) {
|
|
313
|
+
let pollInterval = 3;
|
|
314
|
+
const step = 1;
|
|
315
|
+
const pollTimeout = 300; // 5 minutes
|
|
316
|
+
|
|
317
|
+
let spentTime = 0;
|
|
318
|
+
let isFinished = false;
|
|
319
|
+
|
|
320
|
+
while (spentTime < pollTimeout) {
|
|
321
|
+
const record = await this.keyValueStorage.getValueByKey(
|
|
322
|
+
this.keyValueCollection,
|
|
323
|
+
this.authKey
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
if (record.value) {
|
|
327
|
+
isFinished = true;
|
|
328
|
+
break;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
await timeout(pollInterval);
|
|
332
|
+
spentTime += pollInterval;
|
|
333
|
+
pollInterval += step;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (!isFinished) {
|
|
337
|
+
this.status = AuthStatus.ERROR;
|
|
338
|
+
throw new Error(
|
|
339
|
+
'Authorization was not completed within the allowed time'
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
this.status = AuthStatus.READY;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Returns authorization data and performs refresh token request if needed
|
|
349
|
+
*
|
|
350
|
+
* ```typescript
|
|
351
|
+
* const authData = await oAuthInstance.getAuthData()
|
|
352
|
+
* ```
|
|
353
|
+
*/
|
|
354
|
+
public async getAuthData(): Promise<OAuthData> {
|
|
355
|
+
if (this.status !== AuthStatus.READY) {
|
|
356
|
+
throw new Error('This authorization is not ready');
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
const { value } = await this.keyValueStorage.getValueByKey(
|
|
360
|
+
this.serviceName,
|
|
361
|
+
this.authKey
|
|
362
|
+
);
|
|
363
|
+
|
|
364
|
+
if (!value) {
|
|
365
|
+
this.status = AuthStatus.ERROR;
|
|
366
|
+
throw new Error('Authorization record does not exist');
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const authData = value as OAuthData;
|
|
370
|
+
|
|
371
|
+
if (!authData.expires_in) {
|
|
372
|
+
return authData;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const expired = isExpired(authData.created_at, authData.expires_in);
|
|
376
|
+
|
|
377
|
+
if (!expired) {
|
|
378
|
+
return authData;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
await this.refresh();
|
|
382
|
+
|
|
383
|
+
const { value: refreshedAuthData } =
|
|
384
|
+
await this.keyValueStorage.getValueByKey(this.serviceName, this.authKey);
|
|
385
|
+
|
|
386
|
+
return refreshedAuthData as OAuthData;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Perform token request manually
|
|
391
|
+
*/
|
|
392
|
+
public async refresh(): Promise<void> {
|
|
393
|
+
const { value } = await this.keyValueStorage.getValueByKey(
|
|
394
|
+
this.serviceName,
|
|
395
|
+
this.authKey
|
|
396
|
+
);
|
|
397
|
+
|
|
398
|
+
if (!value) {
|
|
399
|
+
this.status = AuthStatus.ERROR;
|
|
400
|
+
throw new Error('Authorization record does not exist');
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const authData = value as OAuthData;
|
|
404
|
+
|
|
405
|
+
const appId = authData.appId;
|
|
406
|
+
|
|
407
|
+
const currentApp = await this.getOAuthAppById(appId);
|
|
408
|
+
|
|
409
|
+
if (!currentApp) {
|
|
410
|
+
this.status = AuthStatus.ERROR;
|
|
411
|
+
throw new Error('The application used for this authorization does not exist');
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
const serviceDefinition = await this.getServiceDefinition(currentApp);
|
|
415
|
+
|
|
416
|
+
await this.eventManager.makeRequest({
|
|
417
|
+
method: 'POST',
|
|
418
|
+
route: new URL(authData.redirect_uri).pathname,
|
|
419
|
+
data: {
|
|
420
|
+
refreshTokenUrl: authData.refreshUri,
|
|
421
|
+
sendDataType: serviceDefinition.requestDataType,
|
|
422
|
+
currentUserData: authData,
|
|
423
|
+
service: this.keyValueCollection,
|
|
424
|
+
currentAuth: this.authKey,
|
|
425
|
+
},
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Returns a OAuth app which is using in this authorization
|
|
431
|
+
*
|
|
432
|
+
* ```typescript
|
|
433
|
+
* const oAuthApp = await oAuthInstance.getOAuthApp()
|
|
434
|
+
* ```
|
|
435
|
+
*/
|
|
436
|
+
public async getOAuthApp(): Promise<OAuthApp> {
|
|
437
|
+
if (this.status !== AuthStatus.READY) {
|
|
438
|
+
throw new Error('This authorization is not ready');
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
const { value } = await this.keyValueStorage.getValueByKey(
|
|
442
|
+
this.serviceName,
|
|
443
|
+
this.authKey
|
|
444
|
+
);
|
|
445
|
+
|
|
446
|
+
if (!value) {
|
|
447
|
+
this.status = AuthStatus.ERROR;
|
|
448
|
+
throw new Error('Authorization record does not exist');
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
const authData = value as OAuthData;
|
|
452
|
+
|
|
453
|
+
const appId = authData.appId;
|
|
454
|
+
|
|
455
|
+
const currentApp = await this.getOAuthAppById(appId);
|
|
456
|
+
|
|
457
|
+
if (!currentApp) {
|
|
458
|
+
this.status = AuthStatus.ERROR;
|
|
459
|
+
throw new Error('The application used for this authorization does not exist');
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
return currentApp;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Deletes authorization from collection
|
|
467
|
+
*
|
|
468
|
+
* ```typescript
|
|
469
|
+
* await oAuthInstance.delete()
|
|
470
|
+
* ```
|
|
471
|
+
*/
|
|
472
|
+
public async delete(): Promise<void> {
|
|
473
|
+
await this.keyValueStorage.deleteKey(
|
|
474
|
+
this.serviceName,
|
|
475
|
+
this.authKey
|
|
476
|
+
);
|
|
477
|
+
|
|
478
|
+
this.status = AuthStatus.DELETED;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { Token } from '@or-sdk/base';
|
|
2
|
+
import { KeyValueStorage } from '@or-sdk/key-value-storage';
|
|
3
|
+
import { OAuth } from './OAuth';
|
|
4
|
+
|
|
5
|
+
import { OAuthCollectionConfig, CreateOAuthInCollectionConfig, CreateOAuthResult, OAuthApp } from './types';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* OneReach Authorizer service client
|
|
9
|
+
* ## Installation:
|
|
10
|
+
* ```
|
|
11
|
+
* $ npm i @or-sdk/authorizer
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export class OAuthCollection {
|
|
15
|
+
private readonly serviceName: string;
|
|
16
|
+
private readonly keyValueCollection: string;
|
|
17
|
+
private readonly keyValueStorage: KeyValueStorage;
|
|
18
|
+
private readonly localToken: Token;
|
|
19
|
+
private readonly localDiscoveryUrl: string;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Connects to authorization collection
|
|
23
|
+
*
|
|
24
|
+
* ```typescript
|
|
25
|
+
* import { OAuthCollection } from '@or-sdk/authorizer'
|
|
26
|
+
* const oAuthCollectionInstance = new OAuthCollection({
|
|
27
|
+
* token: 'my-account-token-string',
|
|
28
|
+
* discoveryUrl: 'discovery.example.onereach.ai',
|
|
29
|
+
* serviceName: '__authorization_service_test_service',
|
|
30
|
+
* keyValueCollections: 'custom-collection-name' // Pass this if you using custom name for key-value collection that differs from serviceName
|
|
31
|
+
* });
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
constructor(params: OAuthCollectionConfig) {
|
|
35
|
+
const { token, discoveryUrl, serviceName, keyValueCollection } = params;
|
|
36
|
+
|
|
37
|
+
this.localToken = token;
|
|
38
|
+
this.localDiscoveryUrl = discoveryUrl;
|
|
39
|
+
this.serviceName = serviceName;
|
|
40
|
+
this.keyValueCollection = keyValueCollection || serviceName;
|
|
41
|
+
this.keyValueStorage = new KeyValueStorage({
|
|
42
|
+
token,
|
|
43
|
+
discoveryUrl,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Returns array of auth keys from this collection
|
|
49
|
+
*/
|
|
50
|
+
public async listAuthorizations(): Promise<string[]> {
|
|
51
|
+
const records = await this.keyValueStorage.listKeys(
|
|
52
|
+
this.keyValueCollection
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
return records.map((record) => record.key);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Returns array of OAuth applications for this service
|
|
60
|
+
*/
|
|
61
|
+
public async listOAuthApps(): Promise<OAuthApp[]> {
|
|
62
|
+
const appsStorageRecord = await this.keyValueStorage.getValueByKey(
|
|
63
|
+
this.keyValueCollection,
|
|
64
|
+
'__authorizer_apps'
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
if (!appsStorageRecord.value) {
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const appsRecords = appsStorageRecord.value as Array<{
|
|
72
|
+
label: string;
|
|
73
|
+
value: Omit<OAuthApp, 'name'>;
|
|
74
|
+
}>;
|
|
75
|
+
|
|
76
|
+
return appsRecords.map((record) => ({
|
|
77
|
+
name: record.label,
|
|
78
|
+
...record.value,
|
|
79
|
+
}));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Returns OAuth instance for given key
|
|
84
|
+
*/
|
|
85
|
+
public async getAuthorization(key: string): Promise<OAuth> {
|
|
86
|
+
const record = await this.keyValueStorage.getValueByKey(
|
|
87
|
+
this.keyValueCollection,
|
|
88
|
+
key
|
|
89
|
+
);
|
|
90
|
+
if (!record.value) {
|
|
91
|
+
throw new Error('This authorization does not exist');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return new OAuth({
|
|
95
|
+
serviceName: this.serviceName,
|
|
96
|
+
keyValueCollection: this.keyValueCollection,
|
|
97
|
+
authKey: key,
|
|
98
|
+
discoveryUrl: this.localDiscoveryUrl,
|
|
99
|
+
token: this.localToken,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Creates a new auth record in current collection with the given config.
|
|
105
|
+
* @returns Object with OAuth instance connected to created auth and authorizerUrl to redirect the user to for completing authorization
|
|
106
|
+
*
|
|
107
|
+
* ```typescript
|
|
108
|
+
* const {
|
|
109
|
+
* authorizeUrl, // redirect user to this url for completing authorization
|
|
110
|
+
* instance // new instance
|
|
111
|
+
* } = await oAuthCollectionInstance.createAuthorization({
|
|
112
|
+
* authName: 'my-auth-name',
|
|
113
|
+
* appId: 'my-app-id',
|
|
114
|
+
* });
|
|
115
|
+
*
|
|
116
|
+
* // if you want to use returned instance, you must call a method that returns a promise that will be resolved when the user completes authorization process
|
|
117
|
+
* // if not, you can omit this step
|
|
118
|
+
* try {
|
|
119
|
+
* await instance.waitForCompletion()
|
|
120
|
+
* } catch (e) {
|
|
121
|
+
* // will be called if the user does not complete authorization within 5 minutes
|
|
122
|
+
* }
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
125
|
+
public async createAuthorization(
|
|
126
|
+
params: CreateOAuthInCollectionConfig,
|
|
127
|
+
): Promise<CreateOAuthResult> {
|
|
128
|
+
return await OAuth.create({
|
|
129
|
+
discoveryUrl: this.localDiscoveryUrl,
|
|
130
|
+
token: this.localToken,
|
|
131
|
+
appId: params.appId,
|
|
132
|
+
keyValueCollection: this.keyValueCollection,
|
|
133
|
+
scope: params.scope,
|
|
134
|
+
serviceName: this.serviceName,
|
|
135
|
+
authName: params.authName,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}
|