-
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?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,7 @@ | ||
| import { initTRPC, TRPCError } from "@trpc/server"; | ||
| import superjson from "superjson"; | ||
| import type { Context } from "./context.js"; | ||
| import { verifyToken } from "./utils/auth.js"; | ||
| import { verifyToken, type UserWithSubscription } from "./utils/auth.js"; | ||
|
|
||
| const t = initTRPC.context<Context>().create({ | ||
| transformer: superjson, | ||
|
|
@@ -24,7 +24,7 @@ const isAuthed = t.middleware(async ({ ctx, next }) => { | |
| return next({ | ||
| ctx: { | ||
| ...ctx, | ||
| user, | ||
| user: user as UserWithSubscription, | ||
| }, | ||
| }); | ||
| } catch (error) { | ||
|
|
@@ -35,6 +35,32 @@ const isAuthed = t.middleware(async ({ ctx, next }) => { | |
| } | ||
| }); | ||
|
|
||
| const requiresSubscription = t.middleware(async ({ ctx, next }) => { | ||
| if (!ctx.user) { | ||
| throw new TRPCError({ | ||
| code: "UNAUTHORIZED", | ||
| message: "Authentication required", | ||
| }); | ||
| } | ||
|
|
||
| const user = ctx.user as UserWithSubscription; | ||
|
|
||
| if (!user.isPaidUser || !user.subscription) { | ||
| throw new TRPCError({ | ||
| code: "FORBIDDEN", | ||
| message: "Active subscription required", | ||
| }); | ||
| } | ||
|
|
||
| return next({ | ||
| ctx: { | ||
| ...ctx, | ||
| user, | ||
| }, | ||
| }); | ||
| }); | ||
|
|
||
| export const router = t.router; | ||
| export const publicProcedure = t.procedure; | ||
| export const protectedProcedure:any = t.procedure.use(isAuthed); | ||
| export const protectedProcedure = t.procedure.use(isAuthed) as any; | ||
| export const proProcedure = protectedProcedure.use(requiresSubscription) as any; | ||
|
Comment on lines
+65
to
+66
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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:
💡 Result: Short answer
Minimal example (v10/v11 style) References
🏁 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 The 🤖 Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,33 +1,95 @@ | ||
| import jwt from "jsonwebtoken"; | ||
| import prisma from "../prisma.js"; | ||
| import prismaModule from "../prisma.js"; | ||
| import { SUBSCRIPTION_STATUS } from "../constants/subscription.js"; | ||
|
|
||
| const { prisma } = prismaModule; | ||
| const JWT_SECRET = process.env.JWT_SECRET! as string; | ||
|
|
||
| if (!process.env.JWT_SECRET) { | ||
| throw new Error("JWT_SECRET is not defined in the environment variables"); | ||
| } | ||
|
|
||
| export interface UserWithSubscription { | ||
| id: string; | ||
| email: string; | ||
| firstName: string; | ||
| authMethod: string; | ||
| createdAt: Date; | ||
| lastLogin: Date; | ||
| completedSteps: any; | ||
| isPaidUser: boolean; | ||
| subscription: { | ||
| id: string; | ||
| status: string; | ||
| startDate: Date; | ||
| endDate: Date | null; | ||
| planId: string; | ||
| } | null; | ||
| } | ||
|
|
||
| export const generateToken = (email: string): string => { | ||
| return jwt.sign({ email }, JWT_SECRET, { expiresIn: "7d" }); | ||
| }; | ||
|
|
||
| export const verifyToken = async (token: string) => { | ||
| export const verifyToken = async (token: string): Promise<UserWithSubscription> => { | ||
| try { | ||
| const decoded = jwt.verify(token, JWT_SECRET); | ||
|
|
||
| if (typeof decoded === "string") { | ||
| if (typeof decoded === "string" || !decoded || typeof decoded !== "object") { | ||
| throw new Error("Invalid token payload"); | ||
| } | ||
|
|
||
| const user = await prisma.prisma.user.findUnique({ | ||
| where: { email: decoded.email }, | ||
| const email = (decoded as { email?: string }).email; | ||
| if (!email) { | ||
| throw new Error("Email not found in token"); | ||
| } | ||
|
|
||
| const user = await prisma.user.findUnique({ | ||
| where: { email }, | ||
| include: { | ||
| subscriptions: { | ||
| where: { | ||
| status: SUBSCRIPTION_STATUS.ACTIVE, | ||
| endDate: { | ||
| gte: new Date(), | ||
| }, | ||
| }, | ||
| orderBy: { | ||
| startDate: "desc", | ||
| }, | ||
| take: 1, | ||
| include: { | ||
| plan: true, | ||
| }, | ||
| }, | ||
| }, | ||
| }); | ||
|
|
||
| if (!user) { | ||
| throw new Error("User not found"); | ||
| } | ||
|
|
||
| return user; | ||
| const activeSubscription = user.subscriptions[0] || null; | ||
|
|
||
| return { | ||
| id: user.id, | ||
| email: user.email, | ||
| firstName: user.firstName, | ||
| authMethod: user.authMethod, | ||
| createdAt: user.createdAt, | ||
| lastLogin: user.lastLogin, | ||
| completedSteps: user.completedSteps, | ||
| isPaidUser: !!activeSubscription, | ||
| subscription: activeSubscription | ||
| ? { | ||
| id: activeSubscription.id, | ||
| status: activeSubscription.status, | ||
| startDate: activeSubscription.startDate, | ||
| endDate: activeSubscription.endDate, | ||
| planId: activeSubscription.planId, | ||
| } | ||
| : null, | ||
| }; | ||
| } catch (error) { | ||
| throw new Error("Token verification failed"); | ||
| } | ||
|
Comment on lines
93
to
95
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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");
}
🤖 Prompt for AI Agents |
||
|
|
||
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:
Repository: apsinghdev/opensox
Length of output: 117
🏁 Script executed:
Repository: 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:
Repository: apsinghdev/opensox
Length of output: 44
🏁 Script executed:
Repository: apsinghdev/opensox
Length of output: 2138
🏁 Script executed:
Repository: apsinghdev/opensox
Length of output: 44
🏁 Script executed:
Repository: apsinghdev/opensox
Length of output: 321
Use the SUBSCRIPTION_STATUS constant instead of hardcoded string and replace
anytype with proper typing.The code should use the
SUBSCRIPTION_STATUS.ACTIVEconstant (defined inconstants/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 properUserWithSubscriptiontype available fromutils/auth.jsand already imported intrpc.ts. Theanytype violates the API coding guideline that requires avoidinganytype.Required changes:
SUBSCRIPTION_STATUSfromconstants/subscription.jsUserWithSubscriptiontype fromutils/auth.jsstatus: "active"tostatus: SUBSCRIPTION_STATUS.ACTIVE{ ctx: { user: any } }to{ ctx: { user: UserWithSubscription } }🤖 Prompt for AI Agents