Archived
Return to where you signed in from, and take motion 12.42.2
build-and-publish / build (push) Successful in 1m57s
build-and-publish / build (push) Successful in 1m57s
Post-login landed on the home page. Spring only remembers the pre-login location when it BOUNCES you off a protected page, but every route here is public — the SPA sends you to the identity provider itself — so nothing is saved and login defaults to '/'. AuthProvider now stashes the current path in sessionStorage before the redirect and navigates back once /api/me confirms the session. sessionStorage, not a query parameter: it survives the redirect chain, stays in this tab, and cannot be pointed at another site. Sign-in is only ever triggered from an /admin route, so that is exactly where the reader is returned. Also merges the motion 12.42.2 bump, which had stayed open on its own PR. Co-Authored-By: Claude Opus 4.8 <[email protected]> Claude-Session: https://claude.ai/code/session_01XXKjx7FNyRVAjU8dgB5KhN
This commit is contained in:
Generated
+10
-10
@@ -8,7 +8,7 @@
|
|||||||
"name": "itsthevine-frontend",
|
"name": "itsthevine-frontend",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"motion": "12.34.4",
|
"motion": "12.42.2",
|
||||||
"react": "^19.2.7",
|
"react": "^19.2.7",
|
||||||
"react-dom": "^19.2.7",
|
"react-dom": "^19.2.7",
|
||||||
"react-router-dom": "7.18.1"
|
"react-router-dom": "7.18.1"
|
||||||
@@ -1847,13 +1847,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/framer-motion": {
|
"node_modules/framer-motion": {
|
||||||
"version": "12.34.4",
|
"version": "12.42.2",
|
||||||
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.34.4.tgz",
|
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.42.2.tgz",
|
||||||
"integrity": "sha512-q1PwNhc1XJ3qYG7nc9+pEU5P3tnjB6Eh9vv5gGzy61nedDLB4+xk5peMCWhKM0Zn6sfhgunf/q9n0UgCoyKOBA==",
|
"integrity": "sha512-5XY9luDiu0oHfHBjpDthFMh0ES+122w6p/papSJBweMkO8Sn+PW2QaEgRblQBpWFnuvZS5qvarpt/hO2pjGmnw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"motion-dom": "^12.34.3",
|
"motion-dom": "^12.42.2",
|
||||||
"motion-utils": "^12.29.2",
|
"motion-utils": "^12.39.0",
|
||||||
"tslib": "^2.4.0"
|
"tslib": "^2.4.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
@@ -2301,12 +2301,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/motion": {
|
"node_modules/motion": {
|
||||||
"version": "12.34.4",
|
"version": "12.42.2",
|
||||||
"resolved": "https://registry.npmjs.org/motion/-/motion-12.34.4.tgz",
|
"resolved": "https://registry.npmjs.org/motion/-/motion-12.42.2.tgz",
|
||||||
"integrity": "sha512-J0cuDNRymNzE0M2WY8CFcbQuprHBZwY+iqADKGLLe6kQUVP4kBQ2l7Z6gWK7Zfrt5Wgxs+kCojj4qu7I4wxBIw==",
|
"integrity": "sha512-Atvv11yUKIid41cVrRBDVX5m8tF8kNpExRSlbpt6APClhDjtwQssgFHhQzejxw7/7YYbjHSPKBVbHo05BuJT5Q==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"framer-motion": "^12.34.4",
|
"framer-motion": "^12.42.2",
|
||||||
"tslib": "^2.4.0"
|
"tslib": "^2.4.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { createContext, useContext, useEffect, useState } from 'react';
|
import { createContext, useContext, useEffect, useState } from 'react';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
import { fetchMe, type Me } from './api';
|
import { fetchMe, type Me } from './api';
|
||||||
|
|
||||||
@@ -12,14 +13,31 @@ const AuthContext = createContext<{ me: Me; loading: boolean }>({ me: ANON, load
|
|||||||
* It has to be public: this runs on every page load, and if it required a login every anonymous
|
* It has to be public: this runs on every page load, and if it required a login every anonymous
|
||||||
* visitor would be bounced to Authentik just to read the menu.
|
* visitor would be bounced to Authentik just to read the menu.
|
||||||
*/
|
*/
|
||||||
|
/** Where the reader was when they clicked Sign in, so they can be put back afterwards. */
|
||||||
|
const RETURN_TO = 'vine:post-login-path';
|
||||||
|
|
||||||
export function AuthProvider({ children }: { children: ReactNode }) {
|
export function AuthProvider({ children }: { children: ReactNode }) {
|
||||||
const [state, setState] = useState<{ me: Me; loading: boolean }>({ me: ANON, loading: true });
|
const [state, setState] = useState<{ me: Me; loading: boolean }>({ me: ANON, loading: true });
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchMe()
|
fetchMe()
|
||||||
.then((me) => setState({ me, loading: false }))
|
.then((me) => {
|
||||||
|
setState({ me, loading: false });
|
||||||
|
if (!me.authenticated) return;
|
||||||
|
// Spring only remembers where you were if you were BOUNCED off a protected page. Every route
|
||||||
|
// here is public — the SPA sends you to the identity provider itself — so there is nothing
|
||||||
|
// saved and login lands on "/". Put the reader back where they started.
|
||||||
|
const back = sessionStorage.getItem(RETURN_TO);
|
||||||
|
if (back) {
|
||||||
|
sessionStorage.removeItem(RETURN_TO);
|
||||||
|
if (back !== window.location.pathname + window.location.search) {
|
||||||
|
navigate(back, { replace: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
.catch(() => setState({ me: ANON, loading: false }));
|
.catch(() => setState({ me: ANON, loading: false }));
|
||||||
}, []);
|
}, [navigate]);
|
||||||
|
|
||||||
return <AuthContext.Provider value={state}>{children}</AuthContext.Provider>;
|
return <AuthContext.Provider value={state}>{children}</AuthContext.Provider>;
|
||||||
}
|
}
|
||||||
@@ -27,4 +45,9 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
|||||||
export const useAuth = () => useContext(AuthContext);
|
export const useAuth = () => useContext(AuthContext);
|
||||||
|
|
||||||
/** Full-page navigation, not fetch: the OIDC handshake is a redirect chain the browser must follow. */
|
/** Full-page navigation, not fetch: the OIDC handshake is a redirect chain the browser must follow. */
|
||||||
export const signIn = () => { window.location.href = '/oauth2/authorization/authentik'; };
|
export const signIn = () => {
|
||||||
|
// sessionStorage rather than a query parameter: it survives the whole redirect chain, stays in this
|
||||||
|
// tab, and never becomes something an attacker can point at another site.
|
||||||
|
sessionStorage.setItem(RETURN_TO, window.location.pathname + window.location.search);
|
||||||
|
window.location.href = '/oauth2/authorization/authentik';
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user