@byline/host-tanstack-start 5.0.0 → 5.1.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/dist/admin-shell/chrome/admin-app-bar.d.ts +7 -0
- package/dist/admin-shell/chrome/admin-app-bar.js +10 -2
- package/dist/admin-shell/chrome/session-change-boundary.d.ts +14 -0
- package/dist/admin-shell/chrome/session-change-boundary.js +87 -0
- package/dist/admin-shell/chrome/session-change-boundary.test.d.ts +8 -0
- package/dist/admin-shell/chrome/sign-in-page.d.ts +2 -1
- package/dist/admin-shell/chrome/sign-in-page.js +60 -3
- package/dist/integrations/byline-admin-services.js +7 -1
- package/dist/integrations/client-ip.d.ts +19 -0
- package/dist/integrations/client-ip.js +34 -0
- package/dist/integrations/session-coordination.d.ts +24 -0
- package/dist/integrations/session-coordination.js +85 -0
- package/dist/integrations/session-middleware.d.ts +11 -0
- package/dist/integrations/session-middleware.js +94 -0
- package/dist/integrations/session-request-kind.d.ts +9 -0
- package/dist/integrations/session-request-kind.js +10 -0
- package/dist/integrations/session-request-middleware.d.ts +9 -0
- package/dist/integrations/session-request-middleware.js +7 -0
- package/dist/integrations/sign-in-body.d.ts +12 -0
- package/dist/integrations/sign-in-body.js +52 -0
- package/dist/integrations/sign-in-middleware.d.ts +9 -0
- package/dist/integrations/sign-in-middleware.js +10 -0
- package/dist/routes/create-admin-layout-route.js +46 -31
- package/dist/routes/create-sign-in-route.js +4 -2
- package/dist/server-fns/admin-account/change-password.d.ts +3 -1
- package/dist/server-fns/admin-account/change-password.js +9 -3
- package/dist/server-fns/admin-account/get.d.ts +3 -1
- package/dist/server-fns/admin-account/get.js +4 -1
- package/dist/server-fns/admin-account/index.d.ts +5 -5
- package/dist/server-fns/admin-account/update.d.ts +3 -1
- package/dist/server-fns/admin-account/update.js +4 -1
- package/dist/server-fns/admin-activity/get.d.ts +3 -1
- package/dist/server-fns/admin-activity/get.js +4 -1
- package/dist/server-fns/admin-permissions/get-role-abilities.d.ts +3 -1
- package/dist/server-fns/admin-permissions/get-role-abilities.js +4 -1
- package/dist/server-fns/admin-permissions/index.d.ts +4 -4
- package/dist/server-fns/admin-permissions/list-registered.d.ts +3 -1
- package/dist/server-fns/admin-permissions/list-registered.js +4 -1
- package/dist/server-fns/admin-permissions/set-role-abilities.d.ts +3 -1
- package/dist/server-fns/admin-permissions/set-role-abilities.js +4 -1
- package/dist/server-fns/admin-permissions/who-has.d.ts +3 -1
- package/dist/server-fns/admin-permissions/who-has.js +4 -1
- package/dist/server-fns/admin-roles/create.d.ts +3 -1
- package/dist/server-fns/admin-roles/create.js +4 -1
- package/dist/server-fns/admin-roles/delete.d.ts +3 -1
- package/dist/server-fns/admin-roles/delete.js +4 -1
- package/dist/server-fns/admin-roles/get.d.ts +3 -1
- package/dist/server-fns/admin-roles/get.js +4 -1
- package/dist/server-fns/admin-roles/index.d.ts +6 -6
- package/dist/server-fns/admin-roles/list.d.ts +3 -1
- package/dist/server-fns/admin-roles/list.js +4 -1
- package/dist/server-fns/admin-roles/reorder.d.ts +3 -1
- package/dist/server-fns/admin-roles/reorder.js +4 -1
- package/dist/server-fns/admin-roles/update.d.ts +3 -1
- package/dist/server-fns/admin-roles/update.js +4 -1
- package/dist/server-fns/admin-users/create.d.ts +3 -1
- package/dist/server-fns/admin-users/create.js +4 -1
- package/dist/server-fns/admin-users/delete.d.ts +3 -1
- package/dist/server-fns/admin-users/delete.js +4 -1
- package/dist/server-fns/admin-users/disable.d.ts +3 -1
- package/dist/server-fns/admin-users/disable.js +4 -1
- package/dist/server-fns/admin-users/enable.d.ts +3 -1
- package/dist/server-fns/admin-users/enable.js +4 -1
- package/dist/server-fns/admin-users/get-user-roles.d.ts +3 -1
- package/dist/server-fns/admin-users/get-user-roles.js +4 -1
- package/dist/server-fns/admin-users/get.d.ts +3 -1
- package/dist/server-fns/admin-users/get.js +4 -1
- package/dist/server-fns/admin-users/index.d.ts +10 -10
- package/dist/server-fns/admin-users/list.d.ts +3 -1
- package/dist/server-fns/admin-users/list.js +4 -1
- package/dist/server-fns/admin-users/set-password.d.ts +3 -1
- package/dist/server-fns/admin-users/set-password.js +4 -1
- package/dist/server-fns/admin-users/set-user-roles.d.ts +3 -1
- package/dist/server-fns/admin-users/set-user-roles.js +4 -1
- package/dist/server-fns/admin-users/update.d.ts +3 -1
- package/dist/server-fns/admin-users/update.js +4 -1
- package/dist/server-fns/ai/execute.d.ts +3 -1
- package/dist/server-fns/ai/execute.js +4 -1
- package/dist/server-fns/ai/index.d.ts +1 -1
- package/dist/server-fns/analytics/maintenance.d.ts +6 -2
- package/dist/server-fns/analytics/maintenance.js +7 -2
- package/dist/server-fns/analytics/queries.d.ts +18 -6
- package/dist/server-fns/analytics/queries.js +19 -6
- package/dist/server-fns/auth/current-user.d.ts +1 -0
- package/dist/server-fns/auth/current-user.js +5 -2
- package/dist/server-fns/auth/renew.d.ts +13 -0
- package/dist/server-fns/auth/renew.js +45 -0
- package/dist/server-fns/auth/sign-in.d.ts +2 -1
- package/dist/server-fns/auth/sign-in.js +88 -17
- package/dist/server-fns/auth/sign-out.d.ts +3 -1
- package/dist/server-fns/auth/sign-out.js +38 -6
- package/dist/server-fns/collections/audit.d.ts +3 -1
- package/dist/server-fns/collections/audit.js +4 -1
- package/dist/server-fns/collections/copy-to-locale.d.ts +10 -1
- package/dist/server-fns/collections/copy-to-locale.js +5 -2
- package/dist/server-fns/collections/create.d.ts +10 -1
- package/dist/server-fns/collections/create.js +5 -2
- package/dist/server-fns/collections/delete-locale.d.ts +10 -1
- package/dist/server-fns/collections/delete-locale.js +5 -2
- package/dist/server-fns/collections/delete.d.ts +10 -1
- package/dist/server-fns/collections/delete.js +5 -2
- package/dist/server-fns/collections/duplicate.d.ts +10 -1
- package/dist/server-fns/collections/duplicate.js +5 -2
- package/dist/server-fns/collections/get.d.ts +7 -0
- package/dist/server-fns/collections/get.js +8 -3
- package/dist/server-fns/collections/history.d.ts +3 -1
- package/dist/server-fns/collections/history.js +4 -1
- package/dist/server-fns/collections/index.d.ts +21 -21
- package/dist/server-fns/collections/list.d.ts +10 -1
- package/dist/server-fns/collections/list.js +5 -2
- package/dist/server-fns/collections/reindex.d.ts +3 -1
- package/dist/server-fns/collections/reindex.js +4 -1
- package/dist/server-fns/collections/reorder.d.ts +10 -1
- package/dist/server-fns/collections/reorder.js +5 -2
- package/dist/server-fns/collections/restore-version.d.ts +10 -1
- package/dist/server-fns/collections/restore-version.js +5 -2
- package/dist/server-fns/collections/scheduled-publication.d.ts +22 -5
- package/dist/server-fns/collections/scheduled-publication.js +17 -6
- package/dist/server-fns/collections/set-list-view-preference.d.ts +3 -1
- package/dist/server-fns/collections/set-list-view-preference.js +4 -1
- package/dist/server-fns/collections/stats.js +4 -1
- package/dist/server-fns/collections/status.d.ts +13 -2
- package/dist/server-fns/collections/status.js +8 -3
- package/dist/server-fns/collections/tree.d.ts +22 -5
- package/dist/server-fns/collections/tree.js +17 -6
- package/dist/server-fns/collections/update.d.ts +13 -2
- package/dist/server-fns/collections/update.js +8 -3
- package/dist/server-fns/collections/upload.d.ts +10 -1
- package/dist/server-fns/collections/upload.js +4 -1
- package/dist/server-fns/i18n/set-locale.d.ts +3 -1
- package/dist/server-fns/i18n/set-locale.js +18 -3
- package/dist/server-fns/preview/disable.d.ts +3 -1
- package/dist/server-fns/preview/disable.js +4 -1
- package/dist/server-fns/preview/enable.d.ts +3 -1
- package/dist/server-fns/preview/enable.js +4 -1
- package/dist/server-fns/singletons/cancel-scheduled-publish.d.ts +10 -1
- package/dist/server-fns/singletons/cancel-scheduled-publish.js +5 -2
- package/dist/server-fns/singletons/change-status.d.ts +10 -1
- package/dist/server-fns/singletons/change-status.js +5 -2
- package/dist/server-fns/singletons/confirm-scheduled-publish.d.ts +10 -1
- package/dist/server-fns/singletons/confirm-scheduled-publish.js +5 -2
- package/dist/server-fns/singletons/copy-to-locale.d.ts +10 -1
- package/dist/server-fns/singletons/copy-to-locale.js +5 -2
- package/dist/server-fns/singletons/find-by-version.d.ts +3 -1
- package/dist/server-fns/singletons/find-by-version.js +4 -1
- package/dist/server-fns/singletons/get-scheduled-publish.d.ts +3 -1
- package/dist/server-fns/singletons/get-scheduled-publish.js +4 -1
- package/dist/server-fns/singletons/get.d.ts +3 -1
- package/dist/server-fns/singletons/get.js +4 -1
- package/dist/server-fns/singletons/history.d.ts +3 -1
- package/dist/server-fns/singletons/history.js +4 -1
- package/dist/server-fns/singletons/restore-version.d.ts +10 -1
- package/dist/server-fns/singletons/restore-version.js +5 -2
- package/dist/server-fns/singletons/schedule-publish.d.ts +10 -1
- package/dist/server-fns/singletons/schedule-publish.js +5 -2
- package/dist/server-fns/singletons/unpublish.d.ts +10 -1
- package/dist/server-fns/singletons/unpublish.js +5 -2
- package/dist/server-fns/singletons/update.d.ts +10 -1
- package/dist/server-fns/singletons/update.js +5 -2
- package/package.json +19 -19
- package/src/admin-shell/chrome/admin-app-bar.tsx +11 -6
- package/src/admin-shell/chrome/session-change-boundary.test.tsx +78 -0
- package/src/admin-shell/chrome/session-change-boundary.tsx +94 -0
- package/src/admin-shell/chrome/sign-in-page.tsx +68 -3
- package/src/integrations/byline-admin-services.ts +7 -1
- package/src/integrations/client-ip.test.node.ts +45 -0
- package/src/integrations/client-ip.ts +54 -0
- package/src/integrations/framework-fetch-shape.test.node.ts +56 -0
- package/src/integrations/session-client.test.node.ts +121 -0
- package/src/integrations/session-coordination.test.node.ts +77 -0
- package/src/integrations/session-coordination.ts +96 -0
- package/src/integrations/session-middleware.test.node.ts +87 -0
- package/src/integrations/session-middleware.ts +107 -0
- package/src/integrations/session-request-kind.ts +18 -0
- package/src/integrations/session-request-middleware.ts +17 -0
- package/src/integrations/sign-in-body.test.node.ts +59 -0
- package/src/integrations/sign-in-body.ts +50 -0
- package/src/integrations/sign-in-middleware.ts +24 -0
- package/src/routes/create-admin-layout-route.tsx +48 -30
- package/src/routes/create-sign-in-route.tsx +10 -2
- package/src/server-fns/admin-account/change-password.test.node.ts +69 -0
- package/src/server-fns/admin-account/change-password.ts +13 -2
- package/src/server-fns/admin-account/get.ts +2 -0
- package/src/server-fns/admin-account/update.ts +2 -0
- package/src/server-fns/admin-activity/get.ts +2 -0
- package/src/server-fns/admin-permissions/get-role-abilities.ts +2 -0
- package/src/server-fns/admin-permissions/list-registered.ts +5 -4
- package/src/server-fns/admin-permissions/set-role-abilities.ts +2 -0
- package/src/server-fns/admin-permissions/who-has.ts +2 -0
- package/src/server-fns/admin-roles/create.ts +2 -0
- package/src/server-fns/admin-roles/delete.ts +2 -0
- package/src/server-fns/admin-roles/get.ts +2 -0
- package/src/server-fns/admin-roles/list.ts +5 -4
- package/src/server-fns/admin-roles/reorder.ts +2 -0
- package/src/server-fns/admin-roles/update.ts +2 -0
- package/src/server-fns/admin-users/create.ts +2 -0
- package/src/server-fns/admin-users/delete.ts +2 -0
- package/src/server-fns/admin-users/disable.ts +2 -0
- package/src/server-fns/admin-users/enable.ts +2 -0
- package/src/server-fns/admin-users/get-user-roles.ts +2 -0
- package/src/server-fns/admin-users/get.ts +2 -0
- package/src/server-fns/admin-users/list.ts +2 -0
- package/src/server-fns/admin-users/set-password.ts +2 -0
- package/src/server-fns/admin-users/set-user-roles.ts +2 -0
- package/src/server-fns/admin-users/update.ts +2 -0
- package/src/server-fns/ai/execute.ts +3 -0
- package/src/server-fns/analytics/maintenance.ts +4 -0
- package/src/server-fns/analytics/queries.ts +14 -8
- package/src/server-fns/auth/current-user.ts +7 -3
- package/src/server-fns/auth/renew.test.node.ts +85 -0
- package/src/server-fns/auth/renew.ts +54 -0
- package/src/server-fns/auth/sign-in.test.node.ts +114 -0
- package/src/server-fns/auth/sign-in.ts +80 -27
- package/src/server-fns/auth/sign-out.test.node.ts +85 -0
- package/src/server-fns/auth/sign-out.ts +44 -19
- package/src/server-fns/collections/audit.ts +2 -0
- package/src/server-fns/collections/copy-to-locale.ts +3 -1
- package/src/server-fns/collections/create.ts +3 -1
- package/src/server-fns/collections/delete-locale.ts +3 -1
- package/src/server-fns/collections/delete.ts +3 -1
- package/src/server-fns/collections/duplicate.ts +3 -1
- package/src/server-fns/collections/get.ts +4 -1
- package/src/server-fns/collections/history.ts +2 -0
- package/src/server-fns/collections/list.ts +3 -1
- package/src/server-fns/collections/reindex.ts +2 -0
- package/src/server-fns/collections/reorder.ts +3 -1
- package/src/server-fns/collections/restore-version.ts +3 -1
- package/src/server-fns/collections/revision-input.test.node.ts +14 -0
- package/src/server-fns/collections/scheduled-publication.ts +14 -7
- package/src/server-fns/collections/set-list-view-preference.ts +2 -0
- package/src/server-fns/collections/stats.ts +2 -0
- package/src/server-fns/collections/status.ts +4 -1
- package/src/server-fns/collections/tree.ts +7 -1
- package/src/server-fns/collections/update.ts +4 -1
- package/src/server-fns/collections/upload-singleton.test.node.ts +8 -1
- package/src/server-fns/collections/upload.ts +10 -0
- package/src/server-fns/i18n/set-locale.ts +19 -3
- package/src/server-fns/preview/disable.ts +8 -4
- package/src/server-fns/preview/enable.ts +9 -5
- package/src/server-fns/singletons/cancel-scheduled-publish.ts +3 -1
- package/src/server-fns/singletons/change-status.ts +3 -1
- package/src/server-fns/singletons/confirm-scheduled-publish.ts +3 -1
- package/src/server-fns/singletons/copy-to-locale.ts +3 -1
- package/src/server-fns/singletons/find-by-version.ts +2 -0
- package/src/server-fns/singletons/get-scheduled-publish.ts +2 -0
- package/src/server-fns/singletons/get.ts +2 -0
- package/src/server-fns/singletons/history.ts +2 -0
- package/src/server-fns/singletons/restore-version.ts +3 -1
- package/src/server-fns/singletons/schedule-publish.ts +3 -1
- package/src/server-fns/singletons/singletons.test.node.ts +6 -0
- package/src/server-fns/singletons/unpublish.ts +3 -1
- package/src/server-fns/singletons/update.ts +3 -1
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
1
8
|
import { type CurrentAdminUser } from '../../server-fns/auth/index.js';
|
|
2
9
|
interface AdminAppBarProps {
|
|
3
10
|
user: CurrentAdminUser;
|
|
@@ -22,13 +22,17 @@ function AdminAppBar({ user }) {
|
|
|
22
22
|
const { breadCrumbSettings } = useBreadcrumbs();
|
|
23
23
|
const { t } = useTranslation('byline-admin');
|
|
24
24
|
const [signingOut, setSigningOut] = useState(false);
|
|
25
|
+
const [signOutError, setSignOutError] = useState(false);
|
|
25
26
|
async function handleSignOut() {
|
|
26
27
|
if (signingOut) return;
|
|
27
28
|
setSigningOut(true);
|
|
29
|
+
setSignOutError(false);
|
|
28
30
|
try {
|
|
29
31
|
await adminSignOut();
|
|
30
|
-
} catch
|
|
31
|
-
|
|
32
|
+
} catch {
|
|
33
|
+
setSignOutError(true);
|
|
34
|
+
setSigningOut(false);
|
|
35
|
+
return;
|
|
32
36
|
}
|
|
33
37
|
window.location.assign(getSignInRoutePath());
|
|
34
38
|
}
|
|
@@ -62,6 +66,10 @@ function AdminAppBar({ user }) {
|
|
|
62
66
|
})
|
|
63
67
|
]
|
|
64
68
|
}),
|
|
69
|
+
signOutError && /*#__PURE__*/ jsx("span", {
|
|
70
|
+
role: "alert",
|
|
71
|
+
children: "Sign-out could not be confirmed. Please try again."
|
|
72
|
+
}),
|
|
65
73
|
/*#__PURE__*/ jsx(Button, {
|
|
66
74
|
size: "xs",
|
|
67
75
|
intent: "secondary",
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
import { type ReactNode } from 'react';
|
|
9
|
+
import { type CurrentAdminUser } from '../../server-fns/auth/current-user.js';
|
|
10
|
+
/** Blocks the page until the user explicitly acknowledges freshly verified login identity. */
|
|
11
|
+
export declare function SessionChangeBoundary({ user, children, }: {
|
|
12
|
+
user?: CurrentAdminUser;
|
|
13
|
+
children: ReactNode;
|
|
14
|
+
}): string | number | bigint | boolean | import("react").JSX.Element | Iterable<ReactNode> | Promise<string | number | bigint | boolean | Iterable<ReactNode> | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | import("react").ReactPortal | null | undefined> | null | undefined;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState, useSyncExternalStore } from "react";
|
|
3
|
+
import { acceptSession, expectedSession, flagSessionChanged, observeSession, sessionEpoch, sessionIsChanged, startSessionNotifications, subscribeSessionChange } from "../../integrations/session-coordination.js";
|
|
4
|
+
import { getSignInRoutePath } from "../../routes/sign-in-path.js";
|
|
5
|
+
import { getCurrentAdminUser } from "../../server-fns/auth/current-user.js";
|
|
6
|
+
function SessionChangeBoundary({ user, children }) {
|
|
7
|
+
const epoch = useSyncExternalStore(subscribeSessionChange, sessionEpoch, ()=>0);
|
|
8
|
+
const changed = sessionIsChanged();
|
|
9
|
+
const [current, setCurrent] = useState(user);
|
|
10
|
+
const [error, setError] = useState();
|
|
11
|
+
const [busy, setBusy] = useState(false);
|
|
12
|
+
const mismatch = "u" > typeof window && !!user && !!expectedSession() && expectedSession() !== user.sessionId;
|
|
13
|
+
useEffect(()=>{
|
|
14
|
+
startSessionNotifications();
|
|
15
|
+
if (user) try {
|
|
16
|
+
observeSession(user.sessionId);
|
|
17
|
+
} catch {}
|
|
18
|
+
}, [
|
|
19
|
+
user
|
|
20
|
+
]);
|
|
21
|
+
useEffect(()=>{
|
|
22
|
+
if (!changed && !mismatch) return;
|
|
23
|
+
getCurrentAdminUser().then((verified)=>{
|
|
24
|
+
if (sessionEpoch() !== epoch) return;
|
|
25
|
+
setCurrent(verified);
|
|
26
|
+
setError(void 0);
|
|
27
|
+
}).catch(()=>setError('The session is no longer available. Please sign in again.'));
|
|
28
|
+
}, [
|
|
29
|
+
changed,
|
|
30
|
+
mismatch,
|
|
31
|
+
epoch
|
|
32
|
+
]);
|
|
33
|
+
if (!changed && !mismatch) return children;
|
|
34
|
+
async function acknowledge() {
|
|
35
|
+
setBusy(true);
|
|
36
|
+
try {
|
|
37
|
+
const verified = await getCurrentAdminUser();
|
|
38
|
+
if (!current || current.sessionId !== verified.sessionId) {
|
|
39
|
+
setCurrent(verified);
|
|
40
|
+
setError('The active session changed again. Review the account before continuing.');
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
acceptSession(verified.sessionId);
|
|
44
|
+
window.location.reload();
|
|
45
|
+
} catch {
|
|
46
|
+
flagSessionChanged();
|
|
47
|
+
setError('Unable to verify the active session. Please sign in again.');
|
|
48
|
+
} finally{
|
|
49
|
+
setBusy(false);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return /*#__PURE__*/ jsxs("main", {
|
|
53
|
+
role: "alertdialog",
|
|
54
|
+
"aria-modal": "true",
|
|
55
|
+
"aria-labelledby": "session-change-title",
|
|
56
|
+
children: [
|
|
57
|
+
/*#__PURE__*/ jsx("h1", {
|
|
58
|
+
id: "session-change-title",
|
|
59
|
+
children: "Your session has changed"
|
|
60
|
+
}),
|
|
61
|
+
/*#__PURE__*/ jsx("p", {
|
|
62
|
+
children: "Your previous work has not been retried. Check the active account before continuing."
|
|
63
|
+
}),
|
|
64
|
+
current && /*#__PURE__*/ jsxs("p", {
|
|
65
|
+
children: [
|
|
66
|
+
"Active account: ",
|
|
67
|
+
current.email
|
|
68
|
+
]
|
|
69
|
+
}),
|
|
70
|
+
error && /*#__PURE__*/ jsx("p", {
|
|
71
|
+
role: "alert",
|
|
72
|
+
children: error
|
|
73
|
+
}),
|
|
74
|
+
/*#__PURE__*/ jsx("button", {
|
|
75
|
+
type: "button",
|
|
76
|
+
disabled: busy || !current,
|
|
77
|
+
onClick: acknowledge,
|
|
78
|
+
children: "Continue as this account"
|
|
79
|
+
}),
|
|
80
|
+
/*#__PURE__*/ jsx("a", {
|
|
81
|
+
href: `${getSignInRoutePath()}?reauthenticate=1`,
|
|
82
|
+
children: "Sign in again"
|
|
83
|
+
})
|
|
84
|
+
]
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
export { SessionChangeBoundary };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import type { LocaleCode } from '@byline/i18n';
|
|
9
9
|
interface SignInPageProps {
|
|
10
|
+
reauthenticate?: boolean;
|
|
10
11
|
redirectTo: string;
|
|
11
12
|
activeLocale: LocaleCode;
|
|
12
13
|
homeUrl?: string;
|
|
@@ -31,5 +32,5 @@ interface SignInPageProps {
|
|
|
31
32
|
* Threads an optional host-owned `homeUrl` into `SignInForm` so the form's
|
|
32
33
|
* action row can render a plain "Home" link beside the submit button.
|
|
33
34
|
*/
|
|
34
|
-
export declare function SignInPage({ redirectTo, activeLocale, homeUrl }: SignInPageProps): import("react").JSX.Element;
|
|
35
|
+
export declare function SignInPage({ redirectTo, activeLocale, homeUrl, reauthenticate, }: SignInPageProps): import("react").JSX.Element;
|
|
35
36
|
export {};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
2
3
|
import { SignInForm } from "@byline/admin/auth/components/sign-in-form";
|
|
3
4
|
import { BylineAdminServicesProvider } from "@byline/admin/services";
|
|
4
5
|
import { getAdminConfig } from "@byline/core";
|
|
@@ -6,9 +7,44 @@ import { I18nProvider, LanguageMenu } from "@byline/i18n/react";
|
|
|
6
7
|
import clsx from "clsx";
|
|
7
8
|
import { buildLocaleDefinitions } from "../../i18n/locale-definitions.js";
|
|
8
9
|
import { bylineAdminServices } from "../../integrations/byline-admin-services.js";
|
|
10
|
+
import { coordinateAuthAction, expectedSession, flagSessionChanged, observeSession, renewSingleFlight } from "../../integrations/session-coordination.js";
|
|
11
|
+
import { renewAdminSession } from "../../server-fns/auth/renew.js";
|
|
9
12
|
import { setAdminLocaleFn } from "../../server-fns/i18n/index.js";
|
|
13
|
+
import { SessionChangeBoundary } from "./session-change-boundary.js";
|
|
10
14
|
import sign_in_page_module from "./sign-in-page.module.js";
|
|
11
|
-
function SignInPage({ redirectTo, activeLocale, homeUrl }) {
|
|
15
|
+
function SignInPage({ redirectTo, activeLocale, homeUrl, reauthenticate = false }) {
|
|
16
|
+
const [checking, setChecking] = useState(!reauthenticate);
|
|
17
|
+
const [bootstrapError, setBootstrapError] = useState(false);
|
|
18
|
+
useEffect(()=>{
|
|
19
|
+
if (reauthenticate) return;
|
|
20
|
+
let active = true;
|
|
21
|
+
renewSingleFlight(()=>coordinateAuthAction(async ()=>{
|
|
22
|
+
const result = await renewAdminSession({
|
|
23
|
+
data: {
|
|
24
|
+
expectedSessionId: expectedSession() ?? void 0
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
observeSession(result.sessionId);
|
|
28
|
+
})).then(()=>{
|
|
29
|
+
if (active) window.location.replace(redirectTo);
|
|
30
|
+
}).catch((error)=>{
|
|
31
|
+
if (error?.code === 'ERR_SESSION_CHANGED') flagSessionChanged();
|
|
32
|
+
else if (![
|
|
33
|
+
'ERR_UNAUTHENTICATED',
|
|
34
|
+
'ERR_INVALID_TOKEN',
|
|
35
|
+
'ERR_REVOKED_TOKEN',
|
|
36
|
+
'ERR_ACCOUNT_DISABLED'
|
|
37
|
+
].includes(error?.code)) setBootstrapError(true);
|
|
38
|
+
}).finally(()=>{
|
|
39
|
+
if (active) setChecking(false);
|
|
40
|
+
});
|
|
41
|
+
return ()=>{
|
|
42
|
+
active = false;
|
|
43
|
+
};
|
|
44
|
+
}, [
|
|
45
|
+
redirectTo,
|
|
46
|
+
reauthenticate
|
|
47
|
+
]);
|
|
12
48
|
const { i18n } = getAdminConfig();
|
|
13
49
|
const localeDefinitions = buildLocaleDefinitions(i18n.admin.locales, i18n.admin.localeDefinitions);
|
|
14
50
|
const handleSetLocale = async (next)=>{
|
|
@@ -19,7 +55,7 @@ function SignInPage({ redirectTo, activeLocale, homeUrl }) {
|
|
|
19
55
|
});
|
|
20
56
|
window.location.reload();
|
|
21
57
|
};
|
|
22
|
-
|
|
58
|
+
const content = /*#__PURE__*/ jsx(I18nProvider, {
|
|
23
59
|
bundle: i18n.translations ?? {},
|
|
24
60
|
activeLocale: activeLocale,
|
|
25
61
|
defaultLocale: i18n.admin.defaultLocale,
|
|
@@ -30,13 +66,31 @@ function SignInPage({ redirectTo, activeLocale, homeUrl }) {
|
|
|
30
66
|
children: /*#__PURE__*/ jsxs("main", {
|
|
31
67
|
className: clsx('byline-sign-in-page', sign_in_page_module.main),
|
|
32
68
|
children: [
|
|
69
|
+
/*#__PURE__*/ jsx("noscript", {
|
|
70
|
+
children: "JavaScript is required to sign in or renew your admin session."
|
|
71
|
+
}),
|
|
33
72
|
/*#__PURE__*/ jsx("div", {
|
|
34
73
|
className: clsx('byline-sign-in-page-bar', sign_in_page_module.bar),
|
|
35
74
|
children: /*#__PURE__*/ jsx(LanguageMenu, {})
|
|
36
75
|
}),
|
|
37
76
|
/*#__PURE__*/ jsx("div", {
|
|
38
77
|
className: clsx('byline-sign-in-page-inner', sign_in_page_module.inner),
|
|
39
|
-
children: /*#__PURE__*/
|
|
78
|
+
children: bootstrapError ? /*#__PURE__*/ jsxs("div", {
|
|
79
|
+
role: "alert",
|
|
80
|
+
children: [
|
|
81
|
+
/*#__PURE__*/ jsx("p", {
|
|
82
|
+
children: "Unable to check your session. Please try again."
|
|
83
|
+
}),
|
|
84
|
+
/*#__PURE__*/ jsx("button", {
|
|
85
|
+
type: "button",
|
|
86
|
+
onClick: ()=>window.location.reload(),
|
|
87
|
+
children: "Try again"
|
|
88
|
+
})
|
|
89
|
+
]
|
|
90
|
+
}) : checking ? /*#__PURE__*/ jsx("p", {
|
|
91
|
+
role: "status",
|
|
92
|
+
children: "Checking your session…"
|
|
93
|
+
}) : /*#__PURE__*/ jsx(SignInForm, {
|
|
40
94
|
redirectTo: redirectTo,
|
|
41
95
|
homeUrl: homeUrl
|
|
42
96
|
})
|
|
@@ -45,5 +99,8 @@ function SignInPage({ redirectTo, activeLocale, homeUrl }) {
|
|
|
45
99
|
})
|
|
46
100
|
})
|
|
47
101
|
});
|
|
102
|
+
return reauthenticate ? content : /*#__PURE__*/ jsx(SessionChangeBoundary, {
|
|
103
|
+
children: content
|
|
104
|
+
});
|
|
48
105
|
}
|
|
49
106
|
export { SignInPage };
|
|
@@ -5,13 +5,19 @@ import { createAdminUser, setAdminUserPassword, setUserRoles, updateAdminUser }
|
|
|
5
5
|
import { adminSignIn } from "../server-fns/auth/index.js";
|
|
6
6
|
import { getCollectionDocumentVersion } from "../server-fns/collections/get.js";
|
|
7
7
|
import { setAdminLocaleFn } from "../server-fns/i18n/index.js";
|
|
8
|
+
import { acceptSession, coordinateAuthAction, notifySessionAction } from "./session-coordination.js";
|
|
8
9
|
const byline_admin_services_getCollectionDocumentVersion = async (collection, documentId, versionId, locale)=>{
|
|
9
10
|
const result = await getCollectionDocumentVersion(collection, documentId, versionId, locale);
|
|
10
11
|
if (null == result) throw new Error(`Document version not found: collection=${collection} document=${documentId} version=${versionId}`);
|
|
11
12
|
return result;
|
|
12
13
|
};
|
|
13
14
|
const bylineAdminServices = {
|
|
14
|
-
adminSignIn:
|
|
15
|
+
adminSignIn: async (args)=>{
|
|
16
|
+
const result = await coordinateAuthAction(()=>adminSignIn(args));
|
|
17
|
+
acceptSession(result.sessionId);
|
|
18
|
+
notifySessionAction();
|
|
19
|
+
return result;
|
|
20
|
+
},
|
|
15
21
|
updateAccount: updateAccount,
|
|
16
22
|
changeAccountPassword: changeAccountPassword,
|
|
17
23
|
setAdminLocale: setAdminLocaleFn,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
import type { ClientIpResolver } from '@byline/auth';
|
|
9
|
+
/** Normalize equivalent IPv6 spellings and IPv4-mapped IPv6 before choosing a rate-limit key. */
|
|
10
|
+
export declare function normalizeClientIp(value: string | null | undefined): string | null;
|
|
11
|
+
/**
|
|
12
|
+
* Configure either a direct peer resolver or an explicitly trusted proxy header.
|
|
13
|
+
* Header mode requires the deployment to overwrite that header and block proxy bypass.
|
|
14
|
+
* Never falls back from a missing/invalid trusted header to another client-supplied header.
|
|
15
|
+
*/
|
|
16
|
+
export declare function createClientIpResolver(options: {
|
|
17
|
+
trustedProxyHeader?: string;
|
|
18
|
+
resolvePeerIp: ClientIpResolver;
|
|
19
|
+
}): ClientIpResolver;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { isIP } from "node:net";
|
|
2
|
+
function normalizeClientIp(value) {
|
|
3
|
+
if (!value || value.length > 64) return null;
|
|
4
|
+
const ip = value.trim();
|
|
5
|
+
if (4 === isIP(ip)) return ip;
|
|
6
|
+
if (6 !== isIP(ip) || ip.includes('%')) return null;
|
|
7
|
+
const normalized = new URL(`http://[${ip}]/`).hostname.slice(1, -1);
|
|
8
|
+
const mapped = /^::ffff:([0-9a-f]+):([0-9a-f]+)$/.exec(normalized);
|
|
9
|
+
if (mapped) {
|
|
10
|
+
const high = Number.parseInt(mapped[1], 16);
|
|
11
|
+
const low = Number.parseInt(mapped[2], 16);
|
|
12
|
+
return [
|
|
13
|
+
high >>> 8,
|
|
14
|
+
255 & high,
|
|
15
|
+
low >>> 8,
|
|
16
|
+
255 & low
|
|
17
|
+
].join('.');
|
|
18
|
+
}
|
|
19
|
+
return normalized;
|
|
20
|
+
}
|
|
21
|
+
function createClientIpResolver(options) {
|
|
22
|
+
const header = options.trustedProxyHeader;
|
|
23
|
+
if (void 0 !== header && !/^[a-z0-9-]+$/i.test(header)) throw new Error('Invalid trusted IP header');
|
|
24
|
+
let lastDiagnostic = -1 / 0;
|
|
25
|
+
return async (request)=>{
|
|
26
|
+
const ip = normalizeClientIp(void 0 === header ? await options.resolvePeerIp(request) : request.headers.get(header));
|
|
27
|
+
if (!ip && performance.now() - lastDiagnostic >= 60000) {
|
|
28
|
+
lastDiagnostic = performance.now();
|
|
29
|
+
console.warn('[byline:client-ip] No valid single client IP; check the configured peer resolver or trusted proxy header.');
|
|
30
|
+
}
|
|
31
|
+
return ip;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export { createClientIpResolver, normalizeClientIp };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
export declare const sessionChangedError: () => Error;
|
|
9
|
+
export declare function expectedSession(): string | null;
|
|
10
|
+
export declare function sessionSnapshot(): {
|
|
11
|
+
sessionId: string | null;
|
|
12
|
+
epoch: number;
|
|
13
|
+
};
|
|
14
|
+
export declare function requireUnchanged(snapshot: ReturnType<typeof sessionSnapshot>): void;
|
|
15
|
+
export declare function flagSessionChanged(): void;
|
|
16
|
+
export declare function acceptSession(sessionId: string): void;
|
|
17
|
+
export declare function observeSession(sessionId: string): void;
|
|
18
|
+
export declare function sessionEpoch(): number;
|
|
19
|
+
export declare function sessionIsChanged(): boolean;
|
|
20
|
+
export declare function subscribeSessionChange(listener: () => void): () => void;
|
|
21
|
+
export declare function renewSingleFlight(work: () => Promise<void>): Promise<void>;
|
|
22
|
+
export declare function coordinateAuthAction<T>(work: () => Promise<T>): Promise<T>;
|
|
23
|
+
export declare function startSessionNotifications(): void;
|
|
24
|
+
export declare function notifySessionAction(): void;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
const storageKey = 'byline.expected-login';
|
|
2
|
+
let expected = null;
|
|
3
|
+
let epoch = 0;
|
|
4
|
+
let changed = false;
|
|
5
|
+
let renewal;
|
|
6
|
+
const listeners = new Set();
|
|
7
|
+
const sessionChangedError = ()=>new Error('Session changed. Acknowledge the active account before continuing.');
|
|
8
|
+
function expectedSession() {
|
|
9
|
+
if ("u" < typeof window) return null;
|
|
10
|
+
if (!expected) expected = window.sessionStorage.getItem(storageKey);
|
|
11
|
+
return expected;
|
|
12
|
+
}
|
|
13
|
+
function sessionSnapshot() {
|
|
14
|
+
return {
|
|
15
|
+
sessionId: expectedSession(),
|
|
16
|
+
epoch: epoch
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function requireUnchanged(snapshot) {
|
|
20
|
+
if (changed || snapshot.epoch !== epoch || snapshot.sessionId !== expectedSession()) throw sessionChangedError();
|
|
21
|
+
}
|
|
22
|
+
function flagSessionChanged() {
|
|
23
|
+
changed = true;
|
|
24
|
+
epoch++;
|
|
25
|
+
for (const listener of listeners)listener();
|
|
26
|
+
}
|
|
27
|
+
function acceptSession(sessionId) {
|
|
28
|
+
if ("u" < typeof window) return;
|
|
29
|
+
if ('string' != typeof sessionId || !sessionId) throw new Error('Provider omitted login identity');
|
|
30
|
+
window.sessionStorage.setItem(storageKey, sessionId);
|
|
31
|
+
expected = sessionId;
|
|
32
|
+
changed = false;
|
|
33
|
+
epoch++;
|
|
34
|
+
for (const listener of listeners)listener();
|
|
35
|
+
}
|
|
36
|
+
function observeSession(sessionId) {
|
|
37
|
+
const old = expectedSession();
|
|
38
|
+
if (old && old !== sessionId) {
|
|
39
|
+
flagSessionChanged();
|
|
40
|
+
throw sessionChangedError();
|
|
41
|
+
}
|
|
42
|
+
if (!old) acceptSession(sessionId);
|
|
43
|
+
}
|
|
44
|
+
function sessionEpoch() {
|
|
45
|
+
return epoch;
|
|
46
|
+
}
|
|
47
|
+
function sessionIsChanged() {
|
|
48
|
+
return changed;
|
|
49
|
+
}
|
|
50
|
+
function subscribeSessionChange(listener) {
|
|
51
|
+
listeners.add(listener);
|
|
52
|
+
return ()=>{
|
|
53
|
+
listeners.delete(listener);
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function renewSingleFlight(work) {
|
|
57
|
+
if (!renewal) renewal = work().finally(()=>{
|
|
58
|
+
renewal = void 0;
|
|
59
|
+
});
|
|
60
|
+
return renewal;
|
|
61
|
+
}
|
|
62
|
+
let authQueue = Promise.resolve();
|
|
63
|
+
function coordinateAuthAction(work) {
|
|
64
|
+
const run = authQueue.catch(()=>{}).then(async ()=>{
|
|
65
|
+
if ("u" > typeof navigator && navigator.locks) return navigator.locks.request('byline.auth', work);
|
|
66
|
+
return work();
|
|
67
|
+
});
|
|
68
|
+
authQueue = run;
|
|
69
|
+
return run;
|
|
70
|
+
}
|
|
71
|
+
let channel;
|
|
72
|
+
function startSessionNotifications() {
|
|
73
|
+
if ("u" < typeof BroadcastChannel || channel) return;
|
|
74
|
+
channel = new BroadcastChannel('byline.session');
|
|
75
|
+
channel.onmessage = ()=>{
|
|
76
|
+
flagSessionChanged();
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function notifySessionAction() {
|
|
80
|
+
startSessionNotifications();
|
|
81
|
+
channel?.postMessage({
|
|
82
|
+
type: 'session-changed'
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
export { acceptSession, coordinateAuthAction, expectedSession, flagSessionChanged, notifySessionAction, observeSession, renewSingleFlight, requireUnchanged, sessionChangedError, sessionEpoch, sessionIsChanged, sessionSnapshot, startSessionNotifications, subscribeSessionChange };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
/** The boundary owns the retry signal; handler errors cannot turn into retryable outcomes. */
|
|
9
|
+
export declare const adminSessionMiddleware: import("@tanstack/react-start").FunctionMiddlewareAfterServer<{}, unknown, undefined, undefined, {
|
|
10
|
+
bylineExpectedSessionId: string | null;
|
|
11
|
+
}, undefined, undefined>;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { createMiddleware } from "@tanstack/react-start";
|
|
2
|
+
import { getRequest, setResponseHeader } from "@tanstack/react-start/server";
|
|
3
|
+
import { AuthError, ERR_ACCESS_EXPIRED, ERR_SESSION_CHANGED, ERR_UNAUTHENTICATED } from "@byline/auth";
|
|
4
|
+
import { getAdminRequestContext } from "@byline/client/server";
|
|
5
|
+
import { renewAdminSession } from "../server-fns/auth/renew.js";
|
|
6
|
+
import { coordinateAuthAction, flagSessionChanged, observeSession, renewSingleFlight, requireUnchanged, sessionSnapshot } from "./session-coordination.js";
|
|
7
|
+
import { sessionRequestKind } from "./session-request-kind.js";
|
|
8
|
+
const outcomeHeader = 'x-byline-auth-boundary';
|
|
9
|
+
const adminSessionMiddleware = createMiddleware({
|
|
10
|
+
type: 'function'
|
|
11
|
+
}).client(async ({ next })=>{
|
|
12
|
+
if ("u" < typeof window) return next({
|
|
13
|
+
sendContext: {
|
|
14
|
+
bylineExpectedSessionId: null
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
const snapshot = sessionSnapshot();
|
|
18
|
+
requireUnchanged(snapshot);
|
|
19
|
+
return next({
|
|
20
|
+
sendContext: {
|
|
21
|
+
bylineExpectedSessionId: snapshot.sessionId
|
|
22
|
+
},
|
|
23
|
+
fetch: async (input, init)=>{
|
|
24
|
+
const reusable = !(input instanceof Request) && !(init?.body instanceof ReadableStream);
|
|
25
|
+
let response = await fetch(input, init);
|
|
26
|
+
if ('changed' === response.headers.get(outcomeHeader)) {
|
|
27
|
+
flagSessionChanged();
|
|
28
|
+
throw ERR_SESSION_CHANGED({
|
|
29
|
+
message: 'session changed'
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
if ('renew' === response.headers.get(outcomeHeader)) {
|
|
33
|
+
requireUnchanged(snapshot);
|
|
34
|
+
await renewSingleFlight(()=>coordinateAuthAction(async ()=>{
|
|
35
|
+
const result = await renewAdminSession({
|
|
36
|
+
data: {
|
|
37
|
+
expectedSessionId: snapshot.sessionId ?? void 0
|
|
38
|
+
}
|
|
39
|
+
}).catch((error)=>{
|
|
40
|
+
if (error?.code === 'ERR_SESSION_CHANGED') flagSessionChanged();
|
|
41
|
+
throw error;
|
|
42
|
+
});
|
|
43
|
+
if (snapshot.sessionId && result.sessionId !== snapshot.sessionId) {
|
|
44
|
+
flagSessionChanged();
|
|
45
|
+
throw ERR_SESSION_CHANGED({
|
|
46
|
+
message: 'session changed during renewal'
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
observeSession(result.sessionId);
|
|
50
|
+
}));
|
|
51
|
+
requireUnchanged(snapshot);
|
|
52
|
+
if (!reusable) throw ERR_ACCESS_EXPIRED({
|
|
53
|
+
message: 'Session renewed. Please retry the operation.'
|
|
54
|
+
});
|
|
55
|
+
response = await fetch(input, init);
|
|
56
|
+
if ('changed' === response.headers.get(outcomeHeader)) flagSessionChanged();
|
|
57
|
+
}
|
|
58
|
+
requireUnchanged(snapshot);
|
|
59
|
+
return response;
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}).server(async ({ next, context, method })=>{
|
|
63
|
+
const kind = sessionRequestKind(getRequest());
|
|
64
|
+
if (!kind) throw new Error('sessionRequestMiddleware is not configured');
|
|
65
|
+
let auth;
|
|
66
|
+
try {
|
|
67
|
+
auth = await getAdminRequestContext();
|
|
68
|
+
} catch (error) {
|
|
69
|
+
if (error instanceof AuthError && 'ERR_ACCESS_EXPIRED' === error.code) setResponseHeader(outcomeHeader, 'renew');
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
const expected = context.bylineExpectedSessionId;
|
|
73
|
+
if (('serverFn' === kind || 'GET' !== method) && (!expected || expected !== auth.sessionId)) {
|
|
74
|
+
setResponseHeader(outcomeHeader, 'changed');
|
|
75
|
+
throw ERR_SESSION_CHANGED({
|
|
76
|
+
message: 'session changed before operation'
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
if (!auth.sessionId) throw ERR_UNAUTHENTICATED({
|
|
80
|
+
message: 'provider omitted login identity'
|
|
81
|
+
});
|
|
82
|
+
try {
|
|
83
|
+
const result = await next();
|
|
84
|
+
const response = result.result;
|
|
85
|
+
if (response instanceof Response) response.headers.delete(outcomeHeader);
|
|
86
|
+
return result;
|
|
87
|
+
} catch (error) {
|
|
88
|
+
if (error instanceof Response) error.headers.delete(outcomeHeader);
|
|
89
|
+
throw error;
|
|
90
|
+
} finally{
|
|
91
|
+
setResponseHeader(outcomeHeader, 'handled');
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
export { adminSessionMiddleware };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
export declare function recordSessionRequestKind(request: object, kind: string): void;
|
|
9
|
+
export declare function sessionRequestKind(request: object): string | undefined;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
const key = Symbol.for('byline.sessionRequestKinds');
|
|
2
|
+
const kinds = globalThis[key] ?? new WeakMap();
|
|
3
|
+
globalThis[key] = kinds;
|
|
4
|
+
function recordSessionRequestKind(request, kind) {
|
|
5
|
+
kinds.set(request, kind);
|
|
6
|
+
}
|
|
7
|
+
function sessionRequestKind(request) {
|
|
8
|
+
return kinds.get(request);
|
|
9
|
+
}
|
|
10
|
+
export { recordSessionRequestKind, sessionRequestKind };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
/** Register after CSRF. Classifies SSR separately from remotely invoked business functions. */
|
|
9
|
+
export declare const sessionRequestMiddleware: import("@tanstack/react-start").RequestMiddlewareAfterServer<{}, undefined, undefined>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { createMiddleware } from "@tanstack/react-start";
|
|
2
|
+
import { recordSessionRequestKind } from "./session-request-kind.js";
|
|
3
|
+
const sessionRequestMiddleware = createMiddleware().server(async (ctx)=>{
|
|
4
|
+
recordSessionRequestKind(ctx.request, ctx.handlerType);
|
|
5
|
+
return ctx.next();
|
|
6
|
+
});
|
|
7
|
+
export { sessionRequestMiddleware };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
/** Limit the serialized RPC envelope, not just its eventual credential fields. */
|
|
9
|
+
export declare const SIGN_IN_MAX_BODY_BYTES: number;
|
|
10
|
+
export declare function wasSignInBodyChecked(request: Request): boolean;
|
|
11
|
+
/** Read a bounded clone before Start parses the original. Never await tee cancellation. */
|
|
12
|
+
export declare function checkSignInBody(request: Request): Promise<Response | undefined>;
|