feat: integrate refreshToken utility in admin login and auth context …#127
feat: integrate refreshToken utility in admin login and auth context …#127
Conversation
…for improved session management; enhance route security by adding role verification middleware to various endpoints
There was a problem hiding this comment.
6 issues found across 14 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="client/src/components/adminProfile/adminLogin.tsx">
<violation number="1" location="client/src/components/adminProfile/adminLogin.tsx:125">
P1: `refreshToken()` is invoked before admin-role validation, so rejected non-admin SSO users can still receive an `accessToken` cookie in this flow.</violation>
</file>
<file name="server/routes/clinics.js">
<violation number="1" location="server/routes/clinics.js:347">
P1: Volunteer registration mutation routes allow a volunteer to create/cancel registrations for other volunteers because ownership of `volunteerId` is never validated.</violation>
</file>
<file name="server/src/middleware.ts">
<violation number="1" location="server/src/middleware.ts:44">
P1: `verifyRole` now skips authorization for all non-production environments, creating a broad authz bypass on protected routes.</violation>
</file>
<file name="server/routes/users.ts">
<violation number="1" location="server/routes/users.ts:58">
P1: Volunteer-scoped user read/update routes accept any firebaseUid from the request without verifying it matches the authenticated user, allowing volunteers to access or modify other users’ records (horizontal privilege escalation).</violation>
<violation number="2" location="server/routes/users.ts:105">
P1: Horizontal privilege escalation: any volunteer can update any user's email. `verifyRole("volunteer")` does not enforce ownership. Either elevate the required role or add an ownership check (e.g., `res.locals.decodedToken.uid === firebaseUid`) before performing the update.</violation>
<violation number="3" location="server/routes/users.ts:132">
P0: Horizontal privilege escalation: any volunteer can reset any user's password. `verifyRole("volunteer")` only checks the caller's minimum role, not that they own the target account. Either restrict this to `"supervisor"` (admin-initiated resets) or compare `res.locals.decodedToken.uid` against the looked-up `firebaseUid` to enforce ownership.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.
| if (process.env.NODE_ENV !== "production") { | ||
| return next(); | ||
| } |
There was a problem hiding this comment.
P1: verifyRole now skips authorization for all non-production environments, creating a broad authz bypass on protected routes.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At server/src/middleware.ts, line 44:
<comment>`verifyRole` now skips authorization for all non-production environments, creating a broad authz bypass on protected routes.</comment>
<file context>
@@ -41,6 +41,10 @@ export const verifyToken = async (
*/
export const verifyRole = (requiredRole: string | string[]) => {
return async (req: Request, res: Response, next: NextFunction) => {
+ if (process.env.NODE_ENV !== "production") {
+ return next();
+ }
</file context>
| if (process.env.NODE_ENV !== "production") { | |
| return next(); | |
| } | |
| // Do not bypass role checks by NODE_ENV; keep authz behavior consistent across environments. | |
…led consistently; enhance security in clinic and user routes with role verification to prevent unauthorized actions
There was a problem hiding this comment.
2 issues found across 3 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="client/src/components/adminProfile/adminLogin.tsx">
<violation number="1" location="client/src/components/adminProfile/adminLogin.tsx:141">
P1: `refreshToken()` is called **after** `backend.get("/users")`, not before it. The access token cookie won't be set when the `/users` request fires, so with the new `verifyRole` middleware this call will fail. Move `refreshToken()` back above the `backend.get` call to match the stated intent.</violation>
</file>
<file name="server/routes/users.ts">
<violation number="1" location="server/routes/users.ts:63">
P2: Fail-open ownership check: when `callerUid` is `undefined` the guard is silently skipped, allowing the request through. Inverting to `if (!callerUid || callerUid !== firebaseUid)` makes the check fail-closed, which is the safer default for authorization logic. Currently production is protected by the global `verifyToken` middleware always setting `decodedToken`, but a fail-closed guard is more resilient to future middleware changes.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.
…e user route security checks to prevent unauthorized access
There was a problem hiding this comment.
1 issue found across 2 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="client/src/components/adminProfile/adminLogin.tsx">
<violation number="1" location="client/src/components/adminProfile/adminLogin.tsx:132">
P2: Refresh token is set before admin authorization, and denied users are signed out without clearing the access token cookie.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.
…e session management
There was a problem hiding this comment.
1 issue found across 1 file (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="client/src/components/adminProfile/adminLogin.tsx">
<violation number="1" location="client/src/components/adminProfile/adminLogin.tsx:141">
P2: SSO error handling is incomplete: failures after `refreshToken()` do not clear auth state/cookies, leaving a stale authenticated cookie after unsuccessful admin login.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.
…aller UID only in production environment
There was a problem hiding this comment.
1 issue found across 2 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="server/routes/users.ts">
<violation number="1" location="server/routes/users.ts:63">
P1: Gating authorization behind `NODE_ENV === "production"` is a security anti-pattern. If the environment variable is missing, misspelled, or set to an unexpected value in production, the self-only check is silently skipped and any volunteer can read any other user's record. This also makes the authorization logic untestable in dev/staging.
If the goal is easier local development, consider an explicit opt-in flag (e.g., `DISABLE_SELF_ONLY_CHECK=true`) that defaults to enforcing the check, rather than an opt-out that defaults to skipping it.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.
Description
getRedirectResult()resolves inadminLogin.tsxandAuthContext.tsx,refreshToken()is now called before any backend requests, so theaccessTokencookie is set upfront instead of relying on the auth interceptor to rescue thefirst failed request
verifyRolemiddleware to all server routes:volunteer+for reads and self-management,staff+for mutations and admin data,supervisorfor user deletion and role assignmentPOST /users/custom-token,POST /users/create) intentionally left withoutverifyRoleas they are called before a role existsTest plan
No cookie → 400
curl -v http://localhost:3001/volunteers
Expected:
400 @verifyToken invalid access tokenBad token → 400
curl -v -H "Cookie: accessToken=fakejunk" http://localhost:3001/volunteers
Expected:
400 @verifyToken error validating tokenValid token → 200
Open DevTools → Application → Cookies → copy
accessToken, then:curl -v -H "Cookie: accessToken=" http://localhost:3001/volunteers
Expected:
200with volunteer dataWrong role → 403
Use a volunteer's token and hit a staff-only route:
curl -v -H "Cookie: accessToken=" http://localhost:3001/email-templates
Expected:
403 @verifyRole invalid role (required: staff)Correct role → 200
Use a staff/supervisor token on the same route:
curl -v -H "Cookie: accessToken=" http://localhost:3001/email-templates
Expected:
200Google and Microsoft SSO login flows complete without errors and land on the
dashboard
Screenshots/Media
Issues
Closes #107
Summary by cubic
Call
refreshTokenright after SSO redirect so theaccessTokencookie is set before any API calls, preventing first-request failures. Enforce role-based access and self-only rules across server routes to meet Linear #107 for Microsoft/Google SSO security.New Features
verifyRoleacross routes (volunteerfor reads/self,stafffor writes/admin,supervisorfor destructive), with a non-prod bypass.supervisorrequired for delete and password reset./users/custom-token, POST/users/create.Bug Fixes
Written for commit 295bfbe. Summary will update on new commits.