Login और Authentication
Login कैसे काम करता है
Packages और users table
npm install jose bcryptjs # .env.local – a long random secret (generate with: openssl rand -base64 32) SESSION_SECRET=ZmJ4b3Y5c2VjcmV0LWtleS1jaGFuZ2UtbWUtMTIzNDU2
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(80) NOT NULL,
email VARCHAR(120) NOT NULL UNIQUE,
password_hash VARCHAR(100) NOT NULL,
role ENUM('student','admin') DEFAULT 'student'
);
-- create a hash in Node: require('bcryptjs').hashSync('admin123', 10)lib/session.js – signed cookie
Session एक छोटा JWT (JSON Web Token) है जो आपके secret से signed है। कोई भी signature तोड़े बिना role: 'student' को 'admin' नहीं बना सकता।
// lib/session.js
import 'server-only'
import { SignJWT, jwtVerify } from 'jose'
import { cookies } from 'next/headers'
const key = new TextEncoder().encode(process.env.SESSION_SECRET)
const COOKIE = 'session'
export async function createSession(user) {
const expires = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000) // 7 days
const token = await new SignJWT({ id: user.id, name: user.name, role: user.role })
.setProtectedHeader({ alg: 'HS256' })
.setIssuedAt()
.setExpirationTime('7d')
.sign(key)
const store = await cookies()
store.set(COOKIE, token, { httpOnly: true, secure: process.env.NODE_ENV === 'production',
sameSite: 'lax', path: '/', expires })
}
export async function readSession(token) {
if (!token) return null
try {
const { payload } = await jwtVerify(token, key, { algorithms: ['HS256'] })
return payload // { id, name, role, iat, exp }
} catch {
return null // expired or tampered
}
}
export async function getSession() {
const store = await cookies()
return readSession(store.get(COOKIE)?.value)
}
export async function deleteSession() {
const store = await cookies()
store.delete(COOKIE)
}Login Server Action
// app/login/actions.js
'use server'
import bcrypt from 'bcryptjs'
import { redirect } from 'next/navigation'
import { query } from '@/lib/db'
import { createSession, deleteSession } from '@/lib/session'
export async function login(prevState, formData) {
const email = String(formData.get('email') || '').trim().toLowerCase()
const password = String(formData.get('password') || '')
if (!email || !password) return { error: 'Enter email and password.' }
const [user] = await query('SELECT id, name, password_hash, role FROM users WHERE email = ?', [email])
const ok = user && (await bcrypt.compare(password, user.password_hash))
if (!ok) return { error: 'Wrong email or password.' } // same message for both cases
await createSession(user)
redirect(user.role === 'admin' ? '/admin' : '/dashboard')
}
export async function logout() {
await deleteSession()
redirect('/login')
}Login form
// app/login/LoginForm.jsx
'use client'
import { useActionState } from 'react'
import { login } from './actions'
export default function LoginForm() {
const [state, action, pending] = useActionState(login, {})
return (
<form action={action} style={{ display: 'grid', gap: 12, maxWidth: 340 }}>
<input name="email" type="email" placeholder="Email" required />
<input name="password" type="password" placeholder="Password" required />
{state.error && <p style={{ color: 'crimson' }}>{state.error}</p>}
<button className="btn" disabled={pending}>{pending ? 'Checking…' : 'Login'}</button>
</form>
)
}
// app/login/page.jsx
import LoginForm from './LoginForm'
export const metadata = { title: 'Login', robots: { index: false } }
export default function LoginPage() {
return <main className="container section"><h1>Student login</h1><LoginForm /></main>
}Pages protect करना (DAL)
// lib/dal.js — Data Access Layer: every protected read goes through here
import 'server-only'
import { cache } from 'react'
import { redirect } from 'next/navigation'
import { getSession } from './session'
export const requireUser = cache(async () => {
const session = await getSession()
if (!session) redirect('/login')
return session
})
export async function requireAdmin() {
const user = await requireUser()
if (user.role !== 'admin') redirect('/dashboard')
return user
}
// app/dashboard/page.jsx
import { requireUser } from '@/lib/dal'
import { logout } from '../login/actions'
export default async function Dashboard() {
const user = await requireUser()
return (
<main className="container section">
<h1>Welcome, {user.name} 👋</h1>
<form action={logout}><button className="btn light">Logout</button></form>
</main>
)
}proxy.js से तेज़ redirects
Next.js 16 में middleware.js का नाम proxy.js हो गया है। यह page render शुरू होने से पहले ही logged-out users को redirect कर सकता है। इसे पहला gate मानें, अकेला नहीं।
// proxy.js (project root) — runs before matching requests
import { NextResponse } from 'next/server'
import { readSession } from '@/lib/session' // proxy runs on the Node.js runtime in Next.js 16
export async function proxy(request) {
const token = request.cookies.get('session')?.value
const session = await readSession(token)
if (!session) {
const url = new URL('/login', request.url)
url.searchParams.set('next', request.nextUrl.pathname)
return NextResponse.redirect(url)
}
return NextResponse.next()
}
export const config = { matcher: ['/dashboard/:path*', '/admin/:path*'] }Logout
logout action cookie delete करके redirect करता है। छोटे form में use करें: <form action={logout}><button>Logout</button></form>।
Auth libraries (Auth.js, Clerk)
"Login with Google", magic links या 2FA के लिए Auth.js, Better Auth या Clerk जैसी library use करें। जो concepts आपने सीखे (session cookie, server checks, protected routes) वे बिल्कुल वही रहते हैं।
आम गलतियाँ
- Session को
localStorageमें रखना → कोई भी XSS script पढ़ सकती है; httpOnly cookies use करें। - "Email नहीं मिला" और "गलत password" के अलग messages → attackers को accounts ढूँढने में मदद।
- Login सिर्फ़ Client Component में check करना → data फिर भी पहुँच में; server पर check करें।
- छोटा या commit किया हुआ
SESSION_SECRET→ कोई भी sessions forge कर सकता है।
सारांश
- Passwords के लिए bcrypt, sessions के लिए httpOnly cookie में signed JWT।
- Login/logout Server Actions हैं;
requireUser()pages और actions protect करता है। proxy.js= तेज़ redirect gate; data के पास हमेशा दोबारा check करें।
bcrypt hash के साथ admin user बनाइए, /login, /dashboard और /admin बनाइए। Student के रूप में login करके confirm कीजिए कि /admin आपको redirect करता है।
