diff --git a/amplify.yml b/amplify.yml
index 51d8bf22..6a03427b 100644
--- a/amplify.yml
+++ b/amplify.yml
@@ -15,4 +15,9 @@ applications:
- '**/*'
cache:
paths:
- - node_modules/**/*
\ No newline at end of file
+ - node_modules/**/*
+ redirects: # ← add this whole block
+ - source: "/<*>" # match any client‑side route
+ target: "/index.html" # serve SPA shell
+ status: "200" # rewrite, not redirect
+ condition: null
\ No newline at end of file
diff --git a/frontend/index.html b/frontend/index.html
index 1bd99ac0..5e3922b5 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -2,6 +2,7 @@
+
Vite + React + TS
diff --git a/frontend/src/api.ts b/frontend/src/api.ts
index d8b91b49..06878c34 100644
--- a/frontend/src/api.ts
+++ b/frontend/src/api.ts
@@ -1,11 +1,16 @@
+// API INDEX
+
const BASE = (import.meta.env.VITE_API_URL || '').replace(/\/$/, '');
export async function api(
path: string,
- init?: RequestInit
+ init: RequestInit = {}
): Promise {
- // Ensure path starts with a single slash
const cleanPath = path.startsWith('/') ? path : `/${path}`;
const url = `${BASE}${cleanPath}`;
- return fetch(url, init);
+
+ return fetch(url, {
+ credentials: 'include', // ← send & receive the jwt cookie
+ ...init,
+ });
}
diff --git a/frontend/src/context/auth/authContext.tsx b/frontend/src/context/auth/authContext.tsx
index 96b6e45d..19ced61b 100644
--- a/frontend/src/context/auth/authContext.tsx
+++ b/frontend/src/context/auth/authContext.tsx
@@ -1,4 +1,4 @@
-import { useContext, createContext, ReactNode } from 'react';
+import { useContext, createContext, ReactNode, useEffect } from 'react';
import { getAppStore } from '../../external/bcanSatchel/store';
import { setAuthState, logoutUser } from '../../external/bcanSatchel/actions'
import { observer } from 'mobx-react-lite';
@@ -42,13 +42,12 @@ export const AuthProvider = observer(({ children }: { children: ReactNode }) =>
const data = await response.json();
- // TODO: Need to either completely remove access_token
- // or verify it in each action
- if (data.access_token) {
- setAuthState(true, data.user, data.access_token);
- } else {
+ if (data.user) {
+ // cookie was set by /auth/login
+ setAuthState(true, data.user, null);
+ } else {
alert('Login failed. Please check your credentials.');
- }
+ }
};
/**
@@ -74,9 +73,19 @@ export const AuthProvider = observer(({ children }: { children: ReactNode }) =>
/**
* Log out the user
*/
- const logout = () => {
- logoutUser(); // Satchel action that clears state
- };
+ const logout = () => {
+ api('/auth/logout', { method: 'POST' });
+ logoutUser();
+ };
+
+ // Session Level 1.1
+ // Restore on page-load / hard-refresh
+ useEffect(() => {
+ api('/auth/session')
+ .then(r => (r.ok ? r.json() : Promise.reject()))
+ .then(({ user }) => setAuthState(true, user, null))
+ .catch(() => logoutUser());
+ }, []);
return (