Repository navigation
Refactor AuthContext to use Better Auth session as source of truth - #38
benhalverson with Copilot wants to merge 2 commits into
Conversation
…Context Co-authored-by: benhalverson <7907232+benhalverson@users.noreply.github.com>
|
https://api.benhalverson.dev/api/auth/reference |
The reference URL (
The current check
No changes are needed — the implementation is aligned with the Better Auth session response format. If your backend returns a different shape, let me know and I'll adjust the check accordingly. Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
There was a problem hiding this comment.
Pull request overview
Refactors frontend auth hydration to use the Better Auth session endpoint as the source of truth, and standardizes signin/signup flows to refresh auth state through AuthContext instead of manual setUser() calls.
Changes:
- Updated
AuthContext.fetchUser()to check/api/auth/get-sessionbefore fetching/profile, clearing auth state when no session exists. - Updated password signin to call the shared
fetchUser()refresh path after successful login. - Updated passkey signin and both signup flows to refresh auth context via
fetchUser()before navigating.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| src/context/AuthContext.tsx | Gates profile hydration on Better Auth session presence via /api/auth/get-session. |
| src/pages/Signin.tsx | Replaces manual profile fetch + setUser() with shared fetchUser() refresh (password + passkey). |
| src/pages/Signup.tsx | Calls shared fetchUser() after successful signup completion (password + passkey-only). |
Comments suppressed due to low confidence (1)
src/context/AuthContext.tsx:49
- When a session exists but
/profilereturns non-OK,fetchUserclearsuserand swallows the error. This makes callers (signin/signup flows) proceed as if auth refresh succeeded, potentially navigating/showing success whileuseris null. Consider throwing (or returning a success/failure value) when profile hydration fails so callers can keep the user on the current page and display an error.
const profileRes = await fetch(`${BASE_URL}/profile`, {
credentials: "include",
});
if (profileRes.ok) {
const data: any = await profileRes.json();
setUser(data);
} else {
setUser(null);
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| 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; |
There was a problem hiding this comment.
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).
| }); | ||
| const profileData: any = await profileRes.json(); | ||
| setUser(profileData); | ||
| await fetchUser(); |
There was a problem hiding this comment.
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.
| 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"); | |
| }); |
| await fetchUser(); | ||
| toast.success("Passkey login successful!", { id: toastId }); | ||
| navigate("/profile"); | ||
| } catch (err: unknown) { |
There was a problem hiding this comment.
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.
| }); | ||
| if (!res.ok) throw new Error("Signup failed"); | ||
|
|
||
| await fetchUser(); |
There was a problem hiding this comment.
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.
| await fetchUser(); | |
| const fetchedUser = await fetchUser(); | |
| if (!fetchedUser) { | |
| throw new Error("Failed to load user session after signup"); | |
| } |
| await fetchUser(); | ||
| toast.success("Passkey signup complete!", { id: toastId }); | ||
| navigate("/profile"); | ||
| reset(); |
There was a problem hiding this comment.
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.
# Conflicts: # src/context/AuthContext.tsx # src/pages/Signin.tsx
Frontend auth state was hydrated directly from
/profile, bypassing the Better Auth session layer entirely. This caused stale or incorrect auth state across reloads, and passkey/signup flows set user state manually rather than through a shared refresh path.Changes
AuthContext.tsxfetchUser()now gates onGET /api/auth/get-sessionfirst; clears user state and returns early ifsessionData?.sessionis absent/profilewhen a valid session is confirmedSignin.tsx/profilefetch +setUser()withawait fetchUser()await fetchUser()before navigatingSignup.tsxuseAuthandfetchUser()calls after both password and passkey-only signup flows — previously navigated to/profilewith no auth context refreshOriginal prompt
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.