diff --git a/app/components/AppNavigation.vue b/app/components/AppNavigation.vue index 803f8bd..5bbeb7e 100644 --- a/app/components/AppNavigation.vue +++ b/app/components/AppNavigation.vue @@ -22,7 +22,25 @@ > {{ item.label }} + +
+ + Dashboard + + + Logout + +
{{ item.label }} + +
+ + Dashboard + + + Logout + +
{ - const authCookie = useCookie('auth-token') const memberData = useState('auth.member', () => null) - const isAuthenticated = computed(() => !!authCookie.value) + + const isAuthenticated = computed(() => !!memberData.value) + const isMember = computed(() => !!memberData.value) const checkMemberStatus = async () => { - if (!authCookie.value) { - memberData.value = null - return false - } - + console.log('🔍 checkMemberStatus called') + console.log(' - Current memberData:', !!memberData.value) + try { - const response = await $fetch('/api/auth/member', { - headers: { - 'Cookie': `auth-token=${authCookie.value}` - } - }) + console.log(' - Making API call to /api/auth/member...') + const response = await $fetch('/api/auth/member') + console.log(' - API response received:', { email: response.email, id: response.id }) memberData.value = response + console.log(' - ✅ Member authenticated successfully') return true } catch (error) { - console.error('Failed to fetch member status:', error) + console.error(' - ❌ Failed to fetch member status:', error.statusCode, error.statusMessage) memberData.value = null + console.log(' - Cleared memberData') return false } } @@ -30,7 +29,6 @@ export const useAuth = () => { await $fetch('/api/auth/logout', { method: 'POST' }) - authCookie.value = null memberData.value = null await navigateTo('/') } catch (error) { diff --git a/app/middleware/auth.js b/app/middleware/auth.js new file mode 100644 index 0000000..a29566b --- /dev/null +++ b/app/middleware/auth.js @@ -0,0 +1,27 @@ +export default defineNuxtRouteMiddleware(async (to, from) => { + // Skip on server-side rendering + if (process.server) { + console.log('🛡️ Auth middleware - skipping on server') + return + } + + const { memberData, checkMemberStatus } = useAuth() + + console.log('🛡️ Auth middleware (CLIENT) - route:', to.path) + console.log(' - memberData exists:', !!memberData.value) + console.log(' - Running on:', process.server ? 'SERVER' : 'CLIENT') + + // If no member data, try to check authentication + if (!memberData.value) { + console.log(' - No member data, checking authentication...') + const isAuthenticated = await checkMemberStatus() + console.log(' - Authentication result:', isAuthenticated) + + if (!isAuthenticated) { + console.log(' - ❌ Authentication failed, redirecting to login') + return navigateTo('/login') + } + } + + console.log(' - ✅ Authentication successful for:', memberData.value?.email) +}) \ No newline at end of file diff --git a/app/pages/join.vue b/app/pages/join.vue index 60d865f..3e530d4 100644 --- a/app/pages/join.vue +++ b/app/pages/join.vue @@ -277,17 +277,23 @@ -

+

We've sent a confirmation email to {{ form.email }} with your membership details.

+
+

+ You will be automatically redirected to your dashboard in a few seconds... +

+
+
- Go to Dashboard + Go to Dashboard Now { @@ -577,9 +584,8 @@ const handleSubmit = async () => { customerId.value = response.customerId customerCode.value = response.customerCode - // Store token in session - const authToken = useCookie('auth-token') - authToken.value = response.token + // Token is now set as httpOnly cookie by the server + // No need to manually set cookie on client side // Move to next step if (needsPayment.value) { @@ -591,6 +597,13 @@ const handleSubmit = async () => { } else { // For free tier, create subscription directly await createSubscription() + // Check member status to ensure user is properly authenticated + await checkMemberStatus() + + // Automatically redirect to dashboard after a short delay + setTimeout(() => { + navigateTo('/member/dashboard') + }, 3000) // 3 second delay to show success message } } } catch (error) { @@ -681,6 +694,14 @@ const createSubscription = async (cardToken = null) => { console.log('Moving to step 3 - success!') currentStep.value = 3 successMessage.value = 'Your membership has been activated successfully!' + + // Check member status to ensure user is properly authenticated + await checkMemberStatus() + + // Automatically redirect to dashboard after a short delay + setTimeout(() => { + navigateTo('/member/dashboard') + }, 3000) // 3 second delay to show success message } else { throw new Error('Subscription creation failed - response not successful') } diff --git a/app/pages/member/dashboard.vue b/app/pages/member/dashboard.vue new file mode 100644 index 0000000..70e53ff --- /dev/null +++ b/app/pages/member/dashboard.vue @@ -0,0 +1,161 @@ + + + \ No newline at end of file diff --git a/app/plugins/auth-init.client.js b/app/plugins/auth-init.client.js new file mode 100644 index 0000000..44a3e21 --- /dev/null +++ b/app/plugins/auth-init.client.js @@ -0,0 +1,20 @@ +export default defineNuxtPlugin(async () => { + const { memberData, checkMemberStatus } = useAuth() + + console.log('🚀 Auth init plugin running on CLIENT') + + // Only initialize if we don't already have member data + if (!memberData.value) { + console.log(' - No member data, checking auth status...') + + const isAuthenticated = await checkMemberStatus() + + if (isAuthenticated) { + console.log(' - ✅ Authentication successful') + } else { + console.log(' - ❌ No valid authentication') + } + } else { + console.log(' - ✅ Member data already exists:', memberData.value.email) + } +}) \ No newline at end of file diff --git a/server/api/auth/logout.post.js b/server/api/auth/logout.post.js index 87f6065..56c7fdf 100644 --- a/server/api/auth/logout.post.js +++ b/server/api/auth/logout.post.js @@ -1,9 +1,9 @@ export default defineEventHandler(async (event) => { // Clear the auth token cookie setCookie(event, 'auth-token', '', { - httpOnly: true, - secure: process.env.NODE_ENV === 'production', - sameSite: 'strict', + httpOnly: false, // Match the original cookie settings + secure: false, // Don't require HTTPS in development + sameSite: 'lax', maxAge: 0 // Expire immediately }) diff --git a/server/api/auth/member.get.js b/server/api/auth/member.get.js index 259dcc9..6a7d62f 100644 --- a/server/api/auth/member.get.js +++ b/server/api/auth/member.get.js @@ -6,8 +6,10 @@ export default defineEventHandler(async (event) => { await connectDB() const token = getCookie(event, 'auth-token') + console.log('Auth check - token found:', !!token) if (!token) { + console.log('No auth token found in cookies') throw createError({ statusCode: 401, statusMessage: 'Not authenticated' diff --git a/server/api/auth/status.get.js b/server/api/auth/status.get.js new file mode 100644 index 0000000..9a7d2da --- /dev/null +++ b/server/api/auth/status.get.js @@ -0,0 +1,40 @@ +import jwt from 'jsonwebtoken' +import Member from '../../models/member.js' +import { connectDB } from '../../utils/mongoose.js' + +export default defineEventHandler(async (event) => { + await connectDB() + + const token = getCookie(event, 'auth-token') + console.log('🔍 Auth status check - token exists:', !!token) + + if (!token) { + return { authenticated: false, member: null } + } + + try { + const decoded = jwt.verify(token, process.env.JWT_SECRET) + const member = await Member.findById(decoded.memberId).select('-__v') + + if (!member) { + console.log('⚠️ Token valid but member not found') + return { authenticated: false, member: null } + } + + console.log('✅ Auth status check - member found:', member.email) + return { + authenticated: true, + member: { + id: member._id, + email: member.email, + name: member.name, + circle: member.circle, + contributionTier: member.contributionTier, + membershipLevel: `${member.circle}-${member.contributionTier}` + } + } + } catch (err) { + console.error('❌ Auth status check - token verification failed:', err.message) + return { authenticated: false, member: null } + } +}) \ No newline at end of file diff --git a/server/api/auth/verify.get.js b/server/api/auth/verify.get.js index b2eee10..31a66f2 100644 --- a/server/api/auth/verify.get.js +++ b/server/api/auth/verify.get.js @@ -38,8 +38,8 @@ export default defineEventHandler(async (event) => { // Set the session cookie setCookie(event, 'auth-token', sessionToken, { - httpOnly: true, - secure: process.env.NODE_ENV === 'production', + httpOnly: false, // Allow JavaScript access for debugging in development + secure: false, // Don't require HTTPS in development sameSite: 'lax', maxAge: 60 * 60 * 24 * 30 // 30 days }) diff --git a/server/api/helcim/customer.post.js b/server/api/helcim/customer.post.js index d7e3454..782a0da 100644 --- a/server/api/helcim/customer.post.js +++ b/server/api/helcim/customer.post.js @@ -108,10 +108,23 @@ export default defineEventHandler(async (event) => { email: body.email, helcimCustomerId: customerData.id }, - config.jwtSecret, + process.env.JWT_SECRET, { expiresIn: '24h' } ) + // Set the session cookie server-side + console.log('Setting auth-token cookie for member:', member.email) + console.log('NODE_ENV:', process.env.NODE_ENV) + setCookie(event, 'auth-token', token, { + httpOnly: true, // Server-only for security + secure: false, // Don't require HTTPS in development + sameSite: 'lax', + maxAge: 60 * 60 * 24, // 24 hours + path: '/', + domain: undefined // Let browser set domain automatically + }) + console.log('Cookie set successfully') + return { success: true, customerId: customerData.id,