Skip to content

feat: integrate refreshToken utility in admin login and auth context …#127

Open
brelieu05 wants to merge 6 commits intomainfrom
107-fix-implement-verify-token-w-microsoftgoogle-sso
Open

feat: integrate refreshToken utility in admin login and auth context …#127
brelieu05 wants to merge 6 commits intomainfrom
107-fix-implement-verify-token-w-microsoftgoogle-sso

Conversation

@brelieu05
Copy link
Copy Markdown
Collaborator

@brelieu05 brelieu05 commented Mar 31, 2026

Description

  • After getRedirectResult() resolves in adminLogin.tsx and AuthContext.tsx, refreshToken() is now called before any backend requests, so the accessToken cookie is set upfront instead of relying on the auth interceptor to rescue the
    first failed request
  • Added verifyRole middleware to all server routes: volunteer+ for reads and self-management, staff+ for mutations and admin data, supervisor for user deletion and role assignment
  • Bootstrap endpoints (POST /users/custom-token, POST /users/create) intentionally left without verifyRole as they are called before a role exists

Test plan

  • No cookie → 400
    curl -v http://localhost:3001/volunteers
    Expected: 400 @verifyToken invalid access token

  • Bad token → 400
    curl -v -H "Cookie: accessToken=fakejunk" http://localhost:3001/volunteers
    Expected: 400 @verifyToken error validating token

  • Valid token → 200
    Open DevTools → Application → Cookies → copy accessToken, then:
    curl -v -H "Cookie: accessToken=" http://localhost:3001/volunteers
    Expected: 200 with volunteer data

  • Wrong 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: 200

  • Google and Microsoft SSO login flows complete without errors and land on the
    dashboard

Screenshots/Media

Issues

Closes #107


Summary by cubic

Call refreshToken right after SSO redirect so the accessToken cookie 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

    • Enforce verifyRole across routes (volunteer for reads/self, staff for writes/admin, supervisor for destructive), with a non-prod bypass.
    • Self-only guards: volunteers can only register/cancel clinics for themselves; user read/update is self-only in production; supervisor required for delete and password reset.
    • Keep bootstrap endpoints open: POST /users/custom-token, POST /users/create.
  • Bug Fixes

    • On SSO errors or when the SSO email isn’t linked to a staff account, sign out and clear auth cookies to prevent stale sessions.

Written for commit 295bfbe. Summary will update on new commits.

…for improved session management; enhance route security by adding role verification middleware to various endpoints
@brelieu05 brelieu05 self-assigned this Mar 31, 2026
@brelieu05 brelieu05 linked an issue Mar 31, 2026 that may be closed by this pull request
1 task
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

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.

Comment thread server/routes/users.ts Outdated
Comment thread client/src/components/adminProfile/adminLogin.tsx Outdated
Comment thread server/routes/clinics.js
Comment thread server/src/middleware.ts
Comment on lines +44 to +46
if (process.env.NODE_ENV !== "production") {
return next();
}
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 31, 2026

Choose a reason for hiding this comment

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

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>
Suggested change
if (process.env.NODE_ENV !== "production") {
return next();
}
// Do not bypass role checks by NODE_ENV; keep authz behavior consistent across environments.
Fix with Cubic

Comment thread server/routes/users.ts
Comment thread server/routes/users.ts
…led consistently; enhance security in clinic and user routes with role verification to prevent unauthorized actions
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

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.

Comment thread client/src/components/adminProfile/adminLogin.tsx Outdated
Comment thread server/routes/users.ts Outdated
…e user route security checks to prevent unauthorized access
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

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.

Comment thread client/src/components/adminProfile/adminLogin.tsx
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

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.

Comment thread client/src/components/adminProfile/adminLogin.tsx
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

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.

Comment thread server/routes/users.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FIX] Implement Verify Token w/ Microsoft/Google SSO

1 participant