Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix failing redirect to create new organization page on no organizations #3125

Merged
merged 6 commits into from
Feb 19, 2025

Conversation

dangtony98
Copy link
Collaborator

@dangtony98 dangtony98 commented Feb 17, 2025

Description 📣

This PR fixes a bug where users get stuck on an infinitely-loading select organization page when they have deleted all their organizations (or are no longer part of any); the user now gets correctly redirected to the page to create a new organization.

Update: This PR now also fixes another edge-case that is an existing user already logged into a deleted organization getting stuck on an error/spinner screen; in such case, the user token is no longer valid so currently I have them re-login.

Type ✨

  • Bug fix
  • New feature
  • Improvement
  • Breaking change
  • Documentation

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • Introduced a streamlined navigation path for users without an associated organization.
  • Bug Fixes

    • Improved error handling during authentication, logout, and invitation processes to ensure smoother session management.
  • Style

    • Updated the layout and presentation of the organization-less page for enhanced user experience.
  • Refactor

    • Consolidated token validation and session management logic, and simplified routing configuration for more reliable navigation.
  • Chores

    • Enhanced session clearing logic and modularized logout functionality for improved maintainability.

Copy link

coderabbitai bot commented Feb 17, 2025

Walkthrough

The changes update both the frontend and backend logic to refine authentication, routing, and error handling workflows. On the frontend, adjustments include a new condition in the authentication middleware that allows access to the page when the pathname is "/organization/none" and modifications to route paths to simplify navigation for cases when no organization is associated. Component layouts have been altered in the NoOrgPage, and error handling in login and sign-up flows has been enhanced with new utility functions for session clearing and logout. In the backend, the authentication logic is streamlined by centralizing refresh token validation through a new method that consolidates error checks and token verification processes. Additionally, minor reorganization of import statements and formatting changes have been made to improve code clarity and maintainability.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 012f265 and 86b6d23.

📒 Files selected for processing (1)
  • frontend/src/pages/middlewares/authenticate.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/src/pages/middlewares/authenticate.tsx
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: Check Frontend Type and Lint check
  • GitHub Check: Check TS and Lint
  • GitHub Check: Run integration test
  • GitHub Check: Check API Changes

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
frontend/src/routeTree.gen.ts (1)

1-9: Caution: This is an auto-generated file.

The file header explicitly states that this file should not be manually modified as it is automatically generated by TanStack Router. Any changes should be made through the router configuration instead of direct edits.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2c82561 and 8518fed.

⛔ Files ignored due to path filters (1)
  • backend/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (6)
  • frontend/src/pages/middlewares/authenticate.tsx (1 hunks)
  • frontend/src/pages/organization/NoOrgPage/NoOrgPage.tsx (1 hunks)
  • frontend/src/pages/organization/NoOrgPage/route.tsx (1 hunks)
  • frontend/src/pages/organization/layout.tsx (1 hunks)
  • frontend/src/routeTree.gen.ts (13 hunks)
  • frontend/src/routes.ts (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • frontend/src/pages/organization/NoOrgPage/NoOrgPage.tsx
  • frontend/src/pages/organization/NoOrgPage/route.tsx
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: Run integration test
  • GitHub Check: Check Frontend Type and Lint check
  • GitHub Check: Check TS and Lint
  • GitHub Check: Mintlify Deployment
🔇 Additional comments (3)
frontend/src/pages/organization/layout.tsx (1)

1-9: LGTM! Style improvements look good.

The changes are purely stylistic, maintaining consistent use of single quotes and improving code formatting.

frontend/src/routes.ts (1)

344-344: LGTM! Route placement is strategically correct.

The new route for handling users with no organizations is correctly placed:

  1. Within the authenticated routes section (requires user authentication)
  2. Before the organization details injection (prevents redirect loops)
frontend/src/routeTree.gen.ts (1)

381-387: LGTM! Route integration looks correct.

The new route /organization/none is properly integrated into the routing tree:

  1. Defined under the authentication middleware
  2. Correctly configured with path and parent route
  3. Properly typed in the interfaces

Also applies to: 469-469, 1693-1699

Copy link
Contributor

@scott-ray-wilson scott-ray-wilson left a comment

Choose a reason for hiding this comment

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

Code looks good, was able to create a new org 👍 one potential improvement would be handling this situation:

Screen.Recording.2025-02-17.at.10.13.22.AM.mov

And minor nit but I think you might not have your editor configured to use or prettier rules as commits have single quotation and no semicolons

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🔭 Outside diff range comments (1)
frontend/src/pages/auth/LoginPage/LoginPage.tsx (1)

22-35: 🛠️ Refactor suggestion

Enhance error handling and add no-organization case.

The current implementation doesn't fully address the infinite loading issue when users have no organizations. Consider these improvements:

  1. Handle the no-organization case by redirecting to the create organization page.
  2. Improve error handling to be more specific and accurate.

Here's a suggested implementation:

 const handleRedirects = async () => {
   try {
+    // Check if user has any organizations
+    const orgs = await getUserOrganizations();
+    if (!orgs?.length) {
+      // Redirect to create organization page when user has no organizations
+      navigate({ to: '/organization/none' });
+      return;
+    }
+
     const callbackPort = queryParams?.get("callback_port");
     // case: a callback port is set, meaning it's a cli login request: redirect to select org with callback port
     if (callbackPort) {
       navigateToSelectOrganization(callbackPort);
     } else {
       // case: no callback port, meaning it's a regular login request: redirect to select org
       navigateToSelectOrganization();
     }
-  } catch {
-    console.log("Error - Not logged in yet");
+  } catch (error) {
+    console.error('Failed to handle redirects:', error);
+    // Handle specific error cases if needed
   }
 };

Don't forget to:

  1. Import the getUserOrganizations function
  2. Import the navigate function from your routing library
🧹 Nitpick comments (3)
frontend/src/pages/auth/SignUpInvitePage/SignUpInvitePage.tsx (1)

252-273: LGTM! Error handling improvement aligns with PR objective.

The addition of try-catch block effectively resolves the infinite loading issue by redirecting users to the login page when organization selection fails.

Consider logging the error before redirecting to help with debugging:

                    } catch (err) {
+                     console.error('Failed to select organization:', err);
                      navigate({ to: "/login" });
                    }
frontend/src/hooks/api/users/queries.tsx (1)

286-302: Consider consolidating session cleanup logic.

The clearSession function duplicates storage cleanup logic that exists in the useDeleteMe hook. Consider extracting a common utility function to handle storage cleanup to maintain DRY principles.

-// Utility function to clear session storage and query cache
-export const clearSession = (keepQueryClient?: boolean) => {
+// Common utility function for storage cleanup
+const clearStorageItems = () => {
   setAuthToken(""); // Clear authentication token
   localStorage.removeItem("protectedKey");
   localStorage.removeItem("protectedKeyIV");
   localStorage.removeItem("protectedKeyTag");
   localStorage.removeItem("publicKey");
   localStorage.removeItem("encryptedPrivateKey");
   localStorage.removeItem("iv");
   localStorage.removeItem("tag");
   localStorage.removeItem("PRIVATE_KEY");
   localStorage.removeItem("orgData.id");
   sessionStorage.removeItem(SessionStorageKeys.CLI_TERMINAL_TOKEN);
+};
+
+// Utility function to clear session storage and query cache
+export const clearSession = (keepQueryClient?: boolean) => {
+  clearStorageItems();
 
   if (!keepQueryClient) {
     queryClient.clear(); // Clear React Query cache
   }
 };

Then update useDeleteMe to use the common utility:

 onSuccess: () => {
-  localStorage.removeItem("protectedKey");
-  localStorage.removeItem("protectedKeyIV");
-  localStorage.removeItem("protectedKeyTag");
-  localStorage.removeItem("publicKey");
-  localStorage.removeItem("encryptedPrivateKey");
-  localStorage.removeItem("iv");
-  localStorage.removeItem("tag");
-  localStorage.removeItem("PRIVATE_KEY");
-  localStorage.removeItem("orgData.id");
+  clearStorageItems();
   queryClient.clear();
 }
backend/src/services/auth-token/auth-token-service.ts (1)

104-136: Refactor for explicit error handling of expired or invalid tokens.

While this method correctly throws an error for invalid tokens, consider adding a try-catch to differentiate between expired tokens (TokenExpiredError) and other JWT errors. This will provide more precise feedback to clients, e.g.:

- const decodedToken = jwt.verify(refreshToken, appCfg.AUTH_SECRET) as AuthModeRefreshJwtTokenPayload;
+ let decodedToken: AuthModeRefreshJwtTokenPayload;
+ try {
+   decodedToken = jwt.verify(refreshToken, appCfg.AUTH_SECRET) as AuthModeRefreshJwtTokenPayload;
+ } catch (err) {
+   if (err.name === "TokenExpiredError") {
+     throw new UnauthorizedError({
+       message: "Refresh token has expired",
+       name: "TokenExpired"
+     });
+   }
+   throw new UnauthorizedError({
+     message: "Invalid or malformed token",
+     name: "InvalidToken"
+   });
+ }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8518fed and 823bf13.

📒 Files selected for processing (7)
  • backend/src/server/routes/v1/auth-router.ts (3 hunks)
  • backend/src/services/auth-token/auth-token-service.ts (4 hunks)
  • backend/src/services/auth/auth-fns.ts (0 hunks)
  • frontend/src/hooks/api/users/queries.tsx (2 hunks)
  • frontend/src/pages/auth/LoginPage/LoginPage.tsx (1 hunks)
  • frontend/src/pages/auth/SignUpInvitePage/SignUpInvitePage.tsx (1 hunks)
  • frontend/src/pages/middlewares/authenticate.tsx (2 hunks)
💤 Files with no reviewable changes (1)
  • backend/src/services/auth/auth-fns.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/src/pages/middlewares/authenticate.tsx
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Check API Changes
  • GitHub Check: Run integration test
  • GitHub Check: Check TS and Lint
🔇 Additional comments (9)
frontend/src/pages/auth/LoginPage/LoginPage.tsx (1)

36-39: LGTM! Good addition of the authentication check.

The isLoggedIn() check ensures that redirect logic only runs for authenticated users, preventing unnecessary redirects.

frontend/src/pages/auth/SignUpInvitePage/SignUpInvitePage.tsx (1)

237-280: LGTM! Comprehensive error handling for invite verification.

The implementation properly handles all error cases:

  • New users are directed to account setup
  • Existing users go through organization selection
  • Failed verifications redirect to request new invite
frontend/src/hooks/api/users/queries.tsx (3)

27-27: LGTM!

The import is correctly added and follows the project's conventions.


281-283: LGTM!

The logoutUser function is simple, focused, and handles the API call correctly.


304-309: LGTM!

The hook has been simplified by delegating to the new utility functions while maintaining its functionality.

backend/src/server/routes/v1/auth-router.ts (3)

6-6: No issues found with the updated import statement.


23-27: Looks good – centralized token validation.

You’ve successfully streamlined the logout flow by removing the one-off validation checks in favor of the shared validateRefreshToken method. This approach promotes code reuse and consistency. Nice job!


70-71: Consider verifying ongoing user membership.

Currently, you’re reissuing the access token based purely on refresh token validity. If necessary, ensure that any subsequent organization membership checks (e.g., in fnValidateJwtIdentity) remain valid, especially if the user’s org membership has changed.

backend/src/services/auth-token/auth-token-service.ts (1)

2-2: New JWT import is fine.

No further changes recommended here.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
frontend/src/hooks/api/users/queries.tsx (1)

286-302: Consider adding error handling for storage operations.

The clearSession utility effectively centralizes session cleanup logic, but storage operations could fail.

Apply this diff to add error handling:

 export const clearSession = (keepQueryClient?: boolean) => {
+  try {
     setAuthToken(""); // Clear authentication token
     localStorage.removeItem("protectedKey");
     localStorage.removeItem("protectedKeyIV");
     localStorage.removeItem("protectedKeyTag");
     localStorage.removeItem("publicKey");
     localStorage.removeItem("encryptedPrivateKey");
     localStorage.removeItem("iv");
     localStorage.removeItem("tag");
     localStorage.removeItem("PRIVATE_KEY");
     localStorage.removeItem("orgData.id");
     sessionStorage.removeItem(SessionStorageKeys.CLI_TERMINAL_TOKEN);

     if (!keepQueryClient) {
       qc.clear(); // Clear React Query cache
     }
+  } catch (error) {
+    console.error("Failed to clear session:", error);
+    // Re-throw to allow mutation error handler to handle it
+    throw error;
+  }
 };
backend/src/services/auth-token/auth-token-service.ts (1)

154-186: Well-structured token validation with comprehensive error handling.

The validateRefreshToken implementation is robust and secure:

  • Proper null check with descriptive error
  • JWT verification with correct type assertion
  • Token type validation
  • Version verification to prevent replay attacks
  • Clear error messages for each failure case

However, consider adding a try-catch block around jwt.verify to handle malformed tokens gracefully.

-    const decodedToken = jwt.verify(refreshToken, appCfg.AUTH_SECRET) as AuthModeRefreshJwtTokenPayload;
+    let decodedToken: AuthModeRefreshJwtTokenPayload;
+    try {
+      decodedToken = jwt.verify(refreshToken, appCfg.AUTH_SECRET) as AuthModeRefreshJwtTokenPayload;
+    } catch (error) {
+      throw new UnauthorizedError({
+        message: "Invalid refresh token",
+        name: "InvalidToken"
+      });
+    }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 823bf13 and 012f265.

📒 Files selected for processing (6)
  • backend/src/server/routes/v1/auth-router.ts (3 hunks)
  • backend/src/services/auth-token/auth-token-service.ts (3 hunks)
  • backend/src/services/kms/kms-root-config-dal.ts (1 hunks)
  • frontend/src/hooks/api/users/queries.tsx (2 hunks)
  • frontend/src/pages/middlewares/authenticate.tsx (2 hunks)
  • frontend/src/pages/organization/NoOrgPage/route.tsx (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • backend/src/services/kms/kms-root-config-dal.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • frontend/src/pages/organization/NoOrgPage/route.tsx
  • frontend/src/pages/middlewares/authenticate.tsx
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Check API Changes
  • GitHub Check: Check TS and Lint
  • GitHub Check: Run integration test
🔇 Additional comments (5)
frontend/src/hooks/api/users/queries.tsx (2)

281-283: LGTM! Good separation of concerns.

The extraction of the logout API call into a separate function improves code organization and reusability.


304-309: LGTM! Clean and concise implementation.

The hook now leverages the extracted utilities for better maintainability.

backend/src/server/routes/v1/auth-router.ts (2)

24-28: LGTM! Improved error handling in logout flow.

The refactoring simplifies the logout flow by centralizing token validation logic in validateRefreshToken. This reduces code duplication and ensures consistent error handling.


72-72: LGTM! Consistent token validation approach.

The token handler now uses the same centralized validation approach as the logout handler, maintaining consistency across the authentication flows.

backend/src/services/auth-token/auth-token-service.ts (1)

226-226: LGTM! Proper service method export.

The new method is correctly exported as part of the service interface.

Copy link
Contributor

@scott-ray-wilson scott-ray-wilson left a comment

Choose a reason for hiding this comment

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

Two notes:

  • I get a not part of organization error when I'm removed, preventing logout (though refreshing does resolve this)
  • type error is due newly added catch in authenticate.tsx since user is not guaranteed to resolve
Screen.Recording.2025-02-19.at.9.58.23.AM.mov

Copy link
Contributor

@scott-ray-wilson scott-ray-wilson left a comment

Choose a reason for hiding this comment

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

LGTM!

@dangtony98 dangtony98 merged commit 3583a09 into main Feb 19, 2025
8 checks passed
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.

3 participants