ข้ามไปยังเนื้อหา

Authentication Patterns

ทำ optimistic check เบา ๆ ใน middleware เพื่อ redirect request ที่ไม่ auth ชัด ๆ ออกไป แต่ทำ authorization จริงให้ใกล้ data ที่สุด ใน Data Access Layer ที่ verify session ก่อนจะ return อะไรออกมา

auth ส่วนใหญ่วิ่งอยู่บน session ที่เก็บใน cookie แบบ HTTP-only ฝั่ง server เราอ่าน cookie นั้นด้วย cookies() จาก next/headers

// app/lib/session.ts — read the session cookie on the server
import { cookies } from 'next/headers'
import { decrypt } from '@/app/lib/crypto'
export async function getSession() {
const cookie = (await cookies()).get('session')?.value
if (!cookie) return null
return decrypt(cookie)
}

middleware เป็นที่ที่เหมาะกับ gate แบบ optimistic ราคาถูก ถ้าไม่มี session ชัด ๆ ก็ redirect ก่อน render วิธีนี้เร็วก็จริง แต่แค่ redirect ห้ามใช้เป็นด่านเดียว

// middleware.ts — optimistic redirect only
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
const protectedRoutes = ['/dashboard']
export function middleware(request: NextRequest) {
const path = request.nextUrl.pathname
const hasSession = request.cookies.has('session')
if (protectedRoutes.some((p) => path.startsWith(p)) && !hasSession) {
return NextResponse.redirect(new URL('/login', request.url))
}
return NextResponse.next()
}

middleware อย่างเดียวไม่พอ ไม่ได้รันบนทุก data path และ layout ก็ไม่ได้ re-run ทุกครั้งที่ navigate เพราะฉะนั้น request ที่เก่าหรือถูกปลอมอาจหลุดผ่านไปได้ ทางแก้คือ Data Access Layer (DAL) จุดเดียวที่ verify session ก่อน return data ห่อด้วย React cache เพื่อให้รันครั้งเดียวต่อ request

// app/lib/dal.ts — verify the session close to the data
import 'server-only'
import { cache } from 'react'
import { redirect } from 'next/navigation'
import { getSession } from '@/app/lib/session'
export const verifySession = cache(async () => {
const session = await getSession()
if (!session?.userId) {
redirect('/login')
}
return { isAuth: true, userId: session.userId }
})

จากนั้นทุก page, Server Action หรือ route handler ที่แตะ data ที่ป้องกันไว้จะเรียก verifySession() ก่อน สำหรับโปรเจกต์จริง library อย่าง NextAuth (Auth.js), Clerk หรือ Lucia จัดการ session, provider และ token ให้ แต่ pattern DAL สำหรับ authorization ก็ยังต้องวางทับ library พวกนี้อยู่ดี

graph TD
  A["Request"] --> B["middleware: optimistic redirect"]
  B --> C["Page / Server Action"]
  C --> D["verifySession() in DAL"]
  D -->|no session| E["redirect to /login"]
  D -->|valid| F["Return protected data"]
Optimistic gate in middleware, real check in the DAL
How do you read the session cookie on the server?
What should middleware do for auth?
Why is middleware alone not sufficient for authorization?
Where does the authoritative authorization check belong?