From 83a981cabc8446cceff493d2d09e3700924fc442 Mon Sep 17 00:00:00 2001 From: Andrei Date: Sun, 28 Sep 2025 20:22:57 +0000 Subject: [PATCH] Require user authentication for AI chat functionality MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Update chat API to require valid authentication tokens for all requests - Add authentication requirement screens to both chat components - Show "Create Account / Sign In" prompts for unauthenticated users - Hide chat input and functionality until user is logged in - Return 401 errors with clear messages when authentication is missing - Maintain bilingual support (Romanian/English) for auth prompts 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- app/api/chat/route.ts | 43 ++++++---- components/chat/chat-interface.tsx | 95 +++++++++++++++------ components/chat/floating-chat.tsx | 127 ++++++++++++++++++++--------- 3 files changed, 182 insertions(+), 83 deletions(-) diff --git a/app/api/chat/route.ts b/app/api/chat/route.ts index 66604af..dedc796 100644 --- a/app/api/chat/route.ts +++ b/app/api/chat/route.ts @@ -26,24 +26,35 @@ export async function POST(request: Request) { const body = await request.json() const { message, conversationId, locale, history } = chatRequestSchema.parse(body) - // Try to get user from authentication (optional for backward compatibility) + // Require authentication for chat functionality let userId: string | null = null const authHeader = request.headers.get('authorization') - console.log('Chat API - authHeader present:', !!authHeader) - if (authHeader?.startsWith('Bearer ')) { - try { - const token = authHeader.substring(7) - console.log('Chat API - token extracted, length:', token.length) - const payload = await verifyToken(token) - console.log('Chat API - token payload:', payload) - userId = payload.userId - console.log('Chat API - userId extracted from token:', userId) - } catch (error) { - // Continue without authentication for backward compatibility - console.log('Chat API - authentication failed:', (error as any)?.message || error) - } - } else { - console.log('Chat API - no valid auth header') + + if (!authHeader?.startsWith('Bearer ')) { + return NextResponse.json( + { + success: false, + error: 'Authentication required to use chat functionality', + code: 'AUTH_REQUIRED' + }, + { status: 401 } + ) + } + + try { + const token = authHeader.substring(7) + const payload = await verifyToken(token) + userId = payload.userId + console.log('Chat API - authenticated user:', userId) + } catch (error) { + return NextResponse.json( + { + success: false, + error: 'Invalid or expired authentication token', + code: 'AUTH_INVALID' + }, + { status: 401 } + ) } // Handle conversation logic diff --git a/components/chat/chat-interface.tsx b/components/chat/chat-interface.tsx index b56ad01..8a769f9 100644 --- a/components/chat/chat-interface.tsx +++ b/components/chat/chat-interface.tsx @@ -1,13 +1,14 @@ 'use client' import { useState, useRef, useEffect } from 'react' -import { Send } from 'lucide-react' +import { Send, User } from 'lucide-react' import ReactMarkdown from 'react-markdown' export function ChatInterface() { const [messages, setMessages] = useState>([]) const [input, setInput] = useState('') const [loading, setLoading] = useState(false) + const [isAuthenticated, setIsAuthenticated] = useState(false) const messagesEndRef = useRef(null) const scrollToBottom = () => { @@ -16,6 +17,24 @@ export function ChatInterface() { useEffect(scrollToBottom, [messages]) + // Check authentication status on mount + useEffect(() => { + const checkAuth = async () => { + try { + const token = localStorage.getItem('authToken') + if (token) { + const response = await fetch('/api/auth/me', { + headers: { 'Authorization': `Bearer ${token}` } + }) + setIsAuthenticated(response.ok) + } + } catch (error) { + setIsAuthenticated(false) + } + } + checkAuth() + }, []) + const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() if (!input.trim() || loading) return @@ -64,14 +83,32 @@ export function ChatInterface() {
- {messages.length === 0 && ( -
-

Bună ziua! Sunt aici să vă ajut cu întrebările despre Biblie.

-

Puteți începe prin a întreba ceva despre un verset sau o temă biblică.

+ {!isAuthenticated ? ( +
+ +

Bun venit la AI Chat Biblic!

+

+ Pentru a accesa chat-ul AI și a salva conversațiile tale, te rugăm să îți creezi un cont sau să te conectezi. +

+
- )} + ) : ( + <> + {messages.length === 0 && ( +
+

Bună ziua! Sunt aici să vă ajut cu întrebările despre Biblie.

+

Puteți începe prin a întreba ceva despre un verset sau o temă biblică.

+
+ )} - {messages.map((msg, idx) => ( + {messages.map((msg, idx) => (
+ )} +
+ )} -
-
-
- setInput(e.target.value)} - placeholder="Întreabă despre Biblie..." - className="flex-1 px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500" - disabled={loading} - /> - -
-
+ {isAuthenticated && ( +
+
+ setInput(e.target.value)} + placeholder="Întreabă despre Biblie..." + className="flex-1 px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500" + disabled={loading} + /> + +
+
+ )}
) } diff --git a/components/chat/floating-chat.tsx b/components/chat/floating-chat.tsx index 206f424..040b011 100644 --- a/components/chat/floating-chat.tsx +++ b/components/chat/floating-chat.tsx @@ -577,10 +577,16 @@ export default function FloatingChat() { {!isAuthenticated ? ( - ✨ Sign in to save your conversations + ✨ {locale === 'ro' ? 'Conectează-te pentru a salva conversațiile' : 'Sign in to save your conversations'} - ) : isLoadingConversations ? ( @@ -688,7 +694,44 @@ export default function FloatingChat() { p: 1, }} > - {messages.map((message) => ( + {!isAuthenticated ? ( + + + + {locale === 'ro' ? 'Bun venit la AI Chat Biblic!' : 'Welcome to Biblical AI Chat!'} + + + {locale === 'ro' + ? 'Pentru a accesa chat-ul AI și a salva conversațiile tale, te rugăm să îți creezi un cont sau să te conectezi.' + : 'To access the AI chat and save your conversations, please create an account or sign in.' + } + + + + ) : ( + <> + {messages.map((message) => ( )} -
+
+ + )} {/* Input */} - - - setInputMessage(e.target.value)} - onKeyPress={handleKeyPress} - disabled={isLoading} - variant="outlined" - sx={{ - '& .MuiOutlinedInput-root': { + {isAuthenticated && ( + + + setInputMessage(e.target.value)} + onKeyPress={handleKeyPress} + disabled={isLoading} + variant="outlined" + sx={{ + '& .MuiOutlinedInput-root': { + borderRadius: 2, + } + }} + /> + + background: 'linear-gradient(45deg, #009688 30%, #00796B 90%)', + }} + > + + + + + {t('enterToSend')} + - - {t('enterToSend')} - - + )}