@robelest/convex-auth 0.0.4-preview.27 → 0.0.4-preview.28
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 +3 -5
- package/dist/bin.js +6488 -1571
- package/dist/browser/index.js +10 -7
- package/dist/browser/locks.js +3 -5
- package/dist/browser/navigation.js +7 -10
- package/dist/browser/runtime.js +35 -33
- package/dist/client/core/types.js +17 -0
- package/dist/client/factors/device.js +26 -19
- package/dist/client/index.js +151 -163
- package/dist/client/runtime/proxy.js +6 -6
- package/dist/client/services/adapters.js +3 -7
- package/dist/client/services/http.js +2 -5
- package/dist/client/services/resolve.js +5 -11
- package/dist/client/services/runtime.js +2 -5
- package/dist/component/_generated/component.d.ts +46 -0
- package/dist/component/index.d.ts +3 -3
- package/dist/component/model.d.ts +25 -25
- package/dist/component/public/identity/sessions.js +38 -1
- package/dist/component/public/identity/tokens.js +81 -3
- package/dist/component/public/identity/verifiers.js +9 -3
- package/dist/component/public.js +3 -3
- package/dist/component/schema.d.ts +320 -320
- package/dist/core/index.d.ts +380 -0
- package/dist/core/index.js +83 -0
- package/dist/otel.d.ts +13 -17
- package/dist/otel.js +39 -49
- package/dist/providers/email.d.ts +2 -2
- package/dist/providers/password.js +8 -16
- package/dist/providers/phone.js +2 -9
- package/dist/server/auth-context.d.ts +204 -0
- package/dist/server/auth-context.js +76 -0
- package/dist/server/auth.d.ts +25 -187
- package/dist/server/auth.js +5 -96
- package/dist/server/componentContext.d.ts +12 -0
- package/dist/server/componentContext.js +1 -0
- package/dist/server/config.js +1 -12
- package/dist/server/constants.js +6 -0
- package/dist/server/contract.d.ts +1 -1
- package/dist/server/core.js +5 -14
- package/dist/server/crypto.js +26 -18
- package/dist/server/db.js +6 -1
- package/dist/server/device.js +88 -78
- package/dist/server/http.d.ts +4 -3
- package/dist/server/http.js +74 -86
- package/dist/server/index.d.ts +2 -1
- package/dist/server/limits.js +22 -15
- package/dist/server/mounts.d.ts +103 -103
- package/dist/server/mutations/account.js +6 -4
- package/dist/server/mutations/invalidate.js +3 -6
- package/dist/server/mutations/oauth.js +86 -88
- package/dist/server/mutations/refresh.js +45 -87
- package/dist/server/mutations/register.js +19 -19
- package/dist/server/mutations/retrieve.js +17 -15
- package/dist/server/mutations/signature.js +9 -13
- package/dist/server/mutations/signin.js +7 -3
- package/dist/server/mutations/signout.js +10 -15
- package/dist/server/mutations/store.js +22 -12
- package/dist/server/mutations/verifier.js +11 -6
- package/dist/server/mutations/verify.js +55 -46
- package/dist/server/oauth/runtime.js +27 -25
- package/dist/server/passkey.js +299 -250
- package/dist/server/prefetch.js +283 -281
- package/dist/server/refresh.js +7 -60
- package/dist/server/runtime.d.ts +82 -206
- package/dist/server/runtime.js +63 -56
- package/dist/server/services/config.js +5 -3
- package/dist/server/services/logger.js +2 -4
- package/dist/server/services/providers.js +2 -4
- package/dist/server/services/refresh.js +2 -4
- package/dist/server/services/resolve.js +15 -14
- package/dist/server/services/signin.js +2 -4
- package/dist/server/sessions.js +32 -33
- package/dist/server/signin.js +177 -142
- package/dist/server/sso/domain.d.ts +20 -68
- package/dist/server/sso/domain.js +444 -413
- package/dist/server/sso/http.js +53 -59
- package/dist/server/sso/oidc.js +94 -80
- package/dist/server/tokens.js +13 -3
- package/dist/server/totp.js +153 -116
- package/dist/server/types.d.ts +2 -2
- package/dist/server/users.js +18 -23
- package/dist/server/utils/cache.js +51 -0
- package/dist/server/utils/dispatch.js +36 -0
- package/dist/server/utils/retry.js +24 -0
- package/dist/server/utils/span.js +32 -0
- package/dist/shared/errors.js +9 -3
- package/dist/shared/log.js +20 -22
- package/package.json +41 -33
package/dist/client/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { LOG_LEVELS, logMessage } from "../shared/log.js";
|
|
2
|
+
import { createDeferred } from "./core/types.js";
|
|
2
3
|
import { createHandshakeError } from "./errors.js";
|
|
3
4
|
import { createDeviceClient } from "./factors/device.js";
|
|
4
5
|
import { createTotpClient } from "./factors/totp.js";
|
|
@@ -8,10 +9,9 @@ import { isRetriableProxyRefreshError, isTransientNetworkError, parseProxyErrorB
|
|
|
8
9
|
import { createStorageHelpers } from "./runtime/storage.js";
|
|
9
10
|
import { ClientAdapterFactoriesLive, ClientAdaptersLive } from "./services/adapters.js";
|
|
10
11
|
import { ClientHttpLive } from "./services/http.js";
|
|
11
|
-
import { ClientRuntimeLive } from "./services/runtime.js";
|
|
12
12
|
import { resolveClientServices } from "./services/resolve.js";
|
|
13
|
+
import { ClientRuntimeLive } from "./services/runtime.js";
|
|
13
14
|
import { ConvexError } from "convex/values";
|
|
14
|
-
import { Cause, Deferred, Effect, Exit, Layer, Match, Ref, Schedule } from "effect";
|
|
15
15
|
|
|
16
16
|
//#region src/client/index.ts
|
|
17
17
|
const VERIFIER_STORAGE_KEY = "__convexAuthOAuthVerifier";
|
|
@@ -22,18 +22,16 @@ const INVITE_EMAIL_KEY = "__convexAuthPendingInviteEmail";
|
|
|
22
22
|
const RETRY_BASE_MS = 500;
|
|
23
23
|
const RETRY_MAX_RETRIES = 2;
|
|
24
24
|
const AUTH_HANDSHAKE_TIMEOUT_MS = 5e3;
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
function runDetached(program, message) {
|
|
36
|
-
Effect.runFork(program.pipe(Effect.catchCause((cause) => Effect.sync(() => logClientErrorCause(message, cause)))));
|
|
25
|
+
async function retryWithJitteredBackoff(fn, shouldRetry) {
|
|
26
|
+
let attempt = 0;
|
|
27
|
+
while (true) try {
|
|
28
|
+
return await fn();
|
|
29
|
+
} catch (error) {
|
|
30
|
+
if (attempt >= RETRY_MAX_RETRIES || !shouldRetry(error)) throw error;
|
|
31
|
+
const jitter = RETRY_BASE_MS * Math.pow(2, attempt) * (.5 + Math.random());
|
|
32
|
+
await new Promise((resolve) => setTimeout(resolve, jitter));
|
|
33
|
+
attempt++;
|
|
34
|
+
}
|
|
37
35
|
}
|
|
38
36
|
/**
|
|
39
37
|
* Resolve the Convex deployment URL from the client.
|
|
@@ -104,7 +102,12 @@ function buildSignInRequestKey(provider, params) {
|
|
|
104
102
|
function client(options) {
|
|
105
103
|
const { convex, proxyPath, api: apiRefs } = options;
|
|
106
104
|
const proxy = proxyPath;
|
|
107
|
-
const services = resolveClientServices(
|
|
105
|
+
const services = resolveClientServices({
|
|
106
|
+
runtime: ClientRuntimeLive(options.runtime ?? {}),
|
|
107
|
+
adapters: ClientAdaptersLive(options.adapters ?? {}),
|
|
108
|
+
adapterFactories: ClientAdapterFactoriesLive(options.adapterFactories ?? {}),
|
|
109
|
+
http: ClientHttpLive(proxy ? null : options.httpClient ?? null)
|
|
110
|
+
});
|
|
108
111
|
const runtime = services.runtime;
|
|
109
112
|
const adapters = services.adapters;
|
|
110
113
|
function requireProxyRuntime() {
|
|
@@ -183,7 +186,6 @@ function client(options) {
|
|
|
183
186
|
}
|
|
184
187
|
};
|
|
185
188
|
const subscribers = /* @__PURE__ */ new Set();
|
|
186
|
-
const subscribersRef = Ref.makeUnsafe(subscribers);
|
|
187
189
|
let disposeStorageListener = null;
|
|
188
190
|
const httpClient = services.httpClient;
|
|
189
191
|
const serverToken = typeof options.tokenSeed === "string" && options.tokenSeed.trim().length > 0 ? options.tokenSeed : null;
|
|
@@ -196,20 +198,20 @@ function client(options) {
|
|
|
196
198
|
let destroyed = false;
|
|
197
199
|
let activeSignIn = null;
|
|
198
200
|
const handshakeWaiters = /* @__PURE__ */ new Set();
|
|
199
|
-
|
|
201
|
+
let snapshot = {
|
|
200
202
|
phase: hasServerToken ? "authenticated" : isLoading ? "loading" : "unauthenticated",
|
|
201
203
|
isLoading,
|
|
202
204
|
isAuthenticated: hasServerToken,
|
|
203
205
|
token
|
|
204
|
-
}
|
|
206
|
+
};
|
|
205
207
|
let handlingCodeFlow = false;
|
|
206
208
|
const settleHandshakeWaiters = (epoch, outcome) => {
|
|
207
209
|
for (const waiter of Array.from(handshakeWaiters)) {
|
|
208
210
|
if (waiter.epoch !== epoch) continue;
|
|
209
211
|
clearTimeout(waiter.timeoutId);
|
|
210
212
|
handshakeWaiters.delete(waiter);
|
|
211
|
-
if (outcome.type === "resolve")
|
|
212
|
-
else
|
|
213
|
+
if (outcome.type === "resolve") waiter.deferred.resolve(void 0);
|
|
214
|
+
else waiter.deferred.reject(outcome.error);
|
|
213
215
|
}
|
|
214
216
|
};
|
|
215
217
|
const rejectObsoleteHandshakeWaiters = (activeEpoch) => {
|
|
@@ -217,10 +219,10 @@ function client(options) {
|
|
|
217
219
|
if (waiter.epoch >= activeEpoch) continue;
|
|
218
220
|
clearTimeout(waiter.timeoutId);
|
|
219
221
|
handshakeWaiters.delete(waiter);
|
|
220
|
-
|
|
222
|
+
waiter.deferred.reject(createHandshakeError("AUTH_HANDSHAKE_REJECTED", {
|
|
221
223
|
...waiter.context,
|
|
222
224
|
reason: "token_changed"
|
|
223
|
-
}))
|
|
225
|
+
}));
|
|
224
226
|
}
|
|
225
227
|
};
|
|
226
228
|
const waitForAuthHandshake = async (context) => {
|
|
@@ -231,22 +233,22 @@ function client(options) {
|
|
|
231
233
|
reason: "auth_rejected"
|
|
232
234
|
});
|
|
233
235
|
const epoch = authEpoch;
|
|
234
|
-
const deferred =
|
|
236
|
+
const deferred = createDeferred();
|
|
235
237
|
const waiter = {
|
|
236
238
|
epoch,
|
|
237
239
|
context,
|
|
238
240
|
deferred,
|
|
239
241
|
timeoutId: setTimeout(() => {
|
|
240
242
|
handshakeWaiters.delete(waiter);
|
|
241
|
-
|
|
243
|
+
deferred.reject(createHandshakeError("AUTH_HANDSHAKE_TIMEOUT", {
|
|
242
244
|
...context,
|
|
243
245
|
timeoutMs: AUTH_HANDSHAKE_TIMEOUT_MS
|
|
244
|
-
}))
|
|
246
|
+
}));
|
|
245
247
|
}, AUTH_HANDSHAKE_TIMEOUT_MS)
|
|
246
248
|
};
|
|
247
249
|
handshakeWaiters.add(waiter);
|
|
248
250
|
try {
|
|
249
|
-
await
|
|
251
|
+
await deferred.promise;
|
|
250
252
|
} finally {
|
|
251
253
|
clearTimeout(waiter.timeoutId);
|
|
252
254
|
handshakeWaiters.delete(waiter);
|
|
@@ -262,24 +264,18 @@ function client(options) {
|
|
|
262
264
|
if (updateSnapshot()) notify();
|
|
263
265
|
};
|
|
264
266
|
const notify = () => {
|
|
265
|
-
for (const cb of
|
|
267
|
+
for (const cb of subscribers) cb();
|
|
266
268
|
};
|
|
267
269
|
const updateSnapshot = () => {
|
|
268
|
-
const phase =
|
|
269
|
-
handshake: "handshake",
|
|
270
|
-
loading: "loading",
|
|
271
|
-
authenticated: "authenticated",
|
|
272
|
-
unauthenticated: "unauthenticated"
|
|
273
|
-
}[{ tag: token !== null && handshakePending ? "handshake" : isLoading ? "loading" : token !== null && authConfirmed ? "authenticated" : "unauthenticated" }.tag];
|
|
270
|
+
const phase = token !== null && handshakePending ? "handshake" : isLoading ? "loading" : token !== null && authConfirmed ? "authenticated" : "unauthenticated";
|
|
274
271
|
const next = {
|
|
275
272
|
phase,
|
|
276
273
|
isLoading: phase === "loading" || phase === "handshake",
|
|
277
274
|
isAuthenticated: phase === "authenticated",
|
|
278
275
|
token
|
|
279
276
|
};
|
|
280
|
-
const snapshot = Ref.getUnsafe(snapshotRef);
|
|
281
277
|
if (snapshot.phase === next.phase && snapshot.isLoading === next.isLoading && snapshot.isAuthenticated === next.isAuthenticated && snapshot.token === next.token) return false;
|
|
282
|
-
|
|
278
|
+
snapshot = next;
|
|
283
279
|
return true;
|
|
284
280
|
};
|
|
285
281
|
const finalizeLoadingState = () => {
|
|
@@ -357,16 +353,10 @@ function client(options) {
|
|
|
357
353
|
};
|
|
358
354
|
const passkeyAdapter = adapters.passkey ?? services.adapterFactories.passkey?.(adapterDeps);
|
|
359
355
|
const verifyCode = async (args) => {
|
|
360
|
-
return
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
} : args),
|
|
365
|
-
catch: (error) => error
|
|
366
|
-
}).pipe(Effect.map((result) => result), Effect.retry({
|
|
367
|
-
schedule: RETRY_SCHEDULE,
|
|
368
|
-
while: isTransientNetworkError
|
|
369
|
-
})));
|
|
356
|
+
return retryWithJitteredBackoff(() => requireHttpClient().action(requireApiRefs().signIn, "code" in args ? {
|
|
357
|
+
params: { code: args.code },
|
|
358
|
+
verifier: args.verifier
|
|
359
|
+
} : args), isTransientNetworkError);
|
|
370
360
|
};
|
|
371
361
|
const verifyCodeAndSetToken = async (args, opts) => {
|
|
372
362
|
const result = await verifyCode(args);
|
|
@@ -430,27 +420,28 @@ function client(options) {
|
|
|
430
420
|
})() : args ?? {};
|
|
431
421
|
const flow = typeof params.flow === "string" && params.flow.length > 0 ? params.flow : "signIn";
|
|
432
422
|
const signInKey = buildSignInRequestKey(provider, params);
|
|
433
|
-
const handleSignInActionResult = async (result,
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
await runtime.location.redirect(redirectUrl);
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
423
|
+
const handleSignInActionResult = async (result, resultOptions) => {
|
|
424
|
+
if (result.kind === "redirect") {
|
|
425
|
+
const redirectUrl = new URL(result.redirect);
|
|
426
|
+
if (resultOptions.persistVerifier) await storageSet(VERIFIER_STORAGE_KEY, result.verifier);
|
|
427
|
+
if (runtime.location) await runtime.location.redirect(redirectUrl);
|
|
428
|
+
return {
|
|
429
|
+
kind: "redirect",
|
|
430
|
+
redirect: redirectUrl,
|
|
431
|
+
verifier: result.verifier
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
if (result.kind === "totpRequired") return {
|
|
435
|
+
kind: "totpRequired",
|
|
436
|
+
verifier: result.verifier
|
|
437
|
+
};
|
|
438
|
+
if (result.kind === "deviceCode") return {
|
|
439
|
+
kind: "deviceCode",
|
|
440
|
+
deviceCode: normalizeDeviceCodeResult(result.deviceCode)
|
|
443
441
|
};
|
|
444
|
-
|
|
445
|
-
kind: "totpRequired",
|
|
446
|
-
verifier: totpRequiredResult.verifier
|
|
447
|
-
})), Match.when({ kind: "deviceCode" }, (deviceCodeResult) => Effect.succeed({
|
|
448
|
-
kind: "deviceCode",
|
|
449
|
-
deviceCode: normalizeDeviceCodeResult(deviceCodeResult.deviceCode)
|
|
450
|
-
})), Match.when({ kind: "signedIn" }, (signedInResult) => Effect.gen(function* () {
|
|
451
|
-
return (yield* Effect.promise(() => setTokenAndMaybeWait(options$1.shouldStore ? {
|
|
442
|
+
if (result.kind === "signedIn") return await setTokenAndMaybeWait(resultOptions.shouldStore ? {
|
|
452
443
|
shouldStore: true,
|
|
453
|
-
tokens:
|
|
444
|
+
tokens: result.tokens,
|
|
454
445
|
waitForHandshake: true,
|
|
455
446
|
context: {
|
|
456
447
|
provider,
|
|
@@ -458,46 +449,41 @@ function client(options) {
|
|
|
458
449
|
}
|
|
459
450
|
} : {
|
|
460
451
|
shouldStore: false,
|
|
461
|
-
tokens:
|
|
452
|
+
tokens: result.tokens === null ? null : { token: result.tokens.token },
|
|
462
453
|
waitForHandshake: true,
|
|
463
454
|
context: {
|
|
464
455
|
provider,
|
|
465
456
|
flow
|
|
466
457
|
}
|
|
467
|
-
})
|
|
468
|
-
|
|
458
|
+
}) ? { kind: "signedIn" } : { kind: "started" };
|
|
459
|
+
return { kind: "started" };
|
|
460
|
+
};
|
|
469
461
|
if (activeSignIn !== null) {
|
|
470
462
|
if (activeSignIn.key === signInKey) return await activeSignIn.promise;
|
|
471
463
|
throw new Error("Another sign-in flow is already in progress.");
|
|
472
464
|
}
|
|
473
|
-
const signInPromise =
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
args: {
|
|
478
|
-
provider,
|
|
479
|
-
params
|
|
480
|
-
}
|
|
481
|
-
}), {
|
|
482
|
-
shouldStore: false,
|
|
483
|
-
persistVerifier: false
|
|
484
|
-
});
|
|
485
|
-
const verifier = await storageGet(VERIFIER_STORAGE_KEY) ?? void 0;
|
|
486
|
-
await storageRemove(VERIFIER_STORAGE_KEY);
|
|
487
|
-
return await handleSignInActionResult(await convex.action(requireApiRefs().signIn, {
|
|
465
|
+
const signInPromise = (async () => {
|
|
466
|
+
if (proxy) return await handleSignInActionResult(await proxyFetch({
|
|
467
|
+
action: "auth:signIn",
|
|
468
|
+
args: {
|
|
488
469
|
provider,
|
|
489
|
-
params
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
470
|
+
params
|
|
471
|
+
}
|
|
472
|
+
}), {
|
|
473
|
+
shouldStore: false,
|
|
474
|
+
persistVerifier: false
|
|
475
|
+
});
|
|
476
|
+
const verifier = await storageGet(VERIFIER_STORAGE_KEY) ?? void 0;
|
|
477
|
+
await storageRemove(VERIFIER_STORAGE_KEY);
|
|
478
|
+
return await handleSignInActionResult(await convex.action(requireApiRefs().signIn, {
|
|
479
|
+
provider,
|
|
480
|
+
params,
|
|
481
|
+
verifier
|
|
482
|
+
}), {
|
|
483
|
+
shouldStore: true,
|
|
484
|
+
persistVerifier: true
|
|
485
|
+
});
|
|
486
|
+
})();
|
|
501
487
|
activeSignIn = {
|
|
502
488
|
key: signInKey,
|
|
503
489
|
promise: signInPromise
|
|
@@ -516,32 +502,28 @@ function client(options) {
|
|
|
516
502
|
* signed-out user is a no-op.
|
|
517
503
|
*/
|
|
518
504
|
const signOut = async () => {
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
yield* Effect.promise(() => setToken({
|
|
529
|
-
shouldStore: false,
|
|
530
|
-
tokens: null
|
|
531
|
-
}));
|
|
532
|
-
if (convex.clearAuth) convex.clearAuth();
|
|
533
|
-
return;
|
|
534
|
-
}
|
|
535
|
-
yield* Effect.tryPromise({
|
|
536
|
-
try: () => convex.action(requireApiRefs().signOut, {}),
|
|
537
|
-
catch: () => void 0
|
|
538
|
-
}).pipe(Effect.catch(() => Effect.succeed(void 0)));
|
|
539
|
-
yield* Effect.promise(() => setToken({
|
|
540
|
-
shouldStore: true,
|
|
505
|
+
if (proxy) {
|
|
506
|
+
try {
|
|
507
|
+
await proxyFetch({
|
|
508
|
+
action: "auth:signOut",
|
|
509
|
+
args: {}
|
|
510
|
+
});
|
|
511
|
+
} catch {}
|
|
512
|
+
await setToken({
|
|
513
|
+
shouldStore: false,
|
|
541
514
|
tokens: null
|
|
542
|
-
})
|
|
515
|
+
});
|
|
543
516
|
if (convex.clearAuth) convex.clearAuth();
|
|
544
|
-
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
try {
|
|
520
|
+
await convex.action(requireApiRefs().signOut, {});
|
|
521
|
+
} catch {}
|
|
522
|
+
await setToken({
|
|
523
|
+
shouldStore: true,
|
|
524
|
+
tokens: null
|
|
525
|
+
});
|
|
526
|
+
if (convex.clearAuth) convex.clearAuth();
|
|
545
527
|
};
|
|
546
528
|
const fetchAccessToken = async ({ forceRefreshToken }) => {
|
|
547
529
|
if (!forceRefreshToken) return token;
|
|
@@ -551,27 +533,25 @@ function client(options) {
|
|
|
551
533
|
const tokenBeforeRefresh = token;
|
|
552
534
|
return await withMutex("__convexAuthProxyRefresh", async () => {
|
|
553
535
|
if (token !== tokenBeforeRefresh) return token;
|
|
554
|
-
|
|
555
|
-
|
|
536
|
+
try {
|
|
537
|
+
const result = await retryWithJitteredBackoff(() => proxyFetch({
|
|
556
538
|
action: "auth:signIn",
|
|
557
539
|
args: { refreshToken: true }
|
|
558
|
-
}),
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
resyncConvexAuth: false
|
|
571
|
-
}))), Effect.catch((error) => Effect.sync(() => {
|
|
540
|
+
}), isRetriableProxyRefreshError);
|
|
541
|
+
if (isSignedInResult(result) && result.tokens) await setToken({
|
|
542
|
+
shouldStore: false,
|
|
543
|
+
tokens: { token: result.tokens.token },
|
|
544
|
+
resyncConvexAuth: false
|
|
545
|
+
});
|
|
546
|
+
else await setToken({
|
|
547
|
+
shouldStore: false,
|
|
548
|
+
tokens: null,
|
|
549
|
+
resyncConvexAuth: false
|
|
550
|
+
});
|
|
551
|
+
} catch (error) {
|
|
572
552
|
logMessage("convex-auth/client", LOG_LEVELS.ERROR, ["[convex-auth] Proxy refresh failed:", error]);
|
|
573
553
|
if (token === null) finalizeLoadingState();
|
|
574
|
-
}
|
|
554
|
+
}
|
|
575
555
|
return token;
|
|
576
556
|
});
|
|
577
557
|
}
|
|
@@ -584,7 +564,7 @@ function client(options) {
|
|
|
584
564
|
finalizeLoadingState();
|
|
585
565
|
return null;
|
|
586
566
|
}
|
|
587
|
-
await
|
|
567
|
+
await verifyCodeAndSetToken({ refreshToken }, { resyncConvexAuth: false });
|
|
588
568
|
return token;
|
|
589
569
|
});
|
|
590
570
|
};
|
|
@@ -595,18 +575,14 @@ function client(options) {
|
|
|
595
575
|
const code = location.searchParams.get("code");
|
|
596
576
|
if (!code) return;
|
|
597
577
|
handlingCodeFlow = true;
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
},
|
|
605
|
-
catch: () => void 0
|
|
606
|
-
}).pipe(Effect.catch(() => Effect.succeed(void 0)), Effect.ensuring(Effect.sync(() => {
|
|
578
|
+
try {
|
|
579
|
+
await signIn(void 0, { code });
|
|
580
|
+
const codeUrl = new URL(location.toString());
|
|
581
|
+
codeUrl.searchParams.delete("code");
|
|
582
|
+
await replaceUrl(codeUrl.pathname + codeUrl.search + codeUrl.hash);
|
|
583
|
+
} catch {} finally {
|
|
607
584
|
handlingCodeFlow = false;
|
|
608
|
-
}
|
|
609
|
-
handlingCodeFlow = false;
|
|
585
|
+
}
|
|
610
586
|
};
|
|
611
587
|
const hydrateFromStorage = async () => {
|
|
612
588
|
const storedToken = await storageGet(JWT_STORAGE_KEY) ?? null;
|
|
@@ -627,32 +603,44 @@ function client(options) {
|
|
|
627
603
|
* @returns An unsubscribe function.
|
|
628
604
|
*/
|
|
629
605
|
const onChange = (cb) => {
|
|
630
|
-
cb(
|
|
631
|
-
const wrapped = () => cb(
|
|
632
|
-
|
|
606
|
+
cb(snapshot);
|
|
607
|
+
const wrapped = () => cb(snapshot);
|
|
608
|
+
subscribers.add(wrapped);
|
|
633
609
|
return () => {
|
|
634
|
-
|
|
610
|
+
subscribers.delete(wrapped);
|
|
635
611
|
};
|
|
636
612
|
};
|
|
637
613
|
if (!proxy && runtime.sync) disposeStorageListener = runtime.sync.subscribe(key(JWT_STORAGE_KEY), (value) => {
|
|
638
|
-
|
|
614
|
+
setToken({
|
|
639
615
|
shouldStore: false,
|
|
640
616
|
tokens: value === null ? null : { token: value }
|
|
641
|
-
}))
|
|
617
|
+
}).catch((error) => {
|
|
618
|
+
logMessage("convex-auth/client", LOG_LEVELS.ERROR, ["[convex-auth] Storage event handler failed:", error]);
|
|
619
|
+
});
|
|
642
620
|
}) ?? null;
|
|
643
621
|
bindConvexAuth();
|
|
644
622
|
if (proxy) {
|
|
645
|
-
if (!hasServerToken && runtime.environment !== "server")
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
623
|
+
if (!hasServerToken && runtime.environment !== "server") fetchAccessToken({ forceRefreshToken: true }).catch((error) => {
|
|
624
|
+
logMessage("convex-auth/client", LOG_LEVELS.ERROR, ["[convex-auth] Proxy token refresh failed:", error]);
|
|
625
|
+
});
|
|
626
|
+
} else (async () => {
|
|
627
|
+
try {
|
|
628
|
+
await hydrateFromStorage();
|
|
629
|
+
await handleCodeFlow();
|
|
630
|
+
} catch {
|
|
631
|
+
try {
|
|
632
|
+
await setToken({
|
|
633
|
+
shouldStore: false,
|
|
634
|
+
tokens: null
|
|
635
|
+
});
|
|
636
|
+
} catch {}
|
|
637
|
+
}
|
|
638
|
+
})().catch((error) => {
|
|
639
|
+
logMessage("convex-auth/client", LOG_LEVELS.ERROR, ["[convex-auth] SPA initialization failed:", error]);
|
|
640
|
+
});
|
|
653
641
|
return {
|
|
654
642
|
get state() {
|
|
655
|
-
return
|
|
643
|
+
return snapshot;
|
|
656
644
|
},
|
|
657
645
|
param,
|
|
658
646
|
get invite() {
|
|
@@ -688,7 +676,7 @@ function client(options) {
|
|
|
688
676
|
error: createHandshakeError("AUTH_HANDSHAKE_REJECTED", { reason: "destroyed" })
|
|
689
677
|
});
|
|
690
678
|
disposeStorageListener?.();
|
|
691
|
-
|
|
679
|
+
subscribers.clear();
|
|
692
680
|
},
|
|
693
681
|
...passkeyAdapter ? { passkey: passkeyAdapter } : {}
|
|
694
682
|
};
|
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
import { ConvexError } from "convex/values";
|
|
2
|
-
import { Schema } from "effect";
|
|
3
2
|
|
|
4
3
|
//#region src/client/runtime/proxy.ts
|
|
5
4
|
const NETWORK_ERROR_PATTERN = /(network|fetch|load failed|failed to fetch)/i;
|
|
6
|
-
const ProxyErrorBodySchema = Schema.Struct({
|
|
7
|
-
error: Schema.optional(Schema.String),
|
|
8
|
-
authError: Schema.optional(Schema.Unknown)
|
|
9
|
-
});
|
|
10
5
|
/** @internal */
|
|
11
6
|
function isTransientNetworkError(error) {
|
|
12
7
|
return error instanceof TypeError || error instanceof Error && NETWORK_ERROR_PATTERN.test(error.message || "");
|
|
@@ -22,7 +17,12 @@ function isRetriableProxyRefreshError(error) {
|
|
|
22
17
|
}
|
|
23
18
|
/** @internal */
|
|
24
19
|
function parseProxyErrorBody(value) {
|
|
25
|
-
|
|
20
|
+
if (typeof value !== "object" || value === null) return {};
|
|
21
|
+
const obj = value;
|
|
22
|
+
return {
|
|
23
|
+
error: typeof obj.error === "string" ? obj.error : void 0,
|
|
24
|
+
authError: obj.authError
|
|
25
|
+
};
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
//#endregion
|
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
import { Layer, ServiceMap } from "effect";
|
|
2
|
-
|
|
3
1
|
//#region src/client/services/adapters.ts
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const ClientAdaptersLive = (adapters) => Layer.succeed(ClientAdaptersService)(adapters);
|
|
7
|
-
const ClientAdapterFactoriesLive = (adapterFactories) => Layer.succeed(ClientAdapterFactoriesService)(adapterFactories);
|
|
2
|
+
const ClientAdaptersLive = (adapters) => adapters;
|
|
3
|
+
const ClientAdapterFactoriesLive = (adapterFactories) => adapterFactories;
|
|
8
4
|
|
|
9
5
|
//#endregion
|
|
10
|
-
export { ClientAdapterFactoriesLive,
|
|
6
|
+
export { ClientAdapterFactoriesLive, ClientAdaptersLive };
|
|
11
7
|
//# sourceMappingURL=adapters.js.map
|
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
import { Layer, ServiceMap } from "effect";
|
|
2
|
-
|
|
3
1
|
//#region src/client/services/http.ts
|
|
4
|
-
|
|
5
|
-
const ClientHttpLive = (httpClient) => Layer.succeed(ClientHttpService)({ httpClient });
|
|
2
|
+
const ClientHttpLive = (httpClient) => ({ httpClient });
|
|
6
3
|
|
|
7
4
|
//#endregion
|
|
8
|
-
export { ClientHttpLive
|
|
5
|
+
export { ClientHttpLive };
|
|
9
6
|
//# sourceMappingURL=http.js.map
|
|
@@ -1,16 +1,10 @@
|
|
|
1
|
-
import { ClientAdapterFactoriesService, ClientAdaptersService } from "./adapters.js";
|
|
2
|
-
import { ClientHttpService } from "./http.js";
|
|
3
|
-
import { ClientRuntimeService } from "./runtime.js";
|
|
4
|
-
import { Effect, Layer, ServiceMap } from "effect";
|
|
5
|
-
|
|
6
1
|
//#region src/client/services/resolve.ts
|
|
7
|
-
function resolveClientServices(
|
|
8
|
-
const context = Effect.runSync(Effect.scoped(Layer.build(layer)));
|
|
2
|
+
function resolveClientServices(input) {
|
|
9
3
|
return {
|
|
10
|
-
runtime:
|
|
11
|
-
adapters:
|
|
12
|
-
adapterFactories:
|
|
13
|
-
httpClient:
|
|
4
|
+
runtime: input.runtime,
|
|
5
|
+
adapters: input.adapters,
|
|
6
|
+
adapterFactories: input.adapterFactories,
|
|
7
|
+
httpClient: input.http.httpClient
|
|
14
8
|
};
|
|
15
9
|
}
|
|
16
10
|
|
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
import { Layer, ServiceMap } from "effect";
|
|
2
|
-
|
|
3
1
|
//#region src/client/services/runtime.ts
|
|
4
|
-
|
|
5
|
-
const ClientRuntimeLive = (runtime) => Layer.succeed(ClientRuntimeService)(runtime);
|
|
2
|
+
const ClientRuntimeLive = (runtime) => runtime;
|
|
6
3
|
|
|
7
4
|
//#endregion
|
|
8
|
-
export { ClientRuntimeLive
|
|
5
|
+
export { ClientRuntimeLive };
|
|
9
6
|
//# sourceMappingURL=runtime.js.map
|