feat(auth): migrate from Clerk to Better Auth#543
Conversation
📝 WalkthroughWalkthroughReplace Clerk with a new Better Auth stack: add Changes
Sequence Diagram(s)sequenceDiagram
participant Browser as User Browser
participant Web as Next.js App
participant Auth as Better Auth (`@superset/auth`)
participant DB as Database
participant Desktop as Desktop App
rect rgb(230,245,255)
Note over Browser,Auth: Social sign-in (web flow)
Browser->>Web: GET /api/auth/desktop/connect?provider=google
Web->>Auth: auth.api.signInSocial(provider, { callbackURL })
Auth->>DB: findOrCreate user, create session
DB-->>Auth: user + session
Auth-->>Web: response with set-cookie + redirect URL
Web-->>Browser: redirect to provider / set cookies
end
rect rgb(245,255,230)
Note over Browser,Desktop: Desktop deep-link callback / session-based desktop success
Browser->>Web: GET /auth/desktop/success?desktop_state=...
Web->>Auth: auth.api.getSession({ headers })
Auth->>DB: validate session, return activeOrganizationId
DB-->>Auth: session + user + org
Auth-->>Web: session data
Web->>Browser: render DesktopRedirect with desktop URL (token+state)
Browser->>Desktop: open desktop:// callback URL
end
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
📜 Recent review detailsConfiguration used: defaults Review profile: CHILL Plan: Pro 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
🔇 Additional comments (5)
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. Comment |
🧹 Preview Cleanup CompleteThe following preview resources have been cleaned up:
Thank you for your contribution! 🎉 |
There was a problem hiding this comment.
Actionable comments posted: 13
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
packages/trpc/src/router/organization/organization.ts (2)
59-81: Manual UUID generation conflicts with database defaults.Lines 62 and 72 use
crypto.randomUUID()to generate IDs, but the database schema (based on the migration) defines columns withDEFAULT gen_random_uuid(). The PR summary mentionsgenerateId: false, which suggests database-generated IDs are disabled in the Better Auth config.However, this creates inconsistency:
- Some code paths rely on database defaults (e.g., when values are not explicitly provided)
- Other code paths use manual generation
- Different UUID generation methods can have different formatting/casing
For consistency, either:
- Remove the database
DEFAULT gen_random_uuid()from the migration if manual generation is always used- Or rely on database defaults by omitting the
idfield from the insert values🔎 Recommended approach: Let database handle IDs
const [organization] = await db .insert(organizations) - .values({ - id: crypto.randomUUID(), + .values({ name: input.name, slug: input.slug, logo: input.logo, createdAt: new Date(), }) .returning(); if (organization) { await db.insert(members).values({ - id: crypto.randomUUID(), organizationId: organization.id, userId: ctx.session.user.id, role: "owner", createdAt: new Date(), }); }
83-99: Weakened type validation on organization ID.Line 86 changed the
idvalidation fromz.string().uuid()toz.string(). This allows non-UUID strings to pass validation, which will fail at the database level since theidcolumn is of typeuuid.🔎 Proposed fix
.input( z.object({ - id: z.string(), + id: z.string().uuid(), name: z.string().min(1).optional(), logo: z.string().url().optional(), }), )Based on library documentation, Zod 4 maintains
.uuid()validation for strings, so this should work correctly.
♻️ Duplicate comments (1)
apps/web/src/app/(auth)/sign-in/[[...sign-in]]/page.tsx (1)
32-46: Same loading state concern applies to GitHub sign-in.This has the same potential issue as the Google sign-in handler—verify that the loading state management works correctly with OAuth redirects.
🧹 Nitpick comments (16)
apps/desktop/src/main/lib/auth/auth.ts (3)
103-105: Empty catch block silently ignores all errors.While
ENOENT(file doesn't exist) is expected, other errors (permission denied, disk full) would be silently swallowed. Consider logging unexpected errors.🔎 Proposed fix
async signOut(): Promise<void> { this.token = null; try { await fs.unlink(TOKEN_FILE); - } catch {} + } catch (err) { + // ENOENT is expected if token file doesn't exist + if ((err as NodeJS.ErrnoException).code !== "ENOENT") { + console.warn("[auth] Failed to remove token file:", err); + } + } this.emit("state-changed", this.getState()); }
57-59: Add return type annotation for consistency.
getState()has an implicit return type while other methods are explicitly typed. Consider adding the return type for consistency and better IDE support.🔎 Proposed fix
-getAccessToken(): string | null { +getAccessToken(): string | null { return this.token; } -getState() { +getState(): { isSignedIn: boolean } { return { isSignedIn: !!this.token }; }
66-69: State pruning only occurs during signIn calls.The stale state cleanup only runs when
signInis called. If a user initiates sign-in but never completes it, and then doesn't attempt to sign in again, those stale states persist in memory indefinitely.This is a minor concern since:
- Memory impact is negligible (small strings)
- App restarts clear the store
- States older than 10 minutes are functionally useless anyway
Consider this acceptable for now, but if you want periodic cleanup:
🔎 Optional: Move cleanup to a separate method
+private pruneExpiredStates(): void { + const tenMinutesAgo = Date.now() - 10 * 60 * 1000; + for (const [s, ts] of stateStore) { + if (ts < tenMinutesAgo) stateStore.delete(s); + } +} async signIn(provider: AuthProvider): Promise<SignInResult> { try { const state = crypto.randomBytes(32).toString("base64url"); stateStore.set(state, Date.now()); - - const tenMinutesAgo = Date.now() - 10 * 60 * 1000; - for (const [s, ts] of stateStore) { - if (ts < tenMinutesAgo) stateStore.delete(s); - } + this.pruneExpiredStates();apps/web/src/app/(dashboard)/components/Header/Header.tsx (1)
24-27: Consider adding error handling for the sign-out flow.The async
handleSignOutfunction doesn't handle potential errors fromauthClient.signOut(). If the sign-out request fails, the user would be redirected to/sign-inanyway, potentially causing confusion.🔎 Proposed improvement
const handleSignOut = async () => { - await authClient.signOut(); - router.push("/sign-in"); + try { + await authClient.signOut(); + router.push("/sign-in"); + } catch (error) { + console.error("Sign out failed:", error); + // Optionally show a toast or still redirect + router.push("/sign-in"); + } };packages/auth/src/env.ts (3)
6-6: Hardcoded relative path may be fragile.The path
"../../../.env"assumes a fixed directory structure relative topackages/auth/src/. This could break if the package is moved or ifprocess.cwd()differs during execution (e.g., different build/test contexts).🔎 Consider using a more robust path resolution
-config({ path: path.resolve(process.cwd(), "../../../.env") }); +// Find monorepo root by looking for a marker file or use a shared utility +const monorepoRoot = path.resolve(__dirname, "../../../.."); +config({ path: path.resolve(monorepoRoot, ".env") });Alternatively, rely on environment variables being set by the host application rather than loading from a specific file path.
9-15: Add minimum length validation for secrets to catch empty strings early.While
emptyStringAsUndefined: trueconverts empty strings to undefined, adding.min(1)provides clearer error messages when secrets are missing. This aligns with the pattern inapps/api/src/env.tswhere secrets have minimum length validation.🔎 Proposed validation enhancement
server: { - GH_CLIENT_ID: z.string(), - GH_CLIENT_SECRET: z.string(), - GOOGLE_CLIENT_ID: z.string(), - GOOGLE_CLIENT_SECRET: z.string(), - BETTER_AUTH_SECRET: z.string(), + GH_CLIENT_ID: z.string().min(1), + GH_CLIENT_SECRET: z.string().min(1), + GOOGLE_CLIENT_ID: z.string().min(1), + GOOGLE_CLIENT_SECRET: z.string().min(1), + BETTER_AUTH_SECRET: z.string().min(32), },
26-26: Consider makingskipValidationconditional.With
skipValidation: true, environment validation is always skipped, which could mask missing or invalid environment variables until runtime. Other env files in the codebase use a conditional like!!process.env.SKIP_ENV_VALIDATION.🔎 Conditional validation pattern
-skipValidation: true, +skipValidation: !!process.env.SKIP_ENV_VALIDATION,apps/api/src/app/api/electric/[...path]/route.ts (1)
56-70: Consider adding a timeout to the fetch call.The
fetchcall to the Electric origin has no timeout, which could cause the request to hang indefinitely if the upstream service is unresponsive. This could exhaust connection resources under load.🔎 Add AbortSignal timeout
-const response = await fetch(originUrl.toString()); +const response = await fetch(originUrl.toString(), { + signal: AbortSignal.timeout(30_000), // 30 second timeout +});apps/web/src/app/(auth)/sign-up/[[...sign-up]]/page.tsx (1)
16-46: Consider extracting shared OAuth logic.The
signUpWithGoogleandsignUpWithGithubfunctions share nearly identical logic. Consider extracting a shared helper function:const signUpWithProvider = async ( provider: "google" | "github", setLoading: (loading: boolean) => void ) => { setLoading(true); setError(null); try { await authClient.signIn.social({ provider, callbackURL: env.NEXT_PUBLIC_WEB_URL, }); } catch (err) { console.error("Sign up failed:", err); setError("Failed to sign up. Please try again."); setLoading(false); } };This reduces duplication and makes future maintenance easier.
apps/marketing/src/app/components/CTAButtons/CTAButtons.tsx (1)
9-11: Consider error handling for session retrieval.The session retrieval looks correct, but
auth.api.getSession()could potentially throw an error or return an unexpected value if the auth service is unavailable.🔎 Optional: Add error boundary or fallback
export async function CTAButtons() { - const session = await auth.api.getSession({ headers: await headers() }); + let session; + try { + session = await auth.api.getSession({ headers: await headers() }); + } catch (error) { + console.error("Failed to get session:", error); + session = null; + } if (session) {Alternatively, let the error propagate to the nearest error boundary if that's the intended behavior.
apps/api/src/lib/auth.ts (1)
3-7: Consider adding an explicit return type annotation.While TypeScript can infer the return type from
auth.api.getSession, adding an explicit return type would improve code clarity and help catch type mismatches earlier.🔎 Proposed enhancement with explicit typing
+import type { Session } from "better-auth/types"; + -export async function getSession(request: Request) { +export async function getSession(request: Request): Promise<Session | null> { return auth.api.getSession({ headers: request.headers, }); }apps/admin/src/components/PostHogUserIdentifier/PostHogUserIdentifier.tsx (1)
9-24: Consider using session data directly instead of additional TRPC query.The component fetches user data via
trpc.user.me.queryOptions()whensession.userlikely contains the necessary fields (id, email, name). The web app's equivalent component (apps/web/src/components/PostHogUserIdentifier/PostHogUserIdentifier.tsx) usessession.userdirectly, avoiding the extra round-trip.🔎 Proposed simplification matching the web app pattern
export function PostHogUserIdentifier() { const { data: session } = authClient.useSession(); - const trpc = useTRPC(); - - const { data: user } = useQuery({ - ...trpc.user.me.queryOptions(), - enabled: !!session?.user, - }); useEffect(() => { - if (user) { - posthog.identify(user.id, { email: user.email, name: user.name }); + if (session?.user) { + posthog.identify(session.user.id, { + email: session.user.email, + name: session.user.name, + }); - } else if (!session?.user) { + } else if (session === null) { posthog.reset(); } - }, [user, session?.user]); + }, [session]); return null; }apps/admin/src/app/(dashboard)/components/AppSidebar/components/NavUser/NavUser.tsx (1)
42-50: Consider adding error handling for sign-out failures.The
handleSignOutfunction lacks error handling for potential sign-out failures. While sign-out operations typically succeed, network errors or API issues could occur.🔎 Proposed enhancement with error handling
const handleSignOut = async () => { + try { await authClient.signOut({ fetchOptions: { onSuccess: () => { window.location.href = env.NEXT_PUBLIC_WEB_URL; }, }, }); + } catch (error) { + console.error("Sign out failed:", error); + // Optionally show a toast notification to the user + } };packages/trpc/src/router/integration/integration.ts (1)
21-23: Use TRPCError for consistent error handling.The membership check throws a generic
Errorinstead ofTRPCError. This is inconsistent with the TRPC error handling pattern used elsewhere (e.g., intrpc.ts).🔎 Proposed fix
+import { TRPCError } from "@trpc/server"; + // ... if (!membership) { - throw new Error("Not a member of this organization"); + throw new TRPCError({ + code: "FORBIDDEN", + message: "Not a member of this organization", + }); }apps/web/src/proxy.ts (1)
18-37: LGTM with optional refactor suggestion.The proxy logic correctly handles authentication redirects:
- Logged-in users are redirected away from sign-in/sign-up pages
- Logged-out users are redirected to sign-in unless on a public route
For slightly better clarity and to avoid potential edge cases with
startsWith(e.g.,/sign-in-testmatching/sign-in), consider using exact matches or the existingisPublicRoutehelper:🔎 Optional refactor for consistency
const pathname = req.nextUrl.pathname; if ( session && - (pathname.startsWith("/sign-in") || pathname.startsWith("/sign-up")) + (pathname === "/sign-in" || pathname === "/sign-up") ) { return NextResponse.redirect(new URL("/", req.url)); }Or if sign-in/sign-up have sub-paths, this is fine as-is.
packages/trpc/src/router/admin/admin.ts (1)
16-22: Clarify comment and add error handling for non-existent users.The comment on line 19 is misleading. Cascading deletes are handled by the database-level foreign key constraints (configured with
onDelete: "cascade"in the schema), not by the Better Auth library.Additionally, the mutation currently returns
{ success: true }even if the user doesn't exist. Consider verifying that a user was actually deleted.🔎 Proposed improvements
deleteUser: adminProcedure .input(z.object({ userId: z.string() })) .mutation(async ({ input }) => { - // Delete user - Better Auth handles cascading session cleanup - await db.delete(users).where(eq(users.id, input.userId)); - return { success: true }; + // Delete user - database FK constraints handle cascading deletes + const result = await db.delete(users).where(eq(users.id, input.userId)); + + if (result.rowCount === 0) { + throw new TRPCError({ + code: "NOT_FOUND", + message: "User not found", + }); + } + + return { success: true }; }),Note: You'll need to re-add the
TRPCErrorimport if you implement this suggestion.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
bun.lockis excluded by!**/*.lock
📒 Files selected for processing (96)
.env.exampleapps/admin/package.jsonapps/admin/src/app/(dashboard)/components/AppSidebar/AppSidebar.tsxapps/admin/src/app/(dashboard)/components/AppSidebar/components/NavUser/NavUser.tsxapps/admin/src/app/(dashboard)/components/LeaderboardTable/LeaderboardTable.tsxapps/admin/src/app/(dashboard)/users/components/UsersTable/UsersTable.tsxapps/admin/src/app/(dashboard)/users/deleted/components/DeletedUsersTable/DeletedUsersTable.tsxapps/admin/src/app/(dashboard)/users/deleted/components/DeletedUsersTable/index.tsapps/admin/src/app/(dashboard)/users/deleted/page.tsxapps/admin/src/app/layout.tsxapps/admin/src/components/PostHogUserIdentifier/PostHogUserIdentifier.tsxapps/admin/src/env.tsapps/admin/src/proxy.tsapps/admin/src/trpc/react.tsxapps/api/next.config.tsapps/api/package.jsonapps/api/src/app/api/auth/[...all]/route.tsapps/api/src/app/api/auth/desktop/connect/route.tsapps/api/src/app/api/auth/desktop/github/route.tsapps/api/src/app/api/auth/desktop/google/route.tsapps/api/src/app/api/auth/desktop/refresh/route.tsapps/api/src/app/api/auth/desktop/tokens.tsapps/api/src/app/api/electric/[...path]/route.tsapps/api/src/app/api/electric/[...path]/utils.tsapps/api/src/app/api/integrations/linear/connect/route.tsapps/api/src/env.tsapps/api/src/lib/auth.tsapps/api/src/proxy.tsapps/api/src/trpc/context.tsapps/desktop/src/lib/trpc/routers/auth/index.tsapps/desktop/src/lib/trpc/routers/index.tsapps/desktop/src/main/index.tsapps/desktop/src/main/lib/auth/auth-service.tsapps/desktop/src/main/lib/auth/auth.tsapps/desktop/src/main/lib/auth/crypto-storage.tsapps/desktop/src/main/lib/auth/deep-link-handler.tsapps/desktop/src/main/lib/auth/index.tsapps/desktop/src/main/lib/auth/pkce.tsapps/desktop/src/main/lib/auth/token-storage.tsapps/desktop/src/renderer/screens/main/components/AvatarDropdown/AvatarDropdown.tsxapps/desktop/src/renderer/screens/main/components/SettingsView/AccountSettings/AccountSettings.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/OrganizationSwitcher/OrganizationSwitcher.tsxapps/desktop/src/shared/auth.tsapps/marketing/package.jsonapps/marketing/src/app/components/CTAButtons/CTAButtons.tsxapps/marketing/src/app/layout.tsxapps/marketing/src/env.tsapps/marketing/src/proxy.tsapps/web/package.jsonapps/web/src/app/(auth)/layout.tsxapps/web/src/app/(auth)/sign-in/[[...sign-in]]/page.tsxapps/web/src/app/(auth)/sign-up/[[...sign-up]]/page.tsxapps/web/src/app/(auth)/sso-callback/page.tsxapps/web/src/app/(dashboard)/components/Header/Header.tsxapps/web/src/app/api/auth/desktop/github/route.tsapps/web/src/app/api/auth/desktop/google/route.tsapps/web/src/app/auth/desktop/success/components/DesktopAuthSuccess/DesktopAuthSuccess.tsxapps/web/src/app/auth/desktop/success/components/DesktopAuthSuccess/index.tsapps/web/src/app/auth/desktop/success/components/DesktopRedirect/DesktopRedirect.tsxapps/web/src/app/auth/desktop/success/components/DesktopRedirect/index.tsapps/web/src/app/auth/desktop/success/components/LoadingFallback/LoadingFallback.tsxapps/web/src/app/auth/desktop/success/components/LoadingFallback/index.tsapps/web/src/app/auth/desktop/success/page.tsxapps/web/src/app/layout.tsxapps/web/src/components/PostHogUserIdentifier/PostHogUserIdentifier.tsxapps/web/src/env.tsapps/web/src/proxy.tsapps/web/src/trpc/react.tsxpackages/auth/package.jsonpackages/auth/src/client.tspackages/auth/src/env.tspackages/auth/src/index.tspackages/auth/tsconfig.jsonpackages/db/drizzle/0005_back_up_user_tables.sqlpackages/db/drizzle/0006_add_better_auth_tables.sqlpackages/db/drizzle/meta/0005_snapshot.jsonpackages/db/drizzle/meta/0006_snapshot.jsonpackages/db/drizzle/meta/_journal.jsonpackages/db/package.jsonpackages/db/src/env.tspackages/db/src/schema/auth.tspackages/db/src/schema/index.tspackages/db/src/schema/relations.tspackages/db/src/schema/schema.tspackages/trpc/package.jsonpackages/trpc/src/env.tspackages/trpc/src/router/admin/admin.tspackages/trpc/src/router/analytics/analytics.tspackages/trpc/src/router/integration/integration.tspackages/trpc/src/router/integration/linear/linear.tspackages/trpc/src/router/integration/linear/utils.tspackages/trpc/src/router/organization/organization.tspackages/trpc/src/router/task/task.tspackages/trpc/src/router/user/user.tspackages/trpc/src/router/user/utils/sync-user-from-clerk.tspackages/trpc/src/trpc.ts
💤 Files with no reviewable changes (28)
- apps/web/src/app/auth/desktop/success/components/LoadingFallback/LoadingFallback.tsx
- apps/desktop/src/main/lib/auth/crypto-storage.ts
- apps/api/src/env.ts
- packages/trpc/src/env.ts
- apps/web/src/app/api/auth/desktop/github/route.ts
- apps/web/src/app/api/auth/desktop/google/route.ts
- apps/web/src/app/auth/desktop/success/components/LoadingFallback/index.ts
- apps/admin/src/app/(dashboard)/users/deleted/components/DeletedUsersTable/index.ts
- apps/admin/src/app/(dashboard)/users/deleted/components/DeletedUsersTable/DeletedUsersTable.tsx
- apps/desktop/src/main/lib/auth/token-storage.ts
- apps/desktop/src/main/lib/auth/auth-service.ts
- apps/web/src/app/(auth)/sso-callback/page.tsx
- apps/marketing/src/env.ts
- apps/admin/src/app/(dashboard)/users/deleted/page.tsx
- apps/api/src/app/api/auth/desktop/refresh/route.ts
- apps/admin/src/env.ts
- apps/web/src/app/auth/desktop/success/components/DesktopAuthSuccess/DesktopAuthSuccess.tsx
- apps/desktop/src/main/lib/auth/deep-link-handler.ts
- apps/desktop/src/shared/auth.ts
- apps/marketing/src/proxy.ts
- packages/trpc/src/router/user/utils/sync-user-from-clerk.ts
- apps/web/src/app/auth/desktop/success/components/DesktopAuthSuccess/index.ts
- apps/api/src/app/api/auth/desktop/github/route.ts
- apps/api/next.config.ts
- apps/api/src/app/api/auth/desktop/google/route.ts
- apps/api/src/app/api/auth/desktop/tokens.ts
- apps/web/src/env.ts
- apps/desktop/src/main/lib/auth/pkce.ts
🧰 Additional context used
📓 Path-based instructions (8)
apps/desktop/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/desktop/AGENTS.md)
apps/desktop/**/*.{ts,tsx}: For Electron interprocess communication, ALWAYS use tRPC as defined insrc/lib/trpc
Use alias as defined intsconfig.jsonwhen possible
Prefer zustand for state management if it makes sense. Do not use effect unless absolutely necessary.
For tRPC subscriptions with trpc-electron, ALWAYS use the observable pattern from@trpc/server/observableinstead of async generators, as the library explicitly checksisObservable(result)and throws an error otherwise
Files:
apps/desktop/src/renderer/screens/main/components/TasksView/components/OrganizationSwitcher/OrganizationSwitcher.tsxapps/desktop/src/renderer/screens/main/components/SettingsView/AccountSettings/AccountSettings.tsxapps/desktop/src/renderer/screens/main/components/AvatarDropdown/AvatarDropdown.tsxapps/desktop/src/lib/trpc/routers/index.tsapps/desktop/src/lib/trpc/routers/auth/index.tsapps/desktop/src/main/lib/auth/auth.tsapps/desktop/src/main/lib/auth/index.tsapps/desktop/src/main/index.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use type safety and avoid
anytypes unless absolutely necessary in TypeScript files
Files:
apps/desktop/src/renderer/screens/main/components/TasksView/components/OrganizationSwitcher/OrganizationSwitcher.tsxapps/web/src/app/auth/desktop/success/components/DesktopRedirect/index.tsapps/desktop/src/renderer/screens/main/components/SettingsView/AccountSettings/AccountSettings.tsxpackages/db/src/schema/index.tsapps/desktop/src/renderer/screens/main/components/AvatarDropdown/AvatarDropdown.tsxpackages/trpc/src/router/integration/linear/linear.tsapps/web/src/app/layout.tsxapps/web/src/app/auth/desktop/success/components/DesktopRedirect/DesktopRedirect.tsxapps/api/src/app/api/auth/desktop/connect/route.tspackages/auth/src/client.tsapps/admin/src/app/(dashboard)/users/components/UsersTable/UsersTable.tsxpackages/auth/src/index.tsapps/admin/src/trpc/react.tsxpackages/trpc/src/router/task/task.tsapps/admin/src/proxy.tspackages/trpc/src/router/analytics/analytics.tsapps/admin/src/app/(dashboard)/components/AppSidebar/components/NavUser/NavUser.tsxpackages/auth/src/env.tsapps/desktop/src/lib/trpc/routers/index.tsapps/api/src/app/api/auth/[...all]/route.tsapps/api/src/app/api/integrations/linear/connect/route.tsapps/admin/src/app/(dashboard)/components/LeaderboardTable/LeaderboardTable.tsxpackages/db/src/schema/auth.tsapps/desktop/src/lib/trpc/routers/auth/index.tsapps/admin/src/app/(dashboard)/components/AppSidebar/AppSidebar.tsxpackages/trpc/src/router/integration/integration.tsapps/web/src/proxy.tspackages/db/src/env.tsapps/web/src/app/(auth)/sign-up/[[...sign-up]]/page.tsxapps/admin/src/components/PostHogUserIdentifier/PostHogUserIdentifier.tsxapps/web/src/app/(auth)/layout.tsxapps/desktop/src/main/lib/auth/auth.tsapps/admin/src/app/layout.tsxapps/api/src/app/api/electric/[...path]/route.tsapps/api/src/app/api/electric/[...path]/utils.tsapps/web/src/trpc/react.tsxapps/web/src/app/(dashboard)/components/Header/Header.tsxpackages/trpc/src/trpc.tsapps/web/src/app/(auth)/sign-in/[[...sign-in]]/page.tsxapps/web/src/components/PostHogUserIdentifier/PostHogUserIdentifier.tsxapps/desktop/src/main/lib/auth/index.tsapps/marketing/src/app/layout.tsxapps/api/src/trpc/context.tsapps/api/src/lib/auth.tsapps/desktop/src/main/index.tsapps/marketing/src/app/components/CTAButtons/CTAButtons.tsxpackages/trpc/src/router/integration/linear/utils.tspackages/db/src/schema/relations.tsapps/web/src/app/auth/desktop/success/page.tsxpackages/trpc/src/router/admin/admin.tspackages/trpc/src/router/user/user.tspackages/db/src/schema/schema.tsapps/api/src/proxy.tspackages/trpc/src/router/organization/organization.ts
apps/**/src/**/**/[A-Z]*.tsx
📄 CodeRabbit inference engine (AGENTS.md)
Structure component folders with one component per file using format
ComponentName/ComponentName.tsxwithindex.tsbarrel export
Files:
apps/desktop/src/renderer/screens/main/components/TasksView/components/OrganizationSwitcher/OrganizationSwitcher.tsxapps/desktop/src/renderer/screens/main/components/SettingsView/AccountSettings/AccountSettings.tsxapps/desktop/src/renderer/screens/main/components/AvatarDropdown/AvatarDropdown.tsxapps/web/src/app/auth/desktop/success/components/DesktopRedirect/DesktopRedirect.tsxapps/admin/src/app/(dashboard)/users/components/UsersTable/UsersTable.tsxapps/admin/src/app/(dashboard)/components/AppSidebar/components/NavUser/NavUser.tsxapps/admin/src/app/(dashboard)/components/LeaderboardTable/LeaderboardTable.tsxapps/admin/src/app/(dashboard)/components/AppSidebar/AppSidebar.tsxapps/admin/src/components/PostHogUserIdentifier/PostHogUserIdentifier.tsxapps/web/src/app/(dashboard)/components/Header/Header.tsxapps/web/src/components/PostHogUserIdentifier/PostHogUserIdentifier.tsxapps/marketing/src/app/components/CTAButtons/CTAButtons.tsx
apps/desktop/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Move Node.js functionality needed in Electron renderer to
src/main/lib/and communicate via IPC
Files:
apps/desktop/src/renderer/screens/main/components/TasksView/components/OrganizationSwitcher/OrganizationSwitcher.tsxapps/desktop/src/renderer/screens/main/components/SettingsView/AccountSettings/AccountSettings.tsxapps/desktop/src/renderer/screens/main/components/AvatarDropdown/AvatarDropdown.tsxapps/desktop/src/lib/trpc/routers/index.tsapps/desktop/src/lib/trpc/routers/auth/index.tsapps/desktop/src/main/lib/auth/auth.tsapps/desktop/src/main/lib/auth/index.tsapps/desktop/src/main/index.ts
apps/desktop/src/{shared/ipc-channels.ts,main/**/*ipcs.ts,renderer/**/*.tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Define all Electron IPC channel types in
apps/desktop/src/shared/ipc-channels.tsbefore implementing handlers
Files:
apps/desktop/src/renderer/screens/main/components/TasksView/components/OrganizationSwitcher/OrganizationSwitcher.tsxapps/desktop/src/renderer/screens/main/components/SettingsView/AccountSettings/AccountSettings.tsxapps/desktop/src/renderer/screens/main/components/AvatarDropdown/AvatarDropdown.tsx
packages/db/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Use Drizzle ORM for all database operations in the
packages/dbpackage
Files:
packages/db/src/schema/index.tspackages/db/src/schema/auth.tspackages/db/src/env.tspackages/db/src/schema/relations.tspackages/db/src/schema/schema.ts
packages/db/src/schema/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Create database migrations by changing Drizzle schema and running
drizzle-kit generate --name="<sample_name_snake_case>"on a new Neon branch
Files:
packages/db/src/schema/index.tspackages/db/src/schema/auth.tspackages/db/src/schema/relations.tspackages/db/src/schema/schema.ts
apps/desktop/src/main/index.ts
📄 CodeRabbit inference engine (AGENTS.md)
Load environment variables in
src/main/index.tswithoverride: truebefore any other imports in the desktop app
Files:
apps/desktop/src/main/index.ts
🧠 Learnings (12)
📚 Learning: 2025-12-28T01:56:39.031Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T01:56:39.031Z
Learning: Applies to apps/desktop/src/main/index.ts : Load environment variables in `src/main/index.ts` with `override: true` before any other imports in the desktop app
Applied to files:
apps/web/src/app/auth/desktop/success/components/DesktopRedirect/index.tspackages/auth/src/env.tsapps/web/src/app/auth/desktop/success/page.tsx
📚 Learning: 2025-12-28T01:56:39.031Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T01:56:39.031Z
Learning: Applies to apps/desktop/src/**/*.{ts,tsx} : Move Node.js functionality needed in Electron renderer to `src/main/lib/` and communicate via IPC
Applied to files:
apps/web/src/app/auth/desktop/success/components/DesktopRedirect/index.tsapps/web/src/app/auth/desktop/success/components/DesktopRedirect/DesktopRedirect.tsxapps/admin/src/proxy.tsapps/desktop/src/lib/trpc/routers/auth/index.tsapps/desktop/src/main/lib/auth/index.tsapps/desktop/src/main/index.tsapps/web/src/app/auth/desktop/success/page.tsx
📚 Learning: 2025-12-21T04:39:28.543Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: apps/desktop/AGENTS.md:0-0
Timestamp: 2025-12-21T04:39:28.543Z
Learning: Applies to apps/desktop/**/*.{ts,tsx} : Use alias as defined in `tsconfig.json` when possible
Applied to files:
apps/web/src/app/auth/desktop/success/components/DesktopRedirect/index.tspackages/db/package.jsonpackages/auth/tsconfig.json
📚 Learning: 2025-12-28T01:56:39.031Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T01:56:39.031Z
Learning: Applies to packages/db/src/schema/**/*.ts : Create database migrations by changing Drizzle schema and running `drizzle-kit generate --name="<sample_name_snake_case>"` on a new Neon branch
Applied to files:
packages/db/src/schema/index.tspackages/db/drizzle/0006_add_better_auth_tables.sqlpackages/db/package.jsonpackages/db/drizzle/meta/0005_snapshot.jsonpackages/db/src/schema/auth.tspackages/db/src/schema/schema.ts
📚 Learning: 2025-12-28T01:56:39.031Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T01:56:39.031Z
Learning: Applies to packages/db/**/*.ts : Use Drizzle ORM for all database operations in the `packages/db` package
Applied to files:
packages/auth/src/index.tspackages/db/package.jsonapps/admin/package.jsonpackages/db/src/schema/auth.tspackages/trpc/src/router/integration/integration.tsapps/api/src/app/api/electric/[...path]/utils.tspackages/db/src/schema/relations.tspackages/db/src/schema/schema.tspackages/trpc/src/router/organization/organization.ts
📚 Learning: 2025-12-28T01:56:39.031Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T01:56:39.031Z
Learning: Applies to apps/**/{utils,hooks,constants,stores,providers,config,*.stories.tsx} : Co-locate utilities, hooks, constants, config files, and stories next to the files that use them
Applied to files:
packages/db/package.json
📚 Learning: 2025-12-28T01:56:39.031Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T01:56:39.031Z
Learning: Applies to **/*.{ts,tsx} : Use type safety and avoid `any` types unless absolutely necessary in TypeScript files
Applied to files:
packages/db/package.json
📚 Learning: 2025-12-28T01:56:39.031Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T01:56:39.031Z
Learning: Applies to apps/**/src/**/**/[A-Z]*.tsx : Structure component folders with one component per file using format `ComponentName/ComponentName.tsx` with `index.ts` barrel export
Applied to files:
packages/db/package.json
📚 Learning: 2025-12-21T04:39:28.543Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: apps/desktop/AGENTS.md:0-0
Timestamp: 2025-12-21T04:39:28.543Z
Learning: Applies to apps/desktop/**/*.{ts,tsx} : For Electron interprocess communication, ALWAYS use tRPC as defined in `src/lib/trpc`
Applied to files:
apps/admin/src/trpc/react.tsxapps/desktop/src/lib/trpc/routers/auth/index.tsapps/web/src/trpc/react.tsxpackages/trpc/src/trpc.ts
📚 Learning: 2025-12-28T01:56:39.031Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T01:56:39.031Z
Learning: Applies to apps/**/src/(middleware|proxy).ts : In Next.js 16, never create `middleware.ts`. Always use `proxy.ts` instead for request interception
Applied to files:
apps/admin/src/proxy.tsapps/api/src/app/api/auth/[...all]/route.tsapps/web/src/proxy.tsapps/api/src/proxy.ts
📚 Learning: 2025-12-21T04:39:28.543Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: apps/desktop/AGENTS.md:0-0
Timestamp: 2025-12-21T04:39:28.543Z
Learning: Applies to apps/desktop/**/*.{ts,tsx} : For tRPC subscriptions with trpc-electron, ALWAYS use the observable pattern from `trpc/server/observable` instead of async generators, as the library explicitly checks `isObservable(result)` and throws an error otherwise
Applied to files:
apps/desktop/src/lib/trpc/routers/auth/index.ts
📚 Learning: 2025-12-21T04:39:28.543Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: apps/desktop/AGENTS.md:0-0
Timestamp: 2025-12-21T04:39:28.543Z
Learning: Applies to apps/desktop/**/*.{ts,tsx} : Prefer zustand for state management if it makes sense. Do not use effect unless absolutely necessary.
Applied to files:
apps/web/src/app/auth/desktop/success/page.tsx
🧬 Code graph analysis (30)
apps/desktop/src/renderer/screens/main/components/SettingsView/AccountSettings/AccountSettings.tsx (1)
packages/ui/src/components/ui/avatar.tsx (1)
AvatarImage(53-53)
packages/trpc/src/router/integration/linear/linear.ts (1)
packages/trpc/src/router/integration/linear/utils.ts (2)
verifyOrgMembership(55-71)verifyOrgAdmin(73-81)
apps/web/src/app/layout.tsx (4)
packages/ui/src/lib/utils.ts (1)
cn(4-6)apps/admin/src/app/providers.tsx (1)
Providers(13-31)apps/web/src/app/providers.tsx (1)
Providers(13-31)packages/ui/src/components/ui/sonner.tsx (1)
Toaster(40-40)
apps/api/src/app/api/auth/desktop/connect/route.ts (3)
packages/auth/src/env.ts (1)
env(8-27)apps/api/src/env.ts (1)
env(4-47)packages/auth/src/index.ts (1)
auth(9-71)
packages/auth/src/index.ts (2)
packages/auth/src/env.ts (1)
env(8-27)packages/db/src/schema/auth.ts (1)
authSchema(11-11)
apps/admin/src/proxy.ts (4)
apps/web/src/proxy.ts (1)
proxy(18-37)packages/auth/src/index.ts (1)
auth(9-71)apps/admin/src/env.ts (1)
env(5-44)packages/shared/src/constants.ts (1)
COMPANY(12-24)
packages/auth/src/env.ts (4)
apps/api/src/proxy.ts (1)
config(46-53)packages/db/src/env.ts (1)
env(9-23)apps/api/src/env.ts (1)
env(4-47)apps/marketing/src/env.ts (1)
env(5-36)
apps/desktop/src/lib/trpc/routers/index.ts (1)
apps/desktop/src/lib/trpc/routers/auth/index.ts (1)
createAuthRouter(11-87)
apps/api/src/app/api/auth/[...all]/route.ts (1)
packages/auth/src/index.ts (1)
auth(9-71)
apps/desktop/src/lib/trpc/routers/auth/index.ts (2)
apps/desktop/src/main/lib/auth/auth.ts (1)
authService(110-110)apps/desktop/src/main/lib/auth/index.ts (1)
authService(2-2)
packages/trpc/src/router/integration/integration.ts (1)
packages/db/src/schema/auth.ts (1)
members(99-116)
apps/web/src/proxy.ts (3)
apps/admin/src/proxy.ts (1)
proxy(17-37)apps/api/src/proxy.ts (1)
proxy(22-44)packages/auth/src/index.ts (1)
auth(9-71)
apps/web/src/app/(auth)/sign-up/[[...sign-up]]/page.tsx (2)
packages/auth/src/client.ts (1)
authClient(6-9)apps/web/src/env.ts (1)
env(5-46)
apps/admin/src/components/PostHogUserIdentifier/PostHogUserIdentifier.tsx (2)
apps/web/src/components/PostHogUserIdentifier/PostHogUserIdentifier.tsx (1)
PostHogUserIdentifier(7-22)packages/auth/src/client.ts (1)
authClient(6-9)
apps/web/src/app/(auth)/layout.tsx (1)
packages/auth/src/index.ts (1)
auth(9-71)
apps/desktop/src/main/lib/auth/auth.ts (3)
apps/desktop/src/main/lib/app-environment.ts (1)
SUPERSET_HOME_DIR(5-5)packages/shared/src/constants.ts (1)
PROTOCOL_SCHEMES(6-9)apps/desktop/src/main/lib/auth/crypto-storage.ts (2)
decrypt(97-119)encrypt(76-90)
apps/api/src/app/api/electric/[...path]/route.ts (3)
apps/api/src/app/api/integrations/linear/connect/route.ts (1)
GET(7-53)apps/api/src/lib/auth.ts (1)
getSession(3-7)packages/db/src/schema/auth.ts (1)
members(99-116)
packages/trpc/src/trpc.ts (2)
packages/auth/src/index.ts (1)
Session(73-73)packages/shared/src/constants.ts (1)
COMPANY(12-24)
apps/web/src/app/(auth)/sign-in/[[...sign-in]]/page.tsx (2)
packages/auth/src/client.ts (1)
authClient(6-9)apps/web/src/env.ts (1)
env(5-46)
apps/api/src/trpc/context.ts (3)
apps/api/src/lib/auth.ts (1)
getSession(3-7)packages/trpc/src/trpc.ts (1)
createTRPCContext(11-11)packages/trpc/src/index.ts (1)
createTRPCContext(9-9)
apps/api/src/lib/auth.ts (1)
packages/auth/src/index.ts (1)
auth(9-71)
apps/desktop/src/main/index.ts (2)
apps/desktop/src/main/lib/auth/auth.ts (2)
parseAuthDeepLink(20-39)authService(110-110)apps/desktop/src/main/lib/auth/index.ts (2)
parseAuthDeepLink(2-2)authService(2-2)
apps/marketing/src/app/components/CTAButtons/CTAButtons.tsx (1)
packages/auth/src/index.ts (1)
auth(9-71)
packages/trpc/src/router/integration/linear/utils.ts (1)
packages/db/src/schema/auth.ts (1)
members(99-116)
packages/db/src/schema/relations.ts (2)
packages/db/src/schema/auth.ts (6)
users(13-24)sessions(26-44)accounts(46-68)members(99-116)invitations(118-138)organizations(86-97)packages/db/src/schema/schema.ts (3)
tasks(58-129)integrationConnections(135-171)repositories(29-53)
apps/web/src/app/auth/desktop/success/page.tsx (2)
packages/auth/src/index.ts (1)
auth(9-71)apps/web/src/app/auth/desktop/success/components/DesktopRedirect/DesktopRedirect.tsx (1)
DesktopRedirect(7-26)
packages/trpc/src/router/admin/admin.ts (2)
packages/trpc/src/router/admin/index.ts (1)
adminRouter(1-1)packages/trpc/src/trpc.ts (1)
adminProcedure(44-53)
packages/trpc/src/router/user/user.ts (2)
packages/trpc/src/trpc.ts (1)
protectedProcedure(33-42)packages/db/src/schema/auth.ts (1)
members(99-116)
apps/api/src/proxy.ts (2)
apps/admin/src/proxy.ts (1)
proxy(17-37)apps/web/src/proxy.ts (1)
proxy(18-37)
packages/trpc/src/router/organization/organization.ts (1)
packages/db/src/schema/auth.ts (1)
members(99-116)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Deploy API
- GitHub Check: Deploy Web
- GitHub Check: Deploy Admin
- GitHub Check: Deploy Docs
- GitHub Check: Build
| if (!redirectUrl) { | ||
| return new Response(`Failed to initiate OAuth: ${JSON.stringify(result)}`, { | ||
| status: 500, | ||
| }); | ||
| } |
There was a problem hiding this comment.
Avoid leaking internal details in error responses.
JSON.stringify(result) may expose internal auth library state or headers to clients. Use a generic error message instead.
🔎 Proposed fix
if (!redirectUrl) {
- return new Response(`Failed to initiate OAuth: ${JSON.stringify(result)}`, {
+ return new Response("Failed to initiate OAuth flow", {
status: 500,
});
}🤖 Prompt for AI Agents
In apps/api/src/app/api/auth/desktop/connect/route.ts around lines 29 to 33, the
error response currently returns JSON.stringify(result) which can leak internal
auth library state; change the response body to a generic message like "Failed
to initiate OAuth" and return the 500 status, and move any detailed information
about result into a server-side log (e.g., processLogger.error or console.error)
instead of sending it to the client.
| const signUpWithGoogle = async () => { | ||
| if (!isLoaded) return; | ||
|
|
||
| setIsLoadingGoogle(true); | ||
| setError(null); | ||
|
|
||
| try { | ||
| await signUp.authenticateWithRedirect({ | ||
| strategy: "oauth_google", | ||
| redirectUrl: "/sso-callback", | ||
| redirectUrlComplete: "/", | ||
| await authClient.signIn.social({ | ||
| provider: "google", | ||
| callbackURL: env.NEXT_PUBLIC_WEB_URL, | ||
| }); | ||
| } catch (err) { | ||
| console.error("Sign up failed:", err); | ||
| setError("Failed to sign up. Please try again."); | ||
| } finally { | ||
| setIsLoadingGoogle(false); | ||
| } | ||
| }; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Find the auth client implementation
rg -n "signIn.*social" --type ts --type tsx -A 10Repository: superset-sh/superset
Length of output: 184
🏁 Script executed:
# Also check the page.tsx file to understand the full context and check for type safety issues
head -50 apps/web/src/app/\(auth\)/sign-up/\[\[...\]\]/page.tsx 2>/dev/null || find . -path "**/sign-up/**/page.tsx" -type fRepository: superset-sh/superset
Length of output: 121
🏁 Script executed:
# Search for authClient declaration and type
rg -n "authClient\s*=" -B 5 -A 2 apps/web/src/app/\(auth\)/sign-up/Repository: superset-sh/superset
Length of output: 46
🏁 Script executed:
cat -n ./apps/web/src/app/\(auth\)/sign-up/\[\[...\]\]/page.tsx | head -60Repository: superset-sh/superset
Length of output: 148
🏁 Script executed:
# Search for authClient import and definition
rg -n "authClient|import.*auth" ./apps/web/src/app/\(auth\)/sign-up/\[\[...\]\]/page.tsxRepository: superset-sh/superset
Length of output: 154
🏁 Script executed:
# Find auth client implementation in the codebase
find ./packages -name "*.ts" -o -name "*.tsx" | head -20 | xargs rg -l "signIn.*social" 2>/dev/null | head -5Repository: superset-sh/superset
Length of output: 46
🏁 Script executed:
# Use git ls-files to find the sign-up page
git ls-files | grep -i "sign-up.*page.tsx"Repository: superset-sh/superset
Length of output: 119
🏁 Script executed:
# Use fd to find the file by pattern
fd "page.tsx" --path "*sign-up*"Repository: superset-sh/superset
Length of output: 279
🏁 Script executed:
# Check if the repository structure matches expectations
ls -la apps/web/src/app/ 2>/dev/null | head -20Repository: superset-sh/superset
Length of output: 667
🏁 Script executed:
# Use Python to read the file with proper path handling
python3 << 'EOF'
import os
file_path = "apps/web/src/app/(auth)/sign-up/[[...sign-up]]/page.tsx"
try:
with open(file_path, 'r') as f:
lines = f.readlines()
for i, line in enumerate(lines[:80], 1):
print(f"{i:3}: {line}", end='')
except Exception as e:
print(f"Error: {e}")
EOFRepository: superset-sh/superset
Length of output: 150
🏁 Script executed:
# Search for authClient definition and setup
rg "authClient" apps/web/src -B 2 -A 2 --max-count 10Repository: superset-sh/superset
Length of output: 4746
🏁 Script executed:
# Look for type definitions and any types in the file
rg "any" apps/web/src/app/ --type ts --type tsxRepository: superset-sh/superset
Length of output: 91
🏁 Script executed:
# Find the auth client implementation
find . -path "*/auth/*" -name "client*" -type fRepository: superset-sh/superset
Length of output: 93
🏁 Script executed:
# Look for the auth package structure
ls -la packages/auth/ 2>/dev/null || find . -path "*/packages/auth*" -type dRepository: superset-sh/superset
Length of output: 347
🏁 Script executed:
# Search for signIn definition and social method
rg "signIn.*=" packages/ -A 5 --max-count 5Repository: superset-sh/superset
Length of output: 46
🏁 Script executed:
# Check for any types in the entire codebase
rg ": any" apps/web/src/app/\(auth\)/ --type ts -A 1Repository: superset-sh/superset
Length of output: 46
🏁 Script executed:
# Read the auth client implementation
cat -n ./packages/auth/src/client.tsRepository: superset-sh/superset
Length of output: 387
🏁 Script executed:
# Check the auth package src directory
ls -la ./packages/auth/src/Repository: superset-sh/superset
Length of output: 348
🏁 Script executed:
# Search for type definitions in auth package
find ./packages/auth -name "*.ts" -o -name "*.d.ts" | xargs catRepository: superset-sh/superset
Length of output: 2710
🏁 Script executed:
# Read the sign-up page file content
python3 << 'EOF'
import os
file_path = "apps/web/src/app/(auth)/sign-up/[[...sign-up]]/page.tsx"
try:
with open(file_path, 'r') as f:
content = f.read()
lines = content.split('\n')
for i, line in enumerate(lines[:80], 1):
print(f"{i:3}: {line}")
except Exception as e:
print(f"Error: {e}")
EOFRepository: superset-sh/superset
Length of output: 2699
🏁 Script executed:
# Look for better-auth types and signIn method
rg "better-auth" packages/ --type ts -A 3 | head -50Repository: superset-sh/superset
Length of output: 772
🏁 Script executed:
# Search for any unused type annotations or error handling patterns
rg "catch.*err|any\b" apps/web/src/app/\(auth\)/ -B 2 -A 2Repository: superset-sh/superset
Length of output: 1840
🏁 Script executed:
# Check if there's an eslint config for catch clause typing
find . -name "eslint*" -o -name ".eslintrc*" | head -5Repository: superset-sh/superset
Length of output: 46
🏁 Script executed:
# Check tsconfig for strict settings
cat ./apps/web/tsconfig.jsonRepository: superset-sh/superset
Length of output: 285
🏁 Script executed:
# Check the extended TypeScript config
find . -path "*typescript*" -name "*.json" | head -10Repository: superset-sh/superset
Length of output: 237
🏁 Script executed:
# Look at the base TypeScript config
cat ./packages/typescript/next.jsonRepository: superset-sh/superset
Length of output: 127
🏁 Script executed:
# Read the base TypeScript config
cat ./tooling/typescript/next.jsonRepository: superset-sh/superset
Length of output: 271
🏁 Script executed:
# Also check the base config
cat ./tooling/typescript/base.jsonRepository: superset-sh/superset
Length of output: 685
Add type annotation to the error parameter in catch block.
The error parameter err (line 25) lacks a type annotation, violating TypeScript strict mode and the type safety requirement. Under the strict configuration, use catch (err: unknown) to properly type the error, or narrow it to a more specific type like catch (err: Error) if you need to access error properties.
Note: The loading state behavior is correct—it remains true on successful OAuth redirect since the browser will navigate away; only reset it on error.
🤖 Prompt for AI Agents
In apps/web/src/app/(auth)/sign-up/[[...sign-up]]/page.tsx around lines 16 to
30, the catch block uses an untyped error parameter `err` which fails TypeScript
strict-mode; change the signature to `catch (err: unknown)` (or `catch (err:
Error)` if you only expect Error instances) and update the error
handling/logging to narrow the type before accessing properties (e.g., use `if
(err instanceof Error) console.error("Sign up failed:", err.message, err)` else
console.error("Sign up failed:", err) ), then keep setting the user-facing error
and resetting isLoadingGoogle as before.
| */ | ||
| emptyStringAsUndefined: true, | ||
| skipValidation: !!process.env.SKIP_ENV_VALIDATION, | ||
| skipValidation: true, |
There was a problem hiding this comment.
Unconditionally disabling environment validation removes critical safety checks.
Hardcoding skipValidation: true bypasses all zod schema validation for DATABASE_URL and DATABASE_URL_UNPOOLED. Missing or malformed database URLs will no longer be caught at startup, potentially causing runtime failures when database connections are attempted.
🔎 Proposed fix: restore conditional validation
- skipValidation: true,
+ skipValidation: !!process.env.SKIP_ENV_VALIDATION,This allows validation to be skipped only when explicitly needed (e.g., during certain build processes) while maintaining safety checks in production and development.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| skipValidation: true, | |
| skipValidation: !!process.env.SKIP_ENV_VALIDATION, |
🤖 Prompt for AI Agents
In packages/db/src/env.ts around line 22, remove the unconditional
skipValidation: true and instead make skipValidation conditional (for example:
skipValidation: process.env.SKIP_ENV_VALIDATION === 'true' or only when NODE_ENV
=== 'test' or a dedicated build flag); this preserves zod validation for
DATABASE_URL and DATABASE_URL_UNPOOLED in normal dev/prod runs while allowing
callers to explicitly opt out when required.
| export const sessions = authSchema.table( | ||
| "sessions", | ||
| { | ||
| id: uuid("id").primaryKey().defaultRandom(), | ||
| expiresAt: timestamp("expires_at").notNull(), | ||
| token: text("token").notNull().unique(), | ||
| createdAt: timestamp("created_at").defaultNow().notNull(), | ||
| updatedAt: timestamp("updated_at") | ||
| .$onUpdate(() => new Date()) | ||
| .notNull(), | ||
| ipAddress: text("ip_address"), | ||
| userAgent: text("user_agent"), | ||
| userId: uuid("user_id") | ||
| .notNull() | ||
| .references(() => users.id, { onDelete: "cascade" }), | ||
| activeOrganizationId: uuid("active_organization_id"), | ||
| }, | ||
| (table) => [index("sessions_user_id_idx").on(table.userId)], | ||
| ); |
There was a problem hiding this comment.
Add foreign key constraint for activeOrganizationId.
Line 41 defines activeOrganizationId but doesn't add a foreign key reference to organizations.id. This creates referential integrity risk - sessions could reference non-existent organizations.
🔎 Proposed fix
activeOrganizationId: uuid("active_organization_id")
+ .references(() => organizations.id, { onDelete: "set null" }),This aligns with the same issue found in the migration SQL file (packages/db/drizzle/0006_add_better_auth_tables.sql).
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export const sessions = authSchema.table( | |
| "sessions", | |
| { | |
| id: uuid("id").primaryKey().defaultRandom(), | |
| expiresAt: timestamp("expires_at").notNull(), | |
| token: text("token").notNull().unique(), | |
| createdAt: timestamp("created_at").defaultNow().notNull(), | |
| updatedAt: timestamp("updated_at") | |
| .$onUpdate(() => new Date()) | |
| .notNull(), | |
| ipAddress: text("ip_address"), | |
| userAgent: text("user_agent"), | |
| userId: uuid("user_id") | |
| .notNull() | |
| .references(() => users.id, { onDelete: "cascade" }), | |
| activeOrganizationId: uuid("active_organization_id"), | |
| }, | |
| (table) => [index("sessions_user_id_idx").on(table.userId)], | |
| ); | |
| export const sessions = authSchema.table( | |
| "sessions", | |
| { | |
| id: uuid("id").primaryKey().defaultRandom(), | |
| expiresAt: timestamp("expires_at").notNull(), | |
| token: text("token").notNull().unique(), | |
| createdAt: timestamp("created_at").defaultNow().notNull(), | |
| updatedAt: timestamp("updated_at") | |
| .$onUpdate(() => new Date()) | |
| .notNull(), | |
| ipAddress: text("ip_address"), | |
| userAgent: text("user_agent"), | |
| userId: uuid("user_id") | |
| .notNull() | |
| .references(() => users.id, { onDelete: "cascade" }), | |
| activeOrganizationId: uuid("active_organization_id") | |
| .references(() => organizations.id, { onDelete: "set null" }), | |
| }, | |
| (table) => [index("sessions_user_id_idx").on(table.userId)], | |
| ); |
🤖 Prompt for AI Agents
In packages/db/src/schema/auth.ts around lines 26 to 44, the
sessions.activeOrganizationId column is defined but lacks a foreign key
reference to organizations.id; update the column definition to add
.references(() => organizations.id, { onDelete: "SET NULL" }) (or another
desired delete rule like "CASCADE") so sessions cannot reference non-existent
organizations and ensure this change matches the migration SQL in
packages/db/drizzle/0006_add_better_auth_tables.sql.
Code reviewFound 2 issues:
superset/packages/trpc/src/router/admin/admin.ts Lines 15 to 22 in fff4289
superset/apps/desktop/src/main/lib/auth/auth.ts Lines 38 to 55 in fff4289 🤖 Generated with Claude Code - If this code review was useful, please react with 👍. Otherwise, react with 👎. |
There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
packages/trpc/src/router/organization/organization.ts (3)
9-48: Missing soft-delete filtering in organization queries.Based on the PR objectives, the reviewer (saddlepaddle) flagged the loss of soft-delete functionality, noting that "the PR removes deletedAt filtering from organization queries." The
all,byId, andbySlugqueries now return all organizations without filtering out soft-deleted records.If soft-delete semantics need to be preserved (as requested by the reviewer), these queries should filter by
deletedAt IS NULLor equivalent. However, the current auth schema (from relevant code snippets) shows that theorganizationstable lacks adeletedAtcolumn entirely.Based on PR objectives indicating reviewer concerns about soft-delete functionality.
79-95: Restore UUID validation for organization ID.Line 82 weakens type safety by changing the
idvalidation fromz.string().uuid()toz.string(). The organization ID is a UUID field in the database schema (as confirmed by the relevant code snippets showingid: uuid("id")), and the input validation should enforce this constraint.🔎 Restore UUID validation
update: protectedProcedure .input( z.object({ - id: z.string(), + id: z.string().uuid(), name: z.string().min(1).optional(), logo: z.string().url().optional(), }), )As per coding guidelines: Use type safety and avoid weakening type constraints in TypeScript files.
97-102: Hard delete prevents organization recovery.Based on the PR objectives, the reviewer (saddlepaddle) flagged that "the PR converts user deletion from soft delete to hard delete" and noted that "previously there was a restoreUser mutation to recover deleted users; hard deletes prevent recovery and may conflict with compliance or user-requested restores."
The current implementation performs a hard delete on the
organizationstable, which:
- Permanently removes the organization and cascades to delete all associated members (due to foreign key constraints)
- Prevents any possibility of recovery or restoration
- May conflict with compliance requirements or audit trails
If soft-delete semantics need to be preserved (as requested by the reviewer), consider adding a
deletedAtcolumn to the schema and implementing soft-delete logic.Based on PR objectives indicating reviewer concerns about soft-delete functionality.
♻️ Duplicate comments (2)
apps/api/src/app/api/auth/desktop/connect/route.ts (2)
6-13: Past issue not addressed: Provider validation is still missing.The unsafe type assertion on line 8 remains unfixed. Without explicit validation that
provideris either"google"or"github", malicious requests can pass arbitrary strings tosignInSocial, potentially causing security issues or unexpected behavior.🔎 Recommended fix (as previously suggested)
export async function GET(request: Request) { const url = new URL(request.url); - const provider = url.searchParams.get("provider") as "google" | "github"; + const provider = url.searchParams.get("provider"); const state = url.searchParams.get("state"); - if (!provider || !state) { + if (!provider || !state) { return new Response("Missing provider or state", { status: 400 }); } + + if (provider !== "google" && provider !== "github") { + return new Response("Invalid provider", { status: 400 }); + }
29-33: Past issue not addressed: Error response still leaks internal details.Line 30 still uses
JSON.stringify(body)to expose internal auth library state to clients. This should be replaced with a generic error message, with detailed information logged server-side instead.🔎 Recommended fix (as previously suggested)
if (!body.url) { - return new Response(`Failed to initiate OAuth: ${JSON.stringify(body)}`, { + console.error("OAuth initiation failed:", body); + return new Response("Failed to initiate OAuth flow", { status: 500, }); }
🧹 Nitpick comments (6)
apps/api/src/app/api/auth/desktop/connect/route.ts (1)
18-27: Add error handling for the auth API call.The
auth.api.signInSocialcall (lines 18-24) lacks try-catch error handling. If the auth library throws an exception (network error, configuration issue, etc.), the entire route will crash with an unhandled error instead of returning a proper error response.🔎 Proposed fix
+ let result; + try { - const result = await auth.api.signInSocial({ + result = await auth.api.signInSocial({ body: { provider, callbackURL: successUrl.toString(), }, asResponse: true, }); + } catch (error) { + console.error("Failed to initiate OAuth:", error); + return new Response("Failed to initiate OAuth flow", { status: 500 }); + } const cookies = result.headers.get("set-cookie");packages/trpc/src/router/organization/organization.ts (1)
58-77: Consider removing unnecessary conditional check.The conditional check on Line 68 is redundant. The
.returning()method will either return the inserted record or the.insert()operation will throw an error. Theorganizationvalue cannot be undefined/null at this point without an exception being raised first.🔎 Suggested simplification
.mutation(async ({ ctx, input }) => { const [organization] = await db .insert(organizations) .values({ name: input.name, slug: input.slug, logo: input.logo, }) .returning(); - if (organization) { - await db.insert(members).values({ - organizationId: organization.id, - userId: ctx.session.user.id, - role: "owner", - }); - } + await db.insert(members).values({ + organizationId: organization.id, + userId: ctx.session.user.id, + role: "owner", + }); return organization; }),apps/api/src/proxy.ts (1)
5-9: Type inference produces a mixed array before filtering.The conditional expression on line 8 produces
string | false, soallowedOriginsis typed as(string | false)[]before the.filter(Boolean)call. While this works at runtime, TypeScript's type narrowing withfilter(Boolean)doesn't automatically narrow tostring[].Consider adding explicit type assertion for clarity:
Suggested improvement
const allowedOrigins = [ env.NEXT_PUBLIC_WEB_URL, env.NEXT_PUBLIC_ADMIN_URL, env.NODE_ENV === "development" && "http://localhost:5927", -].filter(Boolean); +].filter(Boolean) as string[];apps/web/src/proxy.ts (1)
23-28: Minor redundancy in route checking.The check
pathname.startsWith("/sign-in") || pathname.startsWith("/sign-up")duplicates logic thatisPublicRoutealready handles. Consider reusing the helper for consistency:Suggested simplification
if ( session && - (pathname.startsWith("/sign-in") || pathname.startsWith("/sign-up")) + (pathname.startsWith("/sign-in") || pathname.startsWith("/sign-up")) ) { return NextResponse.redirect(new URL("/", req.url)); }Alternatively, extract sign-in/sign-up paths into a constant shared with
publicRoutesto avoid drift.apps/desktop/src/renderer/lib/pglite/migrations/0001_auth_schema_migration.sql (1)
3-9: Consider adding foreign key constraints for referential integrity.The
memberstable referencesorganization_idanduser_idbut doesn't define foreign key constraints toauth.organizationsandauth.users. This could allow orphaned records and referential integrity violations.🔎 Suggested foreign key additions
CREATE TABLE "auth"."members" ( "id" uuid PRIMARY KEY NOT NULL, "organization_id" uuid NOT NULL, "user_id" uuid NOT NULL, "role" text NOT NULL, - "created_at" timestamp NOT NULL + "created_at" timestamp NOT NULL, + CONSTRAINT "members_organization_fk" FOREIGN KEY ("organization_id") REFERENCES "auth"."organizations"("id") ON DELETE CASCADE, + CONSTRAINT "members_user_fk" FOREIGN KEY ("user_id") REFERENCES "auth"."users"("id") ON DELETE CASCADE );apps/desktop/src/renderer/lib/pglite/schema/auth.ts (1)
42-55: Add foreign key constraints for data integrity.The
memberstable hasorganization_idanduser_idfields but no foreign key constraints. While Drizzle will work without them, adding constraints prevents orphaned records and ensures referential integrity, especially important for synced data.🔎 Recommended foreign key additions
export const members = authSchema.table( "members", { id: uuid("id").primaryKey(), - organization_id: uuid("organization_id").notNull(), - user_id: uuid("user_id").notNull(), + organization_id: uuid("organization_id") + .notNull() + .references(() => organizations.id, { onDelete: "cascade" }), + user_id: uuid("user_id") + .notNull() + .references(() => users.id, { onDelete: "cascade" }), role: text("role").notNull(), created_at: timestamp("created_at").notNull(), },Note: Adding foreign keys requires regenerating the migration with
drizzle-kit generate.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (23)
apps/admin/src/proxy.tsapps/api/src/app/api/auth/desktop/connect/route.tsapps/api/src/app/api/electric/[...path]/utils.tsapps/api/src/proxy.tsapps/desktop/drizzle.config.tsapps/desktop/src/main/lib/api-client.tsapps/desktop/src/main/lib/auth/auth.tsapps/desktop/src/renderer/lib/pglite/migrations/0001_auth_schema_migration.sqlapps/desktop/src/renderer/lib/pglite/migrations/index.tsapps/desktop/src/renderer/lib/pglite/migrations/meta/0001_snapshot.jsonapps/desktop/src/renderer/lib/pglite/migrations/meta/_journal.jsonapps/desktop/src/renderer/lib/pglite/schema/auth.tsapps/desktop/src/renderer/lib/pglite/schema/index.tsapps/desktop/src/renderer/lib/pglite/schema/schema.tsapps/desktop/src/renderer/lib/pglite/sync.tsapps/marketing/src/proxy.tsapps/web/src/app/auth/desktop/success/page.tsxapps/web/src/proxy.tspackages/db/drizzle/0007_add_created_at_default.sqlpackages/db/drizzle/meta/0007_snapshot.jsonpackages/db/drizzle/meta/_journal.jsonpackages/db/src/schema/auth.tspackages/trpc/src/router/organization/organization.ts
💤 Files with no reviewable changes (1)
- apps/marketing/src/proxy.ts
✅ Files skipped from review due to trivial changes (1)
- apps/desktop/src/renderer/lib/pglite/migrations/meta/0001_snapshot.json
🚧 Files skipped from review as they are similar to previous changes (2)
- packages/db/src/schema/auth.ts
- apps/desktop/src/main/lib/auth/auth.ts
🧰 Additional context used
📓 Path-based instructions (3)
apps/desktop/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/desktop/AGENTS.md)
apps/desktop/**/*.{ts,tsx}: For Electron interprocess communication, ALWAYS use tRPC as defined insrc/lib/trpc
Use alias as defined intsconfig.jsonwhen possible
Prefer zustand for state management if it makes sense. Do not use effect unless absolutely necessary.
For tRPC subscriptions with trpc-electron, ALWAYS use the observable pattern from@trpc/server/observableinstead of async generators, as the library explicitly checksisObservable(result)and throws an error otherwise
Files:
apps/desktop/src/renderer/lib/pglite/schema/schema.tsapps/desktop/drizzle.config.tsapps/desktop/src/renderer/lib/pglite/schema/index.tsapps/desktop/src/renderer/lib/pglite/sync.tsapps/desktop/src/renderer/lib/pglite/migrations/index.tsapps/desktop/src/renderer/lib/pglite/schema/auth.tsapps/desktop/src/main/lib/api-client.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use type safety and avoid
anytypes unless absolutely necessary in TypeScript files
Files:
apps/desktop/src/renderer/lib/pglite/schema/schema.tsapps/desktop/drizzle.config.tsapps/desktop/src/renderer/lib/pglite/schema/index.tsapps/api/src/app/api/auth/desktop/connect/route.tsapps/desktop/src/renderer/lib/pglite/sync.tsapps/api/src/app/api/electric/[...path]/utils.tsapps/web/src/app/auth/desktop/success/page.tsxapps/desktop/src/renderer/lib/pglite/migrations/index.tsapps/api/src/proxy.tsapps/desktop/src/renderer/lib/pglite/schema/auth.tspackages/trpc/src/router/organization/organization.tsapps/admin/src/proxy.tsapps/desktop/src/main/lib/api-client.tsapps/web/src/proxy.ts
apps/desktop/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Move Node.js functionality needed in Electron renderer to
src/main/lib/and communicate via IPC
Files:
apps/desktop/src/renderer/lib/pglite/schema/schema.tsapps/desktop/src/renderer/lib/pglite/schema/index.tsapps/desktop/src/renderer/lib/pglite/sync.tsapps/desktop/src/renderer/lib/pglite/migrations/index.tsapps/desktop/src/renderer/lib/pglite/schema/auth.tsapps/desktop/src/main/lib/api-client.ts
🧠 Learnings (4)
📚 Learning: 2025-12-28T01:56:39.031Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T01:56:39.031Z
Learning: Applies to packages/db/src/schema/**/*.ts : Create database migrations by changing Drizzle schema and running `drizzle-kit generate --name="<sample_name_snake_case>"` on a new Neon branch
Applied to files:
apps/desktop/src/renderer/lib/pglite/schema/schema.tsapps/desktop/drizzle.config.tspackages/db/drizzle/meta/0007_snapshot.jsonapps/desktop/src/renderer/lib/pglite/migrations/index.tsapps/desktop/src/renderer/lib/pglite/schema/auth.ts
📚 Learning: 2025-12-28T01:56:39.031Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T01:56:39.031Z
Learning: Applies to packages/db/**/*.ts : Use Drizzle ORM for all database operations in the `packages/db` package
Applied to files:
apps/desktop/src/renderer/lib/pglite/schema/schema.tsapps/desktop/drizzle.config.tsapps/api/src/app/api/electric/[...path]/utils.tsapps/desktop/src/renderer/lib/pglite/schema/auth.tspackages/trpc/src/router/organization/organization.ts
📚 Learning: 2025-12-28T01:56:39.031Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T01:56:39.031Z
Learning: Applies to apps/desktop/src/**/*.{ts,tsx} : Move Node.js functionality needed in Electron renderer to `src/main/lib/` and communicate via IPC
Applied to files:
apps/web/src/app/auth/desktop/success/page.tsxapps/admin/src/proxy.ts
📚 Learning: 2025-12-28T01:56:39.031Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T01:56:39.031Z
Learning: Applies to apps/**/src/(middleware|proxy).ts : In Next.js 16, never create `middleware.ts`. Always use `proxy.ts` instead for request interception
Applied to files:
apps/api/src/proxy.tsapps/admin/src/proxy.tsapps/web/src/proxy.ts
🧬 Code graph analysis (8)
apps/api/src/app/api/auth/desktop/connect/route.ts (3)
apps/api/src/env.ts (1)
env(4-47)packages/auth/src/env.ts (1)
env(8-27)packages/auth/src/index.ts (1)
auth(9-71)
apps/api/src/app/api/electric/[...path]/utils.ts (2)
apps/desktop/src/renderer/lib/pglite/schema/auth.ts (2)
members(42-55)organizations(26-37)packages/db/src/schema/auth.ts (2)
members(99-116)organizations(86-97)
apps/web/src/app/auth/desktop/success/page.tsx (4)
packages/auth/src/index.ts (1)
auth(9-71)apps/desktop/src/main/lib/api-client.ts (1)
headers(19-27)apps/web/src/app/auth/desktop/success/components/DesktopRedirect/DesktopRedirect.tsx (1)
DesktopRedirect(7-26)apps/web/src/app/auth/desktop/success/components/DesktopRedirect/index.ts (1)
DesktopRedirect(1-1)
apps/api/src/proxy.ts (2)
apps/admin/src/proxy.ts (2)
proxy(9-23)config(25-31)apps/web/src/proxy.ts (2)
proxy(16-35)config(37-43)
packages/trpc/src/router/organization/organization.ts (2)
apps/desktop/src/renderer/lib/pglite/schema/auth.ts (2)
organizations(26-37)members(42-55)packages/db/src/schema/auth.ts (2)
organizations(86-97)members(99-116)
apps/admin/src/proxy.ts (3)
apps/web/src/proxy.ts (1)
proxy(16-35)apps/desktop/src/main/lib/api-client.ts (1)
headers(19-27)packages/shared/src/constants.ts (1)
COMPANY(12-24)
apps/desktop/src/main/lib/api-client.ts (2)
apps/desktop/src/main/lib/auth/auth.ts (1)
authService(148-148)apps/desktop/src/main/lib/auth/index.ts (1)
authService(2-2)
apps/web/src/proxy.ts (4)
apps/admin/src/proxy.ts (2)
proxy(9-23)config(25-31)apps/api/src/proxy.ts (2)
proxy(22-37)config(39-46)packages/auth/src/index.ts (1)
auth(9-71)apps/desktop/src/main/lib/api-client.ts (1)
headers(19-27)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: Deploy API
- GitHub Check: Deploy Marketing
- GitHub Check: Deploy Docs
- GitHub Check: Deploy Admin
- GitHub Check: Deploy Web
- GitHub Check: Build
🔇 Additional comments (32)
apps/desktop/drizzle.config.ts (1)
4-7: LGTM! Clean separation of auth schema.The migration from a single schema file to an array supporting both
auth.tsandschema.tsaligns well with the PR's Better Auth migration objectives. This separation improves modularity and maintainability by keeping authentication-related tables isolated from the main schema.apps/api/src/app/api/electric/[...path]/utils.ts (4)
7-11: LGTM!The
AllowedTabletype correctly reflects the newauth.*schema naming convention introduced with the Better Auth migration. The type is well-defined with proper string literals.
18-28: LGTM!The
buildhelper function is well-typed and its logic is preserved from the previous implementation. The approach of extracting the WHERE fragment via regex is functional for the expected SQL format.
38-42: LGTM!The case labels correctly use the new
auth.*prefixed table names, and the column references (members.organizationId,organizations.id) align with the schema definitions inpackages/db/src/schema/auth.ts.
44-62: LGTM!The users case correctly:
- Queries
db.query.members(renamed fromorganizationMembers)- Uses the correct camelCase column reference
userIdthat matches the server schema- Deduplicates user IDs using
Set- Builds an
INclause for filtering users belonging to the organizationThe logic is sound and the migration to the new
auth.userstable is properly handled.packages/db/drizzle/0007_add_created_at_default.sql (1)
1-2: LGTM! Well-structured migration adding timestamp defaults.This migration correctly adds
DEFAULT now()to thecreated_atcolumns forauth.membersandauth.organizations, ensuring consistency with other auth tables. This is a non-breaking change that aligns with the PR's goal to rely on database-generated defaults rather than manual timestamp generation.packages/db/drizzle/meta/_journal.json (1)
39-60: LGTM! Migration journal correctly updated.The three new migration entries are properly formatted and sequentially ordered, tracking the Better Auth migration (0005-0007). The journal structure follows Drizzle conventions.
packages/db/drizzle/meta/0007_snapshot.json (1)
1-1525: LGTM! Schema snapshot correctly reflects migration 0007.The snapshot accurately captures the database state after adding
created_atdefaults toauth.membersandauth.organizations. The schema is well-structured with:
- Appropriate foreign key cascade behaviors
- Proper indexing on key columns
- Consistent use of UUIDs with database-generated defaults
- All auth tables now having
created_atdefaultsThe snapshot aligns with the Better Auth migration objectives.
apps/desktop/src/main/lib/api-client.ts (3)
7-13: Clear documentation of offline-first behavior.The documentation effectively explains the distinction between auth errors and network errors, which directly addresses the reviewer's concern about preserving offline-first functionality.
28-44: Excellent offline-first error handling implementation.This fetch override correctly distinguishes between authentication failures and network errors, directly addressing the reviewer's concern:
- 401/403 responses: Properly triggers sign-out to clear invalid/revoked tokens
- Network errors: Preserves the session by catching and re-throwing, allowing offline work to continue
The implementation properly awaits both the fetch call and the sign-out operation, avoiding race conditions.
19-27: The code is correct. ThegetAccessToken()method is synchronous and returnsstring | nulldirectly (not a Promise), as shown in the auth service implementation at line 84-89 ofapps/desktop/src/main/lib/auth/auth.ts. Removing theawaitis appropriate and does not break authentication.Likely an incorrect or invalid review comment.
apps/api/src/app/api/auth/desktop/connect/route.ts (1)
1-4: LGTM!Imports are appropriate and necessary for the desktop OAuth connect functionality.
packages/trpc/src/router/organization/organization.ts (4)
2-2: LGTM: Import aligns with new auth schema structure.The switch from
organizationMembersanduserstomembersandorganizationscorrectly reflects the new auth schema introduced in this PR.
50-57: LGTM: Input schema correctly defines organization fields.The
logofield appropriately replaces the previousavatarUrl/githubOrgfields with proper URL validation.
104-121: LGTM: Member addition correctly uses new schema.The mutation properly inserts into the
memberstable with the appropriate role ("member") and references the new auth schema structure.
123-140: LGTM: Member removal correctly uses new schema.The mutation properly deletes from the
memberstable with appropriate composite key matching usingorganizationIdanduserId.apps/api/src/proxy.ts (2)
22-37: LGTM!The synchronous proxy function correctly handles CORS preflight requests and attaches CORS headers to all responses. The API authentication is appropriately delegated to the TRPC context layer rather than being handled at the proxy level.
39-46: LGTM!The matcher configuration correctly excludes Next.js internals, static assets, and telemetry routes while ensuring the proxy runs for all API and TRPC routes. The pattern is consistent with the other proxy files in the codebase.
apps/web/src/proxy.ts (3)
16-19: LGTM!The session retrieval pattern using
auth.api.getSessionwithawait headers()is consistent with the admin proxy implementation and correctly integrates with the new Better Auth system.
37-43: LGTM!The matcher configuration is consistent with the other proxy files in the monorepo, correctly excluding internal routes and static assets.
5-14: No changes needed - the review comment is based on an incorrect assumption about the application architecture.The web app and API app are separate Next.js applications. Better Auth callbacks are handled by the API app at
apps/api/src/app/api/auth/[...all]/route.ts, not by the web app. The web app makes HTTP requests to the separate API app (viaNEXT_PUBLIC_API_URL), so Better Auth callback routes never pass through the web app's proxy middleware. The currentpublicRoutesconfiguration is correct for the web app's actual routing needs.Likely an incorrect or invalid review comment.
apps/admin/src/proxy.ts (2)
9-23: LGTM!The admin proxy correctly enforces authentication and email domain restrictions. The pattern is consistent with the web proxy, and using
COMPANY.EMAIL_DOMAINfrom shared constants ensures the domain check stays synchronized across the codebase.
25-31: LGTM!The matcher configuration is consistent with the API and web proxy files.
apps/desktop/src/renderer/lib/pglite/migrations/meta/_journal.json (1)
12-18: LGTM!The migration journal entry follows the correct drizzle-kit format with proper versioning and timestamp.
apps/desktop/src/renderer/lib/pglite/migrations/index.ts (2)
15-15: LGTM!The migration import follows the correct pattern for browser-compatible bundled migrations using Vite's
?rawsuffix.
25-25: LGTM!The migration entry is correctly added with matching timestamp from
_journal.json.apps/desktop/src/renderer/lib/pglite/schema/index.ts (1)
1-2: LGTM!The re-export correctly exposes the new auth schema module alongside existing schema exports.
apps/desktop/src/renderer/lib/pglite/schema/schema.ts (1)
31-32: LGTM!The comment update correctly reflects that auth-related tables have been moved to the
authschema, leaving only the tasks table here.apps/desktop/src/renderer/lib/pglite/schema/auth.ts (1)
1-58: Schema misalignment requires fixes across multiple areas.The desktop schema diverges significantly from the server schema:
- Critical type mismatch:
email_verifiedistexton desktop butbooleanon server (defaults tofalse)- Incomplete schema: Desktop is missing 4 tables—
sessions,accounts,verifications,invitations- Missing defaults and constraints:
- UUIDs lack
.defaultRandom()- Timestamps lack
.defaultNow()or.$onUpdate()members.rolemissing default value"member"- Index type mismatch:
organizations.slugusesindex()instead ofuniqueIndex()- Missing foreign key relationships: No cascade delete references in
members,sessions,accounts,invitations- Property naming inconsistency: Desktop uses snake_case (
email_verified,created_at) while server uses camelCase (emailVerified,createdAt)Update the desktop schema to match the server implementation exactly.
⛔ Skipped due to learnings
Learnt from: CR Repo: superset-sh/superset PR: 0 File: AGENTS.md:0-0 Timestamp: 2025-12-28T01:56:39.031Z Learning: Applies to packages/db/**/*.ts : Use Drizzle ORM for all database operations in the `packages/db` packageLearnt from: CR Repo: superset-sh/superset PR: 0 File: AGENTS.md:0-0 Timestamp: 2025-12-28T01:56:39.031Z Learning: Applies to packages/db/src/schema/**/*.ts : Create database migrations by changing Drizzle schema and running `drizzle-kit generate --name="<sample_name_snake_case>"` on a new Neon branchapps/web/src/app/auth/desktop/success/page.tsx (3)
6-12: LGTM! Correct Next.js 15 async searchParams pattern.The function signature properly types
searchParamsas a Promise and correctly awaits it before destructuring. This aligns with Next.js 15 conventions.
38-42: The 5-minute safety buffer is correctly implemented in the desktop app's token validation logic (apps/desktop/src/main/lib/auth/auth.ts, lines 70-75 in theisExpired()method), not in the web app's URL generation. The web app correctly passes the actualexpiresAtvalue; the desktop app applies the buffer during token validation when it receives the deep link. This is the intended design and the PR objectives are satisfied.Likely an incorrect or invalid review comment.
38-39: Add explicit type annotation for the session variable and verify the property access pattern.The
sessionvariable fromauth.api.getSession()lacks an explicit type annotation. While a null check exists on line 27, the code accessessession.session.tokenandsession.session.expiresAt, which differs from the pattern used elsewhere in the codebase (session.user.*). Ensure this property structure matches Better Auth's return type, or add explicit typing:const session: Session | null = await auth.api.getSession(...).
| if (!session?.user) { | ||
| return NextResponse.redirect(new URL(env.NEXT_PUBLIC_WEB_URL)); | ||
| } | ||
|
|
||
| const user = await db.query.users.findFirst({ | ||
| where: eq(users.clerkId, clerkId), | ||
| }); | ||
|
|
||
| if (!user?.email.endsWith(COMPANY.EMAIL_DOMAIN)) { | ||
| if (!session.user.email.endsWith(COMPANY.EMAIL_DOMAIN)) { | ||
| return NextResponse.redirect(new URL(env.NEXT_PUBLIC_WEB_URL)); | ||
| } |
There was a problem hiding this comment.
Add null check for user email.
If session.user.email is null or undefined (possible with some OAuth providers that don't return email), calling .endsWith() will throw a runtime error. Consider adding a defensive check:
Suggested fix
if (!session?.user) {
return NextResponse.redirect(new URL(env.NEXT_PUBLIC_WEB_URL));
}
- if (!session.user.email.endsWith(COMPANY.EMAIL_DOMAIN)) {
+ if (!session.user.email?.endsWith(COMPANY.EMAIL_DOMAIN)) {
return NextResponse.redirect(new URL(env.NEXT_PUBLIC_WEB_URL));
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (!session?.user) { | |
| return NextResponse.redirect(new URL(env.NEXT_PUBLIC_WEB_URL)); | |
| } | |
| const user = await db.query.users.findFirst({ | |
| where: eq(users.clerkId, clerkId), | |
| }); | |
| if (!user?.email.endsWith(COMPANY.EMAIL_DOMAIN)) { | |
| if (!session.user.email.endsWith(COMPANY.EMAIL_DOMAIN)) { | |
| return NextResponse.redirect(new URL(env.NEXT_PUBLIC_WEB_URL)); | |
| } | |
| if (!session?.user) { | |
| return NextResponse.redirect(new URL(env.NEXT_PUBLIC_WEB_URL)); | |
| } | |
| if (!session.user.email?.endsWith(COMPANY.EMAIL_DOMAIN)) { | |
| return NextResponse.redirect(new URL(env.NEXT_PUBLIC_WEB_URL)); | |
| } |
🤖 Prompt for AI Agents
In apps/admin/src/proxy.ts around lines 14 to 20, the code calls
session.user.email.endsWith(...) without ensuring email is a non-empty string;
when email is null/undefined this throws. Add a defensive check that verifies
typeof session.user.email === "string" and that it is non-empty (or use optional
chaining plus a string guard) before calling .endsWith, and if the check fails
treat the user as unauthenticated and redirect to env.NEXT_PUBLIC_WEB_URL.
| DROP TABLE "organization_members" CASCADE;--> statement-breakpoint | ||
| DROP TABLE "organizations" CASCADE;--> statement-breakpoint | ||
| DROP TABLE "users" CASCADE;--> statement-breakpoint |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n apps/desktop/src/renderer/lib/pglite/migrations/0001_auth_schema_migration.sqlRepository: superset-sh/superset
Length of output: 1739
🏁 Script executed:
# Check for other migration files that might contain data migration logic
ls -la apps/desktop/src/renderer/lib/pglite/migrations/Repository: superset-sh/superset
Length of output: 430
🏁 Script executed:
# Search for backup tables or references to old table names
rg "organization_members|organizations|users" apps/desktop/src/renderer/lib/pglite/migrations/ -A 2 -B 2Repository: superset-sh/superset
Length of output: 18941
🏁 Script executed:
# Search for Electric sync configuration and data persistence strategy
rg "Electric|ephemeral|rebuild|sync" apps/desktop/src/renderer/lib/pglite/ -iRepository: superset-sh/superset
Length of output: 4995
🏁 Script executed:
# Check if there's any migration or setup documentation
find apps/desktop -type f -name "*.md" -o -name "*.ts" -o -name "*.js" | xargs rg -l "auth.*schema|migration|backup" -i | head -20Repository: superset-sh/superset
Length of output: 437
🏁 Script executed:
cat -n apps/desktop/src/renderer/lib/pglite/migrations/0000_init.sql | head -100Repository: superset-sh/superset
Length of output: 4057
🏁 Script executed:
# Look for clerk field references
rg "clerk_id|clerk_org_id" apps/desktop/src/renderer/lib/pglite/ -B 2 -A 2Repository: superset-sh/superset
Length of output: 7721
🏁 Script executed:
# Check the schema definition files
cat -n apps/desktop/src/renderer/lib/pglite/schema/auth.tsRepository: superset-sh/superset
Length of output: 2307
🏁 Script executed:
# Check SYNCED_TABLES definition
cat -n apps/desktop/src/renderer/lib/pglite/sync.ts | head -50Repository: superset-sh/superset
Length of output: 1076
🏁 Script executed:
# Check server schema to understand email_verified type
find apps -name "*.ts" -o -name "*.sql" | xargs rg "email_verified" -B 2 -A 2 | head -50Repository: superset-sh/superset
Length of output: 1050
Add foreign key constraints for referential integrity in the auth schema.
The migration removes the clerk_id and clerk_org_id fields (intentional for auth restructuring) and adds new fields like email_verified and metadata. However, the new auth.members table lacks foreign key constraints linking organization_id and user_id to their respective parent tables, which can cause referential integrity issues.
Additionally, while the migration drops old tables with CASCADE, data is safely repopulated via Electric sync (configured in sync.ts to sync auth.users, auth.organizations, and auth.members from the server). For clarity, consider adding a comment to the migration explaining this dependency on Electric sync.
🤖 Prompt for AI Agents
In
apps/desktop/src/renderer/lib/pglite/migrations/0001_auth_schema_migration.sql
around lines 32-34, the new auth.members table lacks foreign key constraints for
organization_id and user_id and the migration drops legacy tables with CASCADE
without documenting reliance on Electric sync; update the migration to add
foreign keys linking auth.members.organization_id -> auth.organizations(id) and
auth.members.user_id -> auth.users(id) (choose ON DELETE CASCADE or RESTRICT per
domain rules) via ALTER TABLE or in the CREATE TABLE statement, and add a short
SQL comment above the DROP TABLEs noting that data is repopulated by Electric
sync (sync.ts) so the drops are intentional.
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (1)
packages/trpc/src/router/integration/integration.ts (1)
13-13: **UUID validation is now correctly applied using Zod 4 syntax.**The past review comment flagged relaxed validation, but the current code now usesz.uuid()— a Zod 4 top-level function where string format validators moved from methods. The syntaxz.string().uuid()becomesz.uuid().z.uuid()validates RFC 9562/4122 compliant UUIDs.This correctly validates UUIDs and addresses the prior concern.
🧹 Nitpick comments (1)
apps/admin/src/app/(dashboard)/layout.tsx (1)
38-40: Consider case-insensitive email domain check.The
endsWith()method is case-sensitive, which could reject valid emails with different casing (e.g.,user@Superset.SH). While email domains are typically normalized to lowercase, RFC 5321 allows mixed case.🔎 Proposed case-insensitive check
- if (!session.user.email?.endsWith(COMPANY.EMAIL_DOMAIN)) { + if (!session.user.email?.toLowerCase().endsWith(COMPANY.EMAIL_DOMAIN)) { redirect(env.NEXT_PUBLIC_WEB_URL); }
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
apps/admin/src/app/(dashboard)/layout.tsxapps/api/src/app/api/auth/desktop/connect/route.tsapps/web/src/app/(dashboard)/layout.tsxapps/web/src/app/auth/desktop/success/page.tsxpackages/trpc/src/router/integration/integration.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/api/src/app/api/auth/desktop/connect/route.ts
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use type safety and avoid
anytypes unless absolutely necessary in TypeScript files
Files:
apps/web/src/app/auth/desktop/success/page.tsxapps/admin/src/app/(dashboard)/layout.tsxpackages/trpc/src/router/integration/integration.tsapps/web/src/app/(dashboard)/layout.tsx
🧠 Learnings (2)
📚 Learning: 2025-12-28T01:56:39.031Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T01:56:39.031Z
Learning: Applies to apps/desktop/src/**/*.{ts,tsx} : Move Node.js functionality needed in Electron renderer to `src/main/lib/` and communicate via IPC
Applied to files:
apps/web/src/app/auth/desktop/success/page.tsx
📚 Learning: 2025-12-28T01:56:39.031Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T01:56:39.031Z
Learning: Applies to packages/db/**/*.ts : Use Drizzle ORM for all database operations in the `packages/db` package
Applied to files:
packages/trpc/src/router/integration/integration.ts
🧬 Code graph analysis (2)
apps/admin/src/app/(dashboard)/layout.tsx (2)
apps/admin/src/env.ts (1)
env(5-44)packages/shared/src/constants.ts (1)
COMPANY(12-24)
apps/web/src/app/(dashboard)/layout.tsx (1)
packages/auth/src/index.ts (1)
auth(9-71)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: Deploy API
- GitHub Check: Deploy Admin
- GitHub Check: Deploy Docs
- GitHub Check: Deploy Marketing
- GitHub Check: Deploy Web
- GitHub Check: Build
🔇 Additional comments (10)
packages/trpc/src/router/integration/integration.ts (2)
2-2: LGTM!Import updates align with the schema migration to use the new
memberstable. Removing theusersimport is appropriate since the user ID is now obtained directly from the session context.
15-23: LGTM!The membership query correctly uses the new
memberstable with session-based authentication. Theand()andeq()constructs are properly used, and accessingctx.session.user.iddirectly from the session context is cleaner than the previous user lookup approach.apps/web/src/app/(dashboard)/layout.tsx (1)
1-3: LGTM: Imports are appropriate for authentication gate.The imports correctly bring in the Better Auth instance, Next.js headers utility, and redirect function needed for the session-based route guard.
apps/admin/src/app/(dashboard)/layout.tsx (4)
1-2: LGTM! Clean imports for authentication guard.The imports are well-organized and appropriate for implementing server-side authentication with Better Auth in Next.js 15+.
Also applies to: 17-18, 20-20
34-36: LGTM! Proper session validation with optional chaining.The check correctly handles cases where the session is missing or doesn't contain a user, redirecting unauthorized users appropriately.
42-47: LGTM! Improved error handling with redirect.The change from throwing an error to redirecting when user data is missing is a good UX improvement. This provides a cleaner user experience by redirecting to the web app instead of showing an error page.
The separate user data fetch via TRPC after session validation is appropriate, as the session likely contains minimal user data while
AppSidebarneeds the complete user profile.
30-32: This follows the established pattern used throughout the codebase. Theauth.api.getSessioncall without try-catch is consistent with other layout files and middleware (e.g., apps/web/src/app/(dashboard)/layout.tsx, apps/admin/src/proxy.ts). Unhandled errors will propagate to Next.js error boundaries, which is the standard approach here.If explicit error handling is desired, consider addressing this systematically across the codebase rather than in isolated files.
apps/web/src/app/auth/desktop/success/page.tsx (3)
1-4: LGTM!Imports are clean and appropriate for a Next.js server component with Better Auth integration.
6-22: LGTM!State parameter validation is properly implemented with good type safety and user-friendly error messaging.
40-49: LGTM!Session existence check is appropriate and handles the missing session case correctly with user-friendly messaging.
| const session = await auth.api.getSession({ | ||
| headers: await headers(), | ||
| }); | ||
|
|
||
| if (!session) { | ||
| redirect("/sign-in"); | ||
| } |
There was a problem hiding this comment.
Add error handling to prevent layout crashes.
If auth.api.getSession() throws an error (e.g., database connection failure, auth service unavailable), the entire dashboard will crash. Wrap the auth call in try-catch to gracefully handle failures.
🔎 Proposed fix with error handling
+ let session;
+ try {
- const session = await auth.api.getSession({
+ session = await auth.api.getSession({
headers: await headers(),
});
+ } catch (error) {
+ console.error("Failed to fetch session:", error);
+ redirect("/sign-in");
+ }
if (!session) {
redirect("/sign-in");
}Consider explicit type annotation for better type safety.
Adding an explicit type to the session variable improves code clarity and aligns with the TypeScript coding guidelines.
As per coding guidelines, "Use type safety and avoid any types unless absolutely necessary in TypeScript files."
🔎 Suggested type annotation
- const session = await auth.api.getSession({
+ const session: Awaited<ReturnType<typeof auth.api.getSession>> | null = await auth.api.getSession({
headers: await headers(),
});Or import and use the Session type if exported from @superset/auth:
+ import type { Session } from "@superset/auth";
// ...
- const session = await auth.api.getSession({
+ const session: Session | null = await auth.api.getSession({
headers: await headers(),
});🤖 Prompt for AI Agents
In apps/web/src/app/(dashboard)/layout.tsx around lines 15 to 21, the call to
auth.api.getSession is unguarded and can throw, causing the dashboard to crash;
wrap the await auth.api.getSession(...) in a try-catch so any thrown error is
handled (log the error and redirect to "/sign-in" or render a fallback UI) to
prevent a hard crash, and add an explicit type annotation for the session
variable (import and use the Session type from the auth package or define the
appropriate interface) so the variable is strongly typed rather than inferred.
| // Get session from Better Auth | ||
| let session; | ||
| try { | ||
| session = await auth.api.getSession({ headers: await headers() }); | ||
| } catch (error) { | ||
| console.error("Failed to get session for desktop auth:", error); | ||
| return ( | ||
| <div className="flex min-h-screen flex-col items-center justify-center bg-background p-4"> | ||
| <p className="text-xl text-muted-foreground">Authentication failed</p> | ||
| <p className="text-muted-foreground/70"> | ||
| Please try signing in again from the desktop app. | ||
| </p> | ||
| </div> | ||
| ); | ||
| } |
There was a problem hiding this comment.
Distinguish network errors from authentication failures to preserve offline-first functionality.
While the try-catch addresses the previous review comment about error handling, it treats all errors identically and breaks the offline-first behavior. According to the PR objectives, the reviewer specifically flagged this regression: the previous implementation (PR #512) distinguished network errors from invalid tokens to preserve sessions during transient network failures.
The current implementation would clear the session on any error, including temporary network issues, preventing the desktop app from functioning offline or on flaky networks.
Impact:
- Users on unstable connections would be forced to re-authenticate repeatedly
- Offline desktop functionality is broken
- Transient errors become permanent failures
Based on PR objectives, you should:
- Distinguish error types (network vs authentication failures)
- Preserve sessions during network errors (return error state without clearing session)
- Clear sessions only on invalid/401/403 responses
- Consider adding token refresh handling for expired tokens
🔎 Guidance for error handling
The error handling should check the error type and HTTP status:
try {
session = await auth.api.getSession({ headers: await headers() });
} catch (error) {
console.error("Failed to get session for desktop auth:", error);
// Check if it's a network error vs auth failure
const isNetworkError =
error instanceof Error &&
(error.message.includes('fetch failed') ||
error.message.includes('network') ||
error.cause instanceof TypeError);
if (isNetworkError) {
// Network error - preserve session, allow retry
return (
<div className="flex min-h-screen flex-col items-center justify-center bg-background p-4">
<p className="text-xl text-muted-foreground">Network error</p>
<p className="text-muted-foreground/70">
Unable to verify session. Please check your connection and try again.
</p>
</div>
);
}
// Auth failure - clear session
return (
<div className="flex min-h-screen flex-col items-center justify-center bg-background p-4">
<p className="text-xl text-muted-foreground">Authentication failed</p>
<p className="text-muted-foreground/70">
Please try signing in again from the desktop app.
</p>
</div>
);
}Note: The exact error detection logic depends on how Better Auth reports errors. You may need to check for specific error properties or status codes.
Better Auth uses BETTER_AUTH_SECRET, not DESKTOP_AUTH_SECRET. The DESKTOP_AUTH_SECRET was a legacy name that is no longer used in the codebase. Changes: - Removed DESKTOP_AUTH_SECRET env var from deploy-production.yml - Removed DESKTOP_AUTH_SECRET env var from deploy-preview.yml - BETTER_AUTH_SECRET is the only auth secret needed 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/desktop/src/main/windows/main.ts (1)
34-57: Window state persistence removed—document the UX tradeoff or reintroduce it.The window state persistence feature (size, position, maximized state) was removed in the session isolation commit. Users will lose their window layout on every restart. While this simplifies the code and the session isolation is necessary for the auth fix, the UX impact should be explicitly acknowledged.
Either:
- Reintroduce window state persistence with the new session partition setup, or
- If this removal is intentional, document the decision and ensure the default window size (primary display work area) provides acceptable UX across different display configurations.
🤖 Fix all issues with AI Agents
In @apps/desktop/src/main/windows/main.ts:
- Line 32: Wrap the call to screen.getPrimaryDisplay().workAreaSize in a
try/catch inside main.ts so the app doesn't crash if the screen API throws; on
error log the exception (or use processLogger) and fall back to safe default
dimensions (e.g., width=1024, height=768) and continue startup, replacing the
direct destructuring of { width, height } with values from the try block or the
fallback.
In @apps/web/src/app/auth/desktop/success/page.tsx:
- Around line 70-79: Wrap the db.insert(sessions).values(...) call in a
try-catch to handle DB errors gracefully: catch any error thrown by
db.insert(sessions), log it with contextual info (e.g., token, session.user.id,
session.session.activeOrganizationId) using the existing logger or
console.error, and then return or throw a controlled error/user-friendly
response instead of letting Next.js surface a generic crash; update the block
around the insert in page.tsx (the await db.insert(sessions).values({...})
statement) to perform this try-catch and logging.
🧹 Nitpick comments (2)
apps/web/src/app/auth/desktop/success/page.tsx (1)
66-68: Extract session expiry duration to a shared constant.The 30-day expiry (
60 * 60 * 24 * 30 * 1000) is duplicated here and inpackages/auth/src/index.ts(line 25:expiresIn: 60 * 60 * 24 * 30). If the auth config changes, this value could drift out of sync.Consider exporting the session duration from
@superset/authand importing it here.🔎 Suggested approach
In
packages/auth/src/index.ts:export const SESSION_EXPIRY_SECONDS = 60 * 60 * 24 * 30; // 30 daysThen in this file:
+import { SESSION_EXPIRY_SECONDS } from "@superset/auth"; + +const SESSION_EXPIRY_MS = SESSION_EXPIRY_SECONDS * 1000; + // ... const expiresAt = new Date( - Date.now() + 60 * 60 * 24 * 30 * 1000, // 30 days (matching auth config) + Date.now() + SESSION_EXPIRY_MS, );apps/desktop/src/main/windows/main.ts (1)
53-55: Extract partition name to a named constant.Per coding guidelines, hardcoded string values should be extracted to named constants at the module top for maintainability.
🔎 Proposed refactor
At the top of the file (after imports):
+// Electron session partition for desktop auth isolation +const DESKTOP_SESSION_PARTITION = "persist:superset"; + // Singleton IPC handler to prevent duplicate handlers on window reopen (macOS) let ipcHandler: ReturnType<typeof createIPCHandler> | null = null;Then update the usage:
webPreferences: { preload: join(__dirname, "../preload/index.js"), webviewTag: true, // Isolate Electron session from system browser cookies // This ensures desktop uses bearer token auth, not web cookies - partition: "persist:superset", + partition: DESKTOP_SESSION_PARTITION, },
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
apps/desktop/src/main/windows/main.tsapps/web/src/app/auth/desktop/success/page.tsxpackages/auth/src/index.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Use object parameters for functions with 2+ parameters instead of positional arguments
Functions with 2+ parameters should accept a single params object with named properties for self-documentation and extensibility
Use prefixed console logging with context pattern: [domain/operation] message
Extract magic numbers and hardcoded values to named constants at module top
Use lookup objects/maps instead of repeated if (type === ...) conditionals
Avoid usinganytype - maintain type safety in TypeScript code
Never swallow errors silently - at minimum log them with context
Import from concrete files directly when possible - avoid barrel file abuse that creates circular dependencies
Avoid deep nesting (4+ levels) - use early returns, extract functions, and invert conditions
Use named properties in options objects instead of boolean parameters to avoid boolean blindness
Files:
apps/web/src/app/auth/desktop/success/page.tsxapps/desktop/src/main/windows/main.ts
**/*.tsx
📄 CodeRabbit inference engine (AGENTS.md)
One component per file - do not create multi-component files
Files:
apps/web/src/app/auth/desktop/success/page.tsx
apps/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use Drizzle ORM for all database operations - never use raw SQL
Files:
apps/web/src/app/auth/desktop/success/page.tsxapps/desktop/src/main/windows/main.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use Biome for formatting and linting - run at root level with
bun run lint:fixorbiome check --write
Files:
apps/web/src/app/auth/desktop/success/page.tsxapps/desktop/src/main/windows/main.ts
apps/desktop/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/desktop/AGENTS.md)
apps/desktop/**/*.{ts,tsx}: For Electron interprocess communication, ALWAYS use tRPC as defined insrc/lib/trpc
Use alias as defined intsconfig.jsonwhen possible
Prefer zustand for state management if it makes sense. Do not use effect unless absolutely necessary.
For tRPC subscriptions with trpc-electron, ALWAYS use the observable pattern from@trpc/server/observableinstead of async generators, as the library explicitly checksisObservable(result)and throws an error otherwise
Files:
apps/desktop/src/main/windows/main.ts
🧠 Learnings (8)
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to **/*.{ts,tsx} : Never swallow errors silently - at minimum log them with context
Applied to files:
apps/web/src/app/auth/desktop/success/page.tsx
📚 Learning: 2025-12-21T04:39:28.543Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: apps/desktop/AGENTS.md:0-0
Timestamp: 2025-12-21T04:39:28.543Z
Learning: Applies to apps/desktop/**/*.{ts,tsx} : Prefer zustand for state management if it makes sense. Do not use effect unless absolutely necessary.
Applied to files:
apps/desktop/src/main/windows/main.ts
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to apps/desktop/src/lib/electron-router-dom.ts : Do not import Node.js modules like node:path or dotenv in electron-router-dom.ts and similar shared files - they run in both main and renderer processes
Applied to files:
apps/desktop/src/main/windows/main.ts
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to apps/desktop/src/renderer/**/*.{ts,tsx} : Never import Node.js modules (fs, path, os, net) in renderer process or shared code - they are externalized for browser compatibility
Applied to files:
apps/desktop/src/main/windows/main.ts
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to apps/desktop/src/lib/*.ts : Never import Node.js modules in shared code like electron-router-dom.ts - it runs in both main and renderer processes
Applied to files:
apps/desktop/src/main/windows/main.ts
📚 Learning: 2025-12-21T04:39:28.543Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: apps/desktop/AGENTS.md:0-0
Timestamp: 2025-12-21T04:39:28.543Z
Learning: Applies to apps/desktop/**/*.{ts,tsx} : For Electron interprocess communication, ALWAYS use tRPC as defined in `src/lib/trpc`
Applied to files:
apps/desktop/src/main/windows/main.ts
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to apps/desktop/src/shared/ipc-channels.ts : Define IPC channel types in apps/desktop/src/shared/ipc-channels.ts before implementing handlers
Applied to files:
apps/desktop/src/main/windows/main.ts
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to apps/desktop/src/main/index.ts : Load environment variables from monorepo root .env in desktop app with override: true before any imports in src/main/index.ts and electron.vite.config.ts
Applied to files:
apps/desktop/src/main/windows/main.ts
🧬 Code graph analysis (1)
apps/web/src/app/auth/desktop/success/page.tsx (1)
packages/auth/src/index.ts (1)
auth(11-101)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: Deploy Docs
- GitHub Check: Deploy API
- GitHub Check: Deploy Web
- GitHub Check: Deploy Marketing
- GitHub Check: Deploy Admin
- GitHub Check: Build
🔇 Additional comments (6)
apps/web/src/app/auth/desktop/success/page.tsx (4)
1-6: LGTM!Imports are well-organized, using concrete file imports and the shared auth/db packages appropriately.
8-24: LGTM!Good use of early return pattern for validation. The typed
searchParamsasPromiseis correct for Next.js 15 server components.
80-88: LGTM!URL construction is secure with proper
encodeURIComponentusage on all parameters. The protocol switching between development and production environments is handled correctly.
30-40: Network error vs authentication failure handling remains unaddressed.The catch block treats all errors uniformly as authentication failures. Per PR objectives, this regression was explicitly flagged: the previous implementation distinguished network errors from invalid tokens to preserve sessions during transient network failures.
This breaks offline-first functionality—users on unstable connections would be forced to re-authenticate repeatedly.
Likely an incorrect or invalid review comment.
apps/desktop/src/main/windows/main.ts (2)
5-5: LGTM!The
screenimport is necessary for the new dynamic window sizing functionality.
166-175: LGTM!The close handler properly tears down the notifications server, clears listeners, and detaches the IPC handler to prevent resource leaks and duplicate registrations on window reopen (macOS). The removal of window state saving is consistent with the broader changes to eliminate the window-state module.
| export async function MainWindow() { | ||
| const savedWindowState = loadWindowState(); | ||
| const initialBounds = getInitialWindowBounds(savedWindowState); | ||
| const { width, height } = screen.getPrimaryDisplay().workAreaSize; |
There was a problem hiding this comment.
Add error handling for screen API to prevent startup crashes.
screen.getPrimaryDisplay() can throw in edge cases (no display available, headless systems, display disconnection). Without error handling, this will crash the app on startup with no recovery path.
🔎 Proposed fix with fallback dimensions
+// Fallback dimensions if screen API fails
+const DEFAULT_WINDOW_WIDTH = 1200;
+const DEFAULT_WINDOW_HEIGHT = 800;
+
export async function MainWindow() {
- const { width, height } = screen.getPrimaryDisplay().workAreaSize;
+ let width = DEFAULT_WINDOW_WIDTH;
+ let height = DEFAULT_WINDOW_HEIGHT;
+
+ try {
+ const workAreaSize = screen.getPrimaryDisplay().workAreaSize;
+ width = workAreaSize.width;
+ height = workAreaSize.height;
+ } catch (error) {
+ console.error("[window/main] Failed to get primary display, using defaults:", error);
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const { width, height } = screen.getPrimaryDisplay().workAreaSize; | |
| // Fallback dimensions if screen API fails | |
| const DEFAULT_WINDOW_WIDTH = 1200; | |
| const DEFAULT_WINDOW_HEIGHT = 800; | |
| export async function MainWindow() { | |
| let width = DEFAULT_WINDOW_WIDTH; | |
| let height = DEFAULT_WINDOW_HEIGHT; | |
| try { | |
| const workAreaSize = screen.getPrimaryDisplay().workAreaSize; | |
| width = workAreaSize.width; | |
| height = workAreaSize.height; | |
| } catch (error) { | |
| console.error("[window/main] Failed to get primary display, using defaults:", error); | |
| } |
🤖 Prompt for AI Agents
In @apps/desktop/src/main/windows/main.ts around line 32, Wrap the call to
screen.getPrimaryDisplay().workAreaSize in a try/catch inside main.ts so the app
doesn't crash if the screen API throws; on error log the exception (or use
processLogger) and fall back to safe default dimensions (e.g., width=1024,
height=768) and continue startup, replacing the direct destructuring of { width,
height } with values from the try block or the fallback.
| // Create a new session record in the database | ||
| await db.insert(sessions).values({ | ||
| token, | ||
| userId: session.user.id, | ||
| expiresAt, | ||
| ipAddress, | ||
| userAgent, | ||
| activeOrganizationId: session.session.activeOrganizationId, | ||
| updatedAt: now, | ||
| }); |
There was a problem hiding this comment.
Wrap database insert in try-catch for graceful error handling.
If db.insert(sessions) fails (connection issues, constraint violations, etc.), the page will crash with a generic Next.js error instead of showing a user-friendly message. Based on learnings, errors should never be swallowed silently and should be logged with context.
🔎 Suggested fix
// Create a new session record in the database
+ try {
await db.insert(sessions).values({
token,
userId: session.user.id,
expiresAt,
ipAddress,
userAgent,
activeOrganizationId: session.session.activeOrganizationId,
updatedAt: now,
});
+ } catch (error) {
+ console.error("[auth/desktop] Failed to create desktop session:", error);
+ return (
+ <div className="flex min-h-screen flex-col items-center justify-center bg-background p-4">
+ <p className="text-xl text-muted-foreground">Failed to create session</p>
+ <p className="text-muted-foreground/70">
+ Please try signing in again from the desktop app.
+ </p>
+ </div>
+ );
+ }🤖 Prompt for AI Agents
In @apps/web/src/app/auth/desktop/success/page.tsx around lines 70-79, Wrap the
db.insert(sessions).values(...) call in a try-catch to handle DB errors
gracefully: catch any error thrown by db.insert(sessions), log it with
contextual info (e.g., token, session.user.id,
session.session.activeOrganizationId) using the existing logger or
console.error, and then return or throw a controlled error/user-friendly
response instead of letting Next.js surface a generic crash; update the block
around the insert in page.tsx (the await db.insert(sessions).values({...})
statement) to perform this try-catch and logging.
This reverts commit 9ee8911. Reason: The Better Auth migration had unintended side effects: - Desktop app required database dependencies it doesn't need - Many desktop UI files (sidebar, workspace views) were accidentally deleted - Local-db migrations were removed that were already deployed to users We're reverting to unblock users and will re-implement Better Auth properly in a focused PR that only changes the auth implementation. Additional changes: - Reduced minimum desktop version from 0.0.44 to 0.0.39 to unblock existing users
This reverts commit 9ee8911. Reason: The Better Auth migration had unintended side effects: - Desktop app required database dependencies it doesn't need - Many desktop UI files (sidebar, workspace views) were accidentally deleted - Local-db migrations were removed that were already deployed to users We're reverting to unblock users and will re-implement Better Auth properly in a focused PR that only changes the auth implementation. Additional changes: - Reduced minimum desktop version from 0.0.44 to 0.0.39 to unblock existing users
This reverts commit 9ee8911. Reason: The Better Auth migration had unintended side effects: - Desktop app required database dependencies it doesn't need - Many desktop UI files (sidebar, workspace views) were accidentally deleted - Local-db migrations were removed that were already deployed to users We're reverting to unblock users and will re-implement Better Auth properly in a focused PR that only changes the auth implementation. Additional changes: - Reduced minimum desktop version from 0.0.44 to 0.0.39 to unblock existing users
Summary
authPostgreSQL schema with UUID columnsChanges
Auth System
Database
authschema with tables:users,sessions,accounts,verifications,organizations,members,invitationsgenerateId: false)Desktop App
Cleanup
Test plan
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Improvements
Removed
✏️ Tip: You can customize this high-level summary in your review settings.