-
Notifications
You must be signed in to change notification settings - Fork 1
feat: support guest session #8
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 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,13 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { redirect } from 'next/navigation'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { auth, signIn } from '@/app/(auth)/auth'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export async function GET() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const session = await auth(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!session?.user?.id) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await signIn('guest', { redirect: false }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
visz11 marked this conversation as resolved.
Show resolved
Hide resolved
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. Missing Error HandlingThe guest authentication call lacks error handling. Failed authentication attempts will cause unhandled promise rejections, potentially crashing the application or leaving users in an inconsistent state.
Suggested change
Standards
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. Missing Error HandlingThe guest authentication endpoint lacks error handling for the signIn operation. If authentication fails, the function will still redirect users, potentially leading to an inconsistent authentication state and service disruption. Commitable Suggestion
Suggested change
Standards
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| redirect('/'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+7
to
+9
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. Guest Auth LoopRedirecting to '/' after guest sign-in will trigger middleware again, potentially creating an infinite auth loop. The middleware will detect no session during redirect processing and send to /api/auth/guest again. Standards
Comment on lines
+8
to
+9
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. Missing Error HandlingGuest sign-in lacks error handling. If signIn() fails, the code continues to redirect without handling the error, potentially causing navigation to authenticated routes with invalid session state. Standards
Comment on lines
+4
to
+9
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. Missing Rate LimitingGuest account creation lacks rate limiting by IP. Attackers can create unlimited guest accounts through API endpoint. This enables DoS attacks against the database. Standards
Comment on lines
+8
to
+9
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. Missing Error HandlingNo error handling for signIn failure. If guest authentication fails, the error is not caught, potentially causing unhandled exceptions and service disruption. Standards
visz11 marked this conversation as resolved.
Show resolved
Hide resolved
Comment on lines
+7
to
+9
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. Session Update MissingAfter signing in as guest, the code immediately redirects without verifying the session was successfully created. This can lead to redirect loops if signIn fails but the redirect still executes. Standards
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
arvi18 marked this conversation as resolved.
Show resolved
Hide resolved
Comment on lines
+4
to
+10
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. Guest Auth LoopThe guest auth endpoint creates a potential redirect loop. If signIn fails, the code will redirect without handling the failure, potentially causing an infinite redirection cycle. Standards
visz11 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return new Response('Unauthorized', { status: 401 }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+4
to
+12
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. Insufficient Error HandlingGuest authentication lacks error handling for signIn failures. If guest authentication fails, the error is not caught, potentially causing unhandled promise rejections and revealing implementation details. Standards
Comment on lines
+4
to
+12
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. Missing CSRF ProtectionGuest authentication endpoint lacks CSRF protection for automatic sign-in. Attacker could force authentication via CSRF attack. Enables unwanted account creation and potential tracking.
Suggested change
Standards
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
arvi18 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -2,7 +2,7 @@ import { compare } from 'bcrypt-ts'; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import NextAuth, { type User, type Session } from 'next-auth'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import Credentials from 'next-auth/providers/credentials'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { getUser } from '@/lib/db/queries'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { createAnonymousUser, getUser } from '@/lib/db/queries'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { authConfig } from './auth.config'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -21,12 +21,24 @@ export const { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Credentials({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| credentials: {}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async authorize({ email, password }: any) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const users = await getUser(email); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (users.length === 0) return null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // biome-ignore lint: Forbidden non-null assertion. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const passwordsMatch = await compare(password, users[0].password!); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [user] = await getUser(email); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!user) return null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!user.password) return null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const passwordsMatch = await compare(password, user.password); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!passwordsMatch) return null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return users[0] as any; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return user; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Credentials({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| id: 'guest', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| credentials: {}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async authorize() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [anonymousUser] = await createAnonymousUser(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return anonymousUser; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+39
to
+41
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. Predictable Anonymous CredentialsThe guest authentication flow creates anonymous users without requiring any credentials or verification. This allows anyone to access the system as a guest user, potentially bypassing authentication controls and enabling automated abuse. Consider implementing additional verification steps like CAPTCHA for guest sessions. Commitable Suggestion
Suggested change
Standards
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -9,6 +9,7 @@ import { AuthForm } from '@/components/auth-form'; | |||||||||||||||||||||
| import { SubmitButton } from '@/components/submit-button'; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| import { login, type LoginActionState } from '../actions'; | ||||||||||||||||||||||
| import { useSession } from 'next-auth/react'; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| export default function Page() { | ||||||||||||||||||||||
| const router = useRouter(); | ||||||||||||||||||||||
|
|
@@ -23,6 +24,8 @@ export default function Page() { | |||||||||||||||||||||
| }, | ||||||||||||||||||||||
| ); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| const { update: updateSession } = useSession(); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| useEffect(() => { | ||||||||||||||||||||||
| if (state.status === 'failed') { | ||||||||||||||||||||||
| toast({ | ||||||||||||||||||||||
|
|
@@ -36,6 +39,7 @@ export default function Page() { | |||||||||||||||||||||
| }); | ||||||||||||||||||||||
| } else if (state.status === 'success') { | ||||||||||||||||||||||
| setIsSuccessful(true); | ||||||||||||||||||||||
| updateSession(); | ||||||||||||||||||||||
|
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. Session Update HandlingThe session update call lacks error handling. If the session update fails, the application will continue as if successful, potentially causing state inconsistency between client and server. Standards
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. Session Update RiskSession update operation lacks error handling. Failed session updates may leave users in inconsistent authentication states, causing unexpected behavior and potential reliability issues.
Suggested change
Standards
|
||||||||||||||||||||||
| router.refresh(); | ||||||||||||||||||||||
visz11 marked this conversation as resolved.
Show resolved
Hide resolved
Comment on lines
+42
to
43
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. Session Update RaceSession update and router refresh occur in parallel without awaiting session update completion. This can cause race conditions where navigation happens before session state is properly updated, leading to inconsistent authentication state. Standards
Comment on lines
+42
to
43
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. Session Update FailureThe updateSession() call doesn't handle promise rejection. If session update fails, the router.refresh() will execute anyway, potentially causing inconsistent state. This can lead to authentication state mismatch and application errors. Standards
Comment on lines
+42
to
43
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. Missing Await PatternThe updateSession() call is not awaited, which can lead to race conditions where navigation happens before session update completes. This may cause authentication state inconsistencies and unexpected behavior. Standards
Comment on lines
+42
to
43
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. Session Update Race ConditionSession update occurs after UI state change without awaiting completion. If session update fails, UI may show success while authentication state remains inconsistent, causing navigation/permission errors. Standards
Comment on lines
+42
to
43
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. Session Update InefficiencyCalling updateSession() followed immediately by router.refresh() creates redundant rendering cycles. The updateSession() already triggers a revalidation of session data, making the manual router.refresh() unnecessary and potentially causing double-rendering. This can impact client-side performance, especially on lower-end devices.
Suggested change
Standards
Comment on lines
+42
to
43
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. Session Update FailureThe updateSession function is called without awaiting its completion. This can lead to race conditions where the router refreshes before the session is fully updated, potentially causing authentication state inconsistencies. Commitable Suggestion
Suggested change
Standards
|
||||||||||||||||||||||
| } | ||||||||||||||||||||||
| }, [state.status]); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -9,6 +9,7 @@ import { SubmitButton } from '@/components/submit-button'; | |||||||||||
|
|
||||||||||||
| import { register, type RegisterActionState } from '../actions'; | ||||||||||||
| import { toast } from '@/components/toast'; | ||||||||||||
| import { useSession } from 'next-auth/react'; | ||||||||||||
|
|
||||||||||||
| export default function Page() { | ||||||||||||
| const router = useRouter(); | ||||||||||||
|
|
@@ -23,6 +24,8 @@ export default function Page() { | |||||||||||
| }, | ||||||||||||
| ); | ||||||||||||
|
|
||||||||||||
| const { update: updateSession } = useSession(); | ||||||||||||
|
|
||||||||||||
| useEffect(() => { | ||||||||||||
| if (state.status === 'user_exists') { | ||||||||||||
| toast({ type: 'error', description: 'Account already exists!' }); | ||||||||||||
|
|
@@ -37,6 +40,7 @@ export default function Page() { | |||||||||||
| toast({ type: 'success', description: 'Account created successfully!' }); | ||||||||||||
|
|
||||||||||||
| setIsSuccessful(true); | ||||||||||||
| updateSession(); | ||||||||||||
|
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. Missing Await PatternThe updateSession() function is likely asynchronous but called without await. This can lead to race conditions where the router.refresh() executes before session update completes, causing inconsistent state. Standards
Comment on lines
42
to
+43
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. Session Update FailureThe updateSession function is called without awaiting its completion. This can lead to race conditions where the router refreshes before the session is fully updated, potentially causing authentication state inconsistencies. Commitable Suggestion
Suggested change
Standards
|
||||||||||||
| router.refresh(); | ||||||||||||
visz11 marked this conversation as resolved.
Show resolved
Hide resolved
Comment on lines
42
to
44
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. Missing Session UpdateSession update call is not awaited before router refresh. This can lead to navigation with stale session data, causing authentication state inconsistencies and potential user experience issues. Standards
Comment on lines
42
to
44
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. Missing Await PatternThe updateSession() call is not awaited, which can lead to race conditions where navigation happens before session update completes. This may cause authentication state inconsistencies and unexpected behavior. Standards
Comment on lines
42
to
44
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. Race Condition RiskThe updateSession() function is called without awaiting completion, causing a race condition. Router refresh may execute before session update completes, resulting in stale session data being used for rendering. Standards
|
||||||||||||
| } | ||||||||||||
| }, [state]); | ||||||||||||
|
|
||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,5 +1,5 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| UIMessage, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type UIMessage, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| appendResponseMessages, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| createDataStreamResponse, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| smoothStream, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -10,6 +10,7 @@ import { systemPrompt } from '@/lib/ai/prompts'; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| deleteChatById, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| getChatById, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| getMessageCountByUserId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| saveChat, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| saveMessages, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } from '@/lib/db/queries'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -23,8 +24,12 @@ import { createDocument } from '@/lib/ai/tools/create-document'; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { updateDocument } from '@/lib/ai/tools/update-document'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { requestSuggestions } from '@/lib/ai/tools/request-suggestions'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { getWeather } from '@/lib/ai/tools/get-weather'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { isProductionEnvironment } from '@/lib/constants'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { anonymousRegex, isProductionEnvironment } from '@/lib/constants'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { myProvider } from '@/lib/ai/providers'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| entitlementsByMembershipTier, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type MembershipTier, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } from '@/lib/ai/capabilities'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export const maxDuration = 60; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -42,10 +47,33 @@ export async function POST(request: Request) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const session = await auth(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!session || !session.user || !session.user.id) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!session?.user?.id) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return new Response('Unauthorized', { status: 401 }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const membershipTier: MembershipTier = anonymousRegex.test( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| session.user.email ?? '', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? 'guest' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : 'free'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+54
to
+58
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. Membership Tier DuplicationDuplicated user type detection logic appears in multiple components. This creates maintenance burden when user classification rules change and violates DRY principle. Standards
Comment on lines
+54
to
+58
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. Missing Authentication CheckGuest tier determination relies solely on email pattern. Attacker could forge emails matching anonymous pattern. Enables privilege escalation from guest to free tier.
Suggested change
Standards
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const messageCount = await getMessageCountByUserId({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| id: session.user.id, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| differenceInHours: 24, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| messageCount > | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| entitlementsByMembershipTier[membershipTier].maxMessagesPerDay | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+60
to
+68
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. Rate Limiting BottleneckRate limiting check occurs on every request, requiring a database query that counts messages. This creates a performance bottleneck as the database is queried before processing each chat message, adding latency to every request regardless of whether the user is near their limit. Commitable Suggestion
Suggested change
Standards
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return new Response( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'You have exceeded your maximum number of messages for the day', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| status: 429, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
47
to
+73
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. Insufficient Rate Limiting for Guest UsersThe PR implements rate limiting based on message count per user ID, but lacks IP-based rate limiting for guest account creation. An attacker could create unlimited anonymous accounts (each with its own message quota) by repeatedly calling the guest authentication endpoint, effectively bypassing the per-user message limit and potentially causing a denial of service.
Suggested change
Standard: CWE-770 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+60
to
+74
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. Missing Error HandlingThe code doesn't handle potential errors from getMessageCountByUserId() which could throw exceptions as shown in its implementation. This can lead to unhandled promise rejections and server errors when database queries fail. Standards
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+60
to
+75
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. Rate Limiting CheckThe rate limiting check occurs after database query execution, causing unnecessary database load when users have already exceeded their limits. This creates performance overhead as the system performs expensive message counting operations for every request, even when users are already over quota. Under high traffic, this can lead to database contention and increased latency for all users. Standards
visz11 marked this conversation as resolved.
Show resolved
Hide resolved
Comment on lines
+54
to
+75
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. Insufficient Rate LimitingRate limiting uses only user ID without IP-based restrictions. Attackers could create multiple guest accounts to bypass per-user limits, enabling denial of service attacks. Standards
Comment on lines
+54
to
+75
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. Insufficient Rate LimitingRate limiting only checks message count without IP-based restrictions. Attackers can create unlimited guest accounts to bypass per-account message limits. Implement IP-based rate limiting for guest account creation. Standards
visz11 marked this conversation as resolved.
Show resolved
Hide resolved
visz11 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const userMessage = getMostRecentUserMessage(messages); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!userMessage) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,9 @@ | ||
| 'use client'; | ||
|
|
||
| import { ChevronUp } from 'lucide-react'; | ||
| import Image from 'next/image'; | ||
| import type { User } from 'next-auth'; | ||
| import { signOut } from 'next-auth/react'; | ||
| import { signOut, useSession } from 'next-auth/react'; | ||
| import { useTheme } from 'next-themes'; | ||
|
|
||
| import { | ||
|
|
@@ -17,26 +18,50 @@ import { | |
| SidebarMenuButton, | ||
| SidebarMenuItem, | ||
| } from '@/components/ui/sidebar'; | ||
| import { anonymousRegex } from '@/lib/constants'; | ||
| import { useRouter } from 'next/navigation'; | ||
| import { toast } from './toast'; | ||
| import { LoaderIcon } from './icons'; | ||
|
|
||
| export function SidebarUserNav({ user }: { user: User }) { | ||
| const router = useRouter(); | ||
| const { data, status } = useSession(); | ||
| const { setTheme, theme } = useTheme(); | ||
|
|
||
| const isGuest = anonymousRegex.test(data?.user?.email ?? ''); | ||
|
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. Anonymous User DetectionDirect regex testing in component creates tight coupling to user identification logic. This pattern makes changing the anonymous user detection mechanism difficult and violates separation of concerns. Standards
|
||
|
|
||
| return ( | ||
| <SidebarMenu> | ||
| <SidebarMenuItem> | ||
| <DropdownMenu> | ||
| <DropdownMenuTrigger asChild> | ||
| <SidebarMenuButton className="data-[state=open]:bg-sidebar-accent bg-background data-[state=open]:text-sidebar-accent-foreground h-10"> | ||
| <Image | ||
| src={`https://avatar.vercel.sh/${user.email}`} | ||
| alt={user.email ?? 'User Avatar'} | ||
| width={24} | ||
| height={24} | ||
| className="rounded-full" | ||
| /> | ||
| <span className="truncate">{user?.email}</span> | ||
| <ChevronUp className="ml-auto" /> | ||
| </SidebarMenuButton> | ||
| {status === 'loading' ? ( | ||
| <SidebarMenuButton className="data-[state=open]:bg-sidebar-accent bg-background data-[state=open]:text-sidebar-accent-foreground h-10 justify-between"> | ||
| <div className="flex flex-row gap-2"> | ||
| <div className="size-6 bg-zinc-500/30 rounded-full animate-pulse" /> | ||
| <span className="bg-zinc-500/30 text-transparent rounded-md animate-pulse"> | ||
| Loading auth status | ||
| </span> | ||
| </div> | ||
| <div className="animate-spin text-zinc-500"> | ||
| <LoaderIcon /> | ||
| </div> | ||
| </SidebarMenuButton> | ||
| ) : ( | ||
| <SidebarMenuButton className="data-[state=open]:bg-sidebar-accent bg-background data-[state=open]:text-sidebar-accent-foreground h-10"> | ||
| <Image | ||
| src={`https://avatar.vercel.sh/${user.email}`} | ||
|
Comment on lines
+38
to
+53
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. Session Loading RaceUser object accessed during loading state. When status is not 'loading', code assumes user object is available but doesn't verify, risking runtime errors if authentication is incomplete. Standards
|
||
| alt={user.email ?? 'User Avatar'} | ||
| width={24} | ||
| height={24} | ||
| className="rounded-full" | ||
| /> | ||
| <span className="truncate"> | ||
| {isGuest ? 'Guest' : user?.email} | ||
| </span> | ||
| <ChevronUp className="ml-auto" /> | ||
| </SidebarMenuButton> | ||
| )} | ||
|
Comment on lines
+51
to
+64
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. Potential null reference on user prop. The code accesses Apply this diff to safely access user: <SidebarMenuButton className="data-[state=open]:bg-sidebar-accent bg-background data-[state=open]:text-sidebar-accent-foreground h-10">
<Image
- src={`https://avatar.vercel.sh/${user.email}`}
- alt={user.email ?? 'User Avatar'}
+ src={`https://avatar.vercel.sh/${data?.user?.email ?? 'guest'}`}
+ alt={data?.user?.email ?? 'User Avatar'}
width={24}
height={24}
className="rounded-full"
/>
<span className="truncate">
- {isGuest ? 'Guest' : user?.email}
+ {isGuest ? 'Guest' : data?.user?.email}
</span>
<ChevronUp className="ml-auto" />
</SidebarMenuButton>🤖 Prompt for AI AgentsThere 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. We have implemented checks in the backend and db schema holds contraint for user.user not null 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. @crimefighter18, thank you for the clarification! If the backend checks and database constraints guarantee that 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. @coderabbitai We have implemented checks in the backend and db schema holds contraint for user.user not null 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. @crimefighter18, I've noted your clarification. Since the backend validation and database schema constraints ensure ✏️ Learnings added
|
||
| </DropdownMenuTrigger> | ||
| <DropdownMenuContent | ||
| side="top" | ||
|
|
@@ -54,12 +79,26 @@ export function SidebarUserNav({ user }: { user: User }) { | |
| type="button" | ||
| className="w-full cursor-pointer" | ||
| onClick={() => { | ||
| signOut({ | ||
| redirectTo: '/', | ||
| }); | ||
| if (status === 'loading') { | ||
| toast({ | ||
| type: 'error', | ||
| description: | ||
| 'Checking authentication status, please try again!', | ||
| }); | ||
|
|
||
| return; | ||
| } | ||
visz11 marked this conversation as resolved.
Show resolved
Hide resolved
Comment on lines
+82
to
+90
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. Session Status RaceSign out button is clickable during 'loading' state but shows error toast instead of disabling the button. This creates a poor user experience where users can trigger actions that immediately fail with an error message. Standards
visz11 marked this conversation as resolved.
Show resolved
Hide resolved
Comment on lines
+82
to
+90
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. Loading State HandlingThe sign-out functionality shows an error toast when authentication is loading, but doesn't disable the button. Users can repeatedly click the button during loading, causing multiple error toasts and a confusing user experience. Standards
|
||
|
|
||
| if (isGuest) { | ||
| router.push('/login'); | ||
| } else { | ||
| signOut({ | ||
| redirectTo: '/', | ||
| }); | ||
| } | ||
|
Comment on lines
+82
to
+98
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. Inconsistent Session HandlingThe code uses both router.push() for guests and signOut() with redirectTo for regular users, creating inconsistent session termination logic. This can lead to state inconsistencies where guest sessions aren't properly terminated before login. Standards
|
||
| }} | ||
| > | ||
| Sign out | ||
| {isGuest ? 'Login to your account' : 'Sign out'} | ||
| </button> | ||
| </DropdownMenuItem> | ||
| </DropdownMenuContent> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| import type { ChatModel } from './models'; | ||
|
|
||
| export type MembershipTier = 'guest' | 'free'; | ||
|
|
||
| interface Entitlements { | ||
| maxMessagesPerDay: number; | ||
| chatModelsAvailable: Array<ChatModel['id']>; | ||
| } | ||
|
|
||
| export const entitlementsByMembershipTier: Record< | ||
| MembershipTier, | ||
| Entitlements | ||
| > = { | ||
| /* | ||
| * For users without an account | ||
| */ | ||
| guest: { | ||
| maxMessagesPerDay: 20, | ||
| chatModelsAvailable: ['chat-model', 'chat-model-reasoning'], | ||
| }, | ||
|
|
||
| /* | ||
| * For user with an account | ||
| */ | ||
| free: { | ||
| maxMessagesPerDay: 100, | ||
| chatModelsAvailable: ['chat-model', 'chat-model-reasoning'], | ||
| }, | ||
|
Comment on lines
+17
to
+28
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. Hardcoded Rate LimitsRate limits are hardcoded in the capabilities file. This reduces configurability and requires code changes to adjust limits, making it difficult to tune system behavior without deployment. Standards
Comment on lines
+10
to
+28
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. Hardcoded Rate LimitsRate limits are hardcoded in the application code rather than being configurable via environment variables. This reduces maintainability as changing limits requires code changes and redeployment instead of configuration updates. Standards
|
||
|
|
||
| /* | ||
| * TODO: For users with an account and a paid membership | ||
| */ | ||
| }; | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -5,3 +5,5 @@ export const isTestEnvironment = Boolean( | |||||
| process.env.PLAYWRIGHT || | ||||||
| process.env.CI_PLAYWRIGHT, | ||||||
| ); | ||||||
|
|
||||||
| export const anonymousRegex = /^anonymous-\d+$/; | ||||||
|
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. This regular expression is designed to match anonymous user emails created with A simple update could be to match any characters after
Suggested change
|
||||||
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.
Session Fixation Risk
Guest authentication doesn't invalidate existing session tokens. Attackers could potentially set a session identifier, then force guest auth to maintain control. This enables session fixation attacks.
Standards