From 7118d21f34e1357eed6629a9e777330d47186966 Mon Sep 17 00:00:00 2001 From: Alexis Date: Wed, 29 Apr 2026 13:29:23 +0200 Subject: [PATCH] fix: update callback route and enhance user profile link in header - Changed the OAuth callback route from `/auth/callback` to `/callback` in App component and .env.example. - Added user profile link in AppHeader for authenticated users, directing to their dashboard. - Removed bypass mode references from LandingPage to streamline the login flow. - Introduced a utility function to extract ORCID from JWT in AuthContext for better user state management. --- frontend/.env.example | 4 +- frontend/src/App.jsx | 2 +- frontend/src/components/layout/AppHeader.jsx | 20 +++++++++- frontend/src/contexts/AuthContext.jsx | 25 ++++++++++-- frontend/src/pages/LandingPage.jsx | 40 ++------------------ frontend/src/services/api.js | 1 - 6 files changed, 47 insertions(+), 45 deletions(-) diff --git a/frontend/.env.example b/frontend/.env.example index be05084..159ff5a 100644 --- a/frontend/.env.example +++ b/frontend/.env.example @@ -30,8 +30,10 @@ VITE_USE_MOCKS=false # 5. /auth/callback extrae el code y llama al backend para obtener el JWT # # Para que el callback vuelva al frontend, el backend necesita: -# ORCID_REDIRECT_URI=http://localhost:5173/auth/callback +# ORCID_REDIRECT_URI=http://localhost:5173/callback # (en backend/.env — debe coincidir con el redirect URI del app ORCID sandbox) +# En producción con ngrok u otro túnel, el formato sería: +# ORCID_REDIRECT_URI=https:///callback # # ── Modo bypass (solo desarrollo sin credenciales OAuth configuradas) ───────── # Cuando está a "true", el botón "Iniciar sesión" genera un token simulado diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index ff5c2cb..17b6d82 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -19,7 +19,7 @@ export default function App() { } /> } /> } /> - } /> + } /> } /> diff --git a/frontend/src/components/layout/AppHeader.jsx b/frontend/src/components/layout/AppHeader.jsx index 2a94a1d..fd10421 100644 --- a/frontend/src/components/layout/AppHeader.jsx +++ b/frontend/src/components/layout/AppHeader.jsx @@ -12,7 +12,7 @@ import { useAuth } from "../../contexts/AuthContext"; * - `group` → back button to `/` + group label + auth indicator. */ export function AppHeader({ variant = "landing" }) { - const { isAuthenticated, logout } = useAuth(); + const { isAuthenticated, userOrcidId, logout } = useAuth(); const navigate = useNavigate(); function handleLogout() { @@ -36,6 +36,15 @@ export function AppHeader({ variant = "landing" }) {
{isAuthenticated && (
+ {userOrcidId && ( + + + Mi perfil + + )} Sesión activa @@ -67,6 +76,15 @@ export function AppHeader({ variant = "landing" }) { {isAuthenticated && (
+ {userOrcidId && ( + + + Mi perfil + + )} Sesión activa diff --git a/frontend/src/contexts/AuthContext.jsx b/frontend/src/contexts/AuthContext.jsx index 5b0ddc0..206cba1 100644 --- a/frontend/src/contexts/AuthContext.jsx +++ b/frontend/src/contexts/AuthContext.jsx @@ -16,6 +16,19 @@ export const AUTH_ERROR_TYPE = "ORCID_AUTH_ERROR"; const AuthContext = createContext(null); +function extractOrcidFromToken(token) { + if (!token) return null; + try { + const payloadBase64 = token.split(".")[1]; + if (!payloadBase64) return null; + const payloadJson = atob(payloadBase64.replace(/-/g, "+").replace(/_/g, "/")); + const payload = JSON.parse(payloadJson); + return payload?.sub ?? null; + } catch { + return null; + } +} + /** * Provides JWT-based authentication state throughout the app. * @@ -30,8 +43,6 @@ const AuthContext = createContext(null); * opener and closes itself. * 7. This provider's message listener stores the token and updates state. * - * For development / sandbox bypass (VITE_AUTH_BYPASS=true), the token is - * stored directly via storeToken() without going through ORCID. */ export function AuthProvider({ children }) { const [token, setToken] = useState(() => localStorage.getItem(STORAGE_KEY)); @@ -53,7 +64,7 @@ export function AuthProvider({ children }) { }, []); /** - * Stores a JWT directly (used by AuthCallbackPage and bypass mode). + * Stores a JWT directly (used by AuthCallbackPage). * Does NOT trigger any network request. */ const storeToken = useCallback((accessToken) => { @@ -67,7 +78,13 @@ export function AuthProvider({ children }) { }, []); const value = useMemo( - () => ({ token, isAuthenticated: Boolean(token), storeToken, logout }), + () => ({ + token, + isAuthenticated: Boolean(token), + userOrcidId: extractOrcidFromToken(token), + storeToken, + logout, + }), [token, storeToken, logout], ); diff --git a/frontend/src/pages/LandingPage.jsx b/frontend/src/pages/LandingPage.jsx index be0f237..cd0a219 100644 --- a/frontend/src/pages/LandingPage.jsx +++ b/frontend/src/pages/LandingPage.jsx @@ -11,23 +11,17 @@ import { getOrcidAuthorizeUrl, searchResearcher } from "../services/api"; import { useAuth } from "../contexts/AuthContext"; import { AUTH_MESSAGE_TYPE, AUTH_ERROR_TYPE } from "../contexts/AuthContext"; -// When VITE_AUTH_BYPASS=true, skip the real OAuth popup and simulate login -// with the ORCID entered in the text field. Use only in development. -const AUTH_BYPASS = import.meta.env.VITE_AUTH_BYPASS === "true"; - /** * Entry view: login con ORCID iD + búsqueda individual anónima + * buscador grupal para múltiples investigadores. * * Flujo de login: - * - Modo normal: abre popup OAuth → sandbox.orcid.org → /auth/callback - * → JWT → cierra popup → estado actualizado aquí. - * - VITE_AUTH_BYPASS=true (solo dev): genera un token simulado con el - * ORCID del campo de texto, sin tocar el backend de auth. + * - abre popup OAuth → sandbox.orcid.org → /callback + * - recibe JWT → cierra popup → estado actualizado aquí. */ export function LandingPage() { const navigate = useNavigate(); - const { isAuthenticated, storeToken } = useAuth(); + const { isAuthenticated } = useAuth(); const [orcidInput, setOrcidInput] = useState(""); const [error, setError] = useState(""); @@ -76,24 +70,6 @@ export function LandingPage() { } function handleOrcidLogin() { - // ── Modo bypass (solo desarrollo / sandbox sin credenciales OAuth) ── - if (AUTH_BYPASS) { - if (!isValidOrcid(orcidInput)) { - setError( - "Introduce un ORCID iD válido para simular el login (modo bypass).", - ); - return; - } - // Genera un token simulado (no válido en el backend) solo para - // probar la UI en estado autenticado. - storeToken(`bypass_token_${orcidInput}`); - toast.success("Login simulado (modo bypass)", { - description: `Sesión activa para ${orcidInput}. El backend no reconocerá este token.`, - }); - return; - } - - // ── Flujo OAuth real (popup) ── setLoginLoading(true); const authorizeUrl = getOrcidAuthorizeUrl(); @@ -228,16 +204,8 @@ export function LandingPage() { {loginLoading ? : } {loginLoading ? "Abriendo ventana de ORCID..." - : AUTH_BYPASS - ? "Simular login (bypass)" : "Iniciar sesión con ORCID"} - {AUTH_BYPASS && ( -

- Modo bypass activo — introduce un ORCID abajo y pulsa el botón. - No se valida contra el backend. -

- )} )} @@ -295,8 +263,6 @@ export function LandingPage() {

{isAuthenticated ? "Busca un investigador o usa «Cerrar sesión» arriba." - : AUTH_BYPASS - ? "Introduce tu ORCID y pulsa «Simular login» para probar la UI autenticada." : "Pulsa «Iniciar sesión» para autenticarte, o «Buscar» de forma anónima."}

diff --git a/frontend/src/services/api.js b/frontend/src/services/api.js index 0effa9f..6ae0a1d 100644 --- a/frontend/src/services/api.js +++ b/frontend/src/services/api.js @@ -145,7 +145,6 @@ function normalizePublication(p) { hash_fingerprint: p.hash_fingerprint ?? null, last_modified: p.last_modified ?? null, status: p.status ?? null, - // null when request was made without a JWT (user not logged in) downloaded_by_me: p.downloaded_by_me ?? null, }; }