@fluixi/oauth2 1.0.0-alpha.10 → 1.0.0-alpha.12

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.
Files changed (46) hide show
  1. package/LICENSE +1 -1
  2. package/dist/backchannel.cjs +462 -0
  3. package/dist/backchannel.d.ts +39 -0
  4. package/dist/backchannel.d.ts.map +1 -0
  5. package/dist/backchannel.js +97 -0
  6. package/dist/backchannel.mjs +307 -0
  7. package/dist/{browser-36FHB6KG.mjs → browser-VTMW33ZW.mjs} +2 -2
  8. package/dist/browser.cjs +6 -1
  9. package/dist/browser.mjs +6 -1
  10. package/dist/callback.mjs +1 -1
  11. package/dist/chunk-6TQMM3FM.mjs +108 -0
  12. package/dist/{chunk-OGS6MQ3D.mjs → chunk-MT6KXLLJ.mjs} +1 -1
  13. package/dist/{chunk-F52RUVDM.mjs → chunk-WRXP7BLV.mjs} +1 -1
  14. package/dist/discovery.cjs +6 -1
  15. package/dist/discovery.d.ts.map +1 -1
  16. package/dist/discovery.js +6 -1
  17. package/dist/discovery.mjs +6 -1
  18. package/dist/dpop-QIL3GC4U.mjs +22 -0
  19. package/dist/{dpop-GOWCCCFR.mjs → dpop-ZVQMDWG5.mjs} +4 -4
  20. package/dist/index.cjs +107 -7
  21. package/dist/index.d.ts +2 -0
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +1 -0
  24. package/dist/index.mjs +111 -11
  25. package/dist/server.cjs +649 -137
  26. package/dist/server.d.ts +64 -4
  27. package/dist/server.d.ts.map +1 -1
  28. package/dist/server.js +376 -61
  29. package/dist/server.mjs +532 -139
  30. package/dist/service-client.cjs +7 -2
  31. package/dist/service-client.mjs +9 -4
  32. package/dist/service.cjs +104 -5
  33. package/dist/service.d.ts.map +1 -1
  34. package/dist/service.js +38 -6
  35. package/dist/service.mjs +105 -6
  36. package/dist/session-monitor.cjs +98 -0
  37. package/dist/session-monitor.d.ts +32 -0
  38. package/dist/session-monitor.d.ts.map +1 -0
  39. package/dist/session-monitor.js +104 -0
  40. package/dist/session-monitor.mjs +77 -0
  41. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  42. package/dist/types.d.ts +8 -0
  43. package/dist/types.d.ts.map +1 -1
  44. package/dist/verify.cjs +7 -2
  45. package/dist/verify.mjs +12 -7
  46. package/package.json +15 -15
package/dist/index.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! @fluixi/oauth2 v1.0.0-alpha.10 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
1
+ /*! @fluixi/oauth2 v1.0.0-alpha.12 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
2
2
  "use strict";
3
3
  var __create = Object.create;
4
4
  var __defProp = Object.defineProperty;
@@ -51,7 +51,8 @@ __export(index_exports, {
51
51
  paramsFromUrl: () => paramsFromUrl,
52
52
  proofFor: () => proofFor,
53
53
  safeReturnTo: () => safeReturnTo,
54
- thumbprintOf: () => thumbprintOf
54
+ thumbprintOf: () => thumbprintOf,
55
+ watchProviderSession: () => watchProviderSession
55
56
  });
56
57
  module.exports = __toCommonJS(index_exports);
57
58
 
@@ -364,8 +365,12 @@ async function discover(issuer, fetcher = fetch) {
364
365
  try {
365
366
  response = await fetcher(documentUrl(issuer));
366
367
  } catch (cause) {
368
+ const why = cause instanceof Error ? cause.message : String(cause);
367
369
  throw new OAuthError(
368
- `Could not read the discovery document at ${documentUrl(issuer)}. In a browser this is usually the provider refusing a cross-origin read: give \`endpoints\` directly, or resolve them on a server. (${cause instanceof Error ? cause.message : String(cause)})`,
370
+ typeof window === "undefined" ? (
371
+ // On a server nothing blocks the read: the provider is down, or the address is wrong.
372
+ `Could not reach the provider at ${documentUrl(issuer)}: is it running, and is the issuer right? (${why})`
373
+ ) : `Could not read the discovery document at ${documentUrl(issuer)}. In a browser this is usually the provider refusing a cross-origin read: give \`endpoints\` directly, or resolve them on a server. (${why})`,
369
374
  "discovery_unreachable"
370
375
  );
371
376
  }
@@ -390,6 +395,7 @@ async function discover(issuer, fetcher = fetch) {
390
395
  jwks: doc.jwks_uri,
391
396
  introspection: doc.introspection_endpoint,
392
397
  revocation: doc.revocation_endpoint,
398
+ checkSession: doc.check_session_iframe,
393
399
  issuer: doc.issuer ?? issuer
394
400
  };
395
401
  })();
@@ -848,6 +854,81 @@ function createServerClient(config, fetcher = fetch) {
848
854
  };
849
855
  }
850
856
 
857
+ // src/session-monitor.ts
858
+ var SETTLE_MS = 15e3;
859
+ var GUARD_KEY = "fx-session-check";
860
+ function recentlyActed() {
861
+ try {
862
+ return Date.now() - Number(sessionStorage.getItem(GUARD_KEY) ?? 0) < SETTLE_MS;
863
+ } catch {
864
+ return false;
865
+ }
866
+ }
867
+ function markActed() {
868
+ try {
869
+ sessionStorage.setItem(GUARD_KEY, String(Date.now()));
870
+ } catch {
871
+ }
872
+ }
873
+ function watchProviderSession(options = {}) {
874
+ if (typeof window === "undefined" || typeof document === "undefined") return () => {
875
+ };
876
+ const base = (options.basePath ?? "/auth").replace(/\/$/, "");
877
+ const fetcher = options.fetcher ?? fetch;
878
+ const onChange = options.onChange ?? (() => {
879
+ const login = new URLSearchParams({ prompt: "none", returnTo: location.pathname + location.search });
880
+ location.assign(`${base}/login?${login}`);
881
+ });
882
+ let stopped = false;
883
+ const cleanups = [];
884
+ const stop = () => {
885
+ stopped = true;
886
+ for (const cleanup of cleanups.splice(0)) cleanup();
887
+ };
888
+ void (async () => {
889
+ const check = await fetcher(`${base}/session-check`, { credentials: "same-origin" }).then((response) => response.ok ? response.json() : null).catch(() => null);
890
+ if (stopped || !check?.frame || !check.state) return;
891
+ const origin = new URL(check.frame).origin;
892
+ const frame = document.createElement("iframe");
893
+ frame.src = check.frame;
894
+ frame.title = "session check";
895
+ frame.setAttribute("aria-hidden", "true");
896
+ frame.tabIndex = -1;
897
+ frame.style.cssText = "position:absolute;width:0;height:0;border:0;visibility:hidden";
898
+ const message = `${check.clientId} ${check.state}`;
899
+ const ask = () => {
900
+ if (!stopped && document.visibilityState === "visible") frame.contentWindow?.postMessage(message, origin);
901
+ };
902
+ let confirmed = false;
903
+ const answer = (event) => {
904
+ if (event.origin !== origin || event.source !== frame.contentWindow) return;
905
+ if (event.data === "unchanged") confirmed = true;
906
+ else if (event.data === "changed") {
907
+ stop();
908
+ if (!confirmed && recentlyActed()) return;
909
+ markActed();
910
+ onChange();
911
+ } else if (event.data === "error") {
912
+ stop();
913
+ }
914
+ };
915
+ window.addEventListener("message", answer);
916
+ frame.addEventListener("load", ask);
917
+ document.addEventListener("visibilitychange", ask);
918
+ window.addEventListener("focus", ask);
919
+ const timer = setInterval(ask, options.intervalMs ?? 3e3);
920
+ document.body.appendChild(frame);
921
+ cleanups.push(() => {
922
+ window.removeEventListener("message", answer);
923
+ document.removeEventListener("visibilitychange", ask);
924
+ window.removeEventListener("focus", ask);
925
+ clearInterval(timer);
926
+ frame.remove();
927
+ });
928
+ })();
929
+ return stop;
930
+ }
931
+
851
932
  // src/service.ts
852
933
  function paramsFromUrl(url) {
853
934
  const href = url ?? (typeof location === "undefined" ? null : location.href);
@@ -874,16 +955,19 @@ function createAuthService(options) {
874
955
  function buildAuthService(options) {
875
956
  const client = clientFor(options);
876
957
  let latest = null;
958
+ const fetchUserFor = async (token) => {
959
+ if (options.fetchUser) return options.fetchUser(token);
960
+ return client.fetchUser(token === COOKIE_SESSION ? void 0 : token);
961
+ };
962
+ const resumesFromCookie = options.config.tokens === "server" && typeof window !== "undefined";
963
+ const [ready, setReady] = (0, import_signal.createSignal)(!resumesFromCookie);
877
964
  const session = (0, import_session.createSession)({
878
965
  /** The credential is the callback, so this is the code exchange. */
879
966
  async login(params) {
880
967
  latest = await client.handleCallback(params);
881
968
  return { token: latest.accessToken, expiresAt: latest.expiresAt };
882
969
  },
883
- async fetchUser(token) {
884
- if (options.fetchUser) return options.fetchUser(token);
885
- return client.fetchUser(token === COOKIE_SESSION ? void 0 : token);
886
- },
970
+ fetchUser: fetchUserFor,
887
971
  async refresh() {
888
972
  latest = await client.refresh();
889
973
  return { token: latest.accessToken, expiresAt: latest.expiresAt };
@@ -906,11 +990,27 @@ function buildAuthService(options) {
906
990
  });
907
991
  const hooks = (0, import_client.createAuthHooks)({
908
992
  session,
993
+ ready,
909
994
  navigate: options.navigate,
910
995
  loginPath: options.loginPath ?? "/login",
911
996
  homePath: options.homePath ?? "/",
912
997
  forbiddenPath: options.forbiddenPath ?? "/403"
913
998
  });
999
+ if (resumesFromCookie && options.config.tokens === "server" && options.config.watchSession !== false) {
1000
+ watchProviderSession({ basePath: options.config.basePath });
1001
+ }
1002
+ const renderedSignedOut = hooks.store.seeded && hooks.store.value().user == null;
1003
+ if (resumesFromCookie && renderedSignedOut) setReady(true);
1004
+ else if (resumesFromCookie) {
1005
+ void (async () => {
1006
+ try {
1007
+ await session.adopt(COOKIE_SESSION, await fetchUserFor(COOKIE_SESSION));
1008
+ } catch {
1009
+ } finally {
1010
+ setReady(true);
1011
+ }
1012
+ })();
1013
+ }
914
1014
  return Object.assign(session, {
915
1015
  client,
916
1016
  protectedRoute: hooks.protectedRoute,
package/dist/index.d.ts CHANGED
@@ -93,6 +93,8 @@ export type { AuthService, AuthServiceOptions, AuthCredentials } from './service
93
93
  export { createBrowserClient } from './browser.js';
94
94
  export { createServerClient, COOKIE_SESSION } from './server-client.js';
95
95
  export { discover, clearDiscoveryCache } from './discovery.js';
96
+ export { watchProviderSession } from './session-monitor.js';
97
+ export type { SessionCheck, WatchOptions } from './session-monitor.js';
96
98
  export { createPkcePair, createVerifier, createChallenge, createStateValue, expiryFromJwt, } from './pkce.js';
97
99
  export { createDpopKey, proofFor, thumbprintOf, boundKeyOf } from './dpop.js';
98
100
  export type { DpopKey, ProofOptions } from './dpop.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyFG;AACH,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAChE,YAAY,EAAE,WAAW,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAErF,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAExE,OAAO,EAAE,QAAQ,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAC/D,OAAO,EACL,cAAc,EACd,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,aAAa,GACd,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC9E,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAEvD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEtD,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,YAAY,EACV,UAAU,EACV,UAAU,EACV,UAAU,EACV,aAAa,EACb,cAAc,EACd,SAAS,EACT,YAAY,EACZ,YAAY,EACZ,MAAM,GACP,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyFG;AACH,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAChE,YAAY,EAAE,WAAW,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAErF,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAExE,OAAO,EAAE,QAAQ,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAC/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACvE,OAAO,EACL,cAAc,EACd,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,aAAa,GACd,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC9E,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAEvD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEtD,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,YAAY,EACV,UAAU,EACV,UAAU,EACV,UAAU,EACV,aAAa,EACb,cAAc,EACd,SAAS,EACT,YAAY,EACZ,YAAY,EACZ,MAAM,GACP,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC"}
package/dist/index.js CHANGED
@@ -92,6 +92,7 @@ export { createAuthService, paramsFromUrl } from './service.js';
92
92
  export { createBrowserClient } from './browser.js';
93
93
  export { createServerClient, COOKIE_SESSION } from './server-client.js';
94
94
  export { discover, clearDiscoveryCache } from './discovery.js';
95
+ export { watchProviderSession } from './session-monitor.js';
95
96
  export { createPkcePair, createVerifier, createChallenge, createStateValue, expiryFromJwt, } from './pkce.js';
96
97
  export { createDpopKey, proofFor, thumbprintOf, boundKeyOf } from './dpop.js';
97
98
  export { safeReturnTo } from './return-to.js';
package/dist/index.mjs CHANGED
@@ -1,8 +1,8 @@
1
- /*! @fluixi/oauth2 v1.0.0-alpha.10 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
2
- import "./chunk-OGS6MQ3D.mjs";
1
+ /*! @fluixi/oauth2 v1.0.0-alpha.12 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
2
+ import "./chunk-MT6KXLLJ.mjs";
3
3
 
4
4
  // src/service.ts
5
- import { markReactiveScope, openReactiveScope, withReactiveScope } from "@fluixi/reactive/signal";
5
+ import { createSignal, markReactiveScope, openReactiveScope, withReactiveScope } from "@fluixi/reactive/signal";
6
6
  import { createSession } from "@fluixi/session";
7
7
  import { createAuthHooks } from "@fluixi/auth/client";
8
8
 
@@ -112,7 +112,7 @@ function resolveMethod(auth) {
112
112
  }
113
113
  async function loadJose() {
114
114
  try {
115
- return await import("./browser-36FHB6KG.mjs");
115
+ return await import("./browser-VTMW33ZW.mjs");
116
116
  } catch {
117
117
  throw new OAuthError(
118
118
  "private_key_jwt needs `jose`. Install it: npm i jose. It is an optional peer, so a client using a secret does not carry it.",
@@ -152,7 +152,7 @@ async function applyClientAuth(auth, endpoint, headers, form) {
152
152
  // src/dpop.ts
153
153
  async function loadJose2() {
154
154
  try {
155
- return await import("./browser-36FHB6KG.mjs");
155
+ return await import("./browser-VTMW33ZW.mjs");
156
156
  } catch {
157
157
  throw new OAuthError(
158
158
  "DPoP needs `jose`. Install it: npm i jose. It is an optional peer, so a client not using DPoP does not carry it.",
@@ -310,8 +310,12 @@ async function discover(issuer, fetcher = fetch) {
310
310
  try {
311
311
  response = await fetcher(documentUrl(issuer));
312
312
  } catch (cause) {
313
+ const why = cause instanceof Error ? cause.message : String(cause);
313
314
  throw new OAuthError(
314
- `Could not read the discovery document at ${documentUrl(issuer)}. In a browser this is usually the provider refusing a cross-origin read: give \`endpoints\` directly, or resolve them on a server. (${cause instanceof Error ? cause.message : String(cause)})`,
315
+ typeof window === "undefined" ? (
316
+ // On a server nothing blocks the read: the provider is down, or the address is wrong.
317
+ `Could not reach the provider at ${documentUrl(issuer)}: is it running, and is the issuer right? (${why})`
318
+ ) : `Could not read the discovery document at ${documentUrl(issuer)}. In a browser this is usually the provider refusing a cross-origin read: give \`endpoints\` directly, or resolve them on a server. (${why})`,
315
319
  "discovery_unreachable"
316
320
  );
317
321
  }
@@ -336,6 +340,7 @@ async function discover(issuer, fetcher = fetch) {
336
340
  jwks: doc.jwks_uri,
337
341
  introspection: doc.introspection_endpoint,
338
342
  revocation: doc.revocation_endpoint,
343
+ checkSession: doc.check_session_iframe,
339
344
  issuer: doc.issuer ?? issuer
340
345
  };
341
346
  })();
@@ -794,6 +799,81 @@ function createServerClient(config, fetcher = fetch) {
794
799
  };
795
800
  }
796
801
 
802
+ // src/session-monitor.ts
803
+ var SETTLE_MS = 15e3;
804
+ var GUARD_KEY = "fx-session-check";
805
+ function recentlyActed() {
806
+ try {
807
+ return Date.now() - Number(sessionStorage.getItem(GUARD_KEY) ?? 0) < SETTLE_MS;
808
+ } catch {
809
+ return false;
810
+ }
811
+ }
812
+ function markActed() {
813
+ try {
814
+ sessionStorage.setItem(GUARD_KEY, String(Date.now()));
815
+ } catch {
816
+ }
817
+ }
818
+ function watchProviderSession(options = {}) {
819
+ if (typeof window === "undefined" || typeof document === "undefined") return () => {
820
+ };
821
+ const base = (options.basePath ?? "/auth").replace(/\/$/, "");
822
+ const fetcher = options.fetcher ?? fetch;
823
+ const onChange = options.onChange ?? (() => {
824
+ const login = new URLSearchParams({ prompt: "none", returnTo: location.pathname + location.search });
825
+ location.assign(`${base}/login?${login}`);
826
+ });
827
+ let stopped = false;
828
+ const cleanups = [];
829
+ const stop = () => {
830
+ stopped = true;
831
+ for (const cleanup of cleanups.splice(0)) cleanup();
832
+ };
833
+ void (async () => {
834
+ const check = await fetcher(`${base}/session-check`, { credentials: "same-origin" }).then((response) => response.ok ? response.json() : null).catch(() => null);
835
+ if (stopped || !check?.frame || !check.state) return;
836
+ const origin = new URL(check.frame).origin;
837
+ const frame = document.createElement("iframe");
838
+ frame.src = check.frame;
839
+ frame.title = "session check";
840
+ frame.setAttribute("aria-hidden", "true");
841
+ frame.tabIndex = -1;
842
+ frame.style.cssText = "position:absolute;width:0;height:0;border:0;visibility:hidden";
843
+ const message = `${check.clientId} ${check.state}`;
844
+ const ask = () => {
845
+ if (!stopped && document.visibilityState === "visible") frame.contentWindow?.postMessage(message, origin);
846
+ };
847
+ let confirmed = false;
848
+ const answer = (event) => {
849
+ if (event.origin !== origin || event.source !== frame.contentWindow) return;
850
+ if (event.data === "unchanged") confirmed = true;
851
+ else if (event.data === "changed") {
852
+ stop();
853
+ if (!confirmed && recentlyActed()) return;
854
+ markActed();
855
+ onChange();
856
+ } else if (event.data === "error") {
857
+ stop();
858
+ }
859
+ };
860
+ window.addEventListener("message", answer);
861
+ frame.addEventListener("load", ask);
862
+ document.addEventListener("visibilitychange", ask);
863
+ window.addEventListener("focus", ask);
864
+ const timer = setInterval(ask, options.intervalMs ?? 3e3);
865
+ document.body.appendChild(frame);
866
+ cleanups.push(() => {
867
+ window.removeEventListener("message", answer);
868
+ document.removeEventListener("visibilitychange", ask);
869
+ window.removeEventListener("focus", ask);
870
+ clearInterval(timer);
871
+ frame.remove();
872
+ });
873
+ })();
874
+ return stop;
875
+ }
876
+
797
877
  // src/service.ts
798
878
  function paramsFromUrl(url) {
799
879
  const href = url ?? (typeof location === "undefined" ? null : location.href);
@@ -820,16 +900,19 @@ function createAuthService(options) {
820
900
  function buildAuthService(options) {
821
901
  const client = clientFor(options);
822
902
  let latest = null;
903
+ const fetchUserFor = async (token) => {
904
+ if (options.fetchUser) return options.fetchUser(token);
905
+ return client.fetchUser(token === COOKIE_SESSION ? void 0 : token);
906
+ };
907
+ const resumesFromCookie = options.config.tokens === "server" && typeof window !== "undefined";
908
+ const [ready, setReady] = createSignal(!resumesFromCookie);
823
909
  const session = createSession({
824
910
  /** The credential is the callback, so this is the code exchange. */
825
911
  async login(params) {
826
912
  latest = await client.handleCallback(params);
827
913
  return { token: latest.accessToken, expiresAt: latest.expiresAt };
828
914
  },
829
- async fetchUser(token) {
830
- if (options.fetchUser) return options.fetchUser(token);
831
- return client.fetchUser(token === COOKIE_SESSION ? void 0 : token);
832
- },
915
+ fetchUser: fetchUserFor,
833
916
  async refresh() {
834
917
  latest = await client.refresh();
835
918
  return { token: latest.accessToken, expiresAt: latest.expiresAt };
@@ -852,11 +935,27 @@ function buildAuthService(options) {
852
935
  });
853
936
  const hooks = createAuthHooks({
854
937
  session,
938
+ ready,
855
939
  navigate: options.navigate,
856
940
  loginPath: options.loginPath ?? "/login",
857
941
  homePath: options.homePath ?? "/",
858
942
  forbiddenPath: options.forbiddenPath ?? "/403"
859
943
  });
944
+ if (resumesFromCookie && options.config.tokens === "server" && options.config.watchSession !== false) {
945
+ watchProviderSession({ basePath: options.config.basePath });
946
+ }
947
+ const renderedSignedOut = hooks.store.seeded && hooks.store.value().user == null;
948
+ if (resumesFromCookie && renderedSignedOut) setReady(true);
949
+ else if (resumesFromCookie) {
950
+ void (async () => {
951
+ try {
952
+ await session.adopt(COOKIE_SESSION, await fetchUserFor(COOKIE_SESSION));
953
+ } catch {
954
+ } finally {
955
+ setReady(true);
956
+ }
957
+ })();
958
+ }
860
959
  return Object.assign(session, {
861
960
  client,
862
961
  protectedRoute: hooks.protectedRoute,
@@ -930,5 +1029,6 @@ export {
930
1029
  paramsFromUrl,
931
1030
  proofFor,
932
1031
  safeReturnTo,
933
- thumbprintOf
1032
+ thumbprintOf,
1033
+ watchProviderSession
934
1034
  };