@torrow/sdk-auth 0.8.0 → 0.9.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 +27 -9
- package/dist/index.d.ts +27 -56
- package/dist/index.js +819 -430
- package/dist/index.js.map +1 -1
- package/dist/node.d.ts +1 -1
- package/dist/{types-C4e90uOw.d.ts → types-M7DBG6bO.d.ts} +6 -4
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -13,8 +13,69 @@ function appCredentialsToAuthIdentityBasic(appCredentials) {
|
|
|
13
13
|
return { username: clientId, password: clientSecret };
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
// ../../src/auth-session/otp-errors.ts
|
|
17
|
+
import {
|
|
18
|
+
OtpErrorReason,
|
|
19
|
+
TorrowSdkError,
|
|
20
|
+
TorrowSdkErrorCategory,
|
|
21
|
+
TorrowSdkErrorDomain,
|
|
22
|
+
TorrowSdkRequestState
|
|
23
|
+
} from "@torrow/sdk-client";
|
|
24
|
+
function otpFailure(kind, cleanup, cause) {
|
|
25
|
+
let category;
|
|
26
|
+
let reason;
|
|
27
|
+
switch (kind) {
|
|
28
|
+
case OtpErrorReason.InvalidChallenge:
|
|
29
|
+
category = TorrowSdkErrorCategory.InvalidInput;
|
|
30
|
+
reason = kind;
|
|
31
|
+
break;
|
|
32
|
+
case OtpErrorReason.UnsupportedMethod:
|
|
33
|
+
category = TorrowSdkErrorCategory.UnsupportedOperation;
|
|
34
|
+
reason = kind;
|
|
35
|
+
break;
|
|
36
|
+
case OtpErrorReason.AuthHeaderMissing:
|
|
37
|
+
category = TorrowSdkErrorCategory.AuthenticationRequired;
|
|
38
|
+
reason = kind;
|
|
39
|
+
break;
|
|
40
|
+
case OtpErrorReason.CodeRequestLimitExceeded:
|
|
41
|
+
category = TorrowSdkErrorCategory.RateLimited;
|
|
42
|
+
reason = kind;
|
|
43
|
+
break;
|
|
44
|
+
default:
|
|
45
|
+
category = kind;
|
|
46
|
+
}
|
|
47
|
+
return new TorrowSdkError({ category, detail: { domain: TorrowSdkErrorDomain.Otp, ...reason ? { reason } : {} }, sensitive: { cleanup, cause } });
|
|
48
|
+
}
|
|
49
|
+
function otpHttpFailure(error) {
|
|
50
|
+
if (!(error instanceof TorrowSdkError)) return error;
|
|
51
|
+
const body = error.getSensitiveDetails().body;
|
|
52
|
+
if (body === null || typeof body !== "object") return error;
|
|
53
|
+
const value = body;
|
|
54
|
+
if (value.name === "AUTH_HEADER_MISSING" /* AuthHeaderMissing */) {
|
|
55
|
+
return error.withContext({ category: TorrowSdkErrorCategory.AuthenticationRequired, detail: { domain: TorrowSdkErrorDomain.Otp, reason: OtpErrorReason.AuthHeaderMissing } });
|
|
56
|
+
}
|
|
57
|
+
if (value.name !== "CODE_REQUEST_LIMIT_EXCEEDED" /* CodeRequestLimitExceeded */) return error;
|
|
58
|
+
const minutes = duration(value.parameters?.minutes);
|
|
59
|
+
const seconds = duration(value.parameters?.seconds);
|
|
60
|
+
const wait = minutes === void 0 && seconds === void 0 ? void 0 : (minutes ?? 0) * 60 + (seconds ?? 0);
|
|
61
|
+
return error.withContext({ category: TorrowSdkErrorCategory.RateLimited, detail: {
|
|
62
|
+
domain: TorrowSdkErrorDomain.Otp,
|
|
63
|
+
reason: OtpErrorReason.CodeRequestLimitExceeded,
|
|
64
|
+
...wait !== void 0 && Number.isFinite(wait) ? { retryAfterSeconds: wait } : {}
|
|
65
|
+
} });
|
|
66
|
+
}
|
|
67
|
+
function duration(value) {
|
|
68
|
+
const number = typeof value === "number" ? value : typeof value === "string" && /^\d+(?:\.\d+)?$/.test(value) ? Number(value) : void 0;
|
|
69
|
+
return number !== void 0 && Number.isFinite(number) && number >= 0 ? number : void 0;
|
|
70
|
+
}
|
|
71
|
+
function throwIfOtpAborted(signal) {
|
|
72
|
+
if (signal?.aborted) throw new TorrowSdkError({ category: TorrowSdkErrorCategory.Cancelled, request: TorrowSdkRequestState.NotStarted, sensitive: { cause: signal.reason } });
|
|
73
|
+
}
|
|
74
|
+
|
|
16
75
|
// ../../src/auth-session/otp-flow.ts
|
|
17
76
|
import {
|
|
77
|
+
OtpErrorReason as OtpErrorReason2,
|
|
78
|
+
TorrowSdkErrorCategory as TorrowSdkErrorCategory2,
|
|
18
79
|
isAuthCaptchaType,
|
|
19
80
|
isDeviceType,
|
|
20
81
|
isSendCodeAccountType,
|
|
@@ -98,40 +159,24 @@ var OtpPhaseValues = /* @__PURE__ */ ((OtpPhaseValues2) => {
|
|
|
98
159
|
OtpPhaseValues2["Pending"] = "Pending";
|
|
99
160
|
return OtpPhaseValues2;
|
|
100
161
|
})(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
162
|
function createOtpFlow(options) {
|
|
123
163
|
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
164
|
(scope) => typeof scope === "string" && scope.trim()
|
|
125
165
|
)) || options.splitTokens !== void 0 && typeof options.splitTokens !== "boolean")
|
|
126
|
-
throw
|
|
127
|
-
|
|
166
|
+
throw otpFailure(TorrowSdkErrorCategory2.InvalidInput);
|
|
167
|
+
let client;
|
|
168
|
+
try {
|
|
169
|
+
client = createIdentityClient(options);
|
|
170
|
+
} catch (cause) {
|
|
171
|
+
throw otpFailure(TorrowSdkErrorCategory2.InvalidInput, void 0, cause);
|
|
172
|
+
}
|
|
128
173
|
const deviceInfo = { ...options.deviceInfo };
|
|
129
174
|
const scopes = options.scopes ? [...options.scopes] : void 0;
|
|
130
175
|
const splitTokens = options.splitTokens;
|
|
131
176
|
return {
|
|
132
177
|
async start(input, requestOptions) {
|
|
133
178
|
validateStartInput(input);
|
|
134
|
-
requestOptions?.signal
|
|
179
|
+
throwIfOtpAborted(requestOptions?.signal);
|
|
135
180
|
const startedAt = Date.now();
|
|
136
181
|
const response = await sendOtpCode(
|
|
137
182
|
client,
|
|
@@ -141,16 +186,18 @@ function createOtpFlow(options) {
|
|
|
141
186
|
type: input.account.accountType
|
|
142
187
|
},
|
|
143
188
|
requestOptions?.signal
|
|
144
|
-
)
|
|
189
|
+
).catch((error) => {
|
|
190
|
+
throw otpHttpFailure(error);
|
|
191
|
+
});
|
|
145
192
|
if (!isRecord(response) || typeof response.sessionId !== "string" || !response.sessionId.trim())
|
|
146
|
-
throw
|
|
193
|
+
throw otpFailure(TorrowSdkErrorCategory2.InvalidResponse);
|
|
147
194
|
if (!supportedMethod(response.usedMethod, input.account.accountType))
|
|
148
|
-
throw
|
|
195
|
+
throw otpFailure(OtpErrorReason2.UnsupportedMethod);
|
|
149
196
|
const ttl = response.sessionLifeTimeInSeconds ?? 180;
|
|
150
|
-
if (!positive(ttl)) throw
|
|
197
|
+
if (!positive(ttl)) throw otpFailure(TorrowSdkErrorCategory2.InvalidResponse);
|
|
151
198
|
const expiresAtMs = startedAt + ttl * 1e3;
|
|
152
199
|
if (!timestamp(expiresAtMs) || expiresAtMs <= Date.now())
|
|
153
|
-
throw
|
|
200
|
+
throw otpFailure(TorrowSdkErrorCategory2.InvalidResponse);
|
|
154
201
|
const mobile = response.usedMethod === VerificationMethodValues.MtsMobileId;
|
|
155
202
|
const nextSendAtMs = response.nextAvailableUsageTime instanceof Date ? response.nextAvailableUsageTime.getTime() : typeof response.nextAvailableUsageTime === "string" ? Date.parse(response.nextAvailableUsageTime) : void 0;
|
|
156
203
|
return {
|
|
@@ -169,11 +216,11 @@ function createOtpFlow(options) {
|
|
|
169
216
|
};
|
|
170
217
|
},
|
|
171
218
|
async complete(input, requestOptions) {
|
|
172
|
-
if (!isRecord(input)) throw
|
|
173
|
-
requestOptions?.signal
|
|
219
|
+
if (!isRecord(input)) throw otpFailure(TorrowSdkErrorCategory2.InvalidInput);
|
|
220
|
+
throwIfOtpAborted(requestOptions?.signal);
|
|
174
221
|
const continuation = validateChallenge(input.challenge);
|
|
175
222
|
if (continuation.phase === "Pending" /* Pending */ && input.code !== void 0 || continuation.phase === "AwaitingCode" /* AwaitingCode */ && (typeof input.code !== "string" || !input.code.trim()))
|
|
176
|
-
throw
|
|
223
|
+
throw otpFailure(TorrowSdkErrorCategory2.InvalidInput);
|
|
177
224
|
const raw = await requestAuthToken(
|
|
178
225
|
client,
|
|
179
226
|
{
|
|
@@ -184,8 +231,10 @@ function createOtpFlow(options) {
|
|
|
184
231
|
splitTokens
|
|
185
232
|
},
|
|
186
233
|
requestOptions?.signal
|
|
187
|
-
)
|
|
188
|
-
|
|
234
|
+
).catch((error) => {
|
|
235
|
+
throw otpHttpFailure(error);
|
|
236
|
+
});
|
|
237
|
+
if (!isRecord(raw)) throw otpFailure(TorrowSdkErrorCategory2.InvalidResponse);
|
|
189
238
|
const fields = tokenResponseFields(raw);
|
|
190
239
|
const refreshToken = typeof fields.refresh === "string" && fields.refresh.trim() ? fields.refresh.trim() : void 0;
|
|
191
240
|
const cleanup = refreshToken ? { refreshToken } : void 0;
|
|
@@ -193,7 +242,7 @@ function createOtpFlow(options) {
|
|
|
193
242
|
typeof fields.status === "string" ? fields.status.trim() : fields.status
|
|
194
243
|
);
|
|
195
244
|
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
|
|
245
|
+
throw otpFailure(TorrowSdkErrorCategory2.InvalidResponse, cleanup);
|
|
197
246
|
if (status === TokenStatusValues2.Pending)
|
|
198
247
|
return {
|
|
199
248
|
status,
|
|
@@ -220,7 +269,7 @@ function createOtpFlow(options) {
|
|
|
220
269
|
}
|
|
221
270
|
if (status === TokenStatusValues2.Failed) return { status, cleanup };
|
|
222
271
|
if (typeof fields.access !== "string" || !fields.access.trim())
|
|
223
|
-
throw
|
|
272
|
+
throw otpFailure(TorrowSdkErrorCategory2.InvalidResponse, cleanup);
|
|
224
273
|
return {
|
|
225
274
|
status,
|
|
226
275
|
tokens: {
|
|
@@ -258,19 +307,19 @@ function supportedMethod(value, accountType) {
|
|
|
258
307
|
}
|
|
259
308
|
function validateStartInput(input) {
|
|
260
309
|
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
|
|
310
|
+
throw otpFailure(TorrowSdkErrorCategory2.InvalidInput);
|
|
262
311
|
for (const field of ["language", "captcha", "inviteSessionId"]) {
|
|
263
312
|
if (input[field] !== void 0 && typeof input[field] !== "string")
|
|
264
|
-
throw
|
|
313
|
+
throw otpFailure(TorrowSdkErrorCategory2.InvalidInput);
|
|
265
314
|
}
|
|
266
315
|
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
|
|
316
|
+
throw otpFailure(TorrowSdkErrorCategory2.InvalidInput);
|
|
268
317
|
}
|
|
269
318
|
function validateChallenge(value) {
|
|
270
319
|
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
|
|
320
|
+
throw otpFailure(OtpErrorReason2.InvalidChallenge);
|
|
272
321
|
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
|
|
322
|
+
throw otpFailure(OtpErrorReason2.InvalidChallenge);
|
|
274
323
|
return {
|
|
275
324
|
sessionId: value.sessionId.trim(),
|
|
276
325
|
accountType: value.accountType,
|
|
@@ -354,36 +403,92 @@ var LocalStorageTokenStorage = class {
|
|
|
354
403
|
}
|
|
355
404
|
};
|
|
356
405
|
|
|
357
|
-
// ../../src/auth-session/auth-session-not-ready.ts
|
|
358
|
-
import { TorrowHttpError } from "@torrow/sdk-client";
|
|
359
|
-
var AuthSessionNotReadyError = class extends Error {
|
|
360
|
-
name = "AuthSessionNotReadyError";
|
|
361
|
-
cause;
|
|
362
|
-
constructor(message = "Auth session is not ready yet", cause) {
|
|
363
|
-
super(message);
|
|
364
|
-
this.cause = cause;
|
|
365
|
-
}
|
|
366
|
-
};
|
|
367
|
-
function isAuthSessionNotReadyYet(e) {
|
|
368
|
-
if (e instanceof AuthSessionNotReadyError) return true;
|
|
369
|
-
if (!(e instanceof TorrowHttpError)) return false;
|
|
370
|
-
const text = [
|
|
371
|
-
e.message,
|
|
372
|
-
e.apiErrorName,
|
|
373
|
-
typeof e.body === "string" ? e.body : JSON.stringify(e.body ?? "")
|
|
374
|
-
].join(" ").toLowerCase();
|
|
375
|
-
return text.includes("session is not ready") || text.includes("unauthorized_client");
|
|
376
|
-
}
|
|
377
|
-
|
|
378
406
|
// ../../src/auth-session/TokenSession.ts
|
|
379
407
|
import {
|
|
380
408
|
DefaultAuthScopes as DefaultAuthScopes2,
|
|
381
409
|
TorrowClient as TorrowClient2,
|
|
382
|
-
|
|
410
|
+
TorrowSdkError as TorrowSdkError4,
|
|
411
|
+
TorrowSdkErrorCategory as TorrowSdkErrorCategory4,
|
|
412
|
+
TorrowSdkEffectArea,
|
|
413
|
+
TorrowSdkEffectState,
|
|
414
|
+
TorrowSdkRequestState as TorrowSdkRequestState2,
|
|
415
|
+
AuthSessionEffectStep,
|
|
416
|
+
AuthSessionErrorReason as AuthSessionErrorReason2,
|
|
383
417
|
DeviceTypeValues,
|
|
384
418
|
TokenStatusValues as TokenStatusValues3
|
|
385
419
|
} from "@torrow/sdk-client";
|
|
386
420
|
|
|
421
|
+
// ../../src/auth-session/auth-session-not-ready.ts
|
|
422
|
+
import { AuthSessionErrorReason, TorrowSdkError as TorrowSdkError2, TorrowSdkErrorDomain as TorrowSdkErrorDomain2 } from "@torrow/sdk-client";
|
|
423
|
+
function isAuthSessionNotReadyYet(error) {
|
|
424
|
+
if (!(error instanceof TorrowSdkError2)) return false;
|
|
425
|
+
if (error.detail?.domain === TorrowSdkErrorDomain2.AuthSession && error.detail.reason === AuthSessionErrorReason.SessionNotReady) return true;
|
|
426
|
+
if (!error.http) return false;
|
|
427
|
+
const body = error.getSensitiveDetails().body;
|
|
428
|
+
const text = [error.http.apiErrorName, typeof body === "string" ? body : JSON.stringify(body ?? "")].join(" ").toLowerCase();
|
|
429
|
+
return text.includes("session is not ready") || text.includes("unauthorized_client");
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// ../../src/auth-session/session-error.ts
|
|
433
|
+
import {
|
|
434
|
+
TorrowSdkError as TorrowSdkError3,
|
|
435
|
+
TorrowSdkErrorCategory as TorrowSdkErrorCategory3,
|
|
436
|
+
TorrowSdkErrorDomain as TorrowSdkErrorDomain3
|
|
437
|
+
} from "@torrow/sdk-client";
|
|
438
|
+
var SessionFailureStage = /* @__PURE__ */ ((SessionFailureStage2) => {
|
|
439
|
+
SessionFailureStage2["SendCode"] = "SendCode";
|
|
440
|
+
SessionFailureStage2["CreateAuthSession"] = "CreateAuthSession";
|
|
441
|
+
SessionFailureStage2["OnSendCode"] = "OnSendCode";
|
|
442
|
+
SessionFailureStage2["GetCode"] = "GetCode";
|
|
443
|
+
SessionFailureStage2["OnLink"] = "OnLink";
|
|
444
|
+
SessionFailureStage2["PollTokens"] = "PollTokens";
|
|
445
|
+
SessionFailureStage2["ValidateResponse"] = "ValidateResponse";
|
|
446
|
+
SessionFailureStage2["ValidateInput"] = "ValidateInput";
|
|
447
|
+
SessionFailureStage2["ValidateState"] = "ValidateState";
|
|
448
|
+
SessionFailureStage2["LoadStorage"] = "LoadStorage";
|
|
449
|
+
SessionFailureStage2["InitializeDevice"] = "InitializeDevice";
|
|
450
|
+
SessionFailureStage2["BuildClient"] = "BuildClient";
|
|
451
|
+
SessionFailureStage2["IssueTokens"] = "IssueTokens";
|
|
452
|
+
SessionFailureStage2["ValidateTokens"] = "ValidateTokens";
|
|
453
|
+
SessionFailureStage2["ApplyTokens"] = "ApplyTokens";
|
|
454
|
+
SessionFailureStage2["RefreshTokens"] = "RefreshTokens";
|
|
455
|
+
SessionFailureStage2["RefreshFailureCallback"] = "RefreshFailureCallback";
|
|
456
|
+
SessionFailureStage2["ClearTokens"] = "ClearTokens";
|
|
457
|
+
SessionFailureStage2["SaveTokens"] = "SaveTokens";
|
|
458
|
+
SessionFailureStage2["ClearStorage"] = "ClearStorage";
|
|
459
|
+
SessionFailureStage2["OpenSession"] = "OpenSession";
|
|
460
|
+
SessionFailureStage2["SeedStorage"] = "SeedStorage";
|
|
461
|
+
SessionFailureStage2["Connect"] = "Connect";
|
|
462
|
+
return SessionFailureStage2;
|
|
463
|
+
})(SessionFailureStage || {});
|
|
464
|
+
function sessionFailure(error, context) {
|
|
465
|
+
const detail = { domain: TorrowSdkErrorDomain3.AuthSession, ...context.reason ? { reason: context.reason } : {} };
|
|
466
|
+
if (context.category !== void 0) {
|
|
467
|
+
return new TorrowSdkError3({
|
|
468
|
+
...context,
|
|
469
|
+
category: context.category,
|
|
470
|
+
detail,
|
|
471
|
+
sensitive: { cause: error }
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
if (error instanceof TorrowSdkError3) {
|
|
475
|
+
return error.withContext({
|
|
476
|
+
operation: context.operation,
|
|
477
|
+
failureAt: error.operation?.startsWith("TokenSession") ? error.failureAt ?? context.failureAt : context.failureAt,
|
|
478
|
+
effects: context.effects,
|
|
479
|
+
...context.reason ? { detail } : {}
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
return new TorrowSdkError3({
|
|
483
|
+
category: TorrowSdkErrorCategory3.Unknown,
|
|
484
|
+
operation: context.operation,
|
|
485
|
+
failureAt: context.failureAt,
|
|
486
|
+
effects: context.effects,
|
|
487
|
+
detail,
|
|
488
|
+
sensitive: { cause: error }
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
|
|
387
492
|
// ../../src/auth-session/fetch-with-401-retry.ts
|
|
388
493
|
function requestUrl(input) {
|
|
389
494
|
if (typeof input === "string") return input;
|
|
@@ -500,8 +605,8 @@ function assertTokenRespReadyForStorage(tokens, context) {
|
|
|
500
605
|
var DEFAULT_SKEW_MS = 3e4;
|
|
501
606
|
var SESSION_CLOSED_MESSAGE = "TokenSession: session closed (logout)";
|
|
502
607
|
function defaultOnRefreshFailure(err) {
|
|
503
|
-
if (!(err instanceof
|
|
504
|
-
const s = err.status;
|
|
608
|
+
if (!(err instanceof TorrowSdkError4) || !err.http) return "keep";
|
|
609
|
+
const s = err.http?.status;
|
|
505
610
|
if (s === 429 || s === 502 || s === 503 || s === 504) return "keep";
|
|
506
611
|
return "clear";
|
|
507
612
|
}
|
|
@@ -517,22 +622,48 @@ var TokenSession = class _TokenSession {
|
|
|
517
622
|
closed = false;
|
|
518
623
|
/** Set in refreshWithFailurePolicy catch; not cleared on success (see public refresh). */
|
|
519
624
|
lastRefreshError;
|
|
625
|
+
/** Открыть сессию; storage/refresh ошибки описывают только выполненную инициализацию. */
|
|
520
626
|
static async open(opts) {
|
|
627
|
+
return (await _TokenSession.openWithEffects(opts)).session;
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* @internal Инициализация с явными сведениями только текущего вызова для составного login.
|
|
631
|
+
* Не наблюдает fetch/storage и не хранит историю независимых операций.
|
|
632
|
+
*/
|
|
633
|
+
static async openWithEffects(opts) {
|
|
521
634
|
const session = new _TokenSession(opts);
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
635
|
+
const effects = [];
|
|
636
|
+
let failureAt = "LoadStorage" /* LoadStorage */;
|
|
637
|
+
try {
|
|
638
|
+
try {
|
|
639
|
+
session.tokenSnapshot = await opts.tokenStorage.load();
|
|
640
|
+
} catch (error) {
|
|
641
|
+
throw sessionFailure(error, {
|
|
642
|
+
operation: "TokenSession.open",
|
|
643
|
+
failureAt,
|
|
644
|
+
effects,
|
|
645
|
+
category: TorrowSdkErrorCategory4.StorageFailure
|
|
646
|
+
});
|
|
529
647
|
}
|
|
648
|
+
effects.push({ area: TorrowSdkEffectArea.SessionMemory, step: AuthSessionEffectStep.SetTokens, state: TorrowSdkEffectState.Applied });
|
|
649
|
+
failureAt = "InitializeDevice" /* InitializeDevice */;
|
|
650
|
+
if (!session.tokenSnapshot.deviceId?.trim()) {
|
|
651
|
+
const id = session.resolveDeviceInfo().deviceId?.trim();
|
|
652
|
+
if (id) {
|
|
653
|
+
session.tokenSnapshot = { ...session.tokenSnapshot, deviceId: id };
|
|
654
|
+
await session.persist(false, effects);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
failureAt = "BuildClient" /* BuildClient */;
|
|
658
|
+
session._client = session.buildClient();
|
|
659
|
+
if (!opts.skipEnsureFreshOnOpen) {
|
|
660
|
+
failureAt = "RefreshTokens" /* RefreshTokens */;
|
|
661
|
+
await session.runExclusive(() => session.ensureFreshImpl(effects));
|
|
662
|
+
}
|
|
663
|
+
return { session, effects: Object.freeze(effects.map((effect) => Object.freeze({ ...effect }))) };
|
|
664
|
+
} catch (error) {
|
|
665
|
+
throw sessionFailure(error, { operation: "TokenSession.open", failureAt, effects });
|
|
530
666
|
}
|
|
531
|
-
session._client = session.buildClient();
|
|
532
|
-
if (!opts.skipEnsureFreshOnOpen) {
|
|
533
|
-
await session.ensureFresh();
|
|
534
|
-
}
|
|
535
|
-
return session;
|
|
536
667
|
}
|
|
537
668
|
/** Persist, переданный в `TokenSession.open` / `TorrowAuth.*`. */
|
|
538
669
|
get tokenStorage() {
|
|
@@ -567,9 +698,18 @@ var TokenSession = class _TokenSession {
|
|
|
567
698
|
getDeviceInfo() {
|
|
568
699
|
return this.resolveDeviceInfo();
|
|
569
700
|
}
|
|
701
|
+
/** Получить access после проактивного refresh; closed возвращает undefined без записи. */
|
|
570
702
|
async getAccessToken() {
|
|
571
703
|
if (this.closed) return void 0;
|
|
572
|
-
|
|
704
|
+
try {
|
|
705
|
+
await this.ensureFresh();
|
|
706
|
+
} catch (error) {
|
|
707
|
+
throw sessionFailure(error, {
|
|
708
|
+
operation: "TokenSession.getAccessToken",
|
|
709
|
+
failureAt: "RefreshTokens" /* RefreshTokens */,
|
|
710
|
+
effects: error instanceof TorrowSdkError4 ? error.effects ?? [] : []
|
|
711
|
+
});
|
|
712
|
+
}
|
|
573
713
|
return this.tokenSnapshot.accessToken?.trim() || void 0;
|
|
574
714
|
}
|
|
575
715
|
/**
|
|
@@ -578,26 +718,53 @@ var TokenSession = class _TokenSession {
|
|
|
578
718
|
* (refresh-shaped); логин — `{ replace: true }`. После `logout` — новая сессия.
|
|
579
719
|
*/
|
|
580
720
|
async applyTokenResp(tokens, opts) {
|
|
581
|
-
|
|
721
|
+
const effects = [];
|
|
722
|
+
await this.runExclusive(async () => {
|
|
723
|
+
try {
|
|
724
|
+
await this.applyTokenRespUnlocked(tokens, opts, effects);
|
|
725
|
+
} catch (error) {
|
|
726
|
+
throw sessionFailure(error, { operation: "TokenSession.applyTokenResp", failureAt: "ApplyTokens" /* ApplyTokens */, effects });
|
|
727
|
+
}
|
|
728
|
+
});
|
|
582
729
|
}
|
|
730
|
+
/** Очистить память и сохранить snapshot; storage-сбой не отменяет очистку памяти. */
|
|
583
731
|
async clearTokens() {
|
|
584
|
-
|
|
732
|
+
const effects = [];
|
|
733
|
+
await this.runExclusive(async () => {
|
|
734
|
+
try {
|
|
735
|
+
await this.clearTokensUnlocked(effects);
|
|
736
|
+
} catch (error) {
|
|
737
|
+
throw sessionFailure(error, { operation: "TokenSession.clearTokens", failureAt: "ClearTokens" /* ClearTokens */, effects });
|
|
738
|
+
}
|
|
739
|
+
});
|
|
585
740
|
}
|
|
586
741
|
/**
|
|
587
742
|
* Revoke refresh (если есть) + очистка storage.
|
|
588
743
|
* Единственное публичное имя выхода из сессии.
|
|
589
744
|
*/
|
|
590
745
|
async logout() {
|
|
746
|
+
const effects = [];
|
|
591
747
|
await this.runExclusive(async () => {
|
|
592
748
|
this.closed = true;
|
|
749
|
+
effects.push({ area: TorrowSdkEffectArea.SessionMemory, step: AuthSessionEffectStep.CloseSession, state: TorrowSdkEffectState.Applied });
|
|
593
750
|
const refresh = this.tokenSnapshot.refreshToken?.trim();
|
|
594
751
|
if (refresh) {
|
|
752
|
+
let state = TorrowSdkEffectState.Applied;
|
|
753
|
+
let prepared = false;
|
|
595
754
|
try {
|
|
596
|
-
|
|
597
|
-
|
|
755
|
+
const identity = this.identityClient();
|
|
756
|
+
prepared = true;
|
|
757
|
+
await identity.auth.revoke({ token: refresh });
|
|
758
|
+
} catch (error) {
|
|
759
|
+
state = !prepared || error instanceof TorrowSdkError4 && error.request === TorrowSdkRequestState2.NotStarted ? TorrowSdkEffectState.NotApplied : TorrowSdkEffectState.Unknown;
|
|
598
760
|
}
|
|
761
|
+
effects.push({ area: TorrowSdkEffectArea.Remote, step: AuthSessionEffectStep.RevokeTokens, state });
|
|
762
|
+
}
|
|
763
|
+
try {
|
|
764
|
+
await this.clearTokensUnlocked(effects);
|
|
765
|
+
} catch (error) {
|
|
766
|
+
throw sessionFailure(error, { operation: "TokenSession.logout", failureAt: "ClearTokens" /* ClearTokens */, effects });
|
|
599
767
|
}
|
|
600
|
-
await this.clearTokensUnlocked();
|
|
601
768
|
});
|
|
602
769
|
}
|
|
603
770
|
/**
|
|
@@ -606,43 +773,79 @@ var TokenSession = class _TokenSession {
|
|
|
606
773
|
*
|
|
607
774
|
* Один вызов `POST /api/v1/auth/token` (`requestToken`) с `{ sessionId, code }`.
|
|
608
775
|
* При успехе (`TokenReady`) — persist; если сессия ещё не готова —
|
|
609
|
-
*
|
|
776
|
+
* `TorrowSdkError` с причиной `SessionNotReady` (поллинг для deep-link — внутри `TorrowAuth.loginWithLink`).
|
|
610
777
|
*
|
|
611
778
|
* HTTP вне mutex; persist сериализован через {@link applyTokenResp} (не оборачивать
|
|
612
779
|
* весь метод в `runExclusive` — вложенный mutex с публичным `applyTokenResp`).
|
|
613
780
|
*/
|
|
614
781
|
async requestTokenWithCode(args) {
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
const code = args.code.trim();
|
|
618
|
-
if (!sessionId || !code) {
|
|
619
|
-
throw new Error("TokenSession.requestTokenWithCode: sessionId and code are required");
|
|
620
|
-
}
|
|
621
|
-
const scopes = args.scopes && args.scopes.length > 0 ? args.scopes : [...DefaultAuthScopes2];
|
|
622
|
-
const deviceInfo = this.resolveDeviceInfo();
|
|
623
|
-
let resp;
|
|
782
|
+
const effects = [];
|
|
783
|
+
let failureAt = "ValidateInput" /* ValidateInput */;
|
|
624
784
|
try {
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
785
|
+
this.assertNotClosedForWrite();
|
|
786
|
+
const sessionId = args.sessionId.trim();
|
|
787
|
+
const code = args.code.trim();
|
|
788
|
+
if (!sessionId || !code) {
|
|
789
|
+
throw sessionFailure(void 0, {
|
|
790
|
+
operation: "TokenSession.requestTokenWithCode",
|
|
791
|
+
failureAt,
|
|
792
|
+
effects,
|
|
793
|
+
category: TorrowSdkErrorCategory4.InvalidInput
|
|
794
|
+
});
|
|
635
795
|
}
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
796
|
+
const scopes = args.scopes && args.scopes.length > 0 ? args.scopes : [...DefaultAuthScopes2];
|
|
797
|
+
const deviceInfo = this.resolveDeviceInfo();
|
|
798
|
+
failureAt = "IssueTokens" /* IssueTokens */;
|
|
799
|
+
const identity = this.identityClient();
|
|
800
|
+
let resp;
|
|
801
|
+
try {
|
|
802
|
+
resp = normalizeTokenResp(await requestAuthToken(identity, { sessionId, code, scopes, deviceInfo }, args.signal));
|
|
803
|
+
} catch (error) {
|
|
804
|
+
effects.push({
|
|
805
|
+
area: TorrowSdkEffectArea.Remote,
|
|
806
|
+
step: AuthSessionEffectStep.IssueTokens,
|
|
807
|
+
state: error instanceof TorrowSdkError4 && error.request === TorrowSdkRequestState2.NotStarted ? TorrowSdkEffectState.NotApplied : TorrowSdkEffectState.Unknown
|
|
808
|
+
});
|
|
809
|
+
if (isAuthSessionNotReadyYet(error)) {
|
|
810
|
+
throw sessionFailure(error, {
|
|
811
|
+
operation: "TokenSession.requestTokenWithCode",
|
|
812
|
+
failureAt,
|
|
813
|
+
effects,
|
|
814
|
+
reason: AuthSessionErrorReason2.SessionNotReady
|
|
815
|
+
});
|
|
816
|
+
}
|
|
817
|
+
throw error;
|
|
818
|
+
}
|
|
819
|
+
failureAt = "ValidateTokens" /* ValidateTokens */;
|
|
820
|
+
if (tokenTransition(resp.status, true) !== TokenStatusValues3.TokenReady) {
|
|
821
|
+
effects.push({ area: TorrowSdkEffectArea.Remote, step: AuthSessionEffectStep.IssueTokens, state: TorrowSdkEffectState.Unknown });
|
|
822
|
+
throw sessionFailure(void 0, {
|
|
823
|
+
operation: "TokenSession.requestTokenWithCode",
|
|
824
|
+
failureAt,
|
|
825
|
+
effects,
|
|
826
|
+
category: TorrowSdkErrorCategory4.InvalidState,
|
|
827
|
+
reason: AuthSessionErrorReason2.SessionNotReady
|
|
828
|
+
});
|
|
829
|
+
}
|
|
830
|
+
let ready;
|
|
831
|
+
try {
|
|
832
|
+
ready = assertTokenRespReadyForStorage(resp, "TokenSession.requestTokenWithCode");
|
|
833
|
+
} catch (error) {
|
|
834
|
+
effects.push({ area: TorrowSdkEffectArea.Remote, step: AuthSessionEffectStep.IssueTokens, state: TorrowSdkEffectState.Unknown });
|
|
835
|
+
throw sessionFailure(error, {
|
|
836
|
+
operation: "TokenSession.requestTokenWithCode",
|
|
837
|
+
failureAt,
|
|
838
|
+
effects,
|
|
839
|
+
category: TorrowSdkErrorCategory4.InvalidResponse
|
|
840
|
+
});
|
|
841
|
+
}
|
|
842
|
+
effects.push({ area: TorrowSdkEffectArea.Remote, step: AuthSessionEffectStep.IssueTokens, state: TorrowSdkEffectState.Applied });
|
|
843
|
+
failureAt = "ApplyTokens" /* ApplyTokens */;
|
|
844
|
+
await this.runExclusive(() => this.applyTokenRespUnlocked(ready, { replace: true }, effects));
|
|
845
|
+
return ready;
|
|
846
|
+
} catch (error) {
|
|
847
|
+
throw sessionFailure(error, { operation: "TokenSession.requestTokenWithCode", failureAt, effects });
|
|
642
848
|
}
|
|
643
|
-
const ready = assertTokenRespReadyForStorage(resp, "TokenSession.requestTokenWithCode");
|
|
644
|
-
await this.applyTokenResp(ready, { replace: true });
|
|
645
|
-
return ready;
|
|
646
849
|
}
|
|
647
850
|
/**
|
|
648
851
|
* Явный refresh (single-flight через общий mutex сессии).
|
|
@@ -651,86 +854,145 @@ var TokenSession = class _TokenSession {
|
|
|
651
854
|
* не используй return value; смотри `session.status` / `getTokenSnapshot()`.
|
|
652
855
|
*/
|
|
653
856
|
async refresh() {
|
|
857
|
+
const effects = [];
|
|
654
858
|
return this.runExclusive(async () => {
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
859
|
+
try {
|
|
860
|
+
this.assertNotClosedForWrite();
|
|
861
|
+
if (!this.tokenSnapshot.refreshToken?.trim()) {
|
|
862
|
+
throw sessionFailure(void 0, {
|
|
863
|
+
operation: "TokenSession.refresh",
|
|
864
|
+
failureAt: "ValidateState" /* ValidateState */,
|
|
865
|
+
effects,
|
|
866
|
+
category: TorrowSdkErrorCategory4.InvalidState
|
|
867
|
+
});
|
|
868
|
+
}
|
|
869
|
+
const result = await this.refreshWithFailurePolicy(effects);
|
|
870
|
+
if (result.outcome === "refreshed") return result.resp;
|
|
871
|
+
throw result.error ?? this.lastRefreshError;
|
|
872
|
+
} catch (error) {
|
|
873
|
+
throw sessionFailure(error, { operation: "TokenSession.refresh", failureAt: "RefreshTokens" /* RefreshTokens */, effects });
|
|
665
874
|
}
|
|
666
|
-
throw new Error("TokenSession.refresh: refresh failed");
|
|
667
875
|
});
|
|
668
876
|
}
|
|
877
|
+
/** Обновить просроченный access; политика keep/clear сохраняет прежнее resolve-поведение. */
|
|
669
878
|
async ensureFresh() {
|
|
670
|
-
|
|
879
|
+
const effects = [];
|
|
880
|
+
await this.runExclusive(async () => {
|
|
881
|
+
try {
|
|
882
|
+
await this.ensureFreshImpl(effects);
|
|
883
|
+
} catch (error) {
|
|
884
|
+
throw sessionFailure(error, { operation: "TokenSession.ensureFresh", failureAt: "RefreshTokens" /* RefreshTokens */, effects });
|
|
885
|
+
}
|
|
886
|
+
});
|
|
671
887
|
}
|
|
672
888
|
assertNotClosedForWrite() {
|
|
673
889
|
if (this.closed) {
|
|
674
|
-
throw new Error(SESSION_CLOSED_MESSAGE)
|
|
890
|
+
throw sessionFailure(new Error(SESSION_CLOSED_MESSAGE), {
|
|
891
|
+
operation: "TokenSession",
|
|
892
|
+
failureAt: "ValidateState" /* ValidateState */,
|
|
893
|
+
effects: [],
|
|
894
|
+
category: TorrowSdkErrorCategory4.InvalidState
|
|
895
|
+
});
|
|
675
896
|
}
|
|
676
897
|
}
|
|
677
898
|
/** Caller must hold `runExclusive`. Do not call public `applyTokenResp` from here. */
|
|
678
|
-
async applyTokenRespUnlocked(tokens, opts) {
|
|
899
|
+
async applyTokenRespUnlocked(tokens, opts, effects = []) {
|
|
679
900
|
this.assertNotClosedForWrite();
|
|
680
901
|
const normalized = normalizeTokenResp(tokens);
|
|
681
|
-
|
|
902
|
+
try {
|
|
903
|
+
assertTokenRespReadyForStorage(normalized, "TokenSession.applyTokenResp");
|
|
904
|
+
} catch (error) {
|
|
905
|
+
throw sessionFailure(error, {
|
|
906
|
+
operation: "TokenSession",
|
|
907
|
+
failureAt: "ValidateTokens" /* ValidateTokens */,
|
|
908
|
+
effects,
|
|
909
|
+
category: TorrowSdkErrorCategory4.InvalidResponse
|
|
910
|
+
});
|
|
911
|
+
}
|
|
682
912
|
this.tokenSnapshot = mergeAuthTokens(this.tokenSnapshot, normalized, {
|
|
683
913
|
...opts,
|
|
684
914
|
// Host TTL survives refresh without expiration only while still fresh (incl. skew); else cleared → opaque until 401.
|
|
685
915
|
keepPrevTtlIfExpiresAfterMs: Date.now() + (this.opts.accessTokenSkewMs ?? DEFAULT_SKEW_MS)
|
|
686
916
|
});
|
|
687
|
-
|
|
917
|
+
effects.push({ area: TorrowSdkEffectArea.SessionMemory, step: AuthSessionEffectStep.SetTokens, state: TorrowSdkEffectState.Applied });
|
|
918
|
+
await this.persist(true, effects);
|
|
688
919
|
}
|
|
689
|
-
async clearTokensUnlocked() {
|
|
920
|
+
async clearTokensUnlocked(effects = []) {
|
|
690
921
|
this.tokenSnapshot = {
|
|
691
922
|
...this.tokenSnapshot,
|
|
692
923
|
accessToken: void 0,
|
|
693
924
|
refreshToken: void 0,
|
|
694
925
|
accessExpiresAtMs: void 0
|
|
695
926
|
};
|
|
696
|
-
|
|
927
|
+
effects.push({ area: TorrowSdkEffectArea.SessionMemory, step: AuthSessionEffectStep.ClearTokens, state: TorrowSdkEffectState.Applied });
|
|
928
|
+
await this.persist(true, effects, true);
|
|
697
929
|
}
|
|
698
930
|
/** Caller must hold `runExclusive`. `refreshWithFailurePolicy` skips when no refreshToken; guard here is intentional defense-in-depth. */
|
|
699
|
-
async refreshUnlocked() {
|
|
931
|
+
async refreshUnlocked(effects) {
|
|
700
932
|
const refresh = this.tokenSnapshot.refreshToken?.trim();
|
|
701
933
|
if (!refresh) {
|
|
702
|
-
throw
|
|
934
|
+
throw sessionFailure(void 0, {
|
|
935
|
+
operation: "TokenSession",
|
|
936
|
+
failureAt: "ValidateState" /* ValidateState */,
|
|
937
|
+
effects,
|
|
938
|
+
category: TorrowSdkErrorCategory4.InvalidState
|
|
939
|
+
});
|
|
703
940
|
}
|
|
704
941
|
const deviceInfo = this.resolveDeviceInfo();
|
|
705
|
-
const
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
942
|
+
const identity = this.identityClient();
|
|
943
|
+
let resp;
|
|
944
|
+
try {
|
|
945
|
+
resp = await identity.auth.refresh({ refreshToken: refresh, deviceInfo });
|
|
946
|
+
} catch (error) {
|
|
947
|
+
effects.push({
|
|
948
|
+
area: TorrowSdkEffectArea.Remote,
|
|
949
|
+
step: AuthSessionEffectStep.RefreshTokens,
|
|
950
|
+
state: error instanceof TorrowSdkError4 && error.request === TorrowSdkRequestState2.NotStarted ? TorrowSdkEffectState.NotApplied : TorrowSdkEffectState.Unknown
|
|
951
|
+
});
|
|
952
|
+
throw error;
|
|
716
953
|
}
|
|
717
|
-
const
|
|
718
|
-
|
|
719
|
-
|
|
954
|
+
const normalized = normalizeTokenResp(resp);
|
|
955
|
+
try {
|
|
956
|
+
assertTokenRespReadyForStorage(normalized, "TokenSession.refresh");
|
|
957
|
+
} catch (error) {
|
|
958
|
+
effects.push({ area: TorrowSdkEffectArea.Remote, step: AuthSessionEffectStep.RefreshTokens, state: TorrowSdkEffectState.Unknown });
|
|
959
|
+
throw sessionFailure(error, {
|
|
960
|
+
operation: "TokenSession",
|
|
961
|
+
failureAt: "ValidateTokens" /* ValidateTokens */,
|
|
962
|
+
effects,
|
|
963
|
+
category: TorrowSdkErrorCategory4.InvalidResponse
|
|
964
|
+
});
|
|
720
965
|
}
|
|
966
|
+
effects.push({ area: TorrowSdkEffectArea.Remote, step: AuthSessionEffectStep.RefreshTokens, state: TorrowSdkEffectState.Applied });
|
|
967
|
+
await this.applyTokenRespUnlocked(resp, void 0, effects);
|
|
968
|
+
return resp;
|
|
969
|
+
}
|
|
970
|
+
/** Caller must hold `runExclusive`; сведения принадлежат только текущей операции. */
|
|
971
|
+
async refreshWithFailurePolicy(effects = []) {
|
|
972
|
+
if (this.closed || !this.tokenSnapshot.refreshToken?.trim()) return { outcome: "unchanged" };
|
|
721
973
|
try {
|
|
722
|
-
const resp = await this.refreshUnlocked();
|
|
974
|
+
const resp = await this.refreshUnlocked(effects);
|
|
723
975
|
this.lastRefreshError = void 0;
|
|
724
976
|
return { outcome: "refreshed", resp };
|
|
725
|
-
} catch (
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
977
|
+
} catch (cause) {
|
|
978
|
+
const error = sessionFailure(cause, { operation: "TokenSession", failureAt: "RefreshTokens" /* RefreshTokens */, effects });
|
|
979
|
+
this.lastRefreshError = error.withContext({ effects: void 0, operation: void 0, failureAt: void 0 });
|
|
980
|
+
let action;
|
|
981
|
+
try {
|
|
982
|
+
action = await this.opts.onRefreshFailure?.(error) ?? defaultOnRefreshFailure(error);
|
|
983
|
+
} catch (callbackError) {
|
|
984
|
+
throw sessionFailure(callbackError, {
|
|
985
|
+
operation: "TokenSession",
|
|
986
|
+
failureAt: "RefreshFailureCallback" /* RefreshFailureCallback */,
|
|
987
|
+
effects,
|
|
988
|
+
category: TorrowSdkErrorCategory4.Unknown,
|
|
989
|
+
reason: AuthSessionErrorReason2.CallbackFailed
|
|
990
|
+
});
|
|
731
991
|
}
|
|
732
|
-
|
|
733
|
-
return { outcome: "
|
|
992
|
+
if (action === "throw") throw error;
|
|
993
|
+
if (action === "keep") return { outcome: "unchanged", error };
|
|
994
|
+
await this.clearTokensUnlocked(effects);
|
|
995
|
+
return { outcome: "cleared", error: error.withContext({ effects }) };
|
|
734
996
|
}
|
|
735
997
|
}
|
|
736
998
|
async runExclusive(fn) {
|
|
@@ -746,7 +1008,7 @@ var TokenSession = class _TokenSession {
|
|
|
746
1008
|
release();
|
|
747
1009
|
}
|
|
748
1010
|
}
|
|
749
|
-
async ensureFreshImpl() {
|
|
1011
|
+
async ensureFreshImpl(effects = []) {
|
|
750
1012
|
if (this.closed) return;
|
|
751
1013
|
const skew = this.opts.accessTokenSkewMs ?? DEFAULT_SKEW_MS;
|
|
752
1014
|
const access = this.tokenSnapshot.accessToken?.trim() ?? "";
|
|
@@ -756,7 +1018,7 @@ var TokenSession = class _TokenSession {
|
|
|
756
1018
|
}
|
|
757
1019
|
const refresh = this.tokenSnapshot.refreshToken?.trim() ?? "";
|
|
758
1020
|
if (refresh) {
|
|
759
|
-
await this.refreshWithFailurePolicy();
|
|
1021
|
+
await this.refreshWithFailurePolicy(effects);
|
|
760
1022
|
}
|
|
761
1023
|
}
|
|
762
1024
|
isAccessStillFresh(access, expiresAtMs, accessTokenSkewMs) {
|
|
@@ -821,7 +1083,7 @@ var TokenSession = class _TokenSession {
|
|
|
821
1083
|
refresh: async () => {
|
|
822
1084
|
await this.runExclusive(async () => {
|
|
823
1085
|
if (this.closed) {
|
|
824
|
-
|
|
1086
|
+
this.assertNotClosedForWrite();
|
|
825
1087
|
}
|
|
826
1088
|
const result = await this.refreshWithFailurePolicy();
|
|
827
1089
|
if (result.outcome !== "refreshed") {
|
|
@@ -842,19 +1104,47 @@ var TokenSession = class _TokenSession {
|
|
|
842
1104
|
getAccessToken: async () => this.getAccessToken()
|
|
843
1105
|
});
|
|
844
1106
|
}
|
|
845
|
-
async persist(emitStatus) {
|
|
846
|
-
|
|
1107
|
+
async persist(emitStatus, effects = [], clear = false) {
|
|
1108
|
+
const effect = {
|
|
1109
|
+
area: TorrowSdkEffectArea.Storage,
|
|
1110
|
+
step: clear ? AuthSessionEffectStep.ClearStorage : AuthSessionEffectStep.SaveTokens,
|
|
1111
|
+
state: TorrowSdkEffectState.Unknown
|
|
1112
|
+
};
|
|
1113
|
+
effects.push(effect);
|
|
1114
|
+
try {
|
|
1115
|
+
await this.opts.tokenStorage.save(this.tokenSnapshot);
|
|
1116
|
+
} catch (error) {
|
|
1117
|
+
throw sessionFailure(error, {
|
|
1118
|
+
operation: "TokenSession",
|
|
1119
|
+
failureAt: clear ? "ClearStorage" /* ClearStorage */ : "SaveTokens" /* SaveTokens */,
|
|
1120
|
+
effects,
|
|
1121
|
+
category: TorrowSdkErrorCategory4.StorageFailure
|
|
1122
|
+
});
|
|
1123
|
+
}
|
|
1124
|
+
effects[effects.length - 1] = { ...effect, state: TorrowSdkEffectState.Applied };
|
|
847
1125
|
if (emitStatus) this.emitStatusChange();
|
|
848
1126
|
}
|
|
849
1127
|
};
|
|
850
1128
|
|
|
851
1129
|
// ../../src/auth-session/torrow-auth.ts
|
|
852
1130
|
import {
|
|
853
|
-
TorrowClient as TorrowClient3
|
|
1131
|
+
TorrowClient as TorrowClient3,
|
|
1132
|
+
TorrowSdkError as TorrowSdkError8,
|
|
1133
|
+
TorrowSdkErrorCategory as TorrowSdkErrorCategory8,
|
|
1134
|
+
TorrowSdkEffectArea as TorrowSdkEffectArea4,
|
|
1135
|
+
TorrowSdkEffectState as TorrowSdkEffectState4,
|
|
1136
|
+
AuthSessionEffectStep as AuthSessionEffectStep4
|
|
854
1137
|
} from "@torrow/sdk-client";
|
|
855
1138
|
|
|
856
1139
|
// ../../src/auth-session/link-login.ts
|
|
857
1140
|
import {
|
|
1141
|
+
TorrowSdkError as TorrowSdkError5,
|
|
1142
|
+
TorrowSdkErrorCategory as TorrowSdkErrorCategory5,
|
|
1143
|
+
TorrowSdkRequestState as TorrowSdkRequestState3,
|
|
1144
|
+
TorrowSdkEffectArea as TorrowSdkEffectArea2,
|
|
1145
|
+
TorrowSdkEffectState as TorrowSdkEffectState2,
|
|
1146
|
+
AuthSessionEffectStep as AuthSessionEffectStep2,
|
|
1147
|
+
AuthSessionErrorReason as AuthSessionErrorReason3,
|
|
858
1148
|
AccountTypeValues,
|
|
859
1149
|
DefaultAuthScopes as DefaultAuthScopes3
|
|
860
1150
|
} from "@torrow/sdk-client";
|
|
@@ -885,62 +1175,96 @@ var DEFAULT_POLL_TIMEOUT_SEC = 180;
|
|
|
885
1175
|
var DEFAULT_LANGUAGE = "ru";
|
|
886
1176
|
var DEFAULT_REGION = 70;
|
|
887
1177
|
async function runLinkLogin(session, options) {
|
|
888
|
-
const
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
throw new Error(
|
|
904
|
-
"runLinkLogin: createSession response incomplete (need sessionId, code, authUrl)"
|
|
905
|
-
);
|
|
906
|
-
}
|
|
907
|
-
await options.onLink(authUrl);
|
|
908
|
-
const scopes = options.scopes && options.scopes.length > 0 ? options.scopes : [...DefaultAuthScopes3];
|
|
909
|
-
const intervalMs = typeof options.pollIntervalMs === "number" && options.pollIntervalMs > 0 ? options.pollIntervalMs : DEFAULT_POLL_INTERVAL_MS;
|
|
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;
|
|
911
|
-
const deadline = Date.now() + timeoutSec * 1e3;
|
|
912
|
-
for (; ; ) {
|
|
913
|
-
options.signal?.throwIfAborted?.();
|
|
1178
|
+
const effects = [];
|
|
1179
|
+
let failureAt = "CreateAuthSession" /* CreateAuthSession */;
|
|
1180
|
+
try {
|
|
1181
|
+
throwIfOtpAborted(options.signal);
|
|
1182
|
+
const client = session.identityClient();
|
|
1183
|
+
const accountType = options.accountType ?? AccountTypeValues.Telegram;
|
|
1184
|
+
const body = {
|
|
1185
|
+
accountType,
|
|
1186
|
+
language: options.language?.trim() || DEFAULT_LANGUAGE,
|
|
1187
|
+
regionStorageId: typeof options.regionStorageId === "number" && Number.isFinite(options.regionStorageId) ? options.regionStorageId : DEFAULT_REGION,
|
|
1188
|
+
...options.inviteSessionId?.trim() ? { inviteSessionId: options.inviteSessionId.trim() } : {},
|
|
1189
|
+
...options.captcha?.trim() ? { captcha: options.captcha.trim() } : {},
|
|
1190
|
+
...options.deviceInfo.deviceId?.trim() ? { deviceId: options.deviceInfo.deviceId.trim() } : {}
|
|
1191
|
+
};
|
|
1192
|
+
let createResp;
|
|
914
1193
|
try {
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
1194
|
+
createResp = await client.auth.createSession(body, { signal: options.signal });
|
|
1195
|
+
} catch (error) {
|
|
1196
|
+
effects.push({
|
|
1197
|
+
area: TorrowSdkEffectArea2.Remote,
|
|
1198
|
+
step: AuthSessionEffectStep2.CreateAuthSession,
|
|
1199
|
+
state: error instanceof TorrowSdkError5 && error.request === TorrowSdkRequestState3.NotStarted ? TorrowSdkEffectState2.NotApplied : TorrowSdkEffectState2.Unknown
|
|
920
1200
|
});
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
1201
|
+
throw error;
|
|
1202
|
+
}
|
|
1203
|
+
effects.push({ area: TorrowSdkEffectArea2.Remote, step: AuthSessionEffectStep2.CreateAuthSession, state: TorrowSdkEffectState2.Unknown });
|
|
1204
|
+
failureAt = "ValidateResponse" /* ValidateResponse */;
|
|
1205
|
+
const sessionId = createResp.sessionId?.trim();
|
|
1206
|
+
const code = createResp.code?.trim();
|
|
1207
|
+
const authUrl = createResp.authUrl?.trim();
|
|
1208
|
+
if (!sessionId || !code || !authUrl) {
|
|
1209
|
+
throw sessionFailure(void 0, { operation: "TorrowAuth.loginWithLink", failureAt, effects: [], category: TorrowSdkErrorCategory5.InvalidResponse });
|
|
1210
|
+
}
|
|
1211
|
+
effects[effects.length - 1] = { area: TorrowSdkEffectArea2.Remote, step: AuthSessionEffectStep2.CreateAuthSession, state: TorrowSdkEffectState2.Applied };
|
|
1212
|
+
failureAt = "OnLink" /* OnLink */;
|
|
1213
|
+
await options.onLink(authUrl);
|
|
1214
|
+
const scopes = options.scopes && options.scopes.length > 0 ? options.scopes : [...DefaultAuthScopes3];
|
|
1215
|
+
const intervalMs = typeof options.pollIntervalMs === "number" && options.pollIntervalMs > 0 ? options.pollIntervalMs : DEFAULT_POLL_INTERVAL_MS;
|
|
1216
|
+
const timeoutSec = typeof options.pollTimeoutSec === "number" && options.pollTimeoutSec > 0 ? options.pollTimeoutSec : typeof createResp.sessionLifeTimeInSeconds === "number" && createResp.sessionLifeTimeInSeconds > 0 ? createResp.sessionLifeTimeInSeconds : DEFAULT_POLL_TIMEOUT_SEC;
|
|
1217
|
+
const deadline = Date.now() + timeoutSec * 1e3;
|
|
1218
|
+
failureAt = "PollTokens" /* PollTokens */;
|
|
1219
|
+
for (; ; ) {
|
|
1220
|
+
throwIfOtpAborted(options.signal);
|
|
1221
|
+
try {
|
|
1222
|
+
return await session.requestTokenWithCode({
|
|
1223
|
+
sessionId,
|
|
1224
|
+
code,
|
|
1225
|
+
scopes,
|
|
1226
|
+
signal: options.signal
|
|
1227
|
+
});
|
|
1228
|
+
} catch (e) {
|
|
1229
|
+
if (!isAuthSessionNotReadyYet(e)) throw e;
|
|
1230
|
+
if (Date.now() >= deadline) {
|
|
1231
|
+
throw sessionFailure(e, { operation: "TorrowAuth.loginWithLink", failureAt, effects: [], category: TorrowSdkErrorCategory5.InvalidState, reason: AuthSessionErrorReason3.SessionNotReady });
|
|
1232
|
+
}
|
|
1233
|
+
await sleepMs(intervalMs, options.signal);
|
|
927
1234
|
}
|
|
928
|
-
await sleepMs(intervalMs, options.signal);
|
|
929
1235
|
}
|
|
1236
|
+
} catch (cause) {
|
|
1237
|
+
const error = options.signal?.aborted && !(cause instanceof TorrowSdkError5) ? new TorrowSdkError5({ category: TorrowSdkErrorCategory5.Cancelled, sensitive: { cause } }) : cause;
|
|
1238
|
+
const callback = failureAt === "OnLink" /* OnLink */;
|
|
1239
|
+
throw sessionFailure(error, {
|
|
1240
|
+
operation: "TorrowAuth.loginWithLink",
|
|
1241
|
+
failureAt,
|
|
1242
|
+
effects: [...effects, ...failureAt === "PollTokens" /* PollTokens */ && error instanceof TorrowSdkError5 ? error.effects ?? [] : []],
|
|
1243
|
+
...callback ? { category: TorrowSdkErrorCategory5.Unknown, reason: AuthSessionErrorReason3.CallbackFailed } : {}
|
|
1244
|
+
});
|
|
930
1245
|
}
|
|
931
1246
|
}
|
|
932
1247
|
|
|
933
1248
|
// ../../src/auth-session/otp-login.ts
|
|
934
1249
|
import {
|
|
1250
|
+
AuthSessionEffectStep as AuthSessionEffectStep3,
|
|
1251
|
+
AuthSessionErrorReason as AuthSessionErrorReason4,
|
|
1252
|
+
TorrowSdkEffectArea as TorrowSdkEffectArea3,
|
|
1253
|
+
TorrowSdkEffectState as TorrowSdkEffectState3,
|
|
1254
|
+
TorrowSdkErrorCategory as TorrowSdkErrorCategory7,
|
|
1255
|
+
TorrowSdkRequestState as TorrowSdkRequestState4,
|
|
935
1256
|
DefaultAuthScopes as DefaultAuthScopes5,
|
|
936
1257
|
SendCodeAccountTypeValues,
|
|
937
|
-
|
|
1258
|
+
TorrowSdkError as TorrowSdkError7,
|
|
938
1259
|
VerificationMethodValues as VerificationMethodValues2,
|
|
939
1260
|
TokenStatusValues as TokenStatusValues5
|
|
940
1261
|
} from "@torrow/sdk-client";
|
|
941
1262
|
|
|
942
1263
|
// ../../src/auth-session/mobile-id.ts
|
|
943
1264
|
import {
|
|
1265
|
+
TorrowSdkError as TorrowSdkError6,
|
|
1266
|
+
TorrowSdkErrorCategory as TorrowSdkErrorCategory6,
|
|
1267
|
+
TorrowSdkErrorDomain as TorrowSdkErrorDomain4,
|
|
944
1268
|
DefaultAuthScopes as DefaultAuthScopes4,
|
|
945
1269
|
TokenStatusValues as TokenStatusValues4
|
|
946
1270
|
} from "@torrow/sdk-client";
|
|
@@ -952,7 +1276,7 @@ function scopesOrDefault(scopes) {
|
|
|
952
1276
|
async function pollMobileIdOnce(client, args) {
|
|
953
1277
|
const sessionId = args.sessionId.trim();
|
|
954
1278
|
if (!sessionId) {
|
|
955
|
-
throw
|
|
1279
|
+
throw otpFailure(TorrowSdkErrorCategory6.InvalidInput);
|
|
956
1280
|
}
|
|
957
1281
|
const body = {
|
|
958
1282
|
sessionId,
|
|
@@ -961,21 +1285,24 @@ async function pollMobileIdOnce(client, args) {
|
|
|
961
1285
|
scopes: scopesOrDefault(args.scopes),
|
|
962
1286
|
deviceInfo: args.deviceInfo
|
|
963
1287
|
};
|
|
964
|
-
|
|
1288
|
+
const raw = await requestAuthToken(client, body, args.signal);
|
|
1289
|
+
try {
|
|
1290
|
+
return normalizeTokenResp(raw);
|
|
1291
|
+
} catch (cause) {
|
|
1292
|
+
throw otpFailure(TorrowSdkErrorCategory6.InvalidResponse, void 0, cause);
|
|
1293
|
+
}
|
|
965
1294
|
}
|
|
966
1295
|
async function pollMobileIdUntilReady(client, options) {
|
|
967
1296
|
const sessionId = options.sessionId.trim();
|
|
968
1297
|
if (!sessionId) {
|
|
969
|
-
throw
|
|
1298
|
+
throw otpFailure(TorrowSdkErrorCategory6.InvalidInput);
|
|
970
1299
|
}
|
|
971
1300
|
const intervalSec = typeof options.pollIntervalSeconds === "number" && options.pollIntervalSeconds > 0 ? options.pollIntervalSeconds : DEFAULT_POLL_INTERVAL_SEC;
|
|
972
1301
|
const maxWaitSec = typeof options.maxWaitSeconds === "number" && options.maxWaitSeconds > 0 ? options.maxWaitSeconds : DEFAULT_MAX_WAIT_SEC;
|
|
973
1302
|
const deadline = Date.now() + maxWaitSec * 1e3;
|
|
974
1303
|
const scopes = scopesOrDefault(options.scopes);
|
|
975
1304
|
for (; ; ) {
|
|
976
|
-
|
|
977
|
-
throw options.signal.reason ?? new Error("Aborted");
|
|
978
|
-
}
|
|
1305
|
+
throwIfOtpAborted(options.signal);
|
|
979
1306
|
const resp = await pollMobileIdOnce(client, {
|
|
980
1307
|
sessionId,
|
|
981
1308
|
deviceInfo: options.deviceInfo,
|
|
@@ -984,25 +1311,21 @@ async function pollMobileIdUntilReady(client, options) {
|
|
|
984
1311
|
});
|
|
985
1312
|
const status = resp.status;
|
|
986
1313
|
if (tokenTransition(status, true) === TokenStatusValues4.TokenReady) {
|
|
987
|
-
return
|
|
1314
|
+
return readyTokens(resp);
|
|
988
1315
|
}
|
|
989
1316
|
if (status === TokenStatusValues4.Failed) {
|
|
990
|
-
throw
|
|
991
|
-
"pollMobileIdUntilReady: TokenResp.status=Failed \u2014 \u043D\u0443\u0436\u0435\u043D \u043D\u043E\u0432\u044B\u0439 sendCode."
|
|
992
|
-
);
|
|
1317
|
+
throw mobileInvalidState();
|
|
993
1318
|
}
|
|
994
1319
|
if (status === TokenStatusValues4.AwaitingSmsCode) {
|
|
995
1320
|
const obtain = options.obtainSmsCode;
|
|
996
1321
|
if (!obtain) {
|
|
997
|
-
throw
|
|
998
|
-
"pollMobileIdUntilReady: AwaitingSmsCode, \u043D\u043E obtainSmsCode \u043D\u0435 \u0437\u0430\u0434\u0430\u043D."
|
|
999
|
-
);
|
|
1322
|
+
throw otpFailure(TorrowSdkErrorCategory6.InvalidInput);
|
|
1000
1323
|
}
|
|
1001
1324
|
const code = (await obtain({
|
|
1002
1325
|
codeLength: options.smsCodeLength
|
|
1003
1326
|
})).trim();
|
|
1004
1327
|
if (!code) {
|
|
1005
|
-
throw
|
|
1328
|
+
throw otpFailure(TorrowSdkErrorCategory6.InvalidInput);
|
|
1006
1329
|
}
|
|
1007
1330
|
const afterSms = await pollMobileIdOnce(client, {
|
|
1008
1331
|
sessionId,
|
|
@@ -1012,12 +1335,10 @@ async function pollMobileIdUntilReady(client, options) {
|
|
|
1012
1335
|
signal: options.signal
|
|
1013
1336
|
});
|
|
1014
1337
|
if (tokenTransition(afterSms.status, true) === TokenStatusValues4.TokenReady) {
|
|
1015
|
-
return
|
|
1338
|
+
return readyTokens(afterSms);
|
|
1016
1339
|
}
|
|
1017
1340
|
if (afterSms.status === TokenStatusValues4.Failed) {
|
|
1018
|
-
throw
|
|
1019
|
-
"pollMobileIdUntilReady: TokenResp.status=Failed \u043F\u043E\u0441\u043B\u0435 SMS \u2014 \u043D\u0443\u0436\u0435\u043D \u043D\u043E\u0432\u044B\u0439 sendCode."
|
|
1020
|
-
);
|
|
1341
|
+
throw mobileInvalidState();
|
|
1021
1342
|
}
|
|
1022
1343
|
if (afterSms.status === TokenStatusValues4.Pending) {
|
|
1023
1344
|
continue;
|
|
@@ -1025,86 +1346,33 @@ async function pollMobileIdUntilReady(client, options) {
|
|
|
1025
1346
|
if (afterSms.status === TokenStatusValues4.AwaitingSmsCode) {
|
|
1026
1347
|
continue;
|
|
1027
1348
|
}
|
|
1028
|
-
throw
|
|
1029
|
-
`pollMobileIdUntilReady: \u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u044B\u0439 TokenResp.status=${String(afterSms.status)} \u043F\u043E\u0441\u043B\u0435 SMS.`
|
|
1030
|
-
);
|
|
1349
|
+
throw otpFailure(TorrowSdkErrorCategory6.InvalidResponse);
|
|
1031
1350
|
}
|
|
1032
1351
|
if (status !== TokenStatusValues4.Pending) {
|
|
1033
|
-
throw
|
|
1034
|
-
`pollMobileIdUntilReady: \u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u044B\u0439 TokenResp.status=${String(status)}.`
|
|
1035
|
-
);
|
|
1352
|
+
throw otpFailure(TorrowSdkErrorCategory6.InvalidResponse);
|
|
1036
1353
|
}
|
|
1037
1354
|
if (Date.now() >= deadline) {
|
|
1038
|
-
throw
|
|
1039
|
-
`pollMobileIdUntilReady: timeout ~${maxWaitSec}\u0441 (poll \u043A\u0430\u0436\u0434\u044B\u0435 ${intervalSec}\u0441) \u2014 \u0441\u0442\u0430\u0442\u0443\u0441 Pending.`
|
|
1040
|
-
);
|
|
1355
|
+
throw mobileInvalidState();
|
|
1041
1356
|
}
|
|
1042
1357
|
await sleepMs(intervalSec * 1e3, options.signal);
|
|
1043
1358
|
}
|
|
1044
1359
|
}
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
minutes;
|
|
1051
|
-
seconds;
|
|
1052
|
-
traceUrl;
|
|
1053
|
-
apiMessage;
|
|
1054
|
-
body;
|
|
1055
|
-
constructor(body) {
|
|
1056
|
-
const parsed = parseRateLimitBody(body);
|
|
1057
|
-
const wait = parsed.min || parsed.sec ? `\u041F\u043E\u0432\u0442\u043E\u0440 \u043F\u0440\u0438\u043C\u0435\u0440\u043D\u043E \u0447\u0435\u0440\u0435\u0437 ${[parsed.min && `${parsed.min} \u043C\u0438\u043D.`, parsed.sec && `${parsed.sec} \u0441\u0435\u043A.`].filter(Boolean).join(" ")}.` : "\u0422\u043E\u0447\u043D\u043E\u0435 \u0432\u0440\u0435\u043C\u044F \u043E\u0436\u0438\u0434\u0430\u043D\u0438\u044F \u0441\u0435\u0440\u0432\u0435\u0440 \u043D\u0435 \u043F\u0435\u0440\u0435\u0434\u0430\u043B.";
|
|
1058
|
-
super(`sendCode: CODE_REQUEST_LIMIT_EXCEEDED. ${wait}`);
|
|
1059
|
-
this.minutes = parsed.min;
|
|
1060
|
-
this.seconds = parsed.sec;
|
|
1061
|
-
this.traceUrl = parsed.trace;
|
|
1062
|
-
this.apiMessage = parsed.message;
|
|
1063
|
-
this.body = body;
|
|
1064
|
-
}
|
|
1065
|
-
};
|
|
1066
|
-
var OtpAuthHeaderMissingError = class extends Error {
|
|
1067
|
-
name = "OtpAuthHeaderMissingError";
|
|
1068
|
-
body;
|
|
1069
|
-
systemMessage;
|
|
1070
|
-
constructor(body) {
|
|
1071
|
-
const sys = body !== null && typeof body === "object" && typeof body.systemMessage === "string" ? body.systemMessage.trim() : void 0;
|
|
1072
|
-
super(
|
|
1073
|
-
[
|
|
1074
|
-
"requestToken: AUTH_HEADER_MISSING \u2014 \u0437\u0430\u0434\u0430\u0439 appCredentials: { clientId, clientSecret }.",
|
|
1075
|
-
sys ? `\u0421\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u0441\u0435\u0440\u0432\u0435\u0440\u0430: ${sys}` : ""
|
|
1076
|
-
].filter(Boolean).join(" ")
|
|
1077
|
-
);
|
|
1078
|
-
this.body = body;
|
|
1079
|
-
this.systemMessage = sys;
|
|
1360
|
+
function readyTokens(response) {
|
|
1361
|
+
try {
|
|
1362
|
+
return assertTokenRespReadyForStorage(response, "pollMobileIdUntilReady");
|
|
1363
|
+
} catch (cause) {
|
|
1364
|
+
throw otpFailure(TorrowSdkErrorCategory6.InvalidResponse, void 0, cause);
|
|
1080
1365
|
}
|
|
1081
|
-
};
|
|
1082
|
-
function parseRateLimitBody(body) {
|
|
1083
|
-
if (body === null || typeof body !== "object") return {};
|
|
1084
|
-
const o = body;
|
|
1085
|
-
const p = o.parameters ?? {};
|
|
1086
|
-
return {
|
|
1087
|
-
min: p.minutes != null && String(p.minutes) !== "" ? String(p.minutes) : void 0,
|
|
1088
|
-
sec: p.seconds != null && String(p.seconds) !== "" ? String(p.seconds) : void 0,
|
|
1089
|
-
message: typeof o.message === "string" && o.message.trim() ? o.message.trim() : void 0,
|
|
1090
|
-
trace: typeof o.traceUrl === "string" && o.traceUrl.trim() ? o.traceUrl.trim() : void 0
|
|
1091
|
-
};
|
|
1092
1366
|
}
|
|
1093
|
-
function
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
}
|
|
1097
|
-
|
|
1098
|
-
if (body === null || typeof body !== "object") return false;
|
|
1099
|
-
return body.name === "AUTH_HEADER_MISSING";
|
|
1100
|
-
}
|
|
1101
|
-
function httpErrorBody(e) {
|
|
1102
|
-
if (e instanceof TorrowHttpError3) return e.body;
|
|
1103
|
-
if (e !== null && typeof e === "object" && "body" in e) {
|
|
1104
|
-
return e.body;
|
|
1105
|
-
}
|
|
1106
|
-
return void 0;
|
|
1367
|
+
function mobileInvalidState() {
|
|
1368
|
+
return new TorrowSdkError6({
|
|
1369
|
+
category: TorrowSdkErrorCategory6.InvalidState,
|
|
1370
|
+
detail: { domain: TorrowSdkErrorDomain4.AuthSession }
|
|
1371
|
+
});
|
|
1107
1372
|
}
|
|
1373
|
+
|
|
1374
|
+
// ../../src/auth-session/otp-login.ts
|
|
1375
|
+
var MOBILE_ID_SMS_CODE_LENGTH = 4;
|
|
1108
1376
|
function scopesOrDefault2(scopes) {
|
|
1109
1377
|
return scopes && scopes.length > 0 ? scopes : [...DefaultAuthScopes5];
|
|
1110
1378
|
}
|
|
@@ -1113,100 +1381,141 @@ function effectiveCodeLength(raw, fallback) {
|
|
|
1113
1381
|
return fallback > 0 ? fallback : void 0;
|
|
1114
1382
|
}
|
|
1115
1383
|
async function requestTokenOrThrow(client, body, signal) {
|
|
1384
|
+
let raw;
|
|
1116
1385
|
try {
|
|
1117
|
-
|
|
1118
|
-
} catch (
|
|
1119
|
-
|
|
1120
|
-
if (errBody !== void 0 && isAuthHeaderMissingBody(errBody)) {
|
|
1121
|
-
throw new OtpAuthHeaderMissingError(errBody);
|
|
1122
|
-
}
|
|
1123
|
-
throw e;
|
|
1386
|
+
raw = await requestAuthToken(client, body, signal);
|
|
1387
|
+
} catch (error) {
|
|
1388
|
+
throw otpHttpFailure(error);
|
|
1124
1389
|
}
|
|
1125
|
-
}
|
|
1126
|
-
async function runOtpLogin(client, options) {
|
|
1127
|
-
const accountName = options.accountName.trim();
|
|
1128
|
-
const accountType = options.type;
|
|
1129
|
-
const scopes = scopesOrDefault2(options.scopes);
|
|
1130
|
-
const signal = options.signal;
|
|
1131
|
-
let sendResp;
|
|
1132
1390
|
try {
|
|
1133
|
-
|
|
1134
|
-
} catch (
|
|
1135
|
-
|
|
1136
|
-
if (errBody !== void 0 && isSendCodeRateLimitedBody(errBody)) {
|
|
1137
|
-
throw new OtpCodeRequestLimitExceededError(errBody);
|
|
1138
|
-
}
|
|
1139
|
-
throw e;
|
|
1391
|
+
return normalizeTokenResp(raw);
|
|
1392
|
+
} catch (cause) {
|
|
1393
|
+
throw otpFailure(TorrowSdkErrorCategory7.InvalidResponse, void 0, cause);
|
|
1140
1394
|
}
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
availableMethods: sendResp.availableMethods,
|
|
1153
|
-
sessionLifeTimeInSeconds: sendResp.sessionLifeTimeInSeconds,
|
|
1154
|
-
nextAvailableUsageTime: sendResp.nextAvailableUsageTime ?? null
|
|
1155
|
-
};
|
|
1156
|
-
await options.onSendCode(result);
|
|
1157
|
-
if (sendResp.usedMethod === VerificationMethodValues2.MtsMobileId) {
|
|
1395
|
+
}
|
|
1396
|
+
async function runOtpLoginWithEffects(client, options) {
|
|
1397
|
+
const effects = [];
|
|
1398
|
+
let failureAt = "SendCode" /* SendCode */;
|
|
1399
|
+
try {
|
|
1400
|
+
throwIfOtpAborted(options.signal);
|
|
1401
|
+
const accountName = options.accountName.trim();
|
|
1402
|
+
const accountType = options.type;
|
|
1403
|
+
const scopes = scopesOrDefault2(options.scopes);
|
|
1404
|
+
const signal = options.signal;
|
|
1405
|
+
let sendResp;
|
|
1158
1406
|
try {
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
signal,
|
|
1166
|
-
smsCodeLength: typeof sendResp.codeLength === "number" && sendResp.codeLength > 0 ? sendResp.codeLength : MOBILE_ID_SMS_CODE_LENGTH,
|
|
1167
|
-
obtainSmsCode: async (ctx) => {
|
|
1168
|
-
const codeLength2 = effectiveCodeLength(ctx.codeLength, MOBILE_ID_SMS_CODE_LENGTH);
|
|
1169
|
-
return (await options.getCode({
|
|
1170
|
-
sessionId,
|
|
1171
|
-
codeLength: codeLength2,
|
|
1172
|
-
usedMethod: sendResp.usedMethod
|
|
1173
|
-
})).trim();
|
|
1174
|
-
}
|
|
1407
|
+
sendResp = await sendOtpCode(client, options, signal);
|
|
1408
|
+
} catch (error) {
|
|
1409
|
+
effects.push({
|
|
1410
|
+
area: TorrowSdkEffectArea3.Remote,
|
|
1411
|
+
step: AuthSessionEffectStep3.SendCode,
|
|
1412
|
+
state: error instanceof TorrowSdkError7 && error.request === TorrowSdkRequestState4.NotStarted ? TorrowSdkEffectState3.NotApplied : TorrowSdkEffectState3.Unknown
|
|
1175
1413
|
});
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1414
|
+
throw otpHttpFailure(error);
|
|
1415
|
+
}
|
|
1416
|
+
effects.push({ area: TorrowSdkEffectArea3.Remote, step: AuthSessionEffectStep3.SendCode, state: TorrowSdkEffectState3.Unknown });
|
|
1417
|
+
failureAt = "ValidateResponse" /* ValidateResponse */;
|
|
1418
|
+
const sessionId = sendResp.sessionId?.trim();
|
|
1419
|
+
if (!sessionId) {
|
|
1420
|
+
throw otpFailure(TorrowSdkErrorCategory7.InvalidResponse);
|
|
1421
|
+
}
|
|
1422
|
+
effects[effects.length - 1] = { area: TorrowSdkEffectArea3.Remote, step: AuthSessionEffectStep3.SendCode, state: TorrowSdkEffectState3.Applied };
|
|
1423
|
+
const result = {
|
|
1424
|
+
accountName,
|
|
1425
|
+
accountType,
|
|
1426
|
+
sessionId,
|
|
1427
|
+
usedMethod: sendResp.usedMethod,
|
|
1428
|
+
codeLength: sendResp.codeLength,
|
|
1429
|
+
pollIntervalSeconds: sendResp.pollIntervalSeconds,
|
|
1430
|
+
availableMethods: sendResp.availableMethods,
|
|
1431
|
+
sessionLifeTimeInSeconds: sendResp.sessionLifeTimeInSeconds,
|
|
1432
|
+
nextAvailableUsageTime: sendResp.nextAvailableUsageTime ?? null
|
|
1433
|
+
};
|
|
1434
|
+
failureAt = "OnSendCode" /* OnSendCode */;
|
|
1435
|
+
await options.onSendCode(result);
|
|
1436
|
+
if (sendResp.usedMethod === VerificationMethodValues2.MtsMobileId) {
|
|
1437
|
+
try {
|
|
1438
|
+
failureAt = "PollTokens" /* PollTokens */;
|
|
1439
|
+
const tokens = await pollMobileIdUntilReady(client, {
|
|
1440
|
+
sessionId,
|
|
1441
|
+
deviceInfo: options.deviceInfo,
|
|
1442
|
+
scopes,
|
|
1443
|
+
pollIntervalSeconds: sendResp.pollIntervalSeconds,
|
|
1444
|
+
maxWaitSeconds: sendResp.sessionLifeTimeInSeconds,
|
|
1445
|
+
signal,
|
|
1446
|
+
smsCodeLength: typeof sendResp.codeLength === "number" && sendResp.codeLength > 0 ? sendResp.codeLength : MOBILE_ID_SMS_CODE_LENGTH,
|
|
1447
|
+
obtainSmsCode: async (ctx) => {
|
|
1448
|
+
const codeLength2 = effectiveCodeLength(ctx.codeLength, MOBILE_ID_SMS_CODE_LENGTH);
|
|
1449
|
+
failureAt = "GetCode" /* GetCode */;
|
|
1450
|
+
const code2 = (await options.getCode({
|
|
1451
|
+
sessionId,
|
|
1452
|
+
codeLength: codeLength2,
|
|
1453
|
+
usedMethod: sendResp.usedMethod
|
|
1454
|
+
})).trim();
|
|
1455
|
+
failureAt = "PollTokens" /* PollTokens */;
|
|
1456
|
+
return code2;
|
|
1457
|
+
}
|
|
1458
|
+
});
|
|
1459
|
+
effects.push({ area: TorrowSdkEffectArea3.Remote, step: AuthSessionEffectStep3.IssueTokens, state: TorrowSdkEffectState3.Applied });
|
|
1460
|
+
return { tokens, effects };
|
|
1461
|
+
} catch (error) {
|
|
1462
|
+
throw otpHttpFailure(error);
|
|
1180
1463
|
}
|
|
1181
|
-
throw e;
|
|
1182
1464
|
}
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
sessionId,
|
|
1190
|
-
codeLength: classicLength,
|
|
1191
|
-
usedMethod: sendResp.usedMethod
|
|
1192
|
-
})).trim();
|
|
1193
|
-
if (!code) {
|
|
1194
|
-
throw new Error("runOtpLogin: empty OTP code");
|
|
1195
|
-
}
|
|
1196
|
-
const resp = await requestTokenOrThrow(
|
|
1197
|
-
client,
|
|
1198
|
-
{
|
|
1465
|
+
const classicLength = effectiveCodeLength(
|
|
1466
|
+
sendResp.codeLength,
|
|
1467
|
+
accountType === SendCodeAccountTypeValues.Email ? 6 : 4
|
|
1468
|
+
);
|
|
1469
|
+
failureAt = "GetCode" /* GetCode */;
|
|
1470
|
+
const code = (await options.getCode({
|
|
1199
1471
|
sessionId,
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1472
|
+
codeLength: classicLength,
|
|
1473
|
+
usedMethod: sendResp.usedMethod
|
|
1474
|
+
})).trim();
|
|
1475
|
+
failureAt = "ValidateInput" /* ValidateInput */;
|
|
1476
|
+
if (!code) {
|
|
1477
|
+
throw otpFailure(TorrowSdkErrorCategory7.InvalidInput);
|
|
1478
|
+
}
|
|
1479
|
+
failureAt = "IssueTokens" /* IssueTokens */;
|
|
1480
|
+
const resp = await requestTokenOrThrow(
|
|
1481
|
+
client,
|
|
1482
|
+
{
|
|
1483
|
+
sessionId,
|
|
1484
|
+
code,
|
|
1485
|
+
scopes,
|
|
1486
|
+
deviceInfo: options.deviceInfo
|
|
1487
|
+
},
|
|
1488
|
+
signal
|
|
1489
|
+
);
|
|
1490
|
+
if (tokenTransition(resp.status, true) !== TokenStatusValues5.TokenReady) {
|
|
1491
|
+
throw otpFailure(TorrowSdkErrorCategory7.InvalidResponse);
|
|
1492
|
+
}
|
|
1493
|
+
let ready;
|
|
1494
|
+
try {
|
|
1495
|
+
ready = assertTokenRespReadyForStorage(resp, "runOtpLogin");
|
|
1496
|
+
} catch (cause) {
|
|
1497
|
+
throw otpFailure(TorrowSdkErrorCategory7.InvalidResponse, void 0, cause);
|
|
1498
|
+
}
|
|
1499
|
+
effects.push({ area: TorrowSdkEffectArea3.Remote, step: AuthSessionEffectStep3.IssueTokens, state: TorrowSdkEffectState3.Applied });
|
|
1500
|
+
return { tokens: ready, effects };
|
|
1501
|
+
} catch (cause) {
|
|
1502
|
+
const error = options.signal?.aborted && !(cause instanceof TorrowSdkError7) ? new TorrowSdkError7({ category: TorrowSdkErrorCategory7.Cancelled, sensitive: { cause } }) : cause;
|
|
1503
|
+
const tokenStages = ["IssueTokens" /* IssueTokens */, "PollTokens" /* PollTokens */];
|
|
1504
|
+
if (tokenStages.includes(failureAt)) {
|
|
1505
|
+
effects.push({
|
|
1506
|
+
area: TorrowSdkEffectArea3.Remote,
|
|
1507
|
+
step: AuthSessionEffectStep3.IssueTokens,
|
|
1508
|
+
state: error instanceof TorrowSdkError7 && error.request === TorrowSdkRequestState4.NotStarted ? TorrowSdkEffectState3.NotApplied : TorrowSdkEffectState3.Unknown
|
|
1509
|
+
});
|
|
1510
|
+
}
|
|
1511
|
+
const callback = failureAt === "GetCode" /* GetCode */ || failureAt === "OnSendCode" /* OnSendCode */;
|
|
1512
|
+
throw sessionFailure(error, {
|
|
1513
|
+
operation: "TorrowAuth.loginWithOtp",
|
|
1514
|
+
failureAt,
|
|
1515
|
+
effects,
|
|
1516
|
+
...callback && !options.signal?.aborted ? { category: TorrowSdkErrorCategory7.Unknown, reason: AuthSessionErrorReason4.CallbackFailed } : {}
|
|
1517
|
+
});
|
|
1208
1518
|
}
|
|
1209
|
-
return assertTokenRespReadyForStorage(resp, "runOtpLogin");
|
|
1210
1519
|
}
|
|
1211
1520
|
|
|
1212
1521
|
// ../../src/auth-session/torrow-auth.ts
|
|
@@ -1227,72 +1536,157 @@ function sessionOpenOpts(base) {
|
|
|
1227
1536
|
function sessionOpenOptsForLogin(base) {
|
|
1228
1537
|
return { ...sessionOpenOpts(base), skipEnsureFreshOnOpen: true };
|
|
1229
1538
|
}
|
|
1539
|
+
function loginFailureStage(error, current) {
|
|
1540
|
+
return error instanceof TorrowSdkError8 ? Object.values(SessionFailureStage).find((stage) => stage === error.failureAt) ?? current : current;
|
|
1541
|
+
}
|
|
1230
1542
|
var TorrowAuth = {
|
|
1231
1543
|
/** Пошаговый OTP без storage, polling и refresh. */
|
|
1232
1544
|
createOtpFlow,
|
|
1545
|
+
/** Загрузить сессию; общий отказ сохраняет текущую диагностику open/refresh. */
|
|
1233
1546
|
async connect(options) {
|
|
1234
|
-
|
|
1235
|
-
|
|
1547
|
+
try {
|
|
1548
|
+
const session = await TokenSession.open(sessionOpenOpts(options));
|
|
1549
|
+
return { client: session.client, session };
|
|
1550
|
+
} catch (error) {
|
|
1551
|
+
throw sessionFailure(error, {
|
|
1552
|
+
operation: "TorrowAuth.connect",
|
|
1553
|
+
failureAt: "OpenSession" /* OpenSession */,
|
|
1554
|
+
effects: error instanceof TorrowSdkError8 ? error.effects ?? [] : []
|
|
1555
|
+
});
|
|
1556
|
+
}
|
|
1236
1557
|
},
|
|
1237
1558
|
async loginWithOtp(options) {
|
|
1238
|
-
const
|
|
1239
|
-
|
|
1240
|
-
|
|
1559
|
+
const effects = [];
|
|
1560
|
+
let failureAt = "ValidateInput" /* ValidateInput */;
|
|
1561
|
+
try {
|
|
1562
|
+
const accountName = options.account.accountName.trim();
|
|
1563
|
+
if (!accountName) throw sessionFailure(void 0, {
|
|
1564
|
+
operation: "TorrowAuth.loginWithOtp",
|
|
1565
|
+
failureAt,
|
|
1566
|
+
effects,
|
|
1567
|
+
category: TorrowSdkErrorCategory8.InvalidInput
|
|
1568
|
+
});
|
|
1569
|
+
failureAt = "OpenSession" /* OpenSession */;
|
|
1570
|
+
const opened = await TokenSession.openWithEffects(sessionOpenOptsForLogin(options));
|
|
1571
|
+
const session = opened.session;
|
|
1572
|
+
effects.push(...opened.effects);
|
|
1573
|
+
const deviceInfo = session.getDeviceInfo();
|
|
1574
|
+
failureAt = "SendCode" /* SendCode */;
|
|
1575
|
+
const ready = await runOtpLoginWithEffects(session.identityClient(), {
|
|
1576
|
+
accountName,
|
|
1577
|
+
type: options.account.accountType,
|
|
1578
|
+
deviceInfo,
|
|
1579
|
+
onSendCode: options.onSendCode,
|
|
1580
|
+
getCode: options.getCode,
|
|
1581
|
+
scopes: options.scopes,
|
|
1582
|
+
language: options.language,
|
|
1583
|
+
regionStorageId: options.regionStorageId,
|
|
1584
|
+
captcha: options.captcha,
|
|
1585
|
+
captchaType: options.captchaType,
|
|
1586
|
+
inviteSessionId: options.inviteSessionId,
|
|
1587
|
+
preferredMethod: options.preferredMethod,
|
|
1588
|
+
supportedMethods: options.supportedMethods,
|
|
1589
|
+
signal: options.signal
|
|
1590
|
+
});
|
|
1591
|
+
effects.push(...ready.effects);
|
|
1592
|
+
failureAt = "ApplyTokens" /* ApplyTokens */;
|
|
1593
|
+
await session.applyTokenResp(ready.tokens, { replace: true });
|
|
1594
|
+
return { client: session.client, session };
|
|
1595
|
+
} catch (error) {
|
|
1596
|
+
throw sessionFailure(error, {
|
|
1597
|
+
operation: "TorrowAuth.loginWithOtp",
|
|
1598
|
+
failureAt: loginFailureStage(error, failureAt),
|
|
1599
|
+
effects: [...effects, ...error instanceof TorrowSdkError8 ? error.effects ?? [] : []]
|
|
1600
|
+
});
|
|
1241
1601
|
}
|
|
1242
|
-
const session = await TokenSession.open(sessionOpenOptsForLogin(options));
|
|
1243
|
-
const deviceInfo = session.getDeviceInfo();
|
|
1244
|
-
const ready = await runOtpLogin(session.identityClient(), {
|
|
1245
|
-
accountName,
|
|
1246
|
-
type: options.account.accountType,
|
|
1247
|
-
deviceInfo,
|
|
1248
|
-
onSendCode: options.onSendCode,
|
|
1249
|
-
getCode: options.getCode,
|
|
1250
|
-
scopes: options.scopes,
|
|
1251
|
-
language: options.language,
|
|
1252
|
-
regionStorageId: options.regionStorageId,
|
|
1253
|
-
captcha: options.captcha,
|
|
1254
|
-
captchaType: options.captchaType,
|
|
1255
|
-
inviteSessionId: options.inviteSessionId,
|
|
1256
|
-
preferredMethod: options.preferredMethod,
|
|
1257
|
-
supportedMethods: options.supportedMethods,
|
|
1258
|
-
signal: options.signal
|
|
1259
|
-
});
|
|
1260
|
-
await session.applyTokenResp(ready, { replace: true });
|
|
1261
|
-
return { client: session.client, session };
|
|
1262
1602
|
},
|
|
1263
1603
|
async loginWithLink(options) {
|
|
1264
|
-
const
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1604
|
+
const effects = [];
|
|
1605
|
+
let failureAt = "OpenSession" /* OpenSession */;
|
|
1606
|
+
try {
|
|
1607
|
+
const opened = await TokenSession.openWithEffects(sessionOpenOptsForLogin(options));
|
|
1608
|
+
const session = opened.session;
|
|
1609
|
+
effects.push(...opened.effects);
|
|
1610
|
+
failureAt = "CreateAuthSession" /* CreateAuthSession */;
|
|
1611
|
+
await runLinkLogin(session, {
|
|
1612
|
+
deviceInfo: session.getDeviceInfo(),
|
|
1613
|
+
onLink: options.onLink,
|
|
1614
|
+
accountType: options.accountType,
|
|
1615
|
+
language: options.language,
|
|
1616
|
+
regionStorageId: options.regionStorageId,
|
|
1617
|
+
captcha: options.captcha,
|
|
1618
|
+
inviteSessionId: options.inviteSessionId,
|
|
1619
|
+
scopes: options.scopes,
|
|
1620
|
+
pollIntervalMs: options.pollIntervalMs,
|
|
1621
|
+
pollTimeoutSec: options.pollTimeoutSec,
|
|
1622
|
+
signal: options.signal
|
|
1623
|
+
});
|
|
1624
|
+
return { client: session.client, session };
|
|
1625
|
+
} catch (error) {
|
|
1626
|
+
throw sessionFailure(error, {
|
|
1627
|
+
operation: "TorrowAuth.loginWithLink",
|
|
1628
|
+
failureAt: loginFailureStage(error, failureAt),
|
|
1629
|
+
effects: [...effects, ...error instanceof TorrowSdkError8 ? error.effects ?? [] : []]
|
|
1630
|
+
});
|
|
1631
|
+
}
|
|
1279
1632
|
},
|
|
1633
|
+
/** Сохранить host tokens, затем connect; последующий отказ не отменяет Applied сохранение. */
|
|
1280
1634
|
async fromTokens(options) {
|
|
1281
|
-
const
|
|
1282
|
-
|
|
1283
|
-
|
|
1635
|
+
const effects = [];
|
|
1636
|
+
let failureAt = "ValidateInput" /* ValidateInput */;
|
|
1637
|
+
try {
|
|
1638
|
+
const accessToken = options.accessToken.trim();
|
|
1639
|
+
if (!accessToken) {
|
|
1640
|
+
throw sessionFailure(void 0, {
|
|
1641
|
+
operation: "TorrowAuth.fromTokens",
|
|
1642
|
+
failureAt,
|
|
1643
|
+
effects,
|
|
1644
|
+
category: TorrowSdkErrorCategory8.InvalidInput
|
|
1645
|
+
});
|
|
1646
|
+
}
|
|
1647
|
+
const refreshToken = options.refreshToken?.trim() || void 0;
|
|
1648
|
+
failureAt = "LoadStorage" /* LoadStorage */;
|
|
1649
|
+
let prev;
|
|
1650
|
+
try {
|
|
1651
|
+
prev = await options.tokenStorage.load();
|
|
1652
|
+
} catch (error) {
|
|
1653
|
+
throw sessionFailure(error, {
|
|
1654
|
+
operation: "TorrowAuth.fromTokens",
|
|
1655
|
+
failureAt,
|
|
1656
|
+
effects,
|
|
1657
|
+
category: TorrowSdkErrorCategory8.StorageFailure
|
|
1658
|
+
});
|
|
1659
|
+
}
|
|
1660
|
+
const explicitTtl = options.accessExpiresAtMs;
|
|
1661
|
+
const accessExpiresAtMs = typeof explicitTtl === "number" && Number.isFinite(explicitTtl) && explicitTtl > 0 ? explicitTtl : accessExpiresAtMsFromJwt(accessToken);
|
|
1662
|
+
failureAt = "SeedStorage" /* SeedStorage */;
|
|
1663
|
+
effects.push({ area: TorrowSdkEffectArea4.Storage, step: AuthSessionEffectStep4.SaveTokens, state: TorrowSdkEffectState4.Unknown });
|
|
1664
|
+
try {
|
|
1665
|
+
await options.tokenStorage.save({
|
|
1666
|
+
accessToken,
|
|
1667
|
+
refreshToken,
|
|
1668
|
+
accessExpiresAtMs: typeof accessExpiresAtMs === "number" && accessExpiresAtMs > 0 ? accessExpiresAtMs : void 0,
|
|
1669
|
+
deviceId: options.deviceInfo.deviceId?.trim() || prev.deviceId?.trim() || void 0
|
|
1670
|
+
});
|
|
1671
|
+
} catch (error) {
|
|
1672
|
+
throw sessionFailure(error, {
|
|
1673
|
+
operation: "TorrowAuth.fromTokens",
|
|
1674
|
+
failureAt,
|
|
1675
|
+
effects,
|
|
1676
|
+
category: TorrowSdkErrorCategory8.StorageFailure
|
|
1677
|
+
});
|
|
1678
|
+
}
|
|
1679
|
+
effects[0] = { ...effects[0], state: TorrowSdkEffectState4.Applied };
|
|
1680
|
+
failureAt = "Connect" /* Connect */;
|
|
1681
|
+
try {
|
|
1682
|
+
return await TorrowAuth.connect(options);
|
|
1683
|
+
} catch (error) {
|
|
1684
|
+
if (error instanceof TorrowSdkError8) effects.push(...error.effects ?? []);
|
|
1685
|
+
throw error;
|
|
1686
|
+
}
|
|
1687
|
+
} catch (error) {
|
|
1688
|
+
throw sessionFailure(error, { operation: "TorrowAuth.fromTokens", failureAt, effects });
|
|
1284
1689
|
}
|
|
1285
|
-
const refreshToken = options.refreshToken?.trim() || void 0;
|
|
1286
|
-
const prev = await options.tokenStorage.load();
|
|
1287
|
-
const explicitTtl = options.accessExpiresAtMs;
|
|
1288
|
-
const accessExpiresAtMs = typeof explicitTtl === "number" && Number.isFinite(explicitTtl) && explicitTtl > 0 ? explicitTtl : accessExpiresAtMsFromJwt(accessToken);
|
|
1289
|
-
await options.tokenStorage.save({
|
|
1290
|
-
accessToken,
|
|
1291
|
-
refreshToken,
|
|
1292
|
-
...typeof accessExpiresAtMs === "number" && accessExpiresAtMs > 0 ? { accessExpiresAtMs } : { accessExpiresAtMs: void 0 },
|
|
1293
|
-
deviceId: options.deviceInfo.deviceId?.trim() || prev.deviceId?.trim() || void 0
|
|
1294
|
-
});
|
|
1295
|
-
return TorrowAuth.connect(options);
|
|
1296
1690
|
},
|
|
1297
1691
|
anonymous(config = {}) {
|
|
1298
1692
|
return new TorrowClient3({
|
|
@@ -1302,14 +1696,9 @@ var TorrowAuth = {
|
|
|
1302
1696
|
}
|
|
1303
1697
|
};
|
|
1304
1698
|
export {
|
|
1305
|
-
AuthSessionNotReadyError,
|
|
1306
1699
|
DEFAULT_APP_CREDENTIALS,
|
|
1307
1700
|
LocalStorageTokenStorage,
|
|
1308
1701
|
MemoryTokenStorage,
|
|
1309
|
-
OtpAuthHeaderMissingError,
|
|
1310
|
-
OtpCodeRequestLimitExceededError,
|
|
1311
|
-
OtpFlowError,
|
|
1312
|
-
OtpFlowErrorCodeValues,
|
|
1313
1702
|
OtpPhaseValues,
|
|
1314
1703
|
TokenSession,
|
|
1315
1704
|
TorrowAuth
|