feat: auth, persistence, and AI integration

- Cookie-based auth via backend proxy (httpOnly JWTs)
- Supabase Postgres persistence for sessions/messages/profiles + RLS
- Fix cross-user session leak (token cache keyed by full token, not 50-char prefix)
- Fix missing table grants (42501) via migration; auto-provision profiles on user creation
- Chat validation, ownership checks, rate limiting, /api/chat/sessions route ordering
- Frontend auth-state reset + credentials include
- OpenRouter AI provider (OpenAI-compatible base URL, reasoning disabled)
- Tests: chatValidation, appState
This commit is contained in:
2026-08-19 09:41:40 -04:00
parent 93a35a7343
commit ff9da4d324
43 changed files with 4174 additions and 249 deletions
+64
View File
@@ -0,0 +1,64 @@
import { createContext, useContext, useEffect, useState } from "react";
import { auth } from "./backendAuth";
const AuthContext = createContext(null);
/**
* SupabaseAuth — provides auth state and helper methods to the tree.
*
* All auth is handled server-side via httpOnly cookies.
* The frontend never sees raw tokens.
*/
export function AuthProvider({ children }) {
const [user, setUser] = useState(null);
// Restore session on mount (from httpOnly cookie)
useEffect(() => {
auth.me().then(({ user }) => {
setUser(user);
});
}, []);
const signIn = async (email, password) => {
const data = await auth.signin(email, password);
setUser(data.user);
return data;
};
const signUp = async (email, password) => {
const data = await auth.signup(email, password);
setUser(data.user);
return data;
};
const signOut = async () => {
await auth.signout();
setUser(null);
};
const getToken = async () => {
// Token lives in httpOnly cookie — not accessible to JavaScript
return null;
};
return (
<AuthContext.Provider
value={{
user,
isAuthenticated: !!user,
signIn,
signUp,
signOut,
getToken,
}}
>
{children}
</AuthContext.Provider>
);
}
export function useAuth() {
const ctx = useContext(AuthContext);
if (!ctx) throw new Error("useAuth must be used within AuthProvider");
return ctx;
}