localgreenchain/pages/api/auth/reset-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

106 lines
2.9 KiB
TypeScript

import type { NextApiRequest, NextApiResponse } from 'next'
import bcrypt from 'bcryptjs'
import { verifyResetToken, invalidateResetToken } from './forgot-password'
import { getUserById } from './[...nextauth]'
import { AuthResponse, ResetPasswordInput } from '@/lib/auth/types'
import { withRateLimit } from '@/lib/auth/withAuth'
const BCRYPT_ROUNDS = 12
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 { token, password }: ResetPasswordInput = req.body
if (!token || !password) {
return res.status(400).json({
success: false,
message: 'Token and new password are required',
error: 'VALIDATION_ERROR',
})
}
// Validate password strength
if (password.length < 8) {
return res.status(400).json({
success: false,
message: 'Password must be at least 8 characters long',
error: 'WEAK_PASSWORD',
})
}
const hasUpperCase = /[A-Z]/.test(password)
const hasLowerCase = /[a-z]/.test(password)
const hasNumbers = /\d/.test(password)
if (!hasUpperCase || !hasLowerCase || !hasNumbers) {
return res.status(400).json({
success: false,
message: 'Password must contain uppercase, lowercase, and numbers',
error: 'WEAK_PASSWORD',
})
}
// Verify token
const payload = verifyResetToken(token)
if (!payload) {
return res.status(400).json({
success: false,
message: 'Invalid or expired reset token',
error: 'INVALID_TOKEN',
})
}
// Get user
const user = getUserById(payload.userId)
if (!user) {
return res.status(400).json({
success: false,
message: 'User not found',
error: 'USER_NOT_FOUND',
})
}
// Hash new password
const passwordHash = await bcrypt.hash(password, BCRYPT_ROUNDS)
// Update user password (in-memory for now)
user.passwordHash = passwordHash
// Invalidate the used token
invalidateResetToken(token)
// TODO: Invalidate all existing sessions for this user (security best practice)
// TODO: Send confirmation email
console.log(`Password reset successful for user: ${user.email}`)
return res.status(200).json({
success: true,
message: 'Password reset successful. You can now sign in with your new password.',
})
} catch (error) {
console.error('Reset password error:', error)
return res.status(500).json({
success: false,
message: 'An error occurred resetting your password',
error: 'INTERNAL_ERROR',
})
}
}
// Apply rate limiting: 5 requests per minute per IP
export default withRateLimit(handler, {
limit: 5,
windowMs: 60000,
})