-
Notifications
You must be signed in to change notification settings - Fork 172
refactor: update user context and authentication flow [auth revamp] #275
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
@huamanraj is attempting to deploy a commit to the AJEET PRATAP SINGH's projects Team on Vercel. A member of the Team first needs to authorize it. |
WalkthroughThis PR enriches user context with subscription information across the backend and frontend. It introduces subscription-aware authentication middleware, updates user type definitions to include active subscription details, enhances JWT/session handling to track paid status, and protects pro routes based on active subscription eligibility. Changes
Sequence DiagramsequenceDiagram
participant User as User (Client)
participant Middleware as Middleware
participant Session as NextAuth Session
participant API as TRPC API
participant DB as Database
rect rgb(220, 240, 255)
Note over User,DB: Pro Route Access Attempt
User->>Middleware: Request /dashboard/pro
Middleware->>Session: Check token & isPaidUser
alt No token
Middleware-->>User: Redirect to login
else Has token but not paid
Middleware-->>User: Redirect to /pricing
else Paid user
Middleware-->>User: Allow access
end
end
rect rgb(240, 255, 220)
Note over User,DB: Payment Success & Session Refresh
User->>API: Complete payment verification
API->>DB: Validate & create subscription
DB-->>API: Subscription created
API-->>User: Success
User->>Session: Call update() via PaymentFlow
Session->>API: GET /trpc/auth.getSession
API->>DB: Query user + active subscriptions + plan
DB-->>API: User with subscription data
API->>Session: Return enriched user (isPaidUser, subscription)
Session->>Session: Update JWT token & session
User->>User: router.refresh() & redirect to checkout
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes
Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
🧹 Nitpick comments (10)
apps/web/src/hooks/useSubscription.ts (1)
28-28: Avoid usinganytype cast.The
anytype cast on line 28 bypasses TypeScript's type checking. Consider properly typing the tRPC router or using type assertions with specific types to maintain type safety.Replace the
anycast with proper typing:- } = (trpc.user as any).subscriptionStatus.useQuery(undefined, { + } = trpc.user.subscriptionStatus.useQuery(undefined, {If the tRPC router types are not properly exported, ensure your
@/lib/trpcexports include the correct router type, or use a more specific type assertion.apps/api/src/utils/auth.ts (1)
19-19: Avoidanytype forcompletedSteps.Per coding guidelines, avoid
anytype. Define a proper type or useunknownwith type narrowing if the structure is truly dynamic.- completedSteps: any; + completedSteps: Record<string, boolean> | unknown;apps/web/src/lib/auth/config.ts (3)
60-61: Avoid casting toanyfor type safety.These casts bypass TypeScript's type checking. Consider extending the JWT type declarations in
apps/web/src/types/next-auth.d.tsto properly typeisPaidUserandsubscription.- const isPaidUser = (token.isPaidUser as boolean) || false; - const subscription = (token.subscription as any) || null; + const isPaidUser = token.isPaidUser ?? false; + const subscription = token.subscription ?? null;
89-93: Refresh logic may cause excessive API calls for free users.The condition
!token.isPaidUsermeans every token refresh for non-paid users triggers a getSession API call. Consider caching the result or adding a timestamp-based check to reduce load.const shouldRefresh = trigger === "update" || trigger === "signIn" || - !token.isPaidUser || token.isPaidUser === undefined;
97-103: Multipleanycasts indicate missing type definitions.The tempSession and trpc.auth casts suggest the tRPC client types don't match the expected session shape. Consider defining proper types for the authenticated client to maintain type safety throughout the auth flow.
apps/api/src/trpc.ts (2)
27-27: Redundant cast -verifyTokenalready returnsUserWithSubscription.Since
verifyTokenis typed to returnPromise<UserWithSubscription>, this cast is unnecessary.- user: user as UserWithSubscription, + user,
46-53: Redundant subscription check.
isPaidUseris derived from!!activeSubscriptioninverifyToken, so checking both!user.isPaidUser || !user.subscriptionis redundant. One condition suffices.- if (!user.isPaidUser || !user.subscription) { + if (!user.isPaidUser) {apps/api/src/services/auth.service.ts (3)
29-29: Avoidanytype for prisma parameter.Per coding guidelines, avoid
anytype. ThePrismaClienttype is already imported but not used for the parameter.- async handleGoogleAuth(prisma: any, input: GoogleAuthInput) { + async handleGoogleAuth(prisma: PrismaClient, input: GoogleAuthInput) {
43-59: Duplicated subscription query logic.This subscription include block is nearly identical to the one in
apps/api/src/utils/auth.ts(lines 49-64). Consider extracting a shared helper function to reduce duplication and ensure consistency.// example: in a shared utility const activeSubscriptionInclude = { subscriptions: { where: { status: SUBSCRIPTION_STATUS.ACTIVE, endDate: { gte: new Date() }, }, orderBy: { startDate: "desc" as const }, take: 1, include: { plan: true }, }, };
230-230: Type the user parameter properly.Using
anyloses type safety. Import and useUserWithSubscriptionfrom../utils/auth.js.+import { type UserWithSubscription } from "../utils/auth.js"; + - getSession(user: any) { + getSession(user: UserWithSubscription) {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (13)
apps/api/src/context.ts(2 hunks)apps/api/src/index.ts(1 hunks)apps/api/src/routers/auth.ts(1 hunks)apps/api/src/services/auth.service.ts(3 hunks)apps/api/src/trpc.ts(3 hunks)apps/api/src/utils/auth.ts(1 hunks)apps/web/src/components/checkout/CheckoutWrapper.tsx(1 hunks)apps/web/src/components/payment/PaymentFlow.tsx(2 hunks)apps/web/src/hooks/useSubscription.ts(1 hunks)apps/web/src/lib/auth/config.ts(4 hunks)apps/web/src/lib/auth/protected-routes.ts(1 hunks)apps/web/src/middleware.ts(2 hunks)apps/web/src/types/next-auth.d.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (27)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx,js,jsx}: Always use lowercase when writing comments
Avoid unnecessary comments; code should be self-documenting when possible
Use comments to explain 'why', not 'what'
Remove unused imports
Use UPPER_SNAKE_CASE for constants
Use camelCase for functions and variables
Files:
apps/api/src/index.tsapps/web/src/components/checkout/CheckoutWrapper.tsxapps/web/src/lib/auth/protected-routes.tsapps/web/src/components/payment/PaymentFlow.tsxapps/api/src/utils/auth.tsapps/api/src/routers/auth.tsapps/web/src/middleware.tsapps/api/src/trpc.tsapps/api/src/services/auth.service.tsapps/web/src/hooks/useSubscription.tsapps/web/src/lib/auth/config.tsapps/web/src/types/next-auth.d.tsapps/api/src/context.ts
**/*.{tsx,ts}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{tsx,ts}: Prefer functional components with TypeScript and use proper TypeScript types, avoidany
Extract reusable logic into custom hooks
Use descriptive prop names and define prop types using TypeScript interfaces or types
Prefer controlled components over uncontrolled
Use zustand for global state (located insrc/store/)
Use absolute imports from@/prefix when available
Include proper aria labels for accessibility
Ensure keyboard navigation works
Maintain proper heading hierarchy
Provide alt text for images
Avoid unnecessary re-renders
Files:
apps/api/src/index.tsapps/web/src/components/checkout/CheckoutWrapper.tsxapps/web/src/lib/auth/protected-routes.tsapps/web/src/components/payment/PaymentFlow.tsxapps/api/src/utils/auth.tsapps/api/src/routers/auth.tsapps/web/src/middleware.tsapps/api/src/trpc.tsapps/api/src/services/auth.service.tsapps/web/src/hooks/useSubscription.tsapps/web/src/lib/auth/config.tsapps/web/src/types/next-auth.d.tsapps/api/src/context.ts
**/*.{tsx,ts,jsx,js}
📄 CodeRabbit inference engine (.cursorrules)
Organize imports: react → third-party → local components → utils → types
Files:
apps/api/src/index.tsapps/web/src/components/checkout/CheckoutWrapper.tsxapps/web/src/lib/auth/protected-routes.tsapps/web/src/components/payment/PaymentFlow.tsxapps/api/src/utils/auth.tsapps/api/src/routers/auth.tsapps/web/src/middleware.tsapps/api/src/trpc.tsapps/api/src/services/auth.service.tsapps/web/src/hooks/useSubscription.tsapps/web/src/lib/auth/config.tsapps/web/src/types/next-auth.d.tsapps/api/src/context.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{ts,tsx}: Use PascalCase for types and interfaces with descriptive names
Use dynamic imports for code splitting when appropriate
Memoize expensive computations
Files:
apps/api/src/index.tsapps/web/src/components/checkout/CheckoutWrapper.tsxapps/web/src/lib/auth/protected-routes.tsapps/web/src/components/payment/PaymentFlow.tsxapps/api/src/utils/auth.tsapps/api/src/routers/auth.tsapps/web/src/middleware.tsapps/api/src/trpc.tsapps/api/src/services/auth.service.tsapps/web/src/hooks/useSubscription.tsapps/web/src/lib/auth/config.tsapps/web/src/types/next-auth.d.tsapps/api/src/context.ts
**/*.{js,jsx,ts,tsx,py,java,go,rb,php}
📄 CodeRabbit inference engine (.cursor/rules/general_rules.mdc)
**/*.{js,jsx,ts,tsx,py,java,go,rb,php}: Always use lowercase when writing comments
Avoid unnecessary comments; code should be self-documenting when possible
Use comments to explain 'why', not 'what'
Files:
apps/api/src/index.tsapps/web/src/components/checkout/CheckoutWrapper.tsxapps/web/src/lib/auth/protected-routes.tsapps/web/src/components/payment/PaymentFlow.tsxapps/api/src/utils/auth.tsapps/api/src/routers/auth.tsapps/web/src/middleware.tsapps/api/src/trpc.tsapps/api/src/services/auth.service.tsapps/web/src/hooks/useSubscription.tsapps/web/src/lib/auth/config.tsapps/web/src/types/next-auth.d.tsapps/api/src/context.ts
apps/api/src/**/*.{js,ts}
📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)
apps/api/src/**/*.{js,ts}: Log errors with context (userId, endpoint, timestamp) for debugging
Always await async operations; never forget to handle promise rejections
Never log sensitive information (passwords, tokens, credit card numbers)
Files:
apps/api/src/index.tsapps/api/src/utils/auth.tsapps/api/src/routers/auth.tsapps/api/src/trpc.tsapps/api/src/services/auth.service.tsapps/api/src/context.ts
apps/api/src/**/*.ts
📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)
Avoid
anytype; useunknownfor truly dynamic data and narrow with type guards
Files:
apps/api/src/index.tsapps/api/src/utils/auth.tsapps/api/src/routers/auth.tsapps/api/src/trpc.tsapps/api/src/services/auth.service.tsapps/api/src/context.ts
apps/api/src/{index,main,app,server}.{js,ts}
📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)
Validate required environment variables on startup; fail fast if missing
Files:
apps/api/src/index.ts
apps/web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
apps/web/src/**/*.{ts,tsx}: Always follow the design system defined inapps/web/src/lib/design-tokens.tsandapps/web/tailwind.config.ts
NEVER use hardcoded hex values (e.g.,#5519f7) directly in components; ALWAYS reference colors from the design token system using Tailwind classes
Use semantic color names that describe purpose, not appearance
Usefont-sansfor standard UI text (Geist Sans) andfont-monofor code, technical content, or monospace needs (DM Mono)
Follow Tailwind's spacing scale (0.25rem increments); for section padding use mobilep-4(1rem) and desktopp-[60px]
Use appropriate border radius: small elementsrounded-lg, mediumrounded-xl, largerounded-2xl, buttonsrounded-[16px]
Use animation durations: fastduration-100(0.1s), normalduration-300(0.3s), slowduration-600(0.6s)
Files:
apps/web/src/components/checkout/CheckoutWrapper.tsxapps/web/src/lib/auth/protected-routes.tsapps/web/src/components/payment/PaymentFlow.tsxapps/web/src/middleware.tsapps/web/src/hooks/useSubscription.tsapps/web/src/lib/auth/config.tsapps/web/src/types/next-auth.d.ts
**/*[A-Z]*.{tsx,ts}
📄 CodeRabbit inference engine (.cursorrules)
Use PascalCase for component file names (e.g.,
UserProfile.tsx)
Files:
apps/web/src/components/checkout/CheckoutWrapper.tsxapps/web/src/components/payment/PaymentFlow.tsxapps/web/src/hooks/useSubscription.ts
apps/web/src/**/*.{tsx,ts}
📄 CodeRabbit inference engine (.cursorrules)
Optimize images using next/image
apps/web/src/**/*.{tsx,ts}: Use Zustand for global state, located insrc/store/
Use PascalCase for types and interfaces with descriptive names
Use dynamic imports for code splitting when appropriate
Optimize images using next/image
Memoize expensive computations
Define a type when defining const functions
Files:
apps/web/src/components/checkout/CheckoutWrapper.tsxapps/web/src/lib/auth/protected-routes.tsapps/web/src/components/payment/PaymentFlow.tsxapps/web/src/middleware.tsapps/web/src/hooks/useSubscription.tsapps/web/src/lib/auth/config.tsapps/web/src/types/next-auth.d.ts
apps/web/src/components/**/*.{tsx,ts,jsx,js}
📄 CodeRabbit inference engine (apps/web/.cursor/rules/frontend_rules.mdc)
apps/web/src/components/**/*.{tsx,ts,jsx,js}: Never use hardcoded hex values directly in components; always reference colors from the design token system using Tailwind classes
Use semantic color names from the design token system that describe purpose, not appearance (e.g., bg-brand-purple, bg-surface-primary, text-text-primary)
Use font-sans for standard UI text (Geist Sans) and font-mono for code, technical content, or monospace needs (DM Mono)
Follow Tailwind's spacing scale for section padding: p-4 (1rem) on mobile, p-[60px] on desktop
Use rounded-lg (0.5rem) for small elements, rounded-xl (1rem) for medium elements, rounded-2xl (1.5rem) for large elements, and rounded-[16px] for buttons
Use duration-100 (0.1s) for fast transitions, duration-300 (0.3s) for normal transitions, and duration-600 (0.6s) for slow transitions
Use available custom animations: animate-accordion-down, animate-accordion-up, animate-scrollRight, animate-scrollLeft, animate-customspin, animate-spin-slow, animate-spin-slow-reverse, animate-marquee, animate-marquee-vertical, animate-shine
Prefer functional components with TypeScript
Extract reusable logic into custom hooks
Prefer controlled components over uncontrolled
Include proper aria labels for accessibility
Ensure keyboard navigation works in interactive components
Maintain proper heading hierarchy in page components
Provide alt text for images
Use 'class:' instead of the ternary operator in class tags whenever possible
Implement accessibility features on interactive elements (e.g., tabindex='0', aria-label, onClick, onKeyDown)
Always follow the design system defined inapps/web/src/lib/design-tokens.tsandapps/web/tailwind.config.ts
Files:
apps/web/src/components/checkout/CheckoutWrapper.tsxapps/web/src/components/payment/PaymentFlow.tsx
apps/web/src/components/**/*.{tsx,ts}
📄 CodeRabbit inference engine (apps/web/.cursor/rules/frontend_rules.mdc)
apps/web/src/components/**/*.{tsx,ts}: Use proper TypeScript types and avoid usinganytype
Use descriptive prop names and define prop types using TypeScript interfaces or types
Name components using PascalCase (e.g., UserProfile.tsx)
Files:
apps/web/src/components/checkout/CheckoutWrapper.tsxapps/web/src/components/payment/PaymentFlow.tsx
apps/web/src/**/*.{tsx,ts,jsx,js}
📄 CodeRabbit inference engine (apps/web/.cursor/rules/frontend_rules.mdc)
apps/web/src/**/*.{tsx,ts,jsx,js}: Organize imports in order: React → third-party → local components → utils → types
Use absolute imports from@/prefix when available
Remove unused imports
Use UPPER_SNAKE_CASE for constants
Use camelCase for functions and variables
Always use Tailwind classes for styling HTML elements; avoid using CSS or style tags
Use descriptive variable and function names; name event functions with a 'handle' prefix (e.g., handleClick, handleKeyDown)
Use const with arrow functions instead of function declarations (e.g., 'const toggle = () =>')
Files:
apps/web/src/components/checkout/CheckoutWrapper.tsxapps/web/src/lib/auth/protected-routes.tsapps/web/src/components/payment/PaymentFlow.tsxapps/web/src/middleware.tsapps/web/src/hooks/useSubscription.tsapps/web/src/lib/auth/config.tsapps/web/src/types/next-auth.d.ts
**/src/{lib,utils}/**
📄 CodeRabbit inference engine (.cursorrules)
Use kebab-case or camelCase for utility file names
Files:
apps/web/src/lib/auth/protected-routes.tsapps/api/src/utils/auth.tsapps/web/src/lib/auth/config.ts
apps/web/src/{lib,utils}/**/*.{tsx,ts,jsx,js}
📄 CodeRabbit inference engine (apps/web/.cursor/rules/frontend_rules.mdc)
Name files and folders using kebab-case or camelCase for utilities
Files:
apps/web/src/lib/auth/protected-routes.tsapps/web/src/lib/auth/config.ts
apps/api/src/**/{middleware,logging,utils}/*.{js,ts}
📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)
Include correlation IDs in logs for request tracing across services
Files:
apps/api/src/utils/auth.ts
apps/api/src/**/{logging,middleware,utils}/*.{js,ts}
📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)
Use structured logging (JSON format) for easier parsing and analysis
Files:
apps/api/src/utils/auth.ts
apps/api/src/**/{services,models,database}/*.{js,ts}
📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)
Encrypt sensitive data (passwords, tokens, API keys) before storing in database
Files:
apps/api/src/services/auth.service.ts
apps/api/src/**/{database,services}/*.{js,ts}
📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)
Handle database connection failures gracefully with retry logic
Files:
apps/api/src/services/auth.service.ts
apps/api/src/**/{database,models,services}/*.{js,ts}
📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)
Always use parameterized queries or ORM methods to prevent SQL injection
Files:
apps/api/src/services/auth.service.ts
apps/api/src/**/{services,database}/*.{js,ts}
📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)
Wrap database transactions in try-catch blocks with proper rollback on failure
Files:
apps/api/src/services/auth.service.ts
apps/api/src/**/{services,repositories}/*.{js,ts}
📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)
Avoid N+1 queries; use eager loading or batch queries when fetching related data
Files:
apps/api/src/services/auth.service.ts
apps/api/src/**/{services,models}/*.{js,ts}
📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)
Validate data against schema before database operations
Files:
apps/api/src/services/auth.service.ts
apps/api/src/**/{database,clients,services}/*.{js,ts}
📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)
Use connection pooling for database and external service clients
Files:
apps/api/src/services/auth.service.ts
apps/api/src/**/{services,clients,handlers}/*.{js,ts}
📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)
Implement timeouts for external API calls to prevent hanging requests
Files:
apps/api/src/services/auth.service.ts
apps/api/src/**/{middleware,services,routes,controllers}/*.{js,ts}
📄 CodeRabbit inference engine (apps/api/.cursor/rules/backend_rules.mdc)
Log all critical operations (auth attempts, payment processing, data mutations)
Files:
apps/api/src/services/auth.service.ts
🧠 Learnings (3)
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{routes,controllers,handlers,middleware}/*.{js,ts} : Always validate user authentication before processing protected routes
Applied to files:
apps/web/src/lib/auth/protected-routes.tsapps/api/src/routers/auth.tsapps/web/src/middleware.tsapps/api/src/trpc.ts
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{routes,middleware}/*.{js,ts} : Use middleware for auth checks; never duplicate auth logic in route handlers
Applied to files:
apps/web/src/lib/auth/protected-routes.tsapps/web/src/middleware.tsapps/api/src/trpc.ts
📚 Learning: 2025-11-25T07:34:58.984Z
Learnt from: CR
Repo: apsinghdev/opensox PR: 0
File: apps/api/.cursor/rules/backend_rules.mdc:0-0
Timestamp: 2025-11-25T07:34:58.984Z
Learning: Applies to apps/api/src/**/{routes,middleware}/*.{js,ts} : Implement rate limiting on all public endpoints to prevent abuse
Applied to files:
apps/web/src/lib/auth/protected-routes.tsapps/web/src/middleware.ts
🧬 Code graph analysis (10)
apps/api/src/index.ts (2)
apps/web/src/components/checkout/checkout-confirmation.tsx (1)
isJoining(20-62)apps/web/src/app/(main)/dashboard/pro/dashboard/page.tsx (1)
ProDashboardPage(8-126)
apps/web/src/components/checkout/CheckoutWrapper.tsx (3)
apps/web/src/hooks/useSubscription.ts (1)
useSubscription(11-77)apps/web/src/lib/auth/config.ts (1)
session(59-73)apps/web/src/app/(main)/dashboard/account/page.tsx (2)
AccountPage(58-72)AccountPageContent(9-56)
apps/web/src/components/payment/PaymentFlow.tsx (1)
apps/web/src/lib/auth/config.ts (1)
session(59-73)
apps/api/src/utils/auth.ts (2)
apps/web/src/lib/auth/config.ts (1)
jwt(75-116)apps/api/src/constants/subscription.ts (1)
SUBSCRIPTION_STATUS(5-14)
apps/api/src/routers/auth.ts (1)
apps/api/src/services/auth.service.ts (1)
authService(24-245)
apps/web/src/middleware.ts (1)
apps/web/src/lib/auth/protected-routes.ts (1)
PROTECTED_PRO_ROUTES(6-9)
apps/api/src/trpc.ts (1)
apps/api/src/utils/auth.ts (1)
UserWithSubscription(12-28)
apps/api/src/services/auth.service.ts (2)
apps/api/src/constants/subscription.ts (1)
SUBSCRIPTION_STATUS(5-14)apps/api/src/utils/auth.ts (1)
generateToken(30-32)
apps/web/src/lib/auth/config.ts (2)
apps/web/src/lib/trpc.ts (1)
trpc(4-4)apps/web/src/lib/trpc-server.ts (1)
createAuthenticatedClient(24-42)
apps/api/src/context.ts (1)
apps/api/src/utils/auth.ts (1)
UserWithSubscription(12-28)
🔇 Additional comments (14)
apps/web/src/hooks/useSubscription.ts (1)
30-33: Verify aggressive refetch settings are intentional; also address pre-existinganytype cast.The combination of
refetchOnWindowFocus: truewith a 30-secondstaleTimewill trigger a refetch every time the user switches back to the tab (if more than 30 seconds have passed). While subscription status is critical for access control, this configuration is notably more aggressive than other queries in the codebase (e.g., the default 60-secondstaleTimein trpc-provider, or the getCompletedStepsProcedure query which usesrefetchOnWindowFocus: falsewith 5-minutestaleTime).Confirm whether:
- This aggressive refetching is necessary for real-time subscription enforcement
- A longer
staleTime(e.g., 5 minutes) would be sufficientrefetchOnWindowFocusshould befalseor conditional based on the subscription stateAdditionally, line 28 uses
anytype cast on(trpc.user as any), which violates the guideline to avoidany. Replace with proper TypeScript typing.apps/web/src/lib/auth/protected-routes.ts (1)
6-9: LGTM!The new
PROTECTED_PRO_ROUTESconstant follows the same pattern asPROTECTED_DASHBOARD_ROUTESand properly uses const assertion for type safety.apps/web/src/middleware.ts (1)
20-22: LGTM!The
isProRoutecheck follows the same pattern as the existingisProtectedRoutecheck and correctly identifies pro routes.apps/api/src/index.ts (1)
121-125: LGTM! Efficiency improvement by using enriched user data.The change from a database subscription lookup to an in-memory check using
user.isPaidUseranduser.subscription(provided by the enrichedUserWithSubscriptiontype fromverifyToken) improves performance while maintaining security. This aligns with the PR's goal of enriching user context with subscription information.Based on learnings, the authentication is properly validated before processing this protected route.
apps/web/src/components/payment/PaymentFlow.tsx (1)
76-79: LGTM! Proper post-payment flow with session refresh.The updated flow correctly:
- Refreshes the session with
await update()to fetch the latest paid status- Navigates to the success page
- Triggers a router refresh to revalidate server components
This ensures the user's subscription status is immediately reflected across the application.
apps/web/src/components/checkout/CheckoutWrapper.tsx (1)
17-24: LGTM! Proper session refresh after payment success.The
useEffectcorrectly:
- Detects payment success via URL parameter
- Refreshes the session to fetch updated subscription data
- Uses
hasRefreshedflag to prevent multiple refreshes- Triggers router refresh to revalidate server-side data
The dependency array is complete, and the
hasRefreshedguard prevents infinite loops.apps/api/src/context.ts (2)
3-3: LGTM! Proper type alignment with enriched user payload.The import change from Prisma's
Userto the customUserWithSubscriptiontype correctly aligns the context type with the enriched user data that includes subscription information.
13-13: LGTM! Context type updated to support enriched user data.The context's
userfield now properly types toUserWithSubscription | null, supporting the subscription-aware authentication flow throughout the application.apps/web/src/types/next-auth.d.ts (2)
6-19: LGTM! Properly extends Session interface with subscription data.The Session interface extension correctly adds:
isPaidUserboolean flag for quick access checks- Detailed
subscriptionobject with all necessary fieldsThis aligns with the enriched authentication payload from the backend.
26-34: LGTM! JWT interface properly extended.The JWT interface extension mirrors the Session user fields, ensuring consistency across the authentication flow. This enables the JWT callback to pass subscription data from the token to the session.
apps/api/src/utils/auth.ts (1)
47-66: LGTM on subscription query logic.The Prisma query correctly filters for active subscriptions with valid end dates, orders by most recent, and limits to one result. The include for plan data is appropriate.
apps/web/src/lib/auth/config.ts (1)
21-35: LGTM on session and cookie configuration.JWT strategy with 7-day expiry aligns with the backend token expiry. Cookie configuration properly handles production security with
__Secure-prefix and secure flag.apps/api/src/trpc.ts (1)
38-61: Middleware approach for subscription checks is correct.Based on learnings, using middleware for auth checks avoids duplicating auth logic in route handlers. The requiresSubscription middleware properly chains after authentication and provides clear error messages.
apps/api/src/services/auth.service.ts (1)
65-87: LGTM on enriched user response structure.The response includes all necessary user fields plus subscription details with proper null handling. The
planNameinclusion from the related plan is a good addition for display purposes.
| getSession: protectedProcedure.query( | ||
| async ({ ctx }: { ctx: { user: any } }) => { | ||
| return authService.getSession(ctx.user); | ||
| const userId = ctx.user.id; | ||
| const user = await ctx.db.prisma.user.findUnique({ | ||
| where: { id: userId }, | ||
| include: { | ||
| subscriptions: { | ||
| where: { | ||
| status: "active", | ||
| endDate: { | ||
| gte: new Date(), | ||
| }, | ||
| }, | ||
| orderBy: { | ||
| startDate: "desc", | ||
| }, | ||
| take: 1, | ||
| include: { | ||
| plan: true, | ||
| }, | ||
| }, | ||
| }, | ||
| }); | ||
|
|
||
| if (!user) { | ||
| throw new TRPCError({ | ||
| code: "NOT_FOUND", | ||
| message: "User not found", | ||
| }); | ||
| } | ||
|
|
||
| const activeSubscription = user.subscriptions[0] || null; | ||
|
|
||
| return authService.getSession({ | ||
| ...user, | ||
| isPaidUser: !!activeSubscription, | ||
| subscription: activeSubscription | ||
| ? { | ||
| id: activeSubscription.id, | ||
| status: activeSubscription.status, | ||
| startDate: activeSubscription.startDate, | ||
| endDate: activeSubscription.endDate, | ||
| planId: activeSubscription.planId, | ||
| planName: activeSubscription.plan?.name, | ||
| } | ||
| : null, | ||
| }); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
rg -nP '\bSUBSCRIPTION_STATUS\b' --type=ts -A2Repository: apsinghdev/opensox
Length of output: 2348
🏁 Script executed:
# Check the full auth.ts file to see type definitions and context
fd auth.ts --type f | head -5Repository: apsinghdev/opensox
Length of output: 117
🏁 Script executed:
# Search for subscription status constants or enums
rg -n 'status.*active' apps/api/src --type=ts -B2 -A2 | head -50Repository: apsinghdev/opensox
Length of output: 1508
🏁 Script executed:
# Get the full context of the auth.ts file to see type definitions
head -70 apps/api/src/routers/auth.tsRepository: apsinghdev/opensox
Length of output: 2453
🏁 Script executed:
# Check the SUBSCRIPTION_STATUS constant definition
cat -n apps/api/src/constants/subscription.tsRepository: apsinghdev/opensox
Length of output: 1275
🏁 Script executed:
# Check if there's a ProtectedProcedure type or context type definition
rg -n 'protectedProcedure\|type.*Context' apps/api/src/routers/auth.ts -B2 -A2Repository: apsinghdev/opensox
Length of output: 44
🏁 Script executed:
# Check how protectedProcedure is defined and what context type it has
cat -n apps/api/src/trpc.ts | head -100Repository: apsinghdev/opensox
Length of output: 2138
🏁 Script executed:
# Check if there are type definitions for the user object anywhere
rg -n 'type.*User\|interface.*User' apps/api/src/routers/auth.ts -B2 -A5Repository: apsinghdev/opensox
Length of output: 44
🏁 Script executed:
# Check if SUBSCRIPTION_STATUS is imported in auth.ts
head -20 apps/api/src/routers/auth.ts | grep -n importRepository: apsinghdev/opensox
Length of output: 321
Use the SUBSCRIPTION_STATUS constant instead of hardcoded string and replace any type with proper typing.
The code should use the SUBSCRIPTION_STATUS.ACTIVE constant (defined in constants/subscription.ts) instead of the hardcoded string "active" at line 77, consistent with the rest of the codebase (utils/auth.ts, services/auth.service.ts, services/user.service.ts, services/payment.service.ts).
Also, replace ctx: { user: any } with the proper UserWithSubscription type available from utils/auth.js and already imported in trpc.ts. The any type violates the API coding guideline that requires avoiding any type.
Required changes:
- Import
SUBSCRIPTION_STATUSfromconstants/subscription.js - Import
UserWithSubscriptiontype fromutils/auth.js - Change line 77 from
status: "active"tostatus: SUBSCRIPTION_STATUS.ACTIVE - Change line 70 from
{ ctx: { user: any } }to{ ctx: { user: UserWithSubscription } }
🤖 Prompt for AI Agents
In apps/api/src/routers/auth.ts around lines 69 to 116, replace the inline any
type and hardcoded subscription string: change the function signature param from
"{ ctx: { user: any } }" to "{ ctx: { user: UserWithSubscription } }", import
the UserWithSubscription type from utils/auth.js (or .ts), import
SUBSCRIPTION_STATUS from constants/subscription.js (or .ts), and replace the
subscription where clause status: "active" with status:
SUBSCRIPTION_STATUS.ACTIVE; ensure imports are added at the top of the file and
that TypeScript resolves the type import correctly.
| export const protectedProcedure = t.procedure.use(isAuthed) as any; | ||
| export const proProcedure = protectedProcedure.use(requiresSubscription) as any; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
🧩 Analysis chain
🏁 Script executed:
cat -n apps/api/src/trpc.ts | head -80Repository: apsinghdev/opensox
Length of output: 2138
🏁 Script executed:
# Check the Context type definition
rg -A 10 "type Context" apps/api/src/context.ts
# Check how these procedures are used
rg "protectedProcedure|proProcedure" apps/api/src --type tsRepository: apsinghdev/opensox
Length of output: 1253
🏁 Script executed:
cat -n apps/api/src/context.tsRepository: apsinghdev/opensox
Length of output: 886
🏁 Script executed:
# Check if there are any type-related imports from @trpc/server
rg "@trpc/server" apps/api/src/trpc.ts -A 5
# Search for ProcedureBuilder or similar type patterns
rg "ProcedureBuilder|Procedure<" apps/api/src --type ts
# Check TRPC version in package.json
cat apps/api/package.json | grep -A 2 "@trpc"Repository: apsinghdev/opensox
Length of output: 393
🏁 Script executed:
# Get full package.json @trpc section
cat apps/api/package.json | grep -A 20 '"@trpc'Repository: apsinghdev/opensox
Length of output: 361
🏁 Script executed:
# Search for any TRPC type utilities or middleware patterns in the codebase
rg "middleware|context" apps/api/src --type ts -B 2 -A 2 | head -60
# Check if there are any other procedureBuilders or typed procedures
rg "procedure.*use|Procedure" apps/api/src --type ts | head -20Repository: apsinghdev/opensox
Length of output: 3926
🏁 Script executed:
# Check how procedures are actually used in one of the routers to see the type impact
cat -n apps/api/src/routers/user.ts | head -50Repository: apsinghdev/opensox
Length of output: 1517
🏁 Script executed:
# Check if there's any type documentation or comments about this in the codebase
rg "TRPC|middleware|context" apps/api/src --type ts -B 1 | grep -i "type\|TODO\|FIXME" | head -20
# Look for any utility files that might help with typing
fd "type\|util" apps/api/src --type tsRepository: apsinghdev/opensox
Length of output: 314
🏁 Script executed:
# Search for utils directory
fd -t f "\.ts$" apps/api/src/utils/ | head -20
# Check if there are any typing utilities or helpers
rg "type.*Procedure|Builder" apps/api/src --type-add "ts:*.ts" -t tsRepository: apsinghdev/opensox
Length of output: 121
🏁 Script executed:
# Let me check the exact TypeScript error by looking for any tsconfig and running a type check
cat apps/api/tsconfig.json
# Also check if there's a build or type check script
cat apps/api/package.json | grep -A 10 '"scripts"'Repository: apsinghdev/opensox
Length of output: 1514
🏁 Script executed:
# Let me check what specific type issue arises by examining the TRPC types more closely
# First, check if there are any other type-related patterns in the codebase
rg "as.*ProcedureBuilder|as.*Procedure" apps/api/src
# Check the full routers to understand how the context is actually used
cat -n apps/api/src/routers/auth.ts | head -40Repository: apsinghdev/opensox
Length of output: 1697
🏁 Script executed:
# Let me search for any patterns in the codebase that might show a better way to handle this
rg "ProcedureBuilder|BuildProcedure" apps/api
# Also check if there are any type annotations or helpers defined elsewhere
fd -type f -name "*.ts" -path "*/api/src/*" | xargs grep -l "type.*Procedure" 2>/dev/null
# Check auth.ts to see the UserWithSubscription type definition
cat -n apps/api/src/utils/auth.ts | head -30Repository: apsinghdev/opensox
Length of output: 1192
🌐 Web query:
TRPC middleware context narrowing type safety @trpc/server
💡 Result:
Short answer
- Define a broad base context with initTRPC.context().
- In a middleware, return opts.next({ ctx: { ... } }) with the narrowed properties added/overridden; downstream middlewares/procedures will see the refined type. Use middleware chaining (.use / .pipe / unstable_pipe or .concat for reusable plugins) to compose further narrowing. See tRPC docs for examples. [1][2]
Minimal example (v10/v11 style)
import { initTRPC, TRPCError } from '@trpc/server';
const t = initTRPC.context<{ user?: { id: string } }>().create();
const isAuthed = t.middleware(async (opts) => {
const { ctx } = opts;
if (!ctx.user) throw new TRPCError({ code: 'UNAUTHORIZED' });
// return next with a narrowed ctx (user is now non-nullable)
return opts.next({ ctx: { user: ctx.user } });
});
const protectedProcedure = t.procedure.use(isAuthed).query(({ ctx }) => {
// ctx.user is typed as { id: string } (non-nullable) here
return ctx.user.id;
});
References
- tRPC middlewares / context extension and middleware chaining. [1]
- tRPC context typing via initTRPC.context(). [2]
🏁 Script executed:
# Let me check if there's a way to properly type these procedures using TRPC's type system
# Look for any type parameter usage with procedure or middleware
rg "\.use<\|Procedure<" apps/api/src
# Check if there's an initTRPC pattern that allows for proper typing
rg "initTRPC\." apps/api/src -A 3Repository: apsinghdev/opensox
Length of output: 223
🏁 Script executed:
# Let me check if there's a way to create a custom typed procedure builder
# Look for any advanced patterns or if there's a typesafe alternative
rg "createCallerFactory|createServerSideHelpers" apps/api/src
# Check if there's any documentation or commented code about this
rg "TODO|FIXME|NOTE" apps/api/src/trpc.tsRepository: apsinghdev/opensox
Length of output: 44
Remove as any casts and properly type context narrowing through middleware.
The as any casts on lines 65-66 violate the type safety guidelines and prevent TypeScript from catching errors in consumers. TRPC v11.7.2 supports proper context narrowing through middleware chaining—refactor to define the narrowed context types explicitly (consider creating a custom typed procedure builder or using TRPC's middleware context propagation without type assertions).
🤖 Prompt for AI Agents
In apps/api/src/trpc.ts around lines 65-66, remove the `as any` casts and
instead surface the narrowed context types from the middlewares: update the
middleware signatures to return a typed context (e.g., isAuthed should return
ctx + { user: User } and requiresSubscription should return ctx + {
subscription: Subscription }), then build typed procedures by using
t.procedure.use(isAuthed) and
t.procedure.use(isAuthed).use(requiresSubscription) while preserving TypeScript
generics so the resulting protectedProcedure and proProcedure inherit the
narrowed ctx types; if helpful create a small typedProcedure builder (or
explicit Procedure type aliases) that supplies the middleware-returned Context
type to avoid assertions and ensure callers get correct compile-time types.
| } catch (error) { | ||
| throw new Error("Token verification failed"); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Error context is lost when re-throwing.
The catch block swallows the original error, making debugging difficult. Per coding guidelines, log errors with context for debugging.
} catch (error) {
+ console.error("Token verification failed:", { error, tokenPrefix: token.substring(0, 10) });
throw new Error("Token verification failed");
}Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In apps/api/src/utils/auth.ts around lines 93 to 95, the catch block currently
throws a new Error("Token verification failed") and discards the original error;
change it to preserve and surface the original error by logging it with context
(e.g., auth token verification failed) using the project's logger and either
rethrow the original error or throw a new Error that includes the original error
message/stack so debugging information is retained.
| if (isProRoute) { | ||
| if (!token) { | ||
| const signInUrl = new URL("/login", req.url); | ||
| signInUrl.searchParams.set("callbackUrl", pathname); | ||
| return NextResponse.redirect(signInUrl); | ||
| } | ||
|
|
||
| const isPaidUser = (token as any).isPaidUser || false; | ||
|
|
||
| if (!isPaidUser) { | ||
| const pricingUrl = new URL("/pricing", req.url); | ||
| return NextResponse.redirect(pricingUrl); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid using any type; use proper TypeScript typing.
Line 37 uses (token as any).isPaidUser, which violates the coding guideline to avoid any types. Since apps/web/src/types/next-auth.d.ts extends the JWT interface with isPaidUser, you should properly type the token.
Apply this diff to use proper typing:
- const isPaidUser = (token as any).isPaidUser || false;
+ const isPaidUser = token?.isPaidUser || false;Alternatively, if more explicit typing is needed:
+import type { JWT } from "next-auth/jwt";
+
export async function middleware(req: NextRequest) {
- const token = await getToken({
+ const token = await getToken({
req,
secret: process.env.NEXTAUTH_SECRET,
- });
+ }) as JWT | null;
// ... later ...
- const isPaidUser = (token as any).isPaidUser || false;
+ const isPaidUser = token?.isPaidUser || false;As per coding guidelines: apps/web/src/**/*.ts should use proper TypeScript types and avoid any.
📝 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 (isProRoute) { | |
| if (!token) { | |
| const signInUrl = new URL("/login", req.url); | |
| signInUrl.searchParams.set("callbackUrl", pathname); | |
| return NextResponse.redirect(signInUrl); | |
| } | |
| const isPaidUser = (token as any).isPaidUser || false; | |
| if (!isPaidUser) { | |
| const pricingUrl = new URL("/pricing", req.url); | |
| return NextResponse.redirect(pricingUrl); | |
| } | |
| } | |
| if (isProRoute) { | |
| if (!token) { | |
| const signInUrl = new URL("/login", req.url); | |
| signInUrl.searchParams.set("callbackUrl", pathname); | |
| return NextResponse.redirect(signInUrl); | |
| } | |
| const isPaidUser = token?.isPaidUser || false; | |
| if (!isPaidUser) { | |
| const pricingUrl = new URL("/pricing", req.url); | |
| return NextResponse.redirect(pricingUrl); | |
| } | |
| } |
🤖 Prompt for AI Agents
In apps/web/src/middleware.ts around lines 30–43, replace the unsafe (token as
any).isPaidUser usage with a properly typed token: import the JWT type (or the
project's extended JWT interface) and cast token to that type (e.g., JWT & {
isPaidUser?: boolean } or the declared interface from
apps/web/src/types/next-auth.d.ts), then read isPaidUser from that typed object;
update imports if needed and ensure TypeScript no longer uses any.
in this revamp code @apsinghdev
works:
login signup
errors:
stuck at processing for payments
Summary by CodeRabbit
Release Notes
New Features
Improvements
✏️ Tip: You can customize this high-level summary in your review settings.