Skip to content
Merged
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
2 changes: 1 addition & 1 deletion backend/src/controllers/authController.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ export const getSession = asyncHandler(async (req, res) => {
.status(200)
.json({ success: true, authenticated: false, user: null });

res.status(200).json({ success: true, authenticated: true, user });
res.status(200).json({ success: true, authenticated: true, user, token });

Copilot AI Mar 20, 2026

Copy link

Choose a reason for hiding this comment

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

getSession is echoing the cookie JWT back in the response body, which exposes the primary auth token to client-side JavaScript and undermines the httpOnly cookie model. If Socket.IO needs auth, prefer cookie-based socket auth (parse cookie server-side) or issue a short-lived secondary token rather than returning the session JWT here.

Suggested change
res.status(200).json({ success: true, authenticated: true, user, token });
// Issue a short-lived secondary token instead of exposing the primary cookie JWT
const secondaryToken = jwt.sign(
{ id: user.id, purpose: "session" },
process.env.JWT_SECRET,
{ expiresIn: "5m" },
);
res
.status(200)
.json({ success: true, authenticated: true, user, token: secondaryToken });

Copilot uses AI. Check for mistakes.

Copilot AI Mar 20, 2026

Copy link

Choose a reason for hiding this comment

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

This change adds a new token field to the /auth/session response but there are no integration tests covering that endpoint. Add/update tests to validate the session response shape (authenticated true/false) and ensure the token behavior matches the intended security model.

Suggested change
res.status(200).json({ success: true, authenticated: true, user, token });
res.status(200).json({ success: true, authenticated: true, user });

Copilot uses AI. Check for mistakes.
} catch {
res.status(200).json({ success: true, authenticated: false, user: null });
}
Expand Down
1 change: 1 addition & 0 deletions backend/src/utils/tokenUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,6 @@ export const sendTokenResponse = (user, statusCode, res) => {
res.status(statusCode).cookie("token", token, options).json({
success: true,
user: userData,
token, // Include token for Socket.io and client-side storage
});
};
Comment on lines +46 to 48

Copilot AI Mar 20, 2026

Copy link

Choose a reason for hiding this comment

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

Returning the raw JWT in the JSON response negates the protection provided by an httpOnly cookie and makes the token available to any injected script (XSS), enabling easy token exfiltration/replay. Prefer keeping the JWT only in an httpOnly cookie and authenticating Socket.IO via cookies on the handshake, or mint a separate short-lived, socket-scoped token instead of exposing the primary auth JWT.

Suggested change
token, // Include token for Socket.io and client-side storage
});
};
});
};
};

Copilot uses AI. Check for mistakes.
18 changes: 18 additions & 0 deletions frontend/src/context/AuthContext.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,16 @@ export const AuthProvider = ({ children }) => {
const response = await authService.getSession();
setUser(response.user || null);
setIsAuthenticated(Boolean(response.authenticated));
// Store token in localStorage if authenticated for Socket.io connection
if (response.authenticated && response.token) {
localStorage.setItem("token", response.token);
} else {
localStorage.removeItem("token");
}
Comment on lines +37 to +42

Copilot AI Mar 20, 2026

Copy link

Choose a reason for hiding this comment

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

Storing the auth JWT in localStorage makes it retrievable by any XSS payload and increases the blast radius compared to an httpOnly cookie. If the goal is Socket.IO auth, consider authenticating the socket using the existing cookie (send credentials in the handshake and validate cookies server-side) or keep the token only in memory for the current tab/session instead of persistent storage.

Copilot uses AI. Check for mistakes.
} catch {
setUser(null);
setIsAuthenticated(false);
localStorage.removeItem("token");
} finally {
setLoading(false);
}
Expand All @@ -47,6 +54,10 @@ export const AuthProvider = ({ children }) => {
const response = await authService.login(email, password);
setUser(response.user);
setIsAuthenticated(true);
// Store token in localStorage for Socket.io and other client-side usage
if (response.token) {
localStorage.setItem("token", response.token);
}
Comment on lines +57 to +60

Copilot AI Mar 20, 2026

Copy link

Choose a reason for hiding this comment

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

On successful login, the token is only written when response.token is truthy; if the backend ever omits token (or a proxy strips it), an older token already in localStorage would remain and could be used for Socket.IO auth. Consider explicitly removing/overwriting the stored token when the login succeeds but no token is present.

Copilot uses AI. Check for mistakes.
toast.success(`Welcome back, ${response.user.name}!`);
return response;
} catch (error) {
Expand All @@ -65,6 +76,10 @@ export const AuthProvider = ({ children }) => {
const response = await authService.register(userData);
setUser(response.user);
setIsAuthenticated(true);
// Store token in localStorage for Socket.io and other client-side usage
if (response.token) {
localStorage.setItem("token", response.token);

Copilot AI Mar 20, 2026

Copy link

Choose a reason for hiding this comment

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

On successful registration, the token is only written when response.token is truthy; if no token is returned, any previous localStorage token would remain and could be used by Socket.IO. Consider explicitly clearing/overwriting the stored token when registration succeeds but no token is present.

Suggested change
localStorage.setItem("token", response.token);
localStorage.setItem("token", response.token);
} else {
localStorage.removeItem("token");

Copilot uses AI. Check for mistakes.
}
toast.success("Account created successfully!");
return response;
} catch (error) {
Expand Down Expand Up @@ -94,6 +109,9 @@ export const AuthProvider = ({ children }) => {
setUser(null);
setIsAuthenticated(false);

// Clear token from localStorage
localStorage.removeItem("token");

// Clear any cached data (optional, add if there's caching in localStorage)
// sessionStorage.clear(); // Only if you're using sessionStorage

Expand Down
Loading