@fluixi/oauth2 1.0.0-alpha.8 → 1.0.0-alpha.99
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/LICENSE +1 -1
- package/dist/backchannel.cjs +462 -0
- package/dist/backchannel.d.ts +39 -0
- package/dist/backchannel.d.ts.map +1 -0
- package/dist/backchannel.js +97 -0
- package/dist/backchannel.mjs +307 -0
- package/dist/{browser-NYKDDB73.mjs → browser-LQSO5OS2.mjs} +2 -2
- package/dist/browser.cjs +6 -1
- package/dist/browser.mjs +6 -1
- package/dist/callback.cjs +8 -7
- package/dist/callback.js +1 -1
- package/dist/callback.mjs +3 -2
- package/dist/chunk-32562UUL.mjs +108 -0
- package/dist/{chunk-7R2PVIX6.mjs → chunk-P6SSCHQX.mjs} +1 -1
- package/dist/{chunk-QPL6B2YE.mjs → chunk-V27WQGTT.mjs} +1 -1
- package/dist/discovery.cjs +6 -1
- package/dist/discovery.d.ts.map +1 -1
- package/dist/discovery.js +6 -1
- package/dist/discovery.mjs +6 -1
- package/dist/dpop-2Q6R3KLB.mjs +22 -0
- package/dist/{dpop-BR4S6SCZ.mjs → dpop-4FGDUD4B.mjs} +4 -4
- package/dist/index.cjs +116 -8
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.mjs +119 -11
- package/dist/module.cjs +1 -1
- package/dist/module.mjs +1 -1
- package/dist/server.cjs +649 -137
- package/dist/server.d.ts +64 -4
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +376 -61
- package/dist/server.mjs +532 -139
- package/dist/service-client.cjs +7 -2
- package/dist/service-client.mjs +9 -4
- package/dist/service.cjs +112 -5
- package/dist/service.d.ts +0 -14
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +48 -5
- package/dist/service.mjs +112 -5
- package/dist/session-monitor.cjs +98 -0
- package/dist/session-monitor.d.ts +32 -0
- package/dist/session-monitor.d.ts.map +1 -0
- package/dist/session-monitor.js +104 -0
- package/dist/session-monitor.mjs +77 -0
- package/dist/tokens.cjs +1 -1
- package/dist/tokens.d.ts +2 -2
- package/dist/tokens.d.ts.map +1 -1
- package/dist/tokens.js +1 -1
- package/dist/tokens.mjs +1 -1
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/dist/types.d.ts +8 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/verify.cjs +7 -2
- package/dist/verify.mjs +12 -7
- package/package.json +17 -15
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @fluixi/oauth2 v1.0.0-alpha.
|
|
1
|
+
/*! @fluixi/oauth2 v1.0.0-alpha.99 | (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,11 +51,13 @@ __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
|
|
|
58
59
|
// src/service.ts
|
|
60
|
+
var import_signal = require("@fluixi/reactive/signal");
|
|
59
61
|
var import_session = require("@fluixi/session");
|
|
60
62
|
var import_client = require("@fluixi/auth/client");
|
|
61
63
|
|
|
@@ -363,8 +365,12 @@ async function discover(issuer, fetcher = fetch) {
|
|
|
363
365
|
try {
|
|
364
366
|
response = await fetcher(documentUrl(issuer));
|
|
365
367
|
} catch (cause) {
|
|
368
|
+
const why = cause instanceof Error ? cause.message : String(cause);
|
|
366
369
|
throw new OAuthError(
|
|
367
|
-
|
|
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})`,
|
|
368
374
|
"discovery_unreachable"
|
|
369
375
|
);
|
|
370
376
|
}
|
|
@@ -389,6 +395,7 @@ async function discover(issuer, fetcher = fetch) {
|
|
|
389
395
|
jwks: doc.jwks_uri,
|
|
390
396
|
introspection: doc.introspection_endpoint,
|
|
391
397
|
revocation: doc.revocation_endpoint,
|
|
398
|
+
checkSession: doc.check_session_iframe,
|
|
392
399
|
issuer: doc.issuer ?? issuer
|
|
393
400
|
};
|
|
394
401
|
})();
|
|
@@ -847,6 +854,81 @@ function createServerClient(config, fetcher = fetch) {
|
|
|
847
854
|
};
|
|
848
855
|
}
|
|
849
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
|
+
|
|
850
932
|
// src/service.ts
|
|
851
933
|
function paramsFromUrl(url) {
|
|
852
934
|
const href = url ?? (typeof location === "undefined" ? null : location.href);
|
|
@@ -864,18 +946,28 @@ function clientFor(options) {
|
|
|
864
946
|
return options.config.tokens === "server" ? createServerClient(options.config) : createBrowserClient(options.config);
|
|
865
947
|
}
|
|
866
948
|
function createAuthService(options) {
|
|
949
|
+
const tokens = options.config.tokens;
|
|
950
|
+
const scope = (0, import_signal.openReactiveScope)({ kind: "service", name: "AuthService", ...tokens ? { detail: `tokens: ${tokens}` } : {} });
|
|
951
|
+
const service = (0, import_signal.withReactiveScope)(scope, () => buildAuthService(options));
|
|
952
|
+
(0, import_signal.markReactiveScope)(service, scope);
|
|
953
|
+
return service;
|
|
954
|
+
}
|
|
955
|
+
function buildAuthService(options) {
|
|
867
956
|
const client = clientFor(options);
|
|
868
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);
|
|
869
964
|
const session = (0, import_session.createSession)({
|
|
870
965
|
/** The credential is the callback, so this is the code exchange. */
|
|
871
966
|
async login(params) {
|
|
872
967
|
latest = await client.handleCallback(params);
|
|
873
968
|
return { token: latest.accessToken, expiresAt: latest.expiresAt };
|
|
874
969
|
},
|
|
875
|
-
|
|
876
|
-
if (options.fetchUser) return options.fetchUser(token);
|
|
877
|
-
return client.fetchUser(token === COOKIE_SESSION ? void 0 : token);
|
|
878
|
-
},
|
|
970
|
+
fetchUser: fetchUserFor,
|
|
879
971
|
async refresh() {
|
|
880
972
|
latest = await client.refresh();
|
|
881
973
|
return { token: latest.accessToken, expiresAt: latest.expiresAt };
|
|
@@ -898,11 +990,27 @@ function createAuthService(options) {
|
|
|
898
990
|
});
|
|
899
991
|
const hooks = (0, import_client.createAuthHooks)({
|
|
900
992
|
session,
|
|
993
|
+
ready,
|
|
901
994
|
navigate: options.navigate,
|
|
902
995
|
loginPath: options.loginPath ?? "/login",
|
|
903
996
|
homePath: options.homePath ?? "/",
|
|
904
997
|
forbiddenPath: options.forbiddenPath ?? "/403"
|
|
905
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
|
+
}
|
|
906
1014
|
return Object.assign(session, {
|
|
907
1015
|
client,
|
|
908
1016
|
protectedRoute: hooks.protectedRoute,
|
|
@@ -938,7 +1046,7 @@ function createAuthService(options) {
|
|
|
938
1046
|
}
|
|
939
1047
|
|
|
940
1048
|
// src/tokens.ts
|
|
941
|
-
var import_di = require("@fluixi/
|
|
1049
|
+
var import_di = require("@fluixi/di");
|
|
942
1050
|
var AuthServiceToken = (0, import_di.injectionToken)("AuthService");
|
|
943
1051
|
var AuthClientToken = (0, import_di.injectionToken)("AuthClient");
|
|
944
1052
|
|
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';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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,7 +1,8 @@
|
|
|
1
|
-
/*! @fluixi/oauth2 v1.0.0-alpha.
|
|
2
|
-
import "./chunk-
|
|
1
|
+
/*! @fluixi/oauth2 v1.0.0-alpha.99 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
|
|
2
|
+
import "./chunk-P6SSCHQX.mjs";
|
|
3
3
|
|
|
4
4
|
// src/service.ts
|
|
5
|
+
import { createSignal, markReactiveScope, openReactiveScope, withReactiveScope } from "@fluixi/reactive/signal";
|
|
5
6
|
import { createSession } from "@fluixi/session";
|
|
6
7
|
import { createAuthHooks } from "@fluixi/auth/client";
|
|
7
8
|
|
|
@@ -111,7 +112,7 @@ function resolveMethod(auth) {
|
|
|
111
112
|
}
|
|
112
113
|
async function loadJose() {
|
|
113
114
|
try {
|
|
114
|
-
return await import("./browser-
|
|
115
|
+
return await import("./browser-LQSO5OS2.mjs");
|
|
115
116
|
} catch {
|
|
116
117
|
throw new OAuthError(
|
|
117
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.",
|
|
@@ -151,7 +152,7 @@ async function applyClientAuth(auth, endpoint, headers, form) {
|
|
|
151
152
|
// src/dpop.ts
|
|
152
153
|
async function loadJose2() {
|
|
153
154
|
try {
|
|
154
|
-
return await import("./browser-
|
|
155
|
+
return await import("./browser-LQSO5OS2.mjs");
|
|
155
156
|
} catch {
|
|
156
157
|
throw new OAuthError(
|
|
157
158
|
"DPoP needs `jose`. Install it: npm i jose. It is an optional peer, so a client not using DPoP does not carry it.",
|
|
@@ -309,8 +310,12 @@ async function discover(issuer, fetcher = fetch) {
|
|
|
309
310
|
try {
|
|
310
311
|
response = await fetcher(documentUrl(issuer));
|
|
311
312
|
} catch (cause) {
|
|
313
|
+
const why = cause instanceof Error ? cause.message : String(cause);
|
|
312
314
|
throw new OAuthError(
|
|
313
|
-
|
|
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})`,
|
|
314
319
|
"discovery_unreachable"
|
|
315
320
|
);
|
|
316
321
|
}
|
|
@@ -335,6 +340,7 @@ async function discover(issuer, fetcher = fetch) {
|
|
|
335
340
|
jwks: doc.jwks_uri,
|
|
336
341
|
introspection: doc.introspection_endpoint,
|
|
337
342
|
revocation: doc.revocation_endpoint,
|
|
343
|
+
checkSession: doc.check_session_iframe,
|
|
338
344
|
issuer: doc.issuer ?? issuer
|
|
339
345
|
};
|
|
340
346
|
})();
|
|
@@ -793,6 +799,81 @@ function createServerClient(config, fetcher = fetch) {
|
|
|
793
799
|
};
|
|
794
800
|
}
|
|
795
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
|
+
|
|
796
877
|
// src/service.ts
|
|
797
878
|
function paramsFromUrl(url) {
|
|
798
879
|
const href = url ?? (typeof location === "undefined" ? null : location.href);
|
|
@@ -810,18 +891,28 @@ function clientFor(options) {
|
|
|
810
891
|
return options.config.tokens === "server" ? createServerClient(options.config) : createBrowserClient(options.config);
|
|
811
892
|
}
|
|
812
893
|
function createAuthService(options) {
|
|
894
|
+
const tokens = options.config.tokens;
|
|
895
|
+
const scope = openReactiveScope({ kind: "service", name: "AuthService", ...tokens ? { detail: `tokens: ${tokens}` } : {} });
|
|
896
|
+
const service = withReactiveScope(scope, () => buildAuthService(options));
|
|
897
|
+
markReactiveScope(service, scope);
|
|
898
|
+
return service;
|
|
899
|
+
}
|
|
900
|
+
function buildAuthService(options) {
|
|
813
901
|
const client = clientFor(options);
|
|
814
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);
|
|
815
909
|
const session = createSession({
|
|
816
910
|
/** The credential is the callback, so this is the code exchange. */
|
|
817
911
|
async login(params) {
|
|
818
912
|
latest = await client.handleCallback(params);
|
|
819
913
|
return { token: latest.accessToken, expiresAt: latest.expiresAt };
|
|
820
914
|
},
|
|
821
|
-
|
|
822
|
-
if (options.fetchUser) return options.fetchUser(token);
|
|
823
|
-
return client.fetchUser(token === COOKIE_SESSION ? void 0 : token);
|
|
824
|
-
},
|
|
915
|
+
fetchUser: fetchUserFor,
|
|
825
916
|
async refresh() {
|
|
826
917
|
latest = await client.refresh();
|
|
827
918
|
return { token: latest.accessToken, expiresAt: latest.expiresAt };
|
|
@@ -844,11 +935,27 @@ function createAuthService(options) {
|
|
|
844
935
|
});
|
|
845
936
|
const hooks = createAuthHooks({
|
|
846
937
|
session,
|
|
938
|
+
ready,
|
|
847
939
|
navigate: options.navigate,
|
|
848
940
|
loginPath: options.loginPath ?? "/login",
|
|
849
941
|
homePath: options.homePath ?? "/",
|
|
850
942
|
forbiddenPath: options.forbiddenPath ?? "/403"
|
|
851
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
|
+
}
|
|
852
959
|
return Object.assign(session, {
|
|
853
960
|
client,
|
|
854
961
|
protectedRoute: hooks.protectedRoute,
|
|
@@ -884,7 +991,7 @@ function createAuthService(options) {
|
|
|
884
991
|
}
|
|
885
992
|
|
|
886
993
|
// src/tokens.ts
|
|
887
|
-
import { injectionToken } from "@fluixi/
|
|
994
|
+
import { injectionToken } from "@fluixi/di";
|
|
888
995
|
var AuthServiceToken = injectionToken("AuthService");
|
|
889
996
|
var AuthClientToken = injectionToken("AuthClient");
|
|
890
997
|
|
|
@@ -922,5 +1029,6 @@ export {
|
|
|
922
1029
|
paramsFromUrl,
|
|
923
1030
|
proofFor,
|
|
924
1031
|
safeReturnTo,
|
|
925
|
-
thumbprintOf
|
|
1032
|
+
thumbprintOf,
|
|
1033
|
+
watchProviderSession
|
|
926
1034
|
};
|
package/dist/module.cjs
CHANGED
|
@@ -26,7 +26,7 @@ module.exports = __toCommonJS(module_exports);
|
|
|
26
26
|
var import_app = require("@fluixi/start/app");
|
|
27
27
|
|
|
28
28
|
// src/tokens.ts
|
|
29
|
-
var import_di = require("@fluixi/
|
|
29
|
+
var import_di = require("@fluixi/di");
|
|
30
30
|
var AuthServiceToken = (0, import_di.injectionToken)("AuthService");
|
|
31
31
|
var AuthClientToken = (0, import_di.injectionToken)("AuthClient");
|
|
32
32
|
|
package/dist/module.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { defineModule } from "@fluixi/start/app";
|
|
3
3
|
|
|
4
4
|
// src/tokens.ts
|
|
5
|
-
import { injectionToken } from "@fluixi/
|
|
5
|
+
import { injectionToken } from "@fluixi/di";
|
|
6
6
|
var AuthServiceToken = injectionToken("AuthService");
|
|
7
7
|
var AuthClientToken = injectionToken("AuthClient");
|
|
8
8
|
|