localgreenchain/pages/api/auth/forgot-password.ts
Claude 39b6081baa
Implement comprehensive authentication system (Agent 1)
Add complete user authentication with NextAuth.js supporting:
- Email/password credentials authentication
- OAuth providers (GitHub, Google) with optional configuration
- JWT-based session management with 30-day expiry
- Role-based access control (USER, GROWER, FARM_MANAGER, ADMIN)
- Permission system with granular access control
- Secure password hashing with bcrypt (12 rounds)
- Rate limiting on auth endpoints
- Password reset flow with secure tokens
- Email verification system

Files added:
- lib/auth/: Core auth library (types, permissions, context, hooks, middleware)
- pages/api/auth/: Auth API routes (NextAuth, register, forgot-password, verify-email)
- pages/auth/: Auth pages (signin, signup, forgot-password, reset-password, verify-email)
- components/auth/: Reusable auth components (LoginForm, RegisterForm, AuthGuard, etc.)

Updated _app.tsx to include SessionProvider for auth state management.
2025-11-23 03:52:09 +00:00

102 lines
3 KiB
TypeScript

import type { NextApiRequest, NextApiResponse } from 'next'
import crypto from 'crypto'
import { findUserByEmail } from './[...nextauth]'
import { AuthResponse, ForgotPasswordInput, TokenPayload } from '@/lib/auth/types'
import { withRateLimit } from '@/lib/auth/withAuth'
// In-memory token store (will be replaced with database in Agent 2)
const passwordResetTokens = new Map<string, TokenPayload>()
// Token expiry: 1 hour
const TOKEN_EXPIRY_MS = 60 * 60 * 1000
function generateResetToken(userId: string, email: string): string {
const token = crypto.randomBytes(32).toString('hex')
const payload: TokenPayload = {
userId,
email,
type: 'password_reset',
expiresAt: Date.now() + TOKEN_EXPIRY_MS,
}
passwordResetTokens.set(token, payload)
return token
}
export function verifyResetToken(token: string): TokenPayload | null {
const payload = passwordResetTokens.get(token)
if (!payload) return null
if (Date.now() > payload.expiresAt) {
passwordResetTokens.delete(token)
return null
}
return payload
}
export function invalidateResetToken(token: string): void {
passwordResetTokens.delete(token)
}
async function handler(
req: NextApiRequest,
res: NextApiResponse<AuthResponse>
) {
if (req.method !== 'POST') {
return res.status(405).json({
success: false,
message: 'Method not allowed',
error: 'Only POST requests are accepted',
})
}
try {
const { email }: ForgotPasswordInput = req.body
if (!email) {
return res.status(400).json({
success: false,
message: 'Email is required',
error: 'VALIDATION_ERROR',
})
}
// Always return success to prevent email enumeration attacks
const successResponse: AuthResponse = {
success: true,
message: 'If an account exists with this email, you will receive a password reset link.',
}
const user = findUserByEmail(email)
if (!user) {
// Return success even if user doesn't exist (security best practice)
return res.status(200).json(successResponse)
}
// Generate reset token
const resetToken = generateResetToken(user.id, user.email)
// Build reset URL
const baseUrl = process.env.NEXTAUTH_URL || `http://${req.headers.host}`
const resetUrl = `${baseUrl}/auth/reset-password?token=${resetToken}`
// TODO: Send email with reset link (will be implemented with Agent 8 - Notifications)
// For now, log the reset URL (in development only)
if (process.env.NODE_ENV === 'development') {
console.log(`Password reset link for ${email}: ${resetUrl}`)
}
return res.status(200).json(successResponse)
} catch (error) {
console.error('Forgot password error:', error)
return res.status(500).json({
success: false,
message: 'An error occurred processing your request',
error: 'INTERNAL_ERROR',
})
}
}
// Apply rate limiting: 3 requests per minute per IP
export default withRateLimit(handler, {
limit: 3,
windowMs: 60000,
})