Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions src/context/AuthContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,21 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => {

const fetchUser = async () => {
try {
const res = await fetch(`${BASE_URL}/profile`, {
const sessionRes = await fetch(`${BASE_URL}/api/auth/get-session`, {
credentials: "include",
});
if (res.ok) {
const data: any = await res.json();
const sessionData = sessionRes.ok ? await sessionRes.json() : null;

if (!sessionData?.session) {
setUser(null);
return;
Comment on lines +31 to +38

Copilot AI Mar 7, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fetchUser treats any non-2xx response from /api/auth/get-session as “no session” by setting sessionData to null, which will clear user even on transient server/network errors (e.g., 500) and can desync UI from a still-valid cookie session. Consider distinguishing “no session” (e.g., 200 with { session: null } or a 401) from unexpected failures (throw / keep prior user and surface an error).

Copilot uses AI. Check for mistakes.
}

const profileRes = await fetch(`${BASE_URL}/profile`, {
credentials: "include",
});
if (profileRes.ok) {
const data: any = await profileRes.json();
setUser(data);
} else {
setUser(null);
Expand Down
9 changes: 3 additions & 6 deletions src/pages/Signin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const Signin = () => {
const navigate = useNavigate();
const [loading, setLoading] = useState(false);
const [tab, setTab] = useState<"password" | "passkey">("password");
const { setUser } = useAuth();
const { fetchUser } = useAuth();

const schema = z.object({
email: z.string().email({ message: "Invalid email address" }),
Expand Down Expand Up @@ -55,11 +55,7 @@ const Signin = () => {
});

if (!res.ok) throw new Error("Invalid credentials");
const profileRes = await fetch(`${BASE_URL}/profile`, {
credentials: "include",
});
const profileData: any = await profileRes.json();
setUser(profileData);
await fetchUser();

Copilot AI Mar 7, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This flow always shows a success toast and navigates after await fetchUser(), but fetchUser currently never signals failure (it catches and clears user). If auth refresh fails (e.g., session/profile fetch error), the UI can navigate to /profile with a null auth context. Consider updating fetchUser to throw/return failure on refresh errors so this handler can fall into catch instead of proceeding.

Suggested change
await fetchUser();
await fetchUser().catch((error: unknown) => {
// Ensure auth refresh failures are treated as login failures.
if (error instanceof Error) {
throw error;
}
throw new Error("Failed to refresh user session after sign-in");
});

Copilot uses AI. Check for mistakes.
toast.success("Signed in!", { id: toastId });
navigate("/profile");
} catch (err: unknown) {
Expand Down Expand Up @@ -149,6 +145,7 @@ const Signin = () => {
throw new Error(result.error || "Passkey verification failed");
}

await fetchUser();
toast.success("Passkey login successful!", { id: toastId });
navigate("/profile");
} catch (err: unknown) {
Comment on lines +148 to 151

Copilot AI Mar 7, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as the password flow: after passkey verification, the code proceeds to success toast + navigation regardless of whether fetchUser() actually hydrated auth state (it currently swallows failures). Consider having fetchUser signal refresh failures so this flow doesn’t navigate with a null auth context.

Copilot uses AI. Check for mistakes.
Expand Down
4 changes: 4 additions & 0 deletions src/pages/Signup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { toast } from "react-hot-toast";
import { useNavigate } from "react-router-dom";
import { z } from "zod";
import { BASE_URL } from "../config";
import { useAuth } from "../context/AuthContext";
import { base64urlToUint8Array, bufferToBase64 } from "../utils/webauthn";

const schema = z.object({
Expand All @@ -17,6 +18,7 @@ type SignupFormData = z.infer<typeof schema>;
const Signup = () => {
const resolver = zodResolver(schema);
const navigate = useNavigate();
const { fetchUser } = useAuth();

const {
register,
Expand All @@ -39,6 +41,7 @@ const Signup = () => {
});
if (!res.ok) throw new Error("Signup failed");

await fetchUser();

Copilot AI Mar 7, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This handler proceeds to success toast + navigation after await fetchUser(), but fetchUser currently swallows refresh failures (and may clear user). If session/profile hydration fails, this will still navigate to /profile with a null auth context. Consider updating fetchUser to throw/return failure so this handler can fall into catch instead of proceeding.

Suggested change
await fetchUser();
const fetchedUser = await fetchUser();
if (!fetchedUser) {
throw new Error("Failed to load user session after signup");
}

Copilot uses AI. Check for mistakes.
toast.success("Signup successful!", { id: toastId });
navigate("/profile");
} catch (err: unknown) {
Expand Down Expand Up @@ -109,6 +112,7 @@ const Signup = () => {
});
if (!finishRes.ok) throw new Error("Failed to finish WebAuthn");

await fetchUser();
toast.success("Passkey signup complete!", { id: toastId });
navigate("/profile");
reset();
Comment on lines +115 to 118

Copilot AI Mar 7, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same concern for the passkey-only signup flow: navigation/success messaging happens even if fetchUser() didn’t successfully hydrate auth state (it currently catches errors internally). Consider making fetchUser signal refresh failure so this path can stop and display an error instead of navigating with a null auth context.

Copilot uses AI. Check for mistakes.
Expand Down