@torrow/sdk-auth 0.6.0 → 0.7.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/README.md +118 -0
- package/dist/index.d.ts +308 -115
- package/dist/index.js +315 -75
- package/dist/index.js.map +1 -1
- package/dist/node.d.ts +1 -1
- package/dist/{types-LCuPy3dY.d.ts → types-C4e90uOw.d.ts} +5 -10
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// ../../src/auth-session/
|
|
1
|
+
// ../../src/auth-session/app-credentials.ts
|
|
2
2
|
var DEFAULT_APP_CREDENTIALS = {
|
|
3
3
|
clientId: "sdk.torrow.net",
|
|
4
4
|
clientSecret: "4YM1qYq87faSXaFqXTBcnJrn/jj0AOXl6tTsfubLyCE="
|
|
@@ -13,6 +13,278 @@ function appCredentialsToAuthIdentityBasic(appCredentials) {
|
|
|
13
13
|
return { username: clientId, password: clientSecret };
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
// ../../src/auth-session/otp-flow.ts
|
|
17
|
+
import {
|
|
18
|
+
isAuthCaptchaType,
|
|
19
|
+
isDeviceType,
|
|
20
|
+
isSendCodeAccountType,
|
|
21
|
+
isVerificationMethod,
|
|
22
|
+
TokenStatusValues as TokenStatusValues2,
|
|
23
|
+
VerificationMethodValues
|
|
24
|
+
} from "@torrow/sdk-client";
|
|
25
|
+
|
|
26
|
+
// ../../src/auth-session/identity-client.ts
|
|
27
|
+
import { TorrowClient } from "@torrow/sdk-client";
|
|
28
|
+
function createIdentityClient(options) {
|
|
29
|
+
const authIdentityBasic = appCredentialsToAuthIdentityBasic(
|
|
30
|
+
resolveAppCredentials(options.appCredentials)
|
|
31
|
+
);
|
|
32
|
+
if (!authIdentityBasic) {
|
|
33
|
+
throw new Error(
|
|
34
|
+
"TokenSession: \u043D\u0435\u043F\u043E\u043B\u043D\u0430\u044F appCredentials (\u043D\u0443\u0436\u043D\u044B \u043D\u0435\u043F\u0443\u0441\u0442\u044B\u0435 clientId \u0438 clientSecret). \u0411\u0435\u0437 \u043E\u043F\u0446\u0438\u0438 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0435\u0442\u0441\u044F DEFAULT_APP_CREDENTIALS."
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
return new TorrowClient({
|
|
38
|
+
baseUrl: options.baseUrl,
|
|
39
|
+
fetch: options.fetch,
|
|
40
|
+
defaultHeaders: options.defaultHeaders,
|
|
41
|
+
userAgent: options.userAgent,
|
|
42
|
+
authIdentityBasic
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ../../src/auth-session/auth-protocol.ts
|
|
47
|
+
import {
|
|
48
|
+
DefaultAuthScopes,
|
|
49
|
+
isTokenStatus,
|
|
50
|
+
TokenStatusValues
|
|
51
|
+
} from "@torrow/sdk-client";
|
|
52
|
+
function buildOtpSendRequest(input) {
|
|
53
|
+
const accountName = input.accountName.trim();
|
|
54
|
+
if (!accountName) throw new Error("runOtpLogin: accountName is empty");
|
|
55
|
+
return {
|
|
56
|
+
accountName,
|
|
57
|
+
type: input.type,
|
|
58
|
+
language: input.language?.trim() || "ru",
|
|
59
|
+
regionStorageId: typeof input.regionStorageId === "number" && Number.isFinite(input.regionStorageId) ? input.regionStorageId : 70,
|
|
60
|
+
...input.captcha?.trim() ? { captcha: input.captcha.trim() } : {},
|
|
61
|
+
...input.captchaType ? { captchaType: input.captchaType } : {},
|
|
62
|
+
...input.inviteSessionId?.trim() ? { inviteSessionId: input.inviteSessionId.trim() } : {},
|
|
63
|
+
...input.preferredMethod ? { preferredMethod: input.preferredMethod } : {},
|
|
64
|
+
...input.supportedMethods?.length ? { supportedMethods: input.supportedMethods } : {}
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function sendOtpCode(client, input, signal) {
|
|
68
|
+
return client.auth.sendCode(buildOtpSendRequest(input), { signal });
|
|
69
|
+
}
|
|
70
|
+
function requestAuthToken(client, input, signal) {
|
|
71
|
+
return client.auth.requestToken(
|
|
72
|
+
{
|
|
73
|
+
sessionId: input.sessionId,
|
|
74
|
+
code: input.code,
|
|
75
|
+
deviceInfo: input.deviceInfo,
|
|
76
|
+
scopes: input.scopes?.length ? input.scopes : [...DefaultAuthScopes],
|
|
77
|
+
...input.splitTokens !== void 0 ? { splitTokens: input.splitTokens } : {}
|
|
78
|
+
},
|
|
79
|
+
{ signal }
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
function tokenTransition(status, legacy = false) {
|
|
83
|
+
if (legacy && status == null) return TokenStatusValues.TokenReady;
|
|
84
|
+
return typeof status === "string" && isTokenStatus(status) ? status : void 0;
|
|
85
|
+
}
|
|
86
|
+
function tokenResponseFields(o) {
|
|
87
|
+
return {
|
|
88
|
+
access: o.accessToken ?? o.AccessToken ?? o.access_token,
|
|
89
|
+
refresh: o.refreshToken ?? o.RefreshToken ?? o.refresh_token,
|
|
90
|
+
expiration: o.expiration ?? o.Expiration ?? o.expires_in,
|
|
91
|
+
status: o.status ?? o.Status
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// ../../src/auth-session/otp-flow.ts
|
|
96
|
+
var OtpPhaseValues = /* @__PURE__ */ ((OtpPhaseValues2) => {
|
|
97
|
+
OtpPhaseValues2["AwaitingCode"] = "AwaitingCode";
|
|
98
|
+
OtpPhaseValues2["Pending"] = "Pending";
|
|
99
|
+
return OtpPhaseValues2;
|
|
100
|
+
})(OtpPhaseValues || {});
|
|
101
|
+
var OtpFlowErrorCodeValues = /* @__PURE__ */ ((OtpFlowErrorCodeValues2) => {
|
|
102
|
+
OtpFlowErrorCodeValues2["InvalidArguments"] = "InvalidArguments";
|
|
103
|
+
OtpFlowErrorCodeValues2["InvalidChallenge"] = "InvalidChallenge";
|
|
104
|
+
OtpFlowErrorCodeValues2["InvalidResponse"] = "InvalidResponse";
|
|
105
|
+
OtpFlowErrorCodeValues2["UnsupportedMethod"] = "UnsupportedMethod";
|
|
106
|
+
return OtpFlowErrorCodeValues2;
|
|
107
|
+
})(OtpFlowErrorCodeValues || {});
|
|
108
|
+
var OtpFlowError = class extends Error {
|
|
109
|
+
constructor(code, cleanup) {
|
|
110
|
+
super(`OTP flow: ${code}`);
|
|
111
|
+
this.code = code;
|
|
112
|
+
this.#cleanup = cleanup ? Object.freeze({ ...cleanup }) : void 0;
|
|
113
|
+
}
|
|
114
|
+
code;
|
|
115
|
+
name = "OtpFlowError";
|
|
116
|
+
#cleanup;
|
|
117
|
+
/** Секрет доступен явно; не является перечисляемым полем исключения. */
|
|
118
|
+
get cleanup() {
|
|
119
|
+
return this.#cleanup;
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
function createOtpFlow(options) {
|
|
123
|
+
if (!isRecord(options) || !isRecord(options.deviceInfo) || typeof options.deviceInfo.deviceId !== "string" || !options.deviceInfo.deviceId.trim() || typeof options.deviceInfo.deviceType !== "string" || !isDeviceType(options.deviceInfo.deviceType) || options.scopes !== void 0 && (!Array.isArray(options.scopes) || !options.scopes.every(
|
|
124
|
+
(scope) => typeof scope === "string" && scope.trim()
|
|
125
|
+
)) || options.splitTokens !== void 0 && typeof options.splitTokens !== "boolean")
|
|
126
|
+
throw new OtpFlowError("InvalidArguments" /* InvalidArguments */);
|
|
127
|
+
const client = createIdentityClient(options);
|
|
128
|
+
const deviceInfo = { ...options.deviceInfo };
|
|
129
|
+
const scopes = options.scopes ? [...options.scopes] : void 0;
|
|
130
|
+
const splitTokens = options.splitTokens;
|
|
131
|
+
return {
|
|
132
|
+
async start(input, requestOptions) {
|
|
133
|
+
validateStartInput(input);
|
|
134
|
+
requestOptions?.signal?.throwIfAborted();
|
|
135
|
+
const startedAt = Date.now();
|
|
136
|
+
const response = await sendOtpCode(
|
|
137
|
+
client,
|
|
138
|
+
{
|
|
139
|
+
...input,
|
|
140
|
+
accountName: input.account.accountName,
|
|
141
|
+
type: input.account.accountType
|
|
142
|
+
},
|
|
143
|
+
requestOptions?.signal
|
|
144
|
+
);
|
|
145
|
+
if (!isRecord(response) || typeof response.sessionId !== "string" || !response.sessionId.trim())
|
|
146
|
+
throw new OtpFlowError("InvalidResponse" /* InvalidResponse */);
|
|
147
|
+
if (!supportedMethod(response.usedMethod, input.account.accountType))
|
|
148
|
+
throw new OtpFlowError("UnsupportedMethod" /* UnsupportedMethod */);
|
|
149
|
+
const ttl = response.sessionLifeTimeInSeconds ?? 180;
|
|
150
|
+
if (!positive(ttl)) throw new OtpFlowError("InvalidResponse" /* InvalidResponse */);
|
|
151
|
+
const expiresAtMs = startedAt + ttl * 1e3;
|
|
152
|
+
if (!timestamp(expiresAtMs) || expiresAtMs <= Date.now())
|
|
153
|
+
throw new OtpFlowError("InvalidResponse" /* InvalidResponse */);
|
|
154
|
+
const mobile = response.usedMethod === VerificationMethodValues.MtsMobileId;
|
|
155
|
+
const nextSendAtMs = response.nextAvailableUsageTime instanceof Date ? response.nextAvailableUsageTime.getTime() : typeof response.nextAvailableUsageTime === "string" ? Date.parse(response.nextAvailableUsageTime) : void 0;
|
|
156
|
+
return {
|
|
157
|
+
sessionId: response.sessionId.trim(),
|
|
158
|
+
accountType: input.account.accountType,
|
|
159
|
+
usedMethod: response.usedMethod,
|
|
160
|
+
phase: mobile ? "Pending" /* Pending */ : "AwaitingCode" /* AwaitingCode */,
|
|
161
|
+
expiresAtMs,
|
|
162
|
+
codeLength: codeLength(response.codeLength) ? response.codeLength : input.account.accountType === "Email" && !mobile ? 6 : 4,
|
|
163
|
+
...mobile ? {
|
|
164
|
+
pollIntervalSeconds: positive(response.pollIntervalSeconds) ? response.pollIntervalSeconds : 3
|
|
165
|
+
} : {},
|
|
166
|
+
...attempts(response.attemptCountToUseSession) ? { attemptsRemaining: response.attemptCountToUseSession } : {},
|
|
167
|
+
...timestamp(nextSendAtMs) ? { nextSendAtMs } : {},
|
|
168
|
+
...methods(response.availableMethods) ? { availableMethods: [...response.availableMethods] } : {}
|
|
169
|
+
};
|
|
170
|
+
},
|
|
171
|
+
async complete(input, requestOptions) {
|
|
172
|
+
if (!isRecord(input)) throw new OtpFlowError("InvalidArguments" /* InvalidArguments */);
|
|
173
|
+
requestOptions?.signal?.throwIfAborted();
|
|
174
|
+
const continuation = validateChallenge(input.challenge);
|
|
175
|
+
if (continuation.phase === "Pending" /* Pending */ && input.code !== void 0 || continuation.phase === "AwaitingCode" /* AwaitingCode */ && (typeof input.code !== "string" || !input.code.trim()))
|
|
176
|
+
throw new OtpFlowError("InvalidArguments" /* InvalidArguments */);
|
|
177
|
+
const raw = await requestAuthToken(
|
|
178
|
+
client,
|
|
179
|
+
{
|
|
180
|
+
sessionId: continuation.sessionId,
|
|
181
|
+
code: input.code?.trim() ?? "",
|
|
182
|
+
deviceInfo,
|
|
183
|
+
scopes,
|
|
184
|
+
splitTokens
|
|
185
|
+
},
|
|
186
|
+
requestOptions?.signal
|
|
187
|
+
);
|
|
188
|
+
if (!isRecord(raw)) throw new OtpFlowError("InvalidResponse" /* InvalidResponse */);
|
|
189
|
+
const fields = tokenResponseFields(raw);
|
|
190
|
+
const refreshToken = typeof fields.refresh === "string" && fields.refresh.trim() ? fields.refresh.trim() : void 0;
|
|
191
|
+
const cleanup = refreshToken ? { refreshToken } : void 0;
|
|
192
|
+
const status = tokenTransition(
|
|
193
|
+
typeof fields.status === "string" ? fields.status.trim() : fields.status
|
|
194
|
+
);
|
|
195
|
+
if (!status || fields.access != null && typeof fields.access !== "string" || fields.refresh != null && typeof fields.refresh !== "string" || fields.expiration != null && (typeof fields.expiration !== "number" || !Number.isFinite(fields.expiration) || fields.expiration < 0))
|
|
196
|
+
throw new OtpFlowError("InvalidResponse" /* InvalidResponse */, cleanup);
|
|
197
|
+
if (status === TokenStatusValues2.Pending)
|
|
198
|
+
return {
|
|
199
|
+
status,
|
|
200
|
+
cleanup,
|
|
201
|
+
challenge: {
|
|
202
|
+
...continuation,
|
|
203
|
+
phase: "Pending" /* Pending */,
|
|
204
|
+
usedMethod: VerificationMethodValues.MtsMobileId,
|
|
205
|
+
pollIntervalSeconds: continuation.pollIntervalSeconds ?? 3
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
if (status === TokenStatusValues2.AwaitingSmsCode) {
|
|
209
|
+
const { pollIntervalSeconds: _interval, ...challenge } = continuation;
|
|
210
|
+
return {
|
|
211
|
+
status,
|
|
212
|
+
cleanup,
|
|
213
|
+
challenge: {
|
|
214
|
+
...challenge,
|
|
215
|
+
phase: "AwaitingCode" /* AwaitingCode */,
|
|
216
|
+
usedMethod: VerificationMethodValues.Sms,
|
|
217
|
+
codeLength: challenge.codeLength || 4
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
if (status === TokenStatusValues2.Failed) return { status, cleanup };
|
|
222
|
+
if (typeof fields.access !== "string" || !fields.access.trim())
|
|
223
|
+
throw new OtpFlowError("InvalidResponse" /* InvalidResponse */, cleanup);
|
|
224
|
+
return {
|
|
225
|
+
status,
|
|
226
|
+
tokens: {
|
|
227
|
+
status,
|
|
228
|
+
accessToken: fields.access.trim(),
|
|
229
|
+
...refreshToken ? { refreshToken } : {},
|
|
230
|
+
...typeof fields.expiration === "number" ? { expiration: fields.expiration } : {}
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
function isRecord(value) {
|
|
237
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
238
|
+
}
|
|
239
|
+
function positive(value) {
|
|
240
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0;
|
|
241
|
+
}
|
|
242
|
+
function codeLength(value) {
|
|
243
|
+
return positive(value) && Number.isInteger(value) && value <= 32;
|
|
244
|
+
}
|
|
245
|
+
function attempts(value) {
|
|
246
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
247
|
+
}
|
|
248
|
+
function timestamp(value) {
|
|
249
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 && value <= 864e13;
|
|
250
|
+
}
|
|
251
|
+
function methods(value) {
|
|
252
|
+
return Array.isArray(value) && value.every(
|
|
253
|
+
(method) => typeof method === "string" && isVerificationMethod(method)
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
function supportedMethod(value, accountType) {
|
|
257
|
+
return typeof value === "string" && isVerificationMethod(value) && (value !== VerificationMethodValues.UnknownMethod || accountType === "Email");
|
|
258
|
+
}
|
|
259
|
+
function validateStartInput(input) {
|
|
260
|
+
if (!isRecord(input) || !isRecord(input.account) || typeof input.account.accountType !== "string" || !isSendCodeAccountType(input.account.accountType) || typeof input.account.accountName !== "string" || !input.account.accountName.trim())
|
|
261
|
+
throw new OtpFlowError("InvalidArguments" /* InvalidArguments */);
|
|
262
|
+
for (const field of ["language", "captcha", "inviteSessionId"]) {
|
|
263
|
+
if (input[field] !== void 0 && typeof input[field] !== "string")
|
|
264
|
+
throw new OtpFlowError("InvalidArguments" /* InvalidArguments */);
|
|
265
|
+
}
|
|
266
|
+
if (input.regionStorageId !== void 0 && !attempts(input.regionStorageId) || input.captchaType !== void 0 && (typeof input.captchaType !== "string" || !isAuthCaptchaType(input.captchaType)) || input.preferredMethod !== void 0 && (typeof input.preferredMethod !== "string" || !isVerificationMethod(input.preferredMethod)) || input.supportedMethods !== void 0 && !methods(input.supportedMethods))
|
|
267
|
+
throw new OtpFlowError("InvalidArguments" /* InvalidArguments */);
|
|
268
|
+
}
|
|
269
|
+
function validateChallenge(value) {
|
|
270
|
+
if (!isRecord(value) || typeof value.sessionId !== "string" || !value.sessionId.trim() || typeof value.accountType !== "string" || !isSendCodeAccountType(value.accountType) || !supportedMethod(value.usedMethod, value.accountType) || value.phase !== "Pending" /* Pending */ && value.phase !== "AwaitingCode" /* AwaitingCode */ || !timestamp(value.expiresAtMs) || value.expiresAtMs <= Date.now() || value.phase === "Pending" /* Pending */ !== (value.usedMethod === VerificationMethodValues.MtsMobileId))
|
|
271
|
+
throw new OtpFlowError("InvalidChallenge" /* InvalidChallenge */);
|
|
272
|
+
if (value.codeLength !== void 0 && !codeLength(value.codeLength) || value.pollIntervalSeconds !== void 0 && !positive(value.pollIntervalSeconds) || value.attemptsRemaining !== void 0 && !attempts(value.attemptsRemaining) || value.nextSendAtMs !== void 0 && !timestamp(value.nextSendAtMs) || value.availableMethods !== void 0 && !methods(value.availableMethods))
|
|
273
|
+
throw new OtpFlowError("InvalidChallenge" /* InvalidChallenge */);
|
|
274
|
+
return {
|
|
275
|
+
sessionId: value.sessionId.trim(),
|
|
276
|
+
accountType: value.accountType,
|
|
277
|
+
usedMethod: value.usedMethod,
|
|
278
|
+
phase: value.phase,
|
|
279
|
+
expiresAtMs: value.expiresAtMs,
|
|
280
|
+
...codeLength(value.codeLength) ? { codeLength: value.codeLength } : {},
|
|
281
|
+
...positive(value.pollIntervalSeconds) ? { pollIntervalSeconds: value.pollIntervalSeconds } : {},
|
|
282
|
+
...attempts(value.attemptsRemaining) ? { attemptsRemaining: value.attemptsRemaining } : {},
|
|
283
|
+
...timestamp(value.nextSendAtMs) ? { nextSendAtMs: value.nextSendAtMs } : {},
|
|
284
|
+
...methods(value.availableMethods) ? { availableMethods: [...value.availableMethods] } : {}
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
|
|
16
288
|
// ../../src/auth-session/token-storage.ts
|
|
17
289
|
var MemoryTokenStorage = class {
|
|
18
290
|
snapshot;
|
|
@@ -105,11 +377,11 @@ function isAuthSessionNotReadyYet(e) {
|
|
|
105
377
|
|
|
106
378
|
// ../../src/auth-session/TokenSession.ts
|
|
107
379
|
import {
|
|
108
|
-
DefaultAuthScopes,
|
|
109
|
-
TorrowClient,
|
|
380
|
+
DefaultAuthScopes as DefaultAuthScopes2,
|
|
381
|
+
TorrowClient as TorrowClient2,
|
|
110
382
|
TorrowHttpError as TorrowHttpError2,
|
|
111
383
|
DeviceTypeValues,
|
|
112
|
-
|
|
384
|
+
TokenStatusValues as TokenStatusValues3
|
|
113
385
|
} from "@torrow/sdk-client";
|
|
114
386
|
|
|
115
387
|
// ../../src/auth-session/fetch-with-401-retry.ts
|
|
@@ -151,10 +423,7 @@ function normalizeTokenResp(raw) {
|
|
|
151
423
|
}
|
|
152
424
|
if (typeof raw !== "object") return {};
|
|
153
425
|
const o = raw;
|
|
154
|
-
const access
|
|
155
|
-
const refresh = o.refreshToken ?? o.RefreshToken ?? o.refresh_token;
|
|
156
|
-
const expRaw = o.expiration ?? o.Expiration ?? o.expires_in;
|
|
157
|
-
const statusRaw = o.status ?? o.Status;
|
|
426
|
+
const { access, refresh, expiration: expRaw, status: statusRaw } = tokenResponseFields(o);
|
|
158
427
|
const out = {};
|
|
159
428
|
if (typeof access === "string" && access.trim()) out.accessToken = access.trim();
|
|
160
429
|
if (typeof refresh === "string" && refresh.trim()) out.refreshToken = refresh.trim();
|
|
@@ -349,14 +618,15 @@ var TokenSession = class _TokenSession {
|
|
|
349
618
|
if (!sessionId || !code) {
|
|
350
619
|
throw new Error("TokenSession.requestTokenWithCode: sessionId and code are required");
|
|
351
620
|
}
|
|
352
|
-
const scopes = args.scopes && args.scopes.length > 0 ? args.scopes : [...
|
|
621
|
+
const scopes = args.scopes && args.scopes.length > 0 ? args.scopes : [...DefaultAuthScopes2];
|
|
353
622
|
const deviceInfo = this.resolveDeviceInfo();
|
|
354
623
|
let resp;
|
|
355
624
|
try {
|
|
356
625
|
resp = normalizeTokenResp(
|
|
357
|
-
await
|
|
626
|
+
await requestAuthToken(
|
|
627
|
+
this.identityClient(),
|
|
358
628
|
{ sessionId, code, scopes, deviceInfo },
|
|
359
|
-
|
|
629
|
+
args.signal
|
|
360
630
|
)
|
|
361
631
|
);
|
|
362
632
|
} catch (e) {
|
|
@@ -365,7 +635,7 @@ var TokenSession = class _TokenSession {
|
|
|
365
635
|
}
|
|
366
636
|
throw e;
|
|
367
637
|
}
|
|
368
|
-
if (
|
|
638
|
+
if (tokenTransition(resp.status, true) !== TokenStatusValues3.TokenReady) {
|
|
369
639
|
throw new AuthSessionNotReadyError(
|
|
370
640
|
`TokenResp.status=${resp.status} (\u043E\u0436\u0438\u0434\u0430\u043B\u0441\u044F TokenReady)`
|
|
371
641
|
);
|
|
@@ -537,21 +807,7 @@ var TokenSession = class _TokenSession {
|
|
|
537
807
|
}
|
|
538
808
|
/** Identity-клиент с Basic приложения, без Bearer пользователя. */
|
|
539
809
|
identityClient() {
|
|
540
|
-
|
|
541
|
-
resolveAppCredentials(this.opts.appCredentials)
|
|
542
|
-
);
|
|
543
|
-
if (!authIdentityBasic) {
|
|
544
|
-
throw new Error(
|
|
545
|
-
"TokenSession: \u043D\u0435\u043F\u043E\u043B\u043D\u0430\u044F appCredentials (\u043D\u0443\u0436\u043D\u044B \u043D\u0435\u043F\u0443\u0441\u0442\u044B\u0435 clientId \u0438 clientSecret). \u0411\u0435\u0437 \u043E\u043F\u0446\u0438\u0438 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0435\u0442\u0441\u044F DEFAULT_APP_CREDENTIALS."
|
|
546
|
-
);
|
|
547
|
-
}
|
|
548
|
-
return new TorrowClient({
|
|
549
|
-
baseUrl: this.opts.baseUrl,
|
|
550
|
-
fetch: this.opts.fetch,
|
|
551
|
-
defaultHeaders: this.opts.defaultHeaders,
|
|
552
|
-
userAgent: this.opts.userAgent,
|
|
553
|
-
authIdentityBasic
|
|
554
|
-
});
|
|
810
|
+
return createIdentityClient(this.opts);
|
|
555
811
|
}
|
|
556
812
|
buildClient() {
|
|
557
813
|
let fetchImpl = this.opts.fetch;
|
|
@@ -575,7 +831,7 @@ var TokenSession = class _TokenSession {
|
|
|
575
831
|
}
|
|
576
832
|
});
|
|
577
833
|
}
|
|
578
|
-
return new
|
|
834
|
+
return new TorrowClient2({
|
|
579
835
|
baseUrl: this.opts.baseUrl,
|
|
580
836
|
fetch: fetchImpl,
|
|
581
837
|
defaultHeaders: this.opts.defaultHeaders,
|
|
@@ -594,13 +850,13 @@ var TokenSession = class _TokenSession {
|
|
|
594
850
|
|
|
595
851
|
// ../../src/auth-session/torrow-auth.ts
|
|
596
852
|
import {
|
|
597
|
-
TorrowClient as
|
|
853
|
+
TorrowClient as TorrowClient3
|
|
598
854
|
} from "@torrow/sdk-client";
|
|
599
855
|
|
|
600
856
|
// ../../src/auth-session/link-login.ts
|
|
601
857
|
import {
|
|
602
858
|
AccountTypeValues,
|
|
603
|
-
DefaultAuthScopes as
|
|
859
|
+
DefaultAuthScopes as DefaultAuthScopes3
|
|
604
860
|
} from "@torrow/sdk-client";
|
|
605
861
|
|
|
606
862
|
// ../../src/auth-session/sleep.ts
|
|
@@ -649,7 +905,7 @@ async function runLinkLogin(session, options) {
|
|
|
649
905
|
);
|
|
650
906
|
}
|
|
651
907
|
await options.onLink(authUrl);
|
|
652
|
-
const scopes = options.scopes && options.scopes.length > 0 ? options.scopes : [...
|
|
908
|
+
const scopes = options.scopes && options.scopes.length > 0 ? options.scopes : [...DefaultAuthScopes3];
|
|
653
909
|
const intervalMs = typeof options.pollIntervalMs === "number" && options.pollIntervalMs > 0 ? options.pollIntervalMs : DEFAULT_POLL_INTERVAL_MS;
|
|
654
910
|
const timeoutSec = typeof options.pollTimeoutSec === "number" && options.pollTimeoutSec > 0 ? options.pollTimeoutSec : typeof createResp.sessionLifeTimeInSeconds === "number" && createResp.sessionLifeTimeInSeconds > 0 ? createResp.sessionLifeTimeInSeconds : DEFAULT_POLL_TIMEOUT_SEC;
|
|
655
911
|
const deadline = Date.now() + timeoutSec * 1e3;
|
|
@@ -676,23 +932,22 @@ async function runLinkLogin(session, options) {
|
|
|
676
932
|
|
|
677
933
|
// ../../src/auth-session/otp-login.ts
|
|
678
934
|
import {
|
|
679
|
-
DefaultAuthScopes as
|
|
680
|
-
isTokenRespReadyStatus as isTokenRespReadyStatus4,
|
|
935
|
+
DefaultAuthScopes as DefaultAuthScopes5,
|
|
681
936
|
SendCodeAccountTypeValues,
|
|
682
937
|
TorrowHttpError as TorrowHttpError3,
|
|
683
|
-
VerificationMethodValues
|
|
938
|
+
VerificationMethodValues as VerificationMethodValues2,
|
|
939
|
+
TokenStatusValues as TokenStatusValues5
|
|
684
940
|
} from "@torrow/sdk-client";
|
|
685
941
|
|
|
686
942
|
// ../../src/auth-session/mobile-id.ts
|
|
687
943
|
import {
|
|
688
|
-
DefaultAuthScopes as
|
|
689
|
-
|
|
690
|
-
TokenStatusValues
|
|
944
|
+
DefaultAuthScopes as DefaultAuthScopes4,
|
|
945
|
+
TokenStatusValues as TokenStatusValues4
|
|
691
946
|
} from "@torrow/sdk-client";
|
|
692
947
|
var DEFAULT_POLL_INTERVAL_SEC = 3;
|
|
693
948
|
var DEFAULT_MAX_WAIT_SEC = 180;
|
|
694
949
|
function scopesOrDefault(scopes) {
|
|
695
|
-
return scopes && scopes.length > 0 ? scopes : [...
|
|
950
|
+
return scopes && scopes.length > 0 ? scopes : [...DefaultAuthScopes4];
|
|
696
951
|
}
|
|
697
952
|
async function pollMobileIdOnce(client, args) {
|
|
698
953
|
const sessionId = args.sessionId.trim();
|
|
@@ -706,7 +961,7 @@ async function pollMobileIdOnce(client, args) {
|
|
|
706
961
|
scopes: scopesOrDefault(args.scopes),
|
|
707
962
|
deviceInfo: args.deviceInfo
|
|
708
963
|
};
|
|
709
|
-
return normalizeTokenResp(await client
|
|
964
|
+
return normalizeTokenResp(await requestAuthToken(client, body, args.signal));
|
|
710
965
|
}
|
|
711
966
|
async function pollMobileIdUntilReady(client, options) {
|
|
712
967
|
const sessionId = options.sessionId.trim();
|
|
@@ -728,15 +983,15 @@ async function pollMobileIdUntilReady(client, options) {
|
|
|
728
983
|
signal: options.signal
|
|
729
984
|
});
|
|
730
985
|
const status = resp.status;
|
|
731
|
-
if (
|
|
986
|
+
if (tokenTransition(status, true) === TokenStatusValues4.TokenReady) {
|
|
732
987
|
return assertTokenRespReadyForStorage(resp, "pollMobileIdUntilReady");
|
|
733
988
|
}
|
|
734
|
-
if (status ===
|
|
989
|
+
if (status === TokenStatusValues4.Failed) {
|
|
735
990
|
throw new Error(
|
|
736
991
|
"pollMobileIdUntilReady: TokenResp.status=Failed \u2014 \u043D\u0443\u0436\u0435\u043D \u043D\u043E\u0432\u044B\u0439 sendCode."
|
|
737
992
|
);
|
|
738
993
|
}
|
|
739
|
-
if (status ===
|
|
994
|
+
if (status === TokenStatusValues4.AwaitingSmsCode) {
|
|
740
995
|
const obtain = options.obtainSmsCode;
|
|
741
996
|
if (!obtain) {
|
|
742
997
|
throw new Error(
|
|
@@ -756,25 +1011,25 @@ async function pollMobileIdUntilReady(client, options) {
|
|
|
756
1011
|
code,
|
|
757
1012
|
signal: options.signal
|
|
758
1013
|
});
|
|
759
|
-
if (
|
|
1014
|
+
if (tokenTransition(afterSms.status, true) === TokenStatusValues4.TokenReady) {
|
|
760
1015
|
return assertTokenRespReadyForStorage(afterSms, "pollMobileIdUntilReady (SMS)");
|
|
761
1016
|
}
|
|
762
|
-
if (afterSms.status ===
|
|
1017
|
+
if (afterSms.status === TokenStatusValues4.Failed) {
|
|
763
1018
|
throw new Error(
|
|
764
1019
|
"pollMobileIdUntilReady: TokenResp.status=Failed \u043F\u043E\u0441\u043B\u0435 SMS \u2014 \u043D\u0443\u0436\u0435\u043D \u043D\u043E\u0432\u044B\u0439 sendCode."
|
|
765
1020
|
);
|
|
766
1021
|
}
|
|
767
|
-
if (afterSms.status ===
|
|
1022
|
+
if (afterSms.status === TokenStatusValues4.Pending) {
|
|
768
1023
|
continue;
|
|
769
1024
|
}
|
|
770
|
-
if (afterSms.status ===
|
|
1025
|
+
if (afterSms.status === TokenStatusValues4.AwaitingSmsCode) {
|
|
771
1026
|
continue;
|
|
772
1027
|
}
|
|
773
1028
|
throw new Error(
|
|
774
1029
|
`pollMobileIdUntilReady: \u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u044B\u0439 TokenResp.status=${String(afterSms.status)} \u043F\u043E\u0441\u043B\u0435 SMS.`
|
|
775
1030
|
);
|
|
776
1031
|
}
|
|
777
|
-
if (status !==
|
|
1032
|
+
if (status !== TokenStatusValues4.Pending) {
|
|
778
1033
|
throw new Error(
|
|
779
1034
|
`pollMobileIdUntilReady: \u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u044B\u0439 TokenResp.status=${String(status)}.`
|
|
780
1035
|
);
|
|
@@ -789,8 +1044,6 @@ async function pollMobileIdUntilReady(client, options) {
|
|
|
789
1044
|
}
|
|
790
1045
|
|
|
791
1046
|
// ../../src/auth-session/otp-login.ts
|
|
792
|
-
var DEFAULT_LANGUAGE2 = "ru";
|
|
793
|
-
var DEFAULT_REGION2 = 70;
|
|
794
1047
|
var MOBILE_ID_SMS_CODE_LENGTH = 4;
|
|
795
1048
|
var OtpCodeRequestLimitExceededError = class extends Error {
|
|
796
1049
|
name = "OtpCodeRequestLimitExceededError";
|
|
@@ -853,24 +1106,7 @@ function httpErrorBody(e) {
|
|
|
853
1106
|
return void 0;
|
|
854
1107
|
}
|
|
855
1108
|
function scopesOrDefault2(scopes) {
|
|
856
|
-
return scopes && scopes.length > 0 ? scopes : [...
|
|
857
|
-
}
|
|
858
|
-
function buildSendCodeReq(options) {
|
|
859
|
-
const accountName = options.accountName.trim();
|
|
860
|
-
if (!accountName) {
|
|
861
|
-
throw new Error("runOtpLogin: accountName is empty");
|
|
862
|
-
}
|
|
863
|
-
return {
|
|
864
|
-
accountName,
|
|
865
|
-
type: options.type,
|
|
866
|
-
language: options.language?.trim() || DEFAULT_LANGUAGE2,
|
|
867
|
-
regionStorageId: typeof options.regionStorageId === "number" && Number.isFinite(options.regionStorageId) ? options.regionStorageId : DEFAULT_REGION2,
|
|
868
|
-
...options.captcha?.trim() ? { captcha: options.captcha.trim() } : {},
|
|
869
|
-
...options.captchaType ? { captchaType: options.captchaType } : {},
|
|
870
|
-
...options.inviteSessionId?.trim() ? { inviteSessionId: options.inviteSessionId.trim() } : {},
|
|
871
|
-
...options.preferredMethod ? { preferredMethod: options.preferredMethod } : {},
|
|
872
|
-
...options.supportedMethods && options.supportedMethods.length > 0 ? { supportedMethods: options.supportedMethods } : {}
|
|
873
|
-
};
|
|
1109
|
+
return scopes && scopes.length > 0 ? scopes : [...DefaultAuthScopes5];
|
|
874
1110
|
}
|
|
875
1111
|
function effectiveCodeLength(raw, fallback) {
|
|
876
1112
|
if (typeof raw === "number" && raw > 0) return raw;
|
|
@@ -878,7 +1114,7 @@ function effectiveCodeLength(raw, fallback) {
|
|
|
878
1114
|
}
|
|
879
1115
|
async function requestTokenOrThrow(client, body, signal) {
|
|
880
1116
|
try {
|
|
881
|
-
return normalizeTokenResp(await client
|
|
1117
|
+
return normalizeTokenResp(await requestAuthToken(client, body, signal));
|
|
882
1118
|
} catch (e) {
|
|
883
1119
|
const errBody = httpErrorBody(e);
|
|
884
1120
|
if (errBody !== void 0 && isAuthHeaderMissingBody(errBody)) {
|
|
@@ -888,14 +1124,13 @@ async function requestTokenOrThrow(client, body, signal) {
|
|
|
888
1124
|
}
|
|
889
1125
|
}
|
|
890
1126
|
async function runOtpLogin(client, options) {
|
|
891
|
-
const sendBody = buildSendCodeReq(options);
|
|
892
1127
|
const accountName = options.accountName.trim();
|
|
893
1128
|
const accountType = options.type;
|
|
894
1129
|
const scopes = scopesOrDefault2(options.scopes);
|
|
895
1130
|
const signal = options.signal;
|
|
896
1131
|
let sendResp;
|
|
897
1132
|
try {
|
|
898
|
-
sendResp = await client
|
|
1133
|
+
sendResp = await sendOtpCode(client, options, signal);
|
|
899
1134
|
} catch (e) {
|
|
900
1135
|
const errBody = httpErrorBody(e);
|
|
901
1136
|
if (errBody !== void 0 && isSendCodeRateLimitedBody(errBody)) {
|
|
@@ -919,7 +1154,7 @@ async function runOtpLogin(client, options) {
|
|
|
919
1154
|
nextAvailableUsageTime: sendResp.nextAvailableUsageTime ?? null
|
|
920
1155
|
};
|
|
921
1156
|
await options.onSendCode(result);
|
|
922
|
-
if (sendResp.usedMethod ===
|
|
1157
|
+
if (sendResp.usedMethod === VerificationMethodValues2.MtsMobileId) {
|
|
923
1158
|
try {
|
|
924
1159
|
return await pollMobileIdUntilReady(client, {
|
|
925
1160
|
sessionId,
|
|
@@ -930,10 +1165,10 @@ async function runOtpLogin(client, options) {
|
|
|
930
1165
|
signal,
|
|
931
1166
|
smsCodeLength: typeof sendResp.codeLength === "number" && sendResp.codeLength > 0 ? sendResp.codeLength : MOBILE_ID_SMS_CODE_LENGTH,
|
|
932
1167
|
obtainSmsCode: async (ctx) => {
|
|
933
|
-
const
|
|
1168
|
+
const codeLength2 = effectiveCodeLength(ctx.codeLength, MOBILE_ID_SMS_CODE_LENGTH);
|
|
934
1169
|
return (await options.getCode({
|
|
935
1170
|
sessionId,
|
|
936
|
-
codeLength,
|
|
1171
|
+
codeLength: codeLength2,
|
|
937
1172
|
usedMethod: sendResp.usedMethod
|
|
938
1173
|
})).trim();
|
|
939
1174
|
}
|
|
@@ -968,7 +1203,7 @@ async function runOtpLogin(client, options) {
|
|
|
968
1203
|
},
|
|
969
1204
|
signal
|
|
970
1205
|
);
|
|
971
|
-
if (
|
|
1206
|
+
if (tokenTransition(resp.status, true) !== TokenStatusValues5.TokenReady) {
|
|
972
1207
|
throw new Error(`runOtpLogin: TokenResp.status=${resp.status} (\u043E\u0436\u0438\u0434\u0430\u043B\u0441\u044F TokenReady)`);
|
|
973
1208
|
}
|
|
974
1209
|
return assertTokenRespReadyForStorage(resp, "runOtpLogin");
|
|
@@ -993,6 +1228,8 @@ function sessionOpenOptsForLogin(base) {
|
|
|
993
1228
|
return { ...sessionOpenOpts(base), skipEnsureFreshOnOpen: true };
|
|
994
1229
|
}
|
|
995
1230
|
var TorrowAuth = {
|
|
1231
|
+
/** Пошаговый OTP без storage, polling и refresh. */
|
|
1232
|
+
createOtpFlow,
|
|
996
1233
|
async connect(options) {
|
|
997
1234
|
const session = await TokenSession.open(sessionOpenOpts(options));
|
|
998
1235
|
return { client: session.client, session };
|
|
@@ -1058,7 +1295,7 @@ var TorrowAuth = {
|
|
|
1058
1295
|
return TorrowAuth.connect(options);
|
|
1059
1296
|
},
|
|
1060
1297
|
anonymous(config = {}) {
|
|
1061
|
-
return new
|
|
1298
|
+
return new TorrowClient3({
|
|
1062
1299
|
...config,
|
|
1063
1300
|
getAccessToken: void 0
|
|
1064
1301
|
});
|
|
@@ -1071,6 +1308,9 @@ export {
|
|
|
1071
1308
|
MemoryTokenStorage,
|
|
1072
1309
|
OtpAuthHeaderMissingError,
|
|
1073
1310
|
OtpCodeRequestLimitExceededError,
|
|
1311
|
+
OtpFlowError,
|
|
1312
|
+
OtpFlowErrorCodeValues,
|
|
1313
|
+
OtpPhaseValues,
|
|
1074
1314
|
TokenSession,
|
|
1075
1315
|
TorrowAuth
|
|
1076
1316
|
};
|