@cloudbase/oauth 1.2.0-beta → 2.2.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/.eslintignore +2 -0
- package/.eslintrc +26 -0
- package/CHANGELOG.md +30 -0
- package/Dockerfile +15 -0
- package/README.md +183 -0
- package/_exmaple/assets/scripts/function/function.ts +99 -0
- package/_exmaple/assets/scripts/index.ts +101 -0
- package/_exmaple/assets/scripts/request.ts +11 -0
- package/_exmaple/index.html +15 -0
- package/_exmaple/package.json +33 -0
- package/_exmaple/tsconfig.json +71 -0
- package/_exmaple/typings.d.ts +0 -0
- package/_exmaple/webpack.config.js +42 -0
- package/dist/cjs/auth/apis.d.ts +55 -0
- package/dist/cjs/auth/apis.js +568 -0
- package/{auth → dist/cjs/auth}/consts.d.ts +9 -21
- package/dist/cjs/auth/consts.js +61 -0
- package/dist/cjs/auth/models.d.ts +354 -0
- package/dist/cjs/auth/models.js +3 -0
- package/dist/cjs/captcha/captcha.d.ts +35 -0
- package/dist/cjs/captcha/captcha.js +267 -0
- package/dist/cjs/index.d.ts +14 -0
- package/dist/cjs/index.js +63 -0
- package/{oauthclient → dist/cjs/oauth2client}/consts.d.ts +22 -1
- package/dist/cjs/oauth2client/consts.js +73 -0
- package/dist/cjs/oauth2client/interface.d.ts +15 -0
- package/dist/cjs/oauth2client/interface.js +10 -0
- package/dist/cjs/oauth2client/models.d.ts +47 -0
- package/dist/cjs/oauth2client/models.js +3 -0
- package/dist/cjs/oauth2client/oauth2client.d.ts +71 -0
- package/dist/cjs/oauth2client/oauth2client.js +640 -0
- package/dist/cjs/utils/function/single-promise.d.ts +5 -0
- package/dist/cjs/utils/function/single-promise.js +89 -0
- package/dist/cjs/utils/uuid.d.ts +1 -0
- package/dist/cjs/utils/uuid.js +12 -0
- package/dist/esm/auth/apis.d.ts +55 -0
- package/dist/esm/auth/apis.js +566 -0
- package/dist/esm/auth/consts.d.ts +54 -0
- package/dist/esm/auth/consts.js +58 -0
- package/dist/esm/auth/models.d.ts +354 -0
- package/dist/esm/auth/models.js +1 -0
- package/dist/esm/captcha/captcha.d.ts +35 -0
- package/dist/esm/captcha/captcha.js +264 -0
- package/dist/esm/index.d.ts +14 -0
- package/dist/esm/index.js +32 -0
- package/dist/esm/oauth2client/consts.d.ts +67 -0
- package/dist/esm/oauth2client/consts.js +70 -0
- package/dist/esm/oauth2client/interface.d.ts +15 -0
- package/dist/esm/oauth2client/interface.js +7 -0
- package/dist/esm/oauth2client/models.d.ts +47 -0
- package/dist/esm/oauth2client/models.js +1 -0
- package/dist/esm/oauth2client/oauth2client.d.ts +71 -0
- package/dist/esm/oauth2client/oauth2client.js +636 -0
- package/dist/esm/utils/function/single-promise.d.ts +5 -0
- package/dist/esm/utils/function/single-promise.js +86 -0
- package/dist/esm/utils/uuid.d.ts +1 -0
- package/dist/esm/utils/uuid.js +8 -0
- package/package.json +27 -14
- package/publish.sh +2 -0
- package/src/auth/apis.ts +642 -0
- package/src/auth/consts.ts +57 -0
- package/src/auth/models.ts +444 -0
- package/src/captcha/captcha.ts +222 -0
- package/src/index.ts +49 -0
- package/src/oauth2client/consts.ts +69 -0
- package/src/oauth2client/interface.ts +57 -0
- package/src/oauth2client/models.ts +61 -0
- package/src/oauth2client/oauth2client.ts +620 -0
- package/src/utils/function/single-promise.ts +40 -0
- package/src/utils/uuid.ts +11 -0
- package/tsconfig.esm.json +44 -0
- package/tsconfig.json +44 -0
- package/wiki/README.md +75 -0
- package/app/index.d.ts +0 -29
- package/app/index.js +0 -50
- package/app/index.js.map +0 -1
- package/app/internal.d.ts +0 -12
- package/app/internal.js +0 -25
- package/app/internal.js.map +0 -1
- package/app/openuri.d.ts +0 -20
- package/app/openuri.js +0 -106
- package/app/openuri.js.map +0 -1
- package/app/request.d.ts +0 -19
- package/app/request.js +0 -52
- package/app/request.js.map +0 -1
- package/app/storage.d.ts +0 -41
- package/app/storage.js +0 -35
- package/app/storage.js.map +0 -1
- package/auth/consts.js +0 -79
- package/auth/consts.js.map +0 -1
- package/auth/index.d.ts +0 -242
- package/auth/index.js +0 -550
- package/auth/index.js.map +0 -1
- package/auth/models.d.ts +0 -284
- package/auth/models.js +0 -4
- package/auth/models.js.map +0 -1
- package/captcha/index.d.ts +0 -45
- package/captcha/index.js +0 -135
- package/captcha/index.js.map +0 -1
- package/function/index.d.ts +0 -38
- package/function/index.js +0 -80
- package/function/index.js.map +0 -1
- package/index.d.ts +0 -8
- package/index.js +0 -17
- package/index.js.map +0 -1
- package/oauthclient/consts.js +0 -53
- package/oauthclient/consts.js.map +0 -1
- package/oauthclient/index.d.ts +0 -18
- package/oauthclient/index.js +0 -21
- package/oauthclient/index.js.map +0 -1
- package/oauthclient/interface.d.ts +0 -29
- package/oauthclient/interface.js +0 -10
- package/oauthclient/interface.js.map +0 -1
- package/oauthclient/models.d.ts +0 -40
- package/oauthclient/models.js +0 -3
- package/oauthclient/models.js.map +0 -1
- package/oauthclient/oauthclient.d.ts +0 -146
- package/oauthclient/oauthclient.js +0 -414
- package/oauthclient/oauthclient.js.map +0 -1
- package/utils/promise.d.ts +0 -18
- package/utils/promise.js +0 -48
- package/utils/promise.js.map +0 -1
- package/utils/uuid.d.ts +0 -5
- package/utils/uuid.js +0 -16
- package/utils/uuid.js.map +0 -1
|
@@ -0,0 +1,620 @@
|
|
|
1
|
+
import { ErrorType } from './consts';
|
|
2
|
+
|
|
3
|
+
import { AuthClient, SimpleStorage } from './interface';
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
Credentials,
|
|
7
|
+
ResponseError,
|
|
8
|
+
RequestOptions,
|
|
9
|
+
RequestFunction,
|
|
10
|
+
OAuth2ClientOptions,
|
|
11
|
+
AuthClientRequestOptions,
|
|
12
|
+
} from './models';
|
|
13
|
+
|
|
14
|
+
import { uuidv4 } from '../utils/uuid';
|
|
15
|
+
|
|
16
|
+
import { SinglePromise } from '../utils/function/single-promise';
|
|
17
|
+
|
|
18
|
+
const RequestIdHeaderName = 'x-request-id';
|
|
19
|
+
const DeviceIdHeaderName = 'x-device-id';
|
|
20
|
+
const DeviceIdSectionName = 'device_id';
|
|
21
|
+
|
|
22
|
+
export interface ToResponseErrorOptions {
|
|
23
|
+
error?: ErrorType;
|
|
24
|
+
error_description?: string | null;
|
|
25
|
+
error_uri?: string | null;
|
|
26
|
+
details?: any | null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const defaultRequest: RequestFunction = async <T>(
|
|
30
|
+
url: string,
|
|
31
|
+
options?: RequestOptions,
|
|
32
|
+
): Promise<T> => {
|
|
33
|
+
let result: T | null = null;
|
|
34
|
+
let responseError: ResponseError | null = null;
|
|
35
|
+
try {
|
|
36
|
+
// Objects must be copied to prevent modification of data such as body.
|
|
37
|
+
const copyOptions = Object.assign({}, options);
|
|
38
|
+
if (!copyOptions.method) {
|
|
39
|
+
copyOptions.method = 'GET';
|
|
40
|
+
}
|
|
41
|
+
if (copyOptions.body && typeof copyOptions.body !== 'string') {
|
|
42
|
+
copyOptions.body = JSON.stringify(copyOptions.body);
|
|
43
|
+
}
|
|
44
|
+
const responseResult: Response = await fetch(url, copyOptions);
|
|
45
|
+
const jsonResponse = await responseResult.json();
|
|
46
|
+
if (jsonResponse?.error) {
|
|
47
|
+
responseError = jsonResponse as ResponseError;
|
|
48
|
+
responseError.error_uri = new URL(url).pathname;
|
|
49
|
+
} else {
|
|
50
|
+
result = jsonResponse as T;
|
|
51
|
+
}
|
|
52
|
+
} catch (error) {
|
|
53
|
+
responseError = {
|
|
54
|
+
error: ErrorType.UNREACHABLE,
|
|
55
|
+
error_description: error.message,
|
|
56
|
+
error_uri: new URL(url).pathname,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
if (responseError) {
|
|
60
|
+
throw responseError;
|
|
61
|
+
} else {
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export const toResponseError = (
|
|
67
|
+
error: ResponseError | Error,
|
|
68
|
+
options?: ToResponseErrorOptions,
|
|
69
|
+
): ResponseError => {
|
|
70
|
+
let responseError: ResponseError;
|
|
71
|
+
const formatOptions: ToResponseErrorOptions = options || {};
|
|
72
|
+
if (error instanceof Error) {
|
|
73
|
+
responseError = {
|
|
74
|
+
error: formatOptions.error || ErrorType.LOCAL,
|
|
75
|
+
error_description: formatOptions.error_description || error.message,
|
|
76
|
+
error_uri: formatOptions.error_uri,
|
|
77
|
+
details: formatOptions.details || error.stack,
|
|
78
|
+
};
|
|
79
|
+
} else {
|
|
80
|
+
const formatError: ToResponseErrorOptions = error || {};
|
|
81
|
+
responseError = {
|
|
82
|
+
error: formatOptions.error || formatError.error || ErrorType.LOCAL,
|
|
83
|
+
error_description:
|
|
84
|
+
formatOptions.error_description || formatError.error_description,
|
|
85
|
+
error_uri: formatOptions.error_uri || formatError.error_uri,
|
|
86
|
+
details: formatOptions.details || formatError.details,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
return responseError;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Generate request id.
|
|
94
|
+
* @return {string}
|
|
95
|
+
*/
|
|
96
|
+
export function generateRequestId(): string {
|
|
97
|
+
return uuidv4();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Default Storage.
|
|
103
|
+
*/
|
|
104
|
+
class DefaultStorage implements SimpleStorage {
|
|
105
|
+
/**
|
|
106
|
+
* Get item.
|
|
107
|
+
* @param {string} key
|
|
108
|
+
*/
|
|
109
|
+
async getItem(key: string): Promise<string | null> {
|
|
110
|
+
return window.localStorage.getItem(key);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Remove item.
|
|
115
|
+
* @param {string} key
|
|
116
|
+
*/
|
|
117
|
+
async removeItem(key: string): Promise<void> {
|
|
118
|
+
window.localStorage.removeItem(key);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Set item.
|
|
123
|
+
* @param {string} key
|
|
124
|
+
* @param {string} value
|
|
125
|
+
*/
|
|
126
|
+
async setItem(key: string, value: string): Promise<void> {
|
|
127
|
+
window.localStorage.setItem(key, value);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Get item sync.
|
|
132
|
+
* @param {string} key
|
|
133
|
+
*/
|
|
134
|
+
getItemSync(key: string): string | null {
|
|
135
|
+
return window.localStorage.getItem(key);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Remove item sync.
|
|
140
|
+
* @param {string} key
|
|
141
|
+
*/
|
|
142
|
+
removeItemSync(key: string): void {
|
|
143
|
+
window.localStorage.removeItem(key);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Set item sync.
|
|
148
|
+
* @param {string} key
|
|
149
|
+
* @param {string} value
|
|
150
|
+
*/
|
|
151
|
+
setItemSync(key: string, value: string): void {
|
|
152
|
+
window.localStorage.setItem(key, value);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export const defaultStorage = new DefaultStorage();
|
|
157
|
+
|
|
158
|
+
interface LocalCredentialsOptions {
|
|
159
|
+
tokenSectionName: string;
|
|
160
|
+
storage: SimpleStorage;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Check if credentials is expired.
|
|
165
|
+
* @param {Credentials} credentials
|
|
166
|
+
* @return {boolean}
|
|
167
|
+
*/
|
|
168
|
+
function isCredentialsExpired(credentials: Credentials): boolean {
|
|
169
|
+
let isExpired = true;
|
|
170
|
+
if (credentials?.expires_at && credentials?.access_token) {
|
|
171
|
+
isExpired = credentials.expires_at < new Date();
|
|
172
|
+
}
|
|
173
|
+
return isExpired;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Local credentials.
|
|
178
|
+
* Local credentials, with memory cache and storage cache.
|
|
179
|
+
* If the memory cache expires, the storage cache is automatically loaded.
|
|
180
|
+
*/
|
|
181
|
+
export class LocalCredentials {
|
|
182
|
+
private _tokenSectionName: string;
|
|
183
|
+
|
|
184
|
+
private _storage: SimpleStorage;
|
|
185
|
+
|
|
186
|
+
private _credentials: Credentials | null = null;
|
|
187
|
+
|
|
188
|
+
private _singlePromise: SinglePromise = new SinglePromise();
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* constructor
|
|
192
|
+
* @param {LocalCredentialsOptions} options
|
|
193
|
+
*/
|
|
194
|
+
constructor(options: LocalCredentialsOptions) {
|
|
195
|
+
this._tokenSectionName = options.tokenSectionName;
|
|
196
|
+
this._storage = options.storage;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* setCredentials Provides an alternative fetch api request implementation with auth credentials
|
|
201
|
+
* @param {Credentials} credentials
|
|
202
|
+
*/
|
|
203
|
+
public async setCredentials(credentials?: Credentials): Promise<void> {
|
|
204
|
+
if (credentials?.expires_in) {
|
|
205
|
+
credentials.expires_at = new Date(
|
|
206
|
+
Date.now() + (credentials.expires_in - 30) * 1000,
|
|
207
|
+
);
|
|
208
|
+
if (this._storage) {
|
|
209
|
+
const tokenStr: string = JSON.stringify(credentials);
|
|
210
|
+
await this._storage.setItem(this._tokenSectionName, tokenStr);
|
|
211
|
+
}
|
|
212
|
+
this._credentials = credentials;
|
|
213
|
+
} else {
|
|
214
|
+
if (this._storage) {
|
|
215
|
+
await this._storage.removeItem(this._tokenSectionName);
|
|
216
|
+
}
|
|
217
|
+
this._credentials = null;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Get credentials.
|
|
223
|
+
* @return {Promise<Credentials | null>}
|
|
224
|
+
*/
|
|
225
|
+
public async getCredentials(): Promise<Credentials | null> {
|
|
226
|
+
return this._singlePromise.run('getCredentials', async () => {
|
|
227
|
+
if (isCredentialsExpired(this._credentials)) {
|
|
228
|
+
this._credentials = await this._getStorageCredentials();
|
|
229
|
+
}
|
|
230
|
+
return this._credentials;
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Get storage credentials.
|
|
236
|
+
*/
|
|
237
|
+
private async _getStorageCredentials(): Promise<Credentials | null> {
|
|
238
|
+
return this._singlePromise.run('_getStorageCredentials', async () => {
|
|
239
|
+
let credentials: Credentials = null;
|
|
240
|
+
const tokenStr: string = await this._storage.getItem(
|
|
241
|
+
this._tokenSectionName,
|
|
242
|
+
);
|
|
243
|
+
if (tokenStr !== undefined && tokenStr !== null) {
|
|
244
|
+
try {
|
|
245
|
+
credentials = JSON.parse(tokenStr);
|
|
246
|
+
if (credentials?.expires_at) {
|
|
247
|
+
credentials.expires_at = new Date(credentials.expires_at);
|
|
248
|
+
}
|
|
249
|
+
} catch (error) {
|
|
250
|
+
await this._storage.removeItem(this._tokenSectionName);
|
|
251
|
+
credentials = null;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return credentials;
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
public _getStorageCredentialsSync(): Credentials | null {
|
|
259
|
+
let credentials: Credentials = null;
|
|
260
|
+
const tokenStr: string = this._storage.getItemSync(
|
|
261
|
+
this._tokenSectionName,
|
|
262
|
+
);
|
|
263
|
+
if (tokenStr !== undefined && tokenStr !== null) {
|
|
264
|
+
try {
|
|
265
|
+
credentials = JSON.parse(tokenStr);
|
|
266
|
+
if (credentials?.expires_at) {
|
|
267
|
+
credentials.expires_at = new Date(credentials.expires_at);
|
|
268
|
+
}
|
|
269
|
+
} catch (error) {
|
|
270
|
+
this._storage.removeItem(this._tokenSectionName);
|
|
271
|
+
credentials = null;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return credentials
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* OAuth2Client
|
|
281
|
+
*/
|
|
282
|
+
export class OAuth2Client implements AuthClient {
|
|
283
|
+
private static _defaultRetry = 2;
|
|
284
|
+
private static _minRetry = 0;
|
|
285
|
+
private static _maxRetry = 5;
|
|
286
|
+
private static _retryInterval = 1000;
|
|
287
|
+
|
|
288
|
+
private _apiOrigin: string;
|
|
289
|
+
private _clientId: string;
|
|
290
|
+
private _retry: number;
|
|
291
|
+
private _clientSecret?: string;
|
|
292
|
+
private _baseRequest: <T>(
|
|
293
|
+
url: string,
|
|
294
|
+
options?: RequestOptions,
|
|
295
|
+
) => Promise<T>;
|
|
296
|
+
private _localCredentials: LocalCredentials;
|
|
297
|
+
private _storage: SimpleStorage;
|
|
298
|
+
private _deviceID?: string;
|
|
299
|
+
private _tokenInURL?: boolean;
|
|
300
|
+
private _refreshTokenFunc: (refreshToken?: string) => Promise<Credentials>;
|
|
301
|
+
private _headers?: { [key: string]: string };
|
|
302
|
+
private _singlePromise: SinglePromise = new SinglePromise();
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* constructor
|
|
306
|
+
* @param {OAuth2ClientOptions} options
|
|
307
|
+
*/
|
|
308
|
+
constructor(options: OAuth2ClientOptions) {
|
|
309
|
+
this._apiOrigin = options.apiOrigin;
|
|
310
|
+
this._clientId = options.clientId;
|
|
311
|
+
this._retry = this._formatRetry(options.retry, OAuth2Client._defaultRetry);
|
|
312
|
+
if (options.baseRequest != undefined) {
|
|
313
|
+
this._baseRequest = options.baseRequest;
|
|
314
|
+
} else {
|
|
315
|
+
this._baseRequest = defaultRequest;
|
|
316
|
+
}
|
|
317
|
+
this._tokenInURL = options.tokenInURL;
|
|
318
|
+
this._headers = options.headers;
|
|
319
|
+
// @ts-ignore
|
|
320
|
+
this._storage = options.storage || defaultStorage;
|
|
321
|
+
this._localCredentials = new LocalCredentials({
|
|
322
|
+
tokenSectionName: 'credentials_' + options.clientId,
|
|
323
|
+
storage: this._storage,
|
|
324
|
+
});
|
|
325
|
+
this._clientSecret = options.clientSecret;
|
|
326
|
+
this._refreshTokenFunc =
|
|
327
|
+
options.refreshTokenFunc || this._defaultRefreshTokenFunc;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* setCredentials Provides an alternative fetch api request implementation with auth credentials
|
|
332
|
+
* @param {Credentials} credentials
|
|
333
|
+
* @return {Promise<void>}
|
|
334
|
+
*/
|
|
335
|
+
public setCredentials(credentials?: Credentials): Promise<void> {
|
|
336
|
+
return this._localCredentials.setCredentials(credentials);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* getAccessToken return a validate access token
|
|
341
|
+
*/
|
|
342
|
+
public async getAccessToken(): Promise<string> {
|
|
343
|
+
const credentials: Credentials = await this._getCredentials();
|
|
344
|
+
if (credentials?.access_token) {
|
|
345
|
+
return Promise.resolve(credentials.access_token);
|
|
346
|
+
}
|
|
347
|
+
return Promise.reject({ error: ErrorType.UNAUTHENTICATED } as ResponseError);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* request http like simple fetch api, exp:request('/v1/user/me', {withCredentials:true})
|
|
352
|
+
* @param {string} url
|
|
353
|
+
* @param {AuthClientRequestOptions} options
|
|
354
|
+
*/
|
|
355
|
+
public async request<T>(
|
|
356
|
+
url: string,
|
|
357
|
+
options?: AuthClientRequestOptions,
|
|
358
|
+
): Promise<T> {
|
|
359
|
+
if (!options) {
|
|
360
|
+
options = {};
|
|
361
|
+
}
|
|
362
|
+
const retry: number = this._formatRetry(options.retry, this._retry);
|
|
363
|
+
options.headers = options.headers || {};
|
|
364
|
+
if (this._headers) {
|
|
365
|
+
options.headers = {
|
|
366
|
+
...this._headers,
|
|
367
|
+
...options.headers,
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
if (!options.headers[RequestIdHeaderName]) {
|
|
371
|
+
options.headers[RequestIdHeaderName] = generateRequestId();
|
|
372
|
+
}
|
|
373
|
+
if (!options.headers[DeviceIdHeaderName]) {
|
|
374
|
+
const deviceId = await this._getDeviceId();
|
|
375
|
+
options.headers[DeviceIdHeaderName] = deviceId;
|
|
376
|
+
}
|
|
377
|
+
if (options?.withCredentials) {
|
|
378
|
+
const credentials = await this._getCredentials();
|
|
379
|
+
if (credentials) {
|
|
380
|
+
if (this._tokenInURL) {
|
|
381
|
+
if (url.indexOf('?') < 0) {
|
|
382
|
+
url += '?';
|
|
383
|
+
}
|
|
384
|
+
url += 'access_token=' + credentials.access_token;
|
|
385
|
+
} else {
|
|
386
|
+
options.headers['Authorization'] =
|
|
387
|
+
credentials.token_type + ' ' + credentials.access_token;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
} else {
|
|
391
|
+
if (this._clientId && url.indexOf('client_id') < 0) {
|
|
392
|
+
url += url.indexOf('?') < 0 ? '?' : '&';
|
|
393
|
+
url += 'client_id=' + this._clientId;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
if (url.startsWith('/')) {
|
|
397
|
+
url = this._apiOrigin + url;
|
|
398
|
+
}
|
|
399
|
+
let response: T | null = null;
|
|
400
|
+
const maxRequestTimes: number = retry + 1;
|
|
401
|
+
for (
|
|
402
|
+
let requestTime = 0;
|
|
403
|
+
requestTime < maxRequestTimes;
|
|
404
|
+
requestTime++
|
|
405
|
+
) {
|
|
406
|
+
try {
|
|
407
|
+
response = await this._baseRequest<T>(url, options);
|
|
408
|
+
break;
|
|
409
|
+
} catch (responseError) {
|
|
410
|
+
if (options.withCredentials && responseError && responseError.error === ErrorType.UNAUTHENTICATED) {
|
|
411
|
+
await this.setCredentials(null);
|
|
412
|
+
return Promise.reject(responseError);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (
|
|
416
|
+
requestTime === retry ||
|
|
417
|
+
!responseError ||
|
|
418
|
+
responseError.error !== 'unreachable'
|
|
419
|
+
) {
|
|
420
|
+
return Promise.reject(responseError);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
await this._sleep(OAuth2Client._retryInterval);
|
|
424
|
+
}
|
|
425
|
+
return response;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Check retry value.
|
|
430
|
+
* @param {number} retry
|
|
431
|
+
* @return {number}
|
|
432
|
+
*/
|
|
433
|
+
private _checkRetry(retry: number): number {
|
|
434
|
+
let responseError: ResponseError | null = null;
|
|
435
|
+
if (
|
|
436
|
+
typeof retry !== 'number' ||
|
|
437
|
+
retry < OAuth2Client._minRetry ||
|
|
438
|
+
retry > OAuth2Client._maxRetry
|
|
439
|
+
) {
|
|
440
|
+
responseError = {
|
|
441
|
+
error: ErrorType.UNREACHABLE,
|
|
442
|
+
error_description: 'wrong options param: retry',
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
if (responseError) {
|
|
446
|
+
throw responseError;
|
|
447
|
+
}
|
|
448
|
+
return retry;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Format retry value.
|
|
453
|
+
* @param {number} retry
|
|
454
|
+
* @param {number} defaultVale
|
|
455
|
+
* @return {number}
|
|
456
|
+
*/
|
|
457
|
+
private _formatRetry(retry: number, defaultVale: number): number {
|
|
458
|
+
if (typeof retry === 'undefined') {
|
|
459
|
+
return defaultVale;
|
|
460
|
+
} else {
|
|
461
|
+
return this._checkRetry(retry);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Sleep.
|
|
467
|
+
* @param {number} ms
|
|
468
|
+
* @return {Promise<void>}
|
|
469
|
+
*/
|
|
470
|
+
private async _sleep(ms: number): Promise<void> {
|
|
471
|
+
return new Promise<void>((resolve) => {
|
|
472
|
+
setTimeout(() => {
|
|
473
|
+
resolve();
|
|
474
|
+
}, ms);
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Refresh expired token.
|
|
480
|
+
* @param {Credentials} credentials
|
|
481
|
+
* @return {Promise<Credentials>}
|
|
482
|
+
*/
|
|
483
|
+
private async _refreshToken(credentials: Credentials): Promise<Credentials> {
|
|
484
|
+
return this._singlePromise.run('_refreshToken', async () => {
|
|
485
|
+
if (!credentials || !credentials.refresh_token) {
|
|
486
|
+
return this._unAuthenticatedError('no refresh token found in credentials');
|
|
487
|
+
}
|
|
488
|
+
try {
|
|
489
|
+
const newCredentials: Credentials = await this._refreshTokenFunc(
|
|
490
|
+
credentials.refresh_token,
|
|
491
|
+
);
|
|
492
|
+
await this._localCredentials.setCredentials(newCredentials);
|
|
493
|
+
return newCredentials
|
|
494
|
+
} catch (error) {
|
|
495
|
+
if (error.error === ErrorType.INVALID_GRANT) {
|
|
496
|
+
await this._localCredentials.setCredentials(null);
|
|
497
|
+
return this._unAuthenticatedError(error.error_description);
|
|
498
|
+
}
|
|
499
|
+
return Promise.reject(error);
|
|
500
|
+
}
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* anonymous signIn
|
|
506
|
+
* @param {Credentials} credentials
|
|
507
|
+
* @return {Promise<Credentials>}
|
|
508
|
+
*/
|
|
509
|
+
private async _anonymousSignIn(credentials: Credentials): Promise<Credentials> {
|
|
510
|
+
return this._singlePromise.run('_anonymous', async () => {
|
|
511
|
+
if (!credentials || credentials.scope !== 'anonymous') {
|
|
512
|
+
return this._unAuthenticatedError('no anonymous in credentials');
|
|
513
|
+
}
|
|
514
|
+
try {
|
|
515
|
+
const newCredentials: Credentials = await this.request('/auth/v1/signin/anonymously', {
|
|
516
|
+
method: 'POST',
|
|
517
|
+
body: {
|
|
518
|
+
client_id: this._clientId,
|
|
519
|
+
client_secret: this._clientSecret,
|
|
520
|
+
},
|
|
521
|
+
});
|
|
522
|
+
await this._localCredentials.setCredentials(newCredentials);
|
|
523
|
+
return newCredentials
|
|
524
|
+
} catch (error) {
|
|
525
|
+
if (error.error === ErrorType.INVALID_GRANT) {
|
|
526
|
+
await this._localCredentials.setCredentials(null);
|
|
527
|
+
return this._unAuthenticatedError(error.error_description);
|
|
528
|
+
}
|
|
529
|
+
return Promise.reject(error);
|
|
530
|
+
}
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* Default refresh token function.
|
|
536
|
+
* @param {string} refreshToken
|
|
537
|
+
* @return {Promise<Credentials>}
|
|
538
|
+
*/
|
|
539
|
+
private _defaultRefreshTokenFunc(
|
|
540
|
+
refreshToken?: string,
|
|
541
|
+
): Promise<Credentials> {
|
|
542
|
+
if (refreshToken === undefined || refreshToken === '') {
|
|
543
|
+
return this._unAuthenticatedError('refresh token not found');
|
|
544
|
+
}
|
|
545
|
+
return this.request('/auth/v1/token', {
|
|
546
|
+
method: 'POST',
|
|
547
|
+
body: {
|
|
548
|
+
client_id: this._clientId,
|
|
549
|
+
client_secret: this._clientSecret,
|
|
550
|
+
grant_type: 'refresh_token',
|
|
551
|
+
refresh_token: refreshToken,
|
|
552
|
+
},
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* Get credentials.
|
|
558
|
+
*/
|
|
559
|
+
public async _getCredentials(): Promise<Credentials | null> {
|
|
560
|
+
let credentials: Credentials = await this._localCredentials.getCredentials();
|
|
561
|
+
if (isCredentialsExpired(credentials)) {
|
|
562
|
+
if (credentials && credentials.scope === 'anonymous') {
|
|
563
|
+
credentials = await this._anonymousSignIn(credentials)
|
|
564
|
+
} else {
|
|
565
|
+
credentials = await this._refreshToken(credentials);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
return credentials;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
public getCredentialsSync(): Credentials | null {
|
|
572
|
+
const credentials: Credentials = this._localCredentials._getStorageCredentialsSync();
|
|
573
|
+
return credentials
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
public getCredentialsAsync(): Promise<Credentials | null> {
|
|
577
|
+
return this._localCredentials.getCredentials()
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
public async getScope(): Promise<string> {
|
|
581
|
+
let credentials: Credentials = await this._localCredentials.getCredentials();
|
|
582
|
+
if (credentials == null) {
|
|
583
|
+
return this._unAuthenticatedError("credentials not found")
|
|
584
|
+
}
|
|
585
|
+
return credentials.scope;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
|
|
589
|
+
|
|
590
|
+
/**
|
|
591
|
+
* Get deviceId
|
|
592
|
+
*/
|
|
593
|
+
private async _getDeviceId(): Promise<string> {
|
|
594
|
+
if (this._deviceID) {
|
|
595
|
+
return this._deviceID;
|
|
596
|
+
}
|
|
597
|
+
let deviceId: string = await this._storage.getItem(
|
|
598
|
+
DeviceIdSectionName,
|
|
599
|
+
);
|
|
600
|
+
if (!(typeof deviceId === 'string' &&
|
|
601
|
+
deviceId.length >= 16 &&
|
|
602
|
+
deviceId.length <= 48)) {
|
|
603
|
+
deviceId = uuidv4();
|
|
604
|
+
await this._storage.setItem(DeviceIdSectionName, deviceId);
|
|
605
|
+
}
|
|
606
|
+
this._deviceID = deviceId;
|
|
607
|
+
return deviceId;
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* Generate unAuthenticated error.
|
|
611
|
+
* @param {string} err
|
|
612
|
+
* @return {Promise<T>}
|
|
613
|
+
*/
|
|
614
|
+
private _unAuthenticatedError<T>(err?: string): Promise<T> {
|
|
615
|
+
return Promise.reject({
|
|
616
|
+
error: ErrorType.UNAUTHENTICATED,
|
|
617
|
+
error_description: err,
|
|
618
|
+
} as ResponseError);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single Promise
|
|
3
|
+
*/
|
|
4
|
+
export class SinglePromise {
|
|
5
|
+
/**
|
|
6
|
+
* Run single promise.
|
|
7
|
+
* @param {string} key
|
|
8
|
+
* @param {Function} fn
|
|
9
|
+
* @return {Promise<T>}
|
|
10
|
+
*/
|
|
11
|
+
async run<T>(key: string, fn: () => Promise<T>): Promise<T> {
|
|
12
|
+
let result: Promise<any> = this._fnPromiseMap.get(key);
|
|
13
|
+
if (!result) {
|
|
14
|
+
result = new Promise<any>(async (resolve, reject) => {
|
|
15
|
+
try {
|
|
16
|
+
// The idle promise must be run to prevent _fnPromiseMap from
|
|
17
|
+
// storing the current promise function.
|
|
18
|
+
await this._runIdlePromise();
|
|
19
|
+
const fnResult: Promise<T> = fn();
|
|
20
|
+
resolve(await fnResult);
|
|
21
|
+
} catch (error) {
|
|
22
|
+
reject(error);
|
|
23
|
+
} finally {
|
|
24
|
+
this._fnPromiseMap.delete(key);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
this._fnPromiseMap.set(key, result);
|
|
28
|
+
}
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Run idle promise.
|
|
33
|
+
* @return {Promise<void>}
|
|
34
|
+
*/
|
|
35
|
+
private _runIdlePromise(): Promise<void> {
|
|
36
|
+
return Promise.resolve();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
private _fnPromiseMap: Map<string, Promise<any>> = new Map();
|
|
40
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate uuidv4 string.
|
|
3
|
+
* @return {string}
|
|
4
|
+
*/
|
|
5
|
+
export function uuidv4(): string {
|
|
6
|
+
return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
|
7
|
+
const r = (Math.random() * 16) | 0;
|
|
8
|
+
const v = c == 'x' ? r : (r & 0x3) | 0x8;
|
|
9
|
+
return v.toString(16);
|
|
10
|
+
});
|
|
11
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compileOnSave": true,
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"allowSyntheticDefaultImports": true,
|
|
5
|
+
"declaration": true,
|
|
6
|
+
"esModuleInterop": true,
|
|
7
|
+
"experimentalDecorators": true,
|
|
8
|
+
"emitDecoratorMetadata": true,
|
|
9
|
+
"module": "es6",
|
|
10
|
+
"noFallthroughCasesInSwitch": true,
|
|
11
|
+
"noUnusedLocals": true,
|
|
12
|
+
"noUnusedParameters": true,
|
|
13
|
+
"outDir": "dist/esm",
|
|
14
|
+
"rootDir": "src",
|
|
15
|
+
"pretty": true,
|
|
16
|
+
"noImplicitAny": false,
|
|
17
|
+
"removeComments": true,
|
|
18
|
+
"skipLibCheck": true,
|
|
19
|
+
"strictBindCallApply": true,
|
|
20
|
+
"declarationMap": false,
|
|
21
|
+
"inlineSourceMap": true,
|
|
22
|
+
"inlineSources": true,
|
|
23
|
+
"target": "es5",
|
|
24
|
+
"moduleResolution": "node",
|
|
25
|
+
"typeRoots": [
|
|
26
|
+
"node_modules/@types"
|
|
27
|
+
],
|
|
28
|
+
"types": [
|
|
29
|
+
"node"
|
|
30
|
+
],
|
|
31
|
+
"lib": [
|
|
32
|
+
"es2015",
|
|
33
|
+
"dom"
|
|
34
|
+
]
|
|
35
|
+
},
|
|
36
|
+
"include": [
|
|
37
|
+
"src/**/*.ts",
|
|
38
|
+
"package.json"
|
|
39
|
+
],
|
|
40
|
+
"exclude": [
|
|
41
|
+
"node_modules",
|
|
42
|
+
"dist"
|
|
43
|
+
]
|
|
44
|
+
}
|