Skip to content

fix: allow team members to access auth page and improve auth API response handling#349

Open
Nitin-kumar-yadav1307 wants to merge 6 commits into
geturbackend:mainfrom
Nitin-kumar-yadav1307:bug/auth-access
Open

fix: allow team members to access auth page and improve auth API response handling#349
Nitin-kumar-yadav1307 wants to merge 6 commits into
geturbackend:mainfrom
Nitin-kumar-yadav1307:bug/auth-access

Conversation

@Nitin-kumar-yadav1307

@Nitin-kumar-yadav1307 Nitin-kumar-yadav1307 commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR fixes the auth page accessibility issue for project team members.

Previously, when a project admin or viewer opened the auth page, the server could return errors such as:

  • "Project not found"
  • "Access denied"

Instead, authorized team members can now retrieve the project auth details correctly.

Additionally, the auth API response handling has been improved on the frontend to properly process both success and error responses.

Changes

Backend

  • Fixed project auth access logic for project team members.
  • Updated controller to return project auth details for authorized users.
  • Improved API response consistency.

Frontend

  • Improved auth page response handling.
  • Added proper success and error state handling.
  • Improved error messages shown to users.

SDK

  • Updated UrAuth component to work with the new API response flow.
  • Improved handling of authorization and API errors.

Files Changed

  • apps/dashboard-api/src/controllers/project.controller.js
  • apps/web-dashboard/src/pages/Auth.jsx
  • sdks/urbackend-react/src/components/UrAuth.tsx

Issue

Closes #348

Summary by CodeRabbit

  • Bug Fixes
    • Improved project access handling so invalid project IDs now return a clear 400 error before lookup.
    • Standardized API error responses for project details, including clearer messages and proper status codes.
    • Auth and dashboard loading now better interpret API response formats, reducing false failures and showing more specific error messages.
    • Project loading for admin users now respects broader access rules instead of relying on a single ownership check.

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 041e43e1-5e00-4c22-b087-2c384ed34b6a

📥 Commits

Reviewing files that changed from the base of the PR and between 7ab85b9 and 06bdfa1.

📒 Files selected for processing (2)
  • apps/dashboard-api/src/__tests__/loadProjectForAdmin.test.js
  • apps/dashboard-api/src/middlewares/loadProjectForAdmin.js

📝 Walkthrough

Walkthrough

This PR standardizes error handling for the project auth flow: getSingleProject now returns a structured { success, data, message } error envelope, loadProjectForAdmin middlewares validate projectId format and use a shared access query, and Auth.jsx unwraps and validates API responses with more descriptive error reporting.

Changes

Auth page access and response flow

Layer / File(s) Summary
Common package access query
packages/common/src/middleware/loadProjectForAdmin.js
Replaces the hard-coded owner filter with getProjectAccessQuery(req.user._id) in the Project.findOne query.
Dashboard-api middleware ObjectId validation and tests
apps/dashboard-api/src/middlewares/loadProjectForAdmin.js, apps/dashboard-api/src/__tests__/loadProjectForAdmin.test.js
Adds a mongoose.Types.ObjectId.isValid guard returning a 400 "Invalid project ID format" before the lookup, and updates tests with new mocks, an invalid-id test case, and a valid hard-coded ObjectId.
Standardized getSingleProject error envelope
apps/dashboard-api/src/controllers/project.controller.js
Changes the catch handler to return { success: false, data: {}, message } using err.statusCode for AppError and 500 otherwise, instead of a generic 500 response.
Auth page response unwrapping
apps/web-dashboard/src/pages/Auth.jsx
Adds unwrapApiResponse helper, validates ok flags for project/users responses, updates state from unwrapped data, and improves error toast messaging.

Estimated code review effort: 2 (Simple) | ~15 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AuthPage
  participant DashboardAPI
  participant loadProjectForAdmin

  AuthPage->>DashboardAPI: request project/auth details
  DashboardAPI->>loadProjectForAdmin: validate projectId format and access
  loadProjectForAdmin-->>DashboardAPI: project or 400/404 AppError
  DashboardAPI-->>AuthPage: sanitized project or { success: false, data: {}, message }
  AuthPage->>AuthPage: unwrapApiResponse and check ok flag
  AuthPage-->>AuthPage: render project/users or detailed error toast
Loading

Possibly related PRs

Suggested labels: backend, enhancement, type:refactor

Suggested reviewers: yash-pouranik

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the auth-page access fix and API response handling changes.
Linked Issues check ✅ Passed The backend access check was broadened for team members and the auth page now unwraps success/error API responses as requested.[#348]
Out of Scope Changes check ✅ Passed The changes stay focused on auth-page access, project loading, and API response handling, with no clear unrelated additions.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
apps/web-dashboard/src/pages/Auth.jsx (1)

101-139: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Stale/unmounted requests can still surface a toast error.

The success path for the users fetch correctly guards against races with if (!isMounted || requestId !== latestUsersRequestId.current) return; (Line 127) before updating state. But the !usersResult.ok branch (Lines 123-125) throws immediately, without this guard, so a stale or post-unmount failure still reaches the outer catch and fires toast.error (Line 137) for a request that's no longer relevant (e.g. user changed page/limit quickly, or navigated away). The finally block already guards setLoading with isMounted but the toast call has no equivalent guard.

🐛 Proposed fix
                         const usersResult = unwrapApiResponse(usersRes.data);
 
-                        if (!usersResult.ok) {
+                        if (!isMounted || requestId !== latestUsersRequestId.current) return;
+
+                        if (!usersResult.ok) {
                             throw new Error(usersResult.message || 'Failed to load auth users');
                         }
 
-                        if (!isMounted || requestId !== latestUsersRequestId.current) return;
                         setUsers(normalizeUsersResponse(usersResult.data));

And guard the toast itself as a defense-in-depth measure:

             } catch (err) {
-                toast.error(err?.response?.data?.message || err?.response?.data?.error || err.message || "Failed to load auth details");
+                if (isMounted) {
+                    toast.error(err?.response?.data?.message || err?.response?.data?.error || err.message || "Failed to load auth details");
+                }
             }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web-dashboard/src/pages/Auth.jsx` around lines 101 - 139, The Auth.jsx
data-loading effect can still show a toast for stale or unmounted requests
because the error path in the users fetch throws before the request-race guard
is checked. Update the useEffect fetch logic around fetchData,
unwrapApiResponse, and latestUsersRequestId so that failures from the admin
users request are ignored when isMounted is false or the requestId no longer
matches, instead of bubbling to the outer catch. Also add an isMounted check
before calling toast.error in the catch block as a defense-in-depth guard.
sdks/urbackend-react/src/components/UrAuth.tsx (2)

490-499: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Wrong/non-localized subtitle text on forgot-password screen.

For mode === 'forgot', the subtitle displays text.loginTitle — the sign-in headline — under a "Forgot Password" heading, which is confusing content unrelated to the flow. Additionally, the reset-mode text `Enter the code sent to ${email}` is hardcoded inline rather than sourced from the text labels object like every other string in this component, breaking the established i18n/customization pattern.

🐛 Proposed fix
-              {mode === 'forgot' ? text.loginTitle : `Enter the code sent to ${email}`}
+              {mode === 'forgot' ? 'Enter your email to receive a reset code' : `Enter the code sent to ${email}`}

Consider reintroducing a dedicated forgotSubtitle/resetSubtitle field in AuthLabels so both strings remain customizable/localizable like the rest of the component's text.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sdks/urbackend-react/src/components/UrAuth.tsx` around lines 490 - 499, The
subtitle logic in UrAuth is using the wrong label for the forgot flow and
hardcoding the reset flow copy. Update the conditional inside the forgot/reset
header block to use dedicated localized fields from the AuthLabels/text object
instead of text.loginTitle and the inline email string. Add or restore
forgotSubtitle and resetSubtitle in the labels type/config, and reference those
fields in the UrAuth component so both modes stay customizable and consistent
with the rest of the i18n pattern.

451-465: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Header hidden by default when no branding is provided.

The header block is gated on branding?.logo || branding?.brandName || branding?.title || branding?.subtitle. Since branding has no default value, most consumers who don't pass any branding prop will never see the header at all — even though brandName (defaults to 'urBackend'), headerTitle, and headerSubtitle (mode-aware fallback text) are always computed with sensible defaults. Those defaults become dead code in the common no-branding case.

🐛 Proposed fix: gate on auth-method availability instead of branding presence
-        {(branding?.logo || branding?.brandName || branding?.title || branding?.subtitle) && (
+        {(hasPasswordAuth || hasSocialAuth) && (
           <div style={styles.header}>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sdks/urbackend-react/src/components/UrAuth.tsx` around lines 451 - 465, The
header in UrAuth is incorrectly hidden when no branding prop is provided,
because the current conditional only checks branding fields and bypasses the
always-available fallback text. Update the header render guard in UrAuth to
depend on auth-method/header availability instead of branding presence, so the
computed defaults in brandName, headerTitle, and headerSubtitle are still shown
for the common no-branding case. Keep the existing header content and fallback
calculations, but move the visibility condition to the appropriate auth-mode
logic used by UrAuth.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/dashboard-api/src/controllers/project.controller.js`:
- Line 456: The success response in the project controller is still returning
the raw sanitized project object instead of the standard `{ success, data,
message }` envelope. Update the success path in `project.controller.js` (the
`res.json(sanitizeProjectResponse(projectObj))` return) so it matches the same
response contract used by the catch block, wrapping
`sanitizeProjectResponse(projectObj)` under `data` with `success: true` and an
appropriate `message`, keeping the API shape consistent for `Auth.jsx` and other
callers.
- Around line 458-466: The generic error path in project.controller.js is
exposing raw internal exception text to clients. Update the catch handling
around the existing AppError branch in the controller action to avoid returning
err.message for non-AppError cases; instead log the original error server-side
and send a safe generic message (or wrap it in a new AppError) from the response
path. Keep the AppError response behavior unchanged, and use the controller’s
existing error handling flow to ensure MongoDB/Mongoose details never reach the
client.

In `@sdks/urbackend-react/src/components/UrAuth.tsx`:
- Around line 313-326: The primaryBtn gradient in UrAuth.tsx contains a dead
ternary where both branches return the same end color, so the theme check has no
effect. Update the gradient logic in the primaryBtn style to use a distinct
dark-mode end color in the theme === 'dark' branch, and make sure the chosen
value still provides sufficient contrast with themeColors.primaryText in dark
mode. Verify the button appearance in both themes after adjusting the
theme-aware background.

---

Outside diff comments:
In `@apps/web-dashboard/src/pages/Auth.jsx`:
- Around line 101-139: The Auth.jsx data-loading effect can still show a toast
for stale or unmounted requests because the error path in the users fetch throws
before the request-race guard is checked. Update the useEffect fetch logic
around fetchData, unwrapApiResponse, and latestUsersRequestId so that failures
from the admin users request are ignored when isMounted is false or the
requestId no longer matches, instead of bubbling to the outer catch. Also add an
isMounted check before calling toast.error in the catch block as a
defense-in-depth guard.

In `@sdks/urbackend-react/src/components/UrAuth.tsx`:
- Around line 490-499: The subtitle logic in UrAuth is using the wrong label for
the forgot flow and hardcoding the reset flow copy. Update the conditional
inside the forgot/reset header block to use dedicated localized fields from the
AuthLabels/text object instead of text.loginTitle and the inline email string.
Add or restore forgotSubtitle and resetSubtitle in the labels type/config, and
reference those fields in the UrAuth component so both modes stay customizable
and consistent with the rest of the i18n pattern.
- Around line 451-465: The header in UrAuth is incorrectly hidden when no
branding prop is provided, because the current conditional only checks branding
fields and bypasses the always-available fallback text. Update the header render
guard in UrAuth to depend on auth-method/header availability instead of branding
presence, so the computed defaults in brandName, headerTitle, and headerSubtitle
are still shown for the common no-branding case. Keep the existing header
content and fallback calculations, but move the visibility condition to the
appropriate auth-mode logic used by UrAuth.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4c7dade0-d841-4915-8f4c-0b912be088ae

📥 Commits

Reviewing files that changed from the base of the PR and between ec6e78e and 5d4d6de.

📒 Files selected for processing (3)
  • apps/dashboard-api/src/controllers/project.controller.js
  • apps/web-dashboard/src/pages/Auth.jsx
  • sdks/urbackend-react/src/components/UrAuth.tsx

Comment thread apps/dashboard-api/src/controllers/project.controller.js
Comment thread apps/dashboard-api/src/controllers/project.controller.js
Comment thread sdks/urbackend-react/src/components/UrAuth.tsx

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

why there are changes in SDK.
@Nitin-kumar-yadav1307

Comment on lines -456 to -459
if (!getProjectRole(projectObj, req.user._id)) {
throw new AppError(403, "Access denied.");
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

why removed role check?
isnt this incorrect??

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

please respond??
@Nitin-kumar-yadav1307

@Nitin-kumar-yadav1307

Copy link
Copy Markdown
Collaborator Author

@yash-pouranik i think i over exxeratted the PR, little bit confused in urAuth.tsx, thats actually unrelated to the project auth page , thnks for pointing it out bro, my bad

This reverts commit 03ffc5d.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/dashboard-api/src/controllers/project.controller.js (1)

431-454: 🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win

Reapply project access checks on cache hits. getProjectById() returns a shared cached project, but getProjectAccessQuery(req.user._id) only runs on the cache-miss path. Add an access check before returning cached data so one user’s cached project can’t be read by another authenticated user.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/dashboard-api/src/controllers/project.controller.js` around lines 431 -
454, getSingleProject currently trusts getProjectById() on cache hits without
rechecking ownership or access, so the cached project can be returned to an
unauthorized user. Add a permission check in getSingleProject before using a
cached projectObj, using getProjectAccessQuery(req.user._id) or equivalent logic
to verify the current user can access req.params.projectId. Keep the existing
Project.findOne fallback for cache misses, and only return cached data after the
access check passes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@apps/dashboard-api/src/controllers/project.controller.js`:
- Around line 431-454: getSingleProject currently trusts getProjectById() on
cache hits without rechecking ownership or access, so the cached project can be
returned to an unauthorized user. Add a permission check in getSingleProject
before using a cached projectObj, using getProjectAccessQuery(req.user._id) or
equivalent logic to verify the current user can access req.params.projectId.
Keep the existing Project.findOne fallback for cache misses, and only return
cached data after the access check passes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 151262fe-74f5-4600-bf60-4fbe1f0cdfe6

📥 Commits

Reviewing files that changed from the base of the PR and between 03ffc5d and f690d62.

📒 Files selected for processing (2)
  • apps/dashboard-api/src/controllers/project.controller.js
  • sdks/urbackend-react/src/components/UrAuth.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • sdks/urbackend-react/src/components/UrAuth.tsx

@yash-pouranik yash-pouranik left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

still u have not rreverted changes done in sdk.
@Nitin-kumar-yadav1307

Comment on lines -456 to -459
if (!getProjectRole(projectObj, req.user._id)) {
throw new AppError(403, "Access denied.");
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

please respond??
@Nitin-kumar-yadav1307

@Nitin-kumar-yadav1307

Nitin-kumar-yadav1307 commented Jul 4, 2026

Copy link
Copy Markdown
Collaborator Author

@yash-pouranik i did git revert HEAD but doesnt it didnt appllied?

@Nitin-kumar-yadav1307

Copy link
Copy Markdown
Collaborator Author

@yash-pouranik
is it good now?

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

Comment on lines 9 to +13
if (!projectId) return next(new AppError(400, "Project ID is required"));

const project = await Project.findOne({ _id: projectId, owner: req.user._id });
const project = await Project.findOne({
_id: projectId,
...getProjectAccessQuery(req.user._id),
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.

Auth Page Accessibility to Team Member

3 participants