@cloudbase/oauth 0.0.4-alpha.0 → 1.0.0-alpha.1

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.
@@ -2,10 +2,11 @@ interface BaseRequest {
2
2
  client_id?: string;
3
3
  }
4
4
 
5
+ export type GetCustomSignTicketFn = () => Promise<string>;
6
+
5
7
  export interface SignInRequest extends BaseRequest {
6
8
  username?: string;
7
9
  password?: string;
8
- verification_code?: string;
9
10
  verification_token?: string;
10
11
  }
11
12
 
@@ -124,6 +125,18 @@ export interface UserProfile {
124
125
  created_from?: string;
125
126
  sub?: string
126
127
  uid?: string
128
+ address?: {
129
+ formatted?: string,
130
+ street_address?: string,
131
+ locality?: string,
132
+ region?: string,
133
+ postal_code?: string,
134
+ country?: string
135
+ }
136
+ nickName?: string // TODO:
137
+ province?: string // TODO:
138
+ country?: string // TODO:
139
+ city?: string // TODO:
127
140
  }
128
141
 
129
142
  export type UserInfo = UserProfile;
@@ -176,3 +189,245 @@ export type ChangeBindedProviderResponse = BaseRequest
176
189
  export interface QueryUserProfileReq extends BaseRequest {
177
190
  appended_params: string;
178
191
  }
192
+
193
+ export interface SignInWithProviderRequest {
194
+ provider_token: string;
195
+ provider_id?: string;
196
+ }
197
+
198
+ export interface SignUpRequest {
199
+ phone_number?: string;
200
+ email?: string;
201
+
202
+ verification_code?: string;
203
+ verification_token?: string;
204
+ provider_token?: string;
205
+
206
+ password?: string;
207
+ name?: string;
208
+ gender?: string;
209
+ picture?: string;
210
+ locale?: string;
211
+ }
212
+
213
+ export interface GetVerificationRequest {
214
+ phone_number?: string;
215
+ email?: string;
216
+ // 可选 ANY,USER,NOT_USER, CUR_USER;
217
+ target?: string | 'ANY';
218
+ usage?: string;
219
+ }
220
+
221
+ export interface GetVerificationResponse {
222
+ verification_id?: string;
223
+ is_user?: boolean | false;
224
+ }
225
+
226
+ export interface VerifyResponse {
227
+ verification_token?: string;
228
+ }
229
+
230
+ export interface VerifyRequest {
231
+ verification_code: string;
232
+ verification_id?: string;
233
+ }
234
+
235
+ export interface ProviderBindRequest {
236
+ provider_token: string;
237
+ }
238
+
239
+ export interface GrantProviderTokenRequest {
240
+ provider_id: string;
241
+ provider_redirect_uri?: string;
242
+ provider_code?: string;
243
+ provider_access_token?: string;
244
+ provider_id_token?: string;
245
+ }
246
+
247
+ export interface GrantProviderTokenResponse {
248
+ provider_token: string;
249
+ expires_in: number;
250
+ }
251
+
252
+ export interface PatchProviderTokenRequest {
253
+ provider_token: string;
254
+ provider_params: {
255
+ encryptedData: string;
256
+ iv: string;
257
+ };
258
+ }
259
+
260
+ export interface PatchProviderTokenResponse {
261
+ provider_token: string;
262
+ expires_in: number;
263
+ provider_profile: ProviderProfile;
264
+ }
265
+
266
+ export interface GenProviderRedirectUriRequest {
267
+ provider_id: string;
268
+ provider_redirect_uri: string;
269
+ state: string;
270
+ other_params?: {
271
+ sign_out_uri?: string;
272
+ };
273
+ }
274
+
275
+ export interface GenProviderRedirectUriResponse {
276
+ uri: string;
277
+ signout_uri?: string;
278
+ }
279
+
280
+ export interface BindWithProviderRequest {
281
+ provider_token: string;
282
+ }
283
+
284
+ export interface BindWithProviderRequest {
285
+ provider_token: string;
286
+ }
287
+
288
+ export interface UserProfileProvider {
289
+ id?: string;
290
+ provider_user_id?: string;
291
+ name?: string;
292
+ }
293
+
294
+ export interface UserProfile {
295
+ name?: string;
296
+ picture?: string;
297
+ username?: string;
298
+ email?: string;
299
+ email_verified?: boolean;
300
+ phone_number?: string;
301
+ providers?: [UserProfileProvider];
302
+ gender?: string;
303
+ birthdate?: string;
304
+ zoneinfo?: string;
305
+ locale?: string;
306
+ created_from?: string;
307
+ }
308
+
309
+ export interface ProviderProfile {
310
+ provider_id: string;
311
+ phone_number?: string;
312
+ }
313
+
314
+ export interface TransByProviderRequest {
315
+ provider_token: string;
316
+ }
317
+
318
+ export interface GrantTokenRequest {
319
+ client_secret?: string;
320
+ code?: string;
321
+ grant_type?: string;
322
+ redirect_uri?: string;
323
+ nonce?: string;
324
+ refresh_token?: string;
325
+ scope?: string;
326
+ }
327
+
328
+ export interface UnbindProviderRequest {
329
+ provider_id: string;
330
+ }
331
+
332
+ export interface CheckPasswordrRequest {
333
+ password: string;
334
+ }
335
+
336
+ export interface BindPhoneRequest {
337
+ phone_number: string;
338
+ sudo_token: string;
339
+ verification_token: string;
340
+ conflict_resolution: string
341
+ // 1. DEFAULT 0, 默认提示用户手机号已被绑定
342
+ // 2. DELETE_ACCOUNT_TRANSFER 1, 标记原账号已被注销,并将手机换绑给自己
343
+ // 3. TRANSFER 2, 仅换绑手机号,不注销原有账号(换绑后原账号无法登录时,则自动注销原账号)
344
+ }
345
+
346
+ export interface BindEmailRequest {
347
+ email: string;
348
+ sudo_token: string;
349
+ verification_token: string;
350
+ }
351
+
352
+ export interface SetPasswordRequest {
353
+ new_password: string;
354
+ sudo_token: string;
355
+ }
356
+
357
+
358
+ export interface SetPasswordRequest {
359
+ new_password: string;
360
+ sudo_token: string;
361
+ }
362
+
363
+ export interface UpdatePasswordRequest {
364
+ old_password: string;
365
+ new_password: string;
366
+ }
367
+
368
+ // password 和 verification_token 而选一,如果绑定了手机号,则必须使用verification_token 进行sudo
369
+ export interface SudoRequest {
370
+ password?: string;
371
+ verification_token?: string
372
+ }
373
+
374
+ export interface SudoResponse {
375
+ sudo_token?: string
376
+ }
377
+
378
+
379
+ export interface ChangeBoundProviderRequest {
380
+ trans_token: string;
381
+ provider_id: string;
382
+ }
383
+
384
+ export interface ChangeBoundProviderResponse {
385
+ client_id: string;
386
+ }
387
+
388
+ export interface QueryUserProfileRequest {
389
+ id?: [string];
390
+ username?: string;
391
+ email?: string;
392
+ phone_number?: string;
393
+ }
394
+
395
+ export interface QueryUserProfileResponse {
396
+ total: string;
397
+ data: SimpleUserProfile[]
398
+ }
399
+
400
+ export interface ResetPasswordRequest extends BaseRequest {
401
+ email: string
402
+ phone_number: string
403
+ new_password: string
404
+ verification_token: string
405
+ }
406
+
407
+ export interface DeviceAuthorizeRequest extends BaseRequest {
408
+ scope?: string
409
+ }
410
+
411
+ export interface DeviceAuthorizeResponse {
412
+ device_code: string
413
+ user_code: string
414
+ expires_in: number
415
+ interval: number
416
+ verification_url: string
417
+ verification_uri_complete: string
418
+ }
419
+
420
+ // 简化版用户信息
421
+ export interface SimpleUserProfile {
422
+ sub: string;
423
+ name: string;
424
+ picture?: string;
425
+ gender?: string;
426
+ locale?: string;
427
+ email?: string;
428
+ phone_number?: string;
429
+ }
430
+
431
+ export interface CheckUsernameRequest {
432
+ username: string
433
+ }
@@ -1,217 +1,222 @@
1
- import {SimpleStorage, RequestFunction} from '../oauth2client/interface';
2
- import {AuthClientRequestOptions} from "../oauth2client/models";
3
- import {defaultStorage} from "../oauth2client/oauth2client";
1
+ import { SimpleStorage, RequestFunction } from '../oauth2client/interface';
2
+ import { AuthClientRequestOptions } from "../oauth2client/models";
3
+ import { defaultStorage } from "../oauth2client/oauth2client";
4
4
 
5
5
  export interface CaptchaOptions {
6
- clientId: string
7
- request: RequestFunction;
8
- storage: SimpleStorage;
9
- // 打开网页并通过URL回调获取 CaptchaToken,针对不通的平台,该函数可以自定义实现, 默认集成浏览器端认证
10
- openURIWithCallback?: OpenURIWithCallbackFuction;
6
+ clientId: string
7
+ request: RequestFunction;
8
+ storage: SimpleStorage;
9
+ // 打开网页并通过URL回调获取 CaptchaToken,针对不通的平台,该函数可以自定义实现, 默认集成浏览器端认证
10
+ openURIWithCallback?: OpenURIWithCallbackFuction;
11
11
  }
12
12
 
13
13
  type OpenURIWithCallbackFuction = (url: string) => Promise<CaptchaToken>;
14
14
 
15
15
  export interface CaptchaToken {
16
- captcha_token: string
17
- expires_in: number
18
- expires_at?: Date | null;
16
+ captcha_token: string
17
+ expires_in: number
18
+ expires_at?: Date | null;
19
19
  }
20
20
 
21
21
  export interface CaptchaRequestOptions extends AuthClientRequestOptions {
22
- withCaptcha?: boolean;
22
+ withCaptcha?: boolean;
23
23
  }
24
24
 
25
25
  export interface GetCaptchaResponse {
26
- captcha_token?: string
27
- expires_in?: number
28
- url?: string
26
+ captcha_token?: string
27
+ expires_in?: number
28
+ url?: string
29
29
  }
30
30
 
31
31
  const GET_CAPTCHA_URL = '/auth/v1/captcha/init'
32
32
 
33
33
  export class Captcha {
34
- private _config: CaptchaOptions;
35
- private _tokenSectionName: string;
34
+ private _config: CaptchaOptions;
35
+ private _tokenSectionName: string;
36
36
 
37
- /**
38
- * constructor
39
- * @param {CaptchaOptions} opts
40
- */
41
- constructor(opts: CaptchaOptions) {
42
- if (!opts.openURIWithCallback) {
43
- opts.openURIWithCallback = this._getDefaultOpenURIWithCallback()
44
- }
45
- if (!opts.storage) {
46
- opts.storage = defaultStorage
47
- }
48
- this._config = opts
49
- this._tokenSectionName = 'captcha_' + opts.clientId
37
+ /**
38
+ * constructor
39
+ * @param {CaptchaOptions} opts
40
+ */
41
+ constructor(opts: CaptchaOptions) {
42
+ if (!opts.openURIWithCallback) {
43
+ opts.openURIWithCallback = this._getDefaultOpenURIWithCallback()
50
44
  }
45
+ if (!opts.storage) {
46
+ opts.storage = defaultStorage
47
+ }
48
+ this._config = opts
49
+ this._tokenSectionName = 'captcha_' + opts.clientId
50
+ }
51
51
 
52
- /**
53
- * request http like simple fetch api, exp:request('/v1/user/me', {withCredentials:true})
54
- * @param {string} url
55
- * @param {AuthClientRequestOptions} options
56
- */
57
- public async request<T>(
58
- url: string,
59
- options?: CaptchaRequestOptions,
60
- ): Promise<T> {
61
- if (!options) {
62
- options = {};
63
- }
64
- if (!options.method) {
65
- options.method = 'GET'
66
- }
67
- const state = options.method + ":" + url
68
- let reqURL = url;
69
- if (options.withCaptcha) {
70
- reqURL = await this._appendCaptchaTokenToURL(url, state, false);
71
- }
72
- try {
73
- return this._config.request<T>(reqURL, options)
74
- } catch (err) {
75
- if (err.error === 'captcha_required' || err.error === 'captcha_invalid') {
76
- url = await this._appendCaptchaTokenToURL(url, state, err.error === 'captcha_invalid')
77
- return this._config.request<T>(url, options)
78
- } else {
79
- return Promise.reject(err)
80
- }
81
- }
52
+ /**
53
+ * request http like simple fetch api, exp:request('/v1/user/me', {withCredentials:true})
54
+ * @param {string} url
55
+ * @param {AuthClientRequestOptions} options
56
+ */
57
+ public async request<T>(
58
+ url: string,
59
+ options?: CaptchaRequestOptions,
60
+ ): Promise<T> {
61
+ if (!options) {
62
+ options = {};
63
+ }
64
+ if (!options.method) {
65
+ options.method = 'GET'
66
+ }
67
+ const state = options.method + ":" + url
68
+ let reqURL = url;
69
+ if (options.withCaptcha) {
70
+ reqURL = await this._appendCaptchaTokenToURL(url, state, false);
82
71
  }
83
72
 
84
- private _getDefaultOpenURIWithCallback(): OpenURIWithCallbackFuction {
85
- if (window.location.search.indexOf('__captcha') > 0) {
86
- document.body.style.display = 'none';
87
- }
88
- if (document.getElementById('captcha_panel_wrap') === null) {
89
- var elementDiv = document.createElement('div');
90
- elementDiv.style.cssText =
91
- 'background-color: rgba(0, 0, 0, 0.7);position: fixed;left: 0px;right: 0px;top: 0px;bottom: 0px;padding: 9vw 0 0 0;display: none;z-index:100;';
92
- elementDiv.setAttribute('id', 'captcha_panel_wrap');
93
- document.body.appendChild(elementDiv);
94
- }
95
- return this._defaultOpenURIWithCallback
73
+ let resp: T;
74
+ try {
75
+ resp = await this._config.request<T>(reqURL, options)
76
+ } catch (err) {
77
+ if (err.error === 'captcha_required' || err.error === 'captcha_invalid') {
78
+ url = await this._appendCaptchaTokenToURL(url, state, err.error === 'captcha_invalid')
79
+ return this._config.request<T>(url, options)
80
+ } else {
81
+ return Promise.reject(err)
82
+ }
96
83
  }
84
+ return resp
85
+ }
97
86
 
98
- /**
99
- * 默认通过浏览器打开网页并获取回调
100
- */
101
- private async _defaultOpenURIWithCallback(url: string): Promise<CaptchaToken> {
102
- const target = document.getElementById('captcha_panel_wrap'),
103
- iframe = document.createElement('iframe')
104
- target.innerHTML = '';
105
- iframe.setAttribute('src', url)
106
- iframe.setAttribute('id', 'review-panel-iframe')
107
- iframe.style.cssText = 'min-width:355px;display:block;height:355px;margin:0 auto;background-color: rgb(255, 255, 255);border: none;';
108
- target.appendChild(iframe);
109
- target.style.display = 'block';
110
- return new Promise<CaptchaToken>((resolve, reject) => {
111
- iframe.onload = function () {
112
- try {
113
- var windowLocation = window.location;
114
- var iframeLocation = iframe.contentWindow.location;
115
- if (
116
- iframeLocation.host +
117
- iframeLocation.pathname ===
118
- windowLocation.host +
119
- windowLocation.pathname
120
- ) {
121
- target.style.display = 'none';
122
- const iframeUrlParams = new URLSearchParams(iframeLocation.search);
123
- const captchToken = iframeUrlParams.get('captcha_token');
124
- if (captchToken) {
125
- return resolve({
126
- captcha_token: captchToken,
127
- expires_in: Number(iframeUrlParams.get('expires_in'))
128
- })
129
- }
130
- return reject({
131
- error: iframeUrlParams.get('error'),
132
- error_description: iframeUrlParams.get('error_description')
133
- })
134
- } else {
135
- target.style.display = 'block';
136
- }
137
- } catch (error) {
138
- target.style.display = 'block';
139
- }
140
- };
141
- })
87
+ private _getDefaultOpenURIWithCallback(): OpenURIWithCallbackFuction {
88
+ if (window.location.search.indexOf('__captcha') > 0) {
89
+ document.body.style.display = 'none';
142
90
  }
143
- /**
144
- * _getCaptchaToken 获取captchaToken
145
- */
146
- private async _getCaptchaToken(forceNewToken: boolean, state: string): Promise<string> {
147
- if (!forceNewToken) {
148
- // 如果本地存在,则直接返回
149
- const captchaToken = await this._findCaptchaToken()
150
- if (captchaToken) {
151
- return captchaToken
152
- }
153
- }
154
- const redirectURL = window.location.origin + window.location.pathname + "?__captcha=on"
155
- const captchaTokenResp = await this._config.request<GetCaptchaResponse>(GET_CAPTCHA_URL, {
156
- method: 'POST',
157
- body: {
158
- client_id: this._config.clientId,
159
- redirect_uri: redirectURL,
160
- state: state
161
- },
162
- withCredentials: false,
163
- })
164
- if (captchaTokenResp.captcha_token) {
165
- const captchaToken = {
166
- captcha_token: captchaTokenResp.captcha_token,
167
- expires_in: captchaTokenResp.expires_in,
168
- }
169
- this._saveCaptchaToken(captchaToken)
170
- return captchaTokenResp.captcha_token
171
- }
172
- const captchaToken = await this._config.openURIWithCallback(captchaTokenResp.url)
173
- this._saveCaptchaToken(captchaToken)
174
- return captchaToken.captcha_token
91
+ if (document.getElementById('captcha_panel_wrap') === null) {
92
+ var elementDiv = document.createElement('div');
93
+ elementDiv.style.cssText =
94
+ 'background-color: rgba(0, 0, 0, 0.7);position: fixed;left: 0px;right: 0px;top: 0px;bottom: 0px;padding: 9vw 0 0 0;display: none;z-index:100;';
95
+ elementDiv.setAttribute('id', 'captcha_panel_wrap');
96
+ setTimeout(() => {
97
+ document.body.appendChild(elementDiv);
98
+ }, 0)
175
99
  }
100
+ return this._defaultOpenURIWithCallback
101
+ }
176
102
 
177
- private async _appendCaptchaTokenToURL(url: string, state: string, forceNewToken: boolean): Promise<string> {
178
- const captchaToken = await this._getCaptchaToken(forceNewToken, state);
179
- if (url.indexOf("?") > 0) {
180
- url += "&captcha_token=" + captchaToken
181
- } else {
182
- url += "?captcha_token=" + captchaToken
103
+ /**
104
+ * 默认通过浏览器打开网页并获取回调
105
+ */
106
+ private async _defaultOpenURIWithCallback(url: string): Promise<CaptchaToken> {
107
+ const target = document.getElementById('captcha_panel_wrap'),
108
+ iframe = document.createElement('iframe')
109
+ target.innerHTML = '';
110
+ iframe.setAttribute('src', url)
111
+ iframe.setAttribute('id', 'review-panel-iframe')
112
+ iframe.style.cssText = 'min-width:355px;display:block;height:355px;margin:0 auto;background-color: rgb(255, 255, 255);border: none;';
113
+ target.appendChild(iframe);
114
+ target.style.display = 'block';
115
+ return new Promise<CaptchaToken>((resolve, reject) => {
116
+ iframe.onload = function () {
117
+ try {
118
+ var windowLocation = window.location;
119
+ var iframeLocation = iframe.contentWindow.location;
120
+ if (
121
+ iframeLocation.host +
122
+ iframeLocation.pathname ===
123
+ windowLocation.host +
124
+ windowLocation.pathname
125
+ ) {
126
+ target.style.display = 'none';
127
+ const iframeUrlParams = new URLSearchParams(iframeLocation.search);
128
+ const captchToken = iframeUrlParams.get('captcha_token');
129
+ if (captchToken) {
130
+ return resolve({
131
+ captcha_token: captchToken,
132
+ expires_in: Number(iframeUrlParams.get('expires_in'))
133
+ })
134
+ }
135
+ return reject({
136
+ error: iframeUrlParams.get('error'),
137
+ error_description: iframeUrlParams.get('error_description')
138
+ })
139
+ } else {
140
+ target.style.display = 'block';
141
+ }
142
+ } catch (error) {
143
+ target.style.display = 'block';
183
144
  }
184
- return url
145
+ };
146
+ })
147
+ }
148
+ /**
149
+ * _getCaptchaToken 获取captchaToken
150
+ */
151
+ private async _getCaptchaToken(forceNewToken: boolean, state: string): Promise<string> {
152
+ if (!forceNewToken) {
153
+ // 如果本地存在,则直接返回
154
+ const captchaToken = await this._findCaptchaToken()
155
+ if (captchaToken) {
156
+ return captchaToken
157
+ }
158
+ }
159
+ const redirectURL = window.location.origin + window.location.pathname + "?__captcha=on"
160
+ const captchaTokenResp = await this._config.request<GetCaptchaResponse>(GET_CAPTCHA_URL, {
161
+ method: 'POST',
162
+ body: {
163
+ client_id: this._config.clientId,
164
+ redirect_uri: redirectURL,
165
+ state: state
166
+ },
167
+ withCredentials: false,
168
+ })
169
+ if (captchaTokenResp.captcha_token) {
170
+ const captchaToken = {
171
+ captcha_token: captchaTokenResp.captcha_token,
172
+ expires_in: captchaTokenResp.expires_in,
173
+ }
174
+ this._saveCaptchaToken(captchaToken)
175
+ return captchaTokenResp.captcha_token
185
176
  }
177
+ const captchaToken = await this._config.openURIWithCallback(captchaTokenResp.url)
178
+ this._saveCaptchaToken(captchaToken)
179
+ return captchaToken.captcha_token
180
+ }
186
181
 
187
- private async _saveCaptchaToken(token: CaptchaToken) {
188
- token.expires_at = new Date(
189
- Date.now() + (token.expires_in - 10) * 1000,
190
- );
191
- const tokenStr: string = JSON.stringify(token);
192
- await this._config.storage.setItem(this._tokenSectionName, tokenStr);
182
+ private async _appendCaptchaTokenToURL(url: string, state: string, forceNewToken: boolean): Promise<string> {
183
+ const captchaToken = await this._getCaptchaToken(forceNewToken, state);
184
+ if (url.indexOf("?") > 0) {
185
+ url += "&captcha_token=" + captchaToken
186
+ } else {
187
+ url += "?captcha_token=" + captchaToken
193
188
  }
189
+ return url
190
+ }
194
191
 
195
- private async _findCaptchaToken(): Promise<string> {
196
- const tokenStr: string = await this._config.storage.getItem(
197
- this._tokenSectionName,
198
- );
199
- if (tokenStr !== undefined && tokenStr !== null) {
200
- try {
201
- const captchaToken = JSON.parse(tokenStr);
202
- if (captchaToken && captchaToken.expires_at) {
203
- captchaToken.expires_at = new Date(captchaToken.expires_at);
204
- }
205
- const isExpired = captchaToken.expires_at < new Date();
206
- if (isExpired) {
207
- return null
208
- }
209
- return captchaToken.captcha_token
210
- } catch (error) {
211
- await this._config.storage.removeItem(this._tokenSectionName);
212
- return null
213
- }
192
+ private async _saveCaptchaToken(token: CaptchaToken) {
193
+ token.expires_at = new Date(
194
+ Date.now() + (token.expires_in - 10) * 1000,
195
+ );
196
+ const tokenStr: string = JSON.stringify(token);
197
+ await this._config.storage.setItem(this._tokenSectionName, tokenStr);
198
+ }
199
+
200
+ private async _findCaptchaToken(): Promise<string> {
201
+ const tokenStr: string = await this._config.storage.getItem(
202
+ this._tokenSectionName,
203
+ );
204
+ if (tokenStr !== undefined && tokenStr !== null) {
205
+ try {
206
+ const captchaToken = JSON.parse(tokenStr);
207
+ if (captchaToken?.expires_at) {
208
+ captchaToken.expires_at = new Date(captchaToken.expires_at);
209
+ }
210
+ const isExpired = captchaToken.expires_at < new Date();
211
+ if (isExpired) {
212
+ return null
214
213
  }
214
+ return captchaToken.captcha_token
215
+ } catch (error) {
216
+ await this._config.storage.removeItem(this._tokenSectionName);
215
217
  return null
218
+ }
216
219
  }
220
+ return null
221
+ }
217
222
  }