localgreenchain/lib/auth/AuthContext.tsx
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

120 lines
3.1 KiB
TypeScript

import * as React from 'react'
import { SessionProvider, useSession, signIn, signOut } from 'next-auth/react'
import { Session } from 'next-auth'
import { AuthUser, UserRole } from './types'
import { hasPermission, hasRole } from './permissions'
interface AuthContextType {
user: AuthUser | null
isAuthenticated: boolean
isLoading: boolean
signIn: typeof signIn
signOut: typeof signOut
hasPermission: (permission: string) => boolean
hasRole: (role: UserRole) => boolean
updateSession: () => Promise<void>
}
const AuthContext = React.createContext<AuthContextType | undefined>(undefined)
interface AuthProviderProps {
children: React.ReactNode
session?: Session | null
}
function AuthProviderContent({ children }: { children: React.ReactNode }) {
const { data: session, status, update } = useSession()
const isLoading = status === 'loading'
const isAuthenticated = status === 'authenticated'
const user: AuthUser | null = session?.user
? {
id: session.user.id,
email: session.user.email!,
name: session.user.name,
image: session.user.image,
role: session.user.role || UserRole.USER,
emailVerified: session.user.emailVerified,
}
: null
const checkPermission = React.useCallback(
(permission: string): boolean => {
if (!user) return false
return hasPermission(user.role, permission)
},
[user]
)
const checkRole = React.useCallback(
(requiredRole: UserRole): boolean => {
if (!user) return false
return hasRole(user.role, requiredRole)
},
[user]
)
const updateSession = React.useCallback(async () => {
await update()
}, [update])
const value: AuthContextType = {
user,
isAuthenticated,
isLoading,
signIn,
signOut,
hasPermission: checkPermission,
hasRole: checkRole,
updateSession,
}
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>
}
export function AuthProvider({ children, session }: AuthProviderProps) {
return (
<SessionProvider session={session}>
<AuthProviderContent>{children}</AuthProviderContent>
</SessionProvider>
)
}
export function useAuth(): AuthContextType {
const context = React.useContext(AuthContext)
if (context === undefined) {
throw new Error('useAuth must be used within an AuthProvider')
}
return context
}
// Higher-order component for components that require auth
export function withAuth<P extends object>(
Component: React.ComponentType<P>,
options?: { requiredRole?: UserRole; fallback?: React.ReactNode }
) {
return function AuthenticatedComponent(props: P) {
const { isAuthenticated, isLoading, user } = useAuth()
if (isLoading) {
return options?.fallback || <div>Loading...</div>
}
if (!isAuthenticated) {
if (typeof window !== 'undefined') {
signIn()
}
return options?.fallback || <div>Redirecting to sign in...</div>
}
if (options?.requiredRole && user) {
if (!hasRole(user.role, options.requiredRole)) {
return <div>Access denied. Insufficient permissions.</div>
}
}
return <Component {...props} />
}
}
export { AuthContext }