รู้จักกับ Next Auth + Prisma
[ สามารถดู video ของหัวข้อนี้ก่อนได้ ดู video
Next Auth คืออะไร ?
NextAuth.js (https://next-auth.js.org/) ****คือ library ระบบยืนยันตัวตนแบบ open-source ที่ออกแบบมาสำหรับ Next.js โดยเฉพาะ library นี้ช่วยแก้ปัญหาในการติดตั้งระบบยืนยันตัวตนและระบบให้สิทธิ์ผู้ใช้งาน โดยรองรับระบบการยืนยันตัวตนหลากหลายรูปแบบ ไม่ว่าจะเป็น OAuth 1.0, 1.0A, 2.0, OpenID Connect การยืนยันตัวตนด้วยอีเมล และการยืนยันตัวตนแบบไร้รหัสผ่าน (passwordless) ได้เช่นเดียวกัน
โดยจุดเด่นของ NextAuth.js มีตั้งแต่
- สามารถเชื่อมต่อกับผู้ให้บริการยอดนิยม ไม่ว่าจะเป็น Google, Facebook และอื่น ๆ อีกมากมาย
- มีตัวเลือกให้ใช้งาน Session ร่วมกับฐานข้อมูล หรือ JSON Web Tokens (JWT) ในการเก็บข้อมูล Session เอาไว้ โดยรองรับทั้ง Session บน Client และ Server
- มี feature ด้าน security ที่ได้รับการออกแบบตาม practice ของ security อยู่แล้ว เช่น signed, prefixed, server-only cookies, การตรวจสอบ CSRF Token บนการส่งข้อมูลแบบ HTTP POST รวมถึง การใช้ JWT ร่วมกับ JWS/JWE/JWK สำหรับการเข้ารหัสด้วย
- อนุญาตให้ปรับแต่งหน้าลงชื่อเข้าใช้และออกจากระบบได้ รวมถึงมี function callback สำหรับจัดการ Event ต่างๆ ที่เกิดขึ้นระหว่างการเข้าสู่ระบบได้
NextAuth.js นั้นจุดเด่นจริงๆคือ ช่วยลดความยุ่งยากในการติดตั้งระบบ authentication ให้กับ application Next.js โดยทำให้กระบวนการต่าง ๆ ไม่ว่าจะเป็นการจัดการ Session ขั้นตอนเข้าสู่ระบบ/ออกจากระบบ และการจัดการข้อมูล user ทำได้ง่ายขึ้นผ่าน NextAuth.js
ทีนี้ในปัจจุบัน หากเราเข้า document ของ NextAuth.js ก็จะเจอว่า NextAuth.js ได้กลายมาเป็นส่วนหนึ่งของ Auth.js (https://authjs.dev/) ไปเป็นที่เรียบร้อยแล้ว โดย Auth.js คือ open source package ที่ได้เตรียม library สำหรับการทำ authentication ของ web application เอาไว้ โดย design ไว้ให้ใช้สำหรับ “modern web framework ใดๆก็ได้” แทน ทำให้เราสามารถใช้ idea ของการ implement ที่แต่เดิมมีอยู่แค่ใน NextAuth.js สามารถใช้งานกับ framework ใดก็ไ ด้ โดยเปลี่ยนมาเรียกใช้ผ่าน Auth.js แทน
เพราะฉะนั้น แม้ว่าในหัวข้อนี้เราจะพูดถึง NextAuth.js ก็ตามแต่ library หลายๆตัว (เช่น adapter สำหรับการต่อ database) ได้โดยย้ายไปที่ Auth.js เป็นที่เรียบร้อย ดังนั้นให้สังเกตให้ดีว่า library ทีใช้อยู่นั้นอยู่ใน NextAuth.js หรือ Auth.js เพื่อให้สามารถอ่าน document จากถูกที่ได้เช่นกัน
เราจะทำอะไรกันในหัวข้อนี้บ้าง
เพื่อให้เห็นภาพการใช้งาน NextAuth.js เราจะมาลองทำระบบ Sign in / Sign up ด้วย Email / Password กันก่อน ทีนี้เพื่อให้ต่อเนื่องกับหัวข้อที่เราเคยทำมาของ Next.js เราจะขอหยิบ PostgreSQL มาเป็น database และ Prisma มาเป็น ORM สำหรับการต่อพูดคุยกับ database (เนื่องจาก NextAuth.js มี adapter ที่ support กับ Prisma อยู่แล้ว ทำให้เราสามาร ถใช้งาน Prisma กับ NextAuth โดยตรงได้เลย
โจทย์ของเราในวันนี้คือ เราจะทำ 3 อย่างกัน
-
Sign in / Sign up ด้วย Prisma และ NextAuth ผ่าน Email และ Password
-
ลองใช้งานร่วมกับ Role เพื่อดึงข้อมูลอื่นๆมาใช้งานร่วมกัน (สำหรับการควบคุมสิทธิ์)
-
ลองใช้งานร่วมกับ Social Login อย่าง Google และทำให้สามารถใช้งานร่วมกับ Email / Password ได้
เริ่มต้น เราจะทำการ setup project กันด้วยท่าประจำเอกสารของ Next.js นั่นคือ
npx create-next-app@latest <ชื่อ project next>
โดย project นี้เราจะขอเลือกเป็น javascript เพื่อให้ทุกคนสามารถจับ concept จาก javascript กันก่อน (รวมถึงตัวอย่าง typescript มีเยอะมากแล้วด้วย) หลังจาก create project มาเรียบร้อย ให้ทำการ
npm install
npm run dev
หาก run เว็บได้ที่ localhost:3000 ก็ถือว่าพร้อมสำหรับ Next.js และ Step ต่อมาเราจะทำการลง Database ไว้ให้พร้อม ซึ่งเป็นวิธีการเดียวกันกับบทความ Prisma ORM ที่ใช้ในหัวข้อก่อนหน้านี้
https://blog.mikelopster.dev/next-prisma/
ดังนั้น docker ที่ใช้สำหรับการ run postgres จะเป็นตัวเดียวกันกับในบทความของ Prisma ให้ทำการวาง docker-compose.yml ใน root project
version: '3.8'
services:
postgres:
environment:
POSTGRES_USER: myuser
POSTGRES_PASSWORD: mypassword
POSTGRES_DB: mydb
ports:
- "5432:5432"
volumes:
- postgres-data:/var/lib/postgresql/data
pgadmin:
image: dpage/pgadmin4
environment:
PGADMIN_DEFAULT_EMAIL: [email protected]
PGADMIN_DEFAULT_PASSWORD: root
ports:
- "5050:80"
depends_on:
- postgres
volumes:
postgres-data:
และทำการ run ด้วย docker-compose up -d --build ขึ้นมา และเมื่อ run ทุกอย่างถูกต้องจะต้องได้ผลลัพธ์แบบนี้ออกมา
ตอนนี้เราก็มีทั้ง project Next.js และ database (PostgreSQL) พร้อมแล้วเรียบร้อย มาเข้าสู่ step สุดท้ายคือการสร้าง Schema ของ Prisma เริ่มต้นเอาไว้ก่อน (ก่อนที่เราจะเพิ่มต่อในหัวข้อถัดไป)
สุดท้ายให้ทำการเริ่มต้น Schema file ของ Prisma ขึ้นม าด้วยการลง package Prisma และ ทำการ init ผ่านคำสั่งของ prisma ออกมา
npm install prisma --save-dev
npm install @prisma/client # สำหรับใช้ใน Next.js ทำการลงไว้ก่อนเลย
หลังจากนั้น ให้ทำการพิมพ์คำสั่งนี้เพื่อเริ่มต้น schema file ของ Prisma ออกมา
npx prisma init
เพียงเท่านี้ก็จะได้ไฟล์เริ่มต้นของ Schema ออกมาเป็นที่เรียบร้อยเป็นอันเสร็จพิธีกรรมการติดตั้ง Next.js + PostgreSQL + Prisma
1. Sign In / Sign up ด้วย Email
สำหรับโจทย์แรกสิ่งที่เราจะต้องทำคือ
- ต้องเพิ่ม database สำหรับการเก็บ account user เอาไว้ โดยจะต้องเก็บ email เป็น credential และ password เป็นตัวเช็ค
- เพิ่ม API สำหรับการทำ Login ผ่าน NextAuth และการ Sign up ผ่าน Prisma
- เพิ่ม Frontend สำหรับหน้าจอของการ Login (เป็นตัวอย่างไว้ให้ สำหรับหน้า Register สามารถไปเพิ่มต่อเองได้)
และนี่คือ structure เริ่มต้นของโจทย์ (หยิบมาแค่ตัวสำคัญๆเท่านั้น)
.
├── app
│ ├── api
│ │ └── auth
│ │ ├── [...nextauth]
│ │ │ └── route.js --> API สำหรับ NextAuth
│ │ └── signup
│ │ └── route.js --> API สำหรับ Sign up
│ ├── components
│ │ └── SessionProvider.jsx
│ ├── layout.js
│ ├── page.js --> สำหรับหน้าหลัก Login
│ └── profile --> สำหรับหน้าแสดง Profile หลัง Login
│ └── page.js
├── docker-compose.yml
├── prisma
│ ├── migrations --> สำหรับเก็บ migration
│ └── schema.prisma
└── tailwind.config.js
จาก Structure ด้านบนเรามีการแบ่งส่วนออกจากกันตามนี้
- folder
apiสำหรับการเก็บ API ทั้งหมดเอาไว้โดยจะเก็บ API ของ NextAuth และ Signup (ผ่าน Prisma) เอาไว้ - folder
componentจะทำการเก็บSessionProviderที่ใช้สำหรับเก็บ session ของการ Login ไว้ (เดี๋ยวเรามาอธิบายเพิ่มอีกที) - ที่เหลือก็จะเป็นการแยกหน้าแต่ละหน้าผ่าน Page Component (
page.js) ทั้งหน้า Login และหน้า Profile (สำหรับการ Sign up จริงๆไอเดียจะคล้ายๆกับการยิง API ทั่วไปจะขอไม่ทำใน Session นี้ สามารถใช้ idea ของหัวข้อ Prisma มาประยุกต์ใช้ได้เลย)
เราจะเริ่มต้นจากการสร้าง database กันก่อน
สร้าง database สำหรับเก็บ user
เริ่มต้นสร้าง schema ของ User ขึ้นมาผ่าน prisma/schema.prisma เพื่อสร้าง table สำหรับเก็บ email และ password
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id Int @id @default(autoincrement())
name String?
email String @unique
password String
image String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
โดย model User นั้นได้ทำการสร้าง email, password สำหรับเก็บเป็นข้อมูลของการ login ไว้ (เก็บเป็น string) และมีการเก็บ name (ชื่อ), image (ภาพของ user) เอาไว้เป็น optional และ createdAt (วันที่สร้าง), updatedAt (วันที่มีการ update) สำหรับเป็นวันที่อ้างอิงในกรณีที่มีการแก้ไขข้อมูล
step ต่อมาต้องมี config สำหรับการชี้ไปยัง database นั่นก็คือ DATABASE_URL โดยการเพิ่มผ่าน .env เข้ามา
DATABASE_URL="postgresql://myuser:mypassword@localhost:5432/mydb?schema=public"
หลังจากนั้นทำการ sync database โดยการ migrate model เข้า PostgreSQL เพื่อสร้าง table user ขึ้นมา
npx prisma migrate dev --name init
เมื่อ run คำสั่งแล้วก็จะเจอ table user ขึ้นมาใน postgreSQL (ผ่าน pgAdmin) หากมี table user และ column แสดงออกมาถูกต้องตาม model User ถ ือว่าทำได้ถูกต้องแล้ว
step ต่อมาเราพร้อมสำหรับการสร้าง API สำหรับเก็บข้อมูล user แล้ว
สร้าง API สำหรับ Signup
เราจะเริ่มทำจาก idea ง่ายๆแบบนี้คือ
- เราจะสร้าง api หนึ่งเส้นขึ้นมา POST
/api/auth/signupโดยจะทำการรับข้อมูล name, email และ password ผ่าน API เข้ามา - โดย password นั้นจะทำการเข้า hash ด้วยวิธีการ bcrypt เพื่อทำการเข้ารหัส password ทางเดียว (เพื่อใช้สำหรับแค่เปรียบเทียบใน database และไม่สามารถถอดรหัสย้อนกลับมาได้)
ดังนั้น เพิ่ม API ขึ้นมาด้วยวิธี Route Handler ของ Next.js ที่ app/api/auth/signup/route.js
// pages/api/auth/signup.js
import { PrismaClient } from '@prisma/client'
import bcrypt from 'bcrypt'
const prisma = new PrismaClient()
export async function POST(request) {
try {
const { email, password, name } = await request.json()
const hashedPassword = bcrypt.hashSync(password, 10)
const user = await prisma.user.create({
data: {
email,
password: hashedPassword,
name,
},
})
return Response.json({ message: 'User created', user })
} catch (error) {
return Response.json({ error: 'User could not be created' })
}
}
อธิบายจาก code
- code ส่วนนี้เป็น API route handler method POST สำหรับฝั่ง server โดยทำหน้าที่เฉพาะในส่วนของการสมัครใช้งานของผู้ใช้
- มีการ import
PrismaClient****จาก@prisma/client****เพื่อใช้ติดต่อกับฐานข้อมูล และ bcrypt สำหรับการเข้ารหัส (hashing) รหัสผ่าน - มีการสร้างตัวแปร
prismaเพื่อเก็บ instance ของPrismaClientที่เป็นตัวแทนของการเรียกใช้งาน database ผ่าน Prisma ORM - โดย API นี้มีการรับข้อมูล email, password และ name ผ่าน JSON body เข้ามา โดย password นั้นมีการเข้า hash password ผ่าน
bcrypt - หลังจากจัดการข้อมูลเรียบร้อยก็ทำการ save ข้อมูลผ่าน
prismaเข้าไปใน user เพื่อสร้างเป็น record ใหม่ผ่านprisma.user.createเข้าไป
เมื่อสร้าง API เรียบร้อยให้ลองทดสอบยิงผ่าน postman ดู หากลองยิงผ่าน postman แล้วมาตรวจสอบผ่าน database ว่าข้อมูล user เข้าเรียบร้อย และเข้ารหัส password แล้วก็ถือว่า API สำหรับการสมัครเสร็จสิ้นเป ็นที่เรียบร้อย
step ต่อมา เราจะลองนำข้อมูลที่ทำการสมัครเข้าไปมาทำการลองยิงผ่าน Login กันบ้าง
สร้าง API สำหรับ NextAuth
สำหรับการ Login นั้น จะแตกต่างกัน Sign up ตรงที่เราจะทำผ่าน NextAuth.js เลย เนื่องจาก จะได้ใช้ feature ของการตรวจสอบ CSRF Token และการ login ด้วย Session และ JWT ไปในตัวเลย (โดยที่ไม่ต้องมา implement เอง) โดยเราจะทำการเพิ่มไปยัง path app/api/auth/[...nextauth]/route.js
เวลาที่เราใช้ NextAuth.js การเพิ่ม route ในรูปแบบ app/api/auth/[...nextauth]/route.js จะเป็นเหมือนข้อตกลง (convention) ในการ setting routes สำหรับระบบ authentication ซึ่ง NextAuth.js จะนำ routes เหล่านี้ไปใช้จัดการขั้นตอนการ authentication ต่างๆ ซึ่ง NextAuth.js ก็ใช้ route เหล่านี้สำหรับการ signing in, signing out, callbacks ในการทำ Authentication ออกมาได้
โดยหน้าตาของ code ที่เพิ่มไป ก็จะประมาณนี้
import NextAuth from 'next-auth'
import CredentialsProvider from 'next-auth/providers/credentials'
import { PrismaClient } from '@prisma/client'
import bcrypt from 'bcrypt'
import { PrismaAdapter } from '@auth/prisma-adapter'
const prisma = new PrismaClient()
export const authOptions = {
providers: [
CredentialsProvider({
name: 'Credentials',
credentials: {
email: { label: 'Email', type: 'email', placeholder: '[email protected]' },
password: { label: 'Password', type: 'password' },
},
async authorize(credentials, req) {
if (!credentials) return null
const user = await prisma.user.findUnique({
where: { email: credentials.email },
})
if (
user &&
(await bcrypt.compare(credentials.password, user.password))
) {
return {
id: user.id,
name: user.name,
email: user.email
}
} else {
throw new Error('Invalid email or password')
}
},
})
],
adapter: PrismaAdapter(prisma),
session: {
strategy: 'jwt',
},
callbacks: {
jwt: async ({ token, user }) => {
if (user) {
token.id = user.id
}
return token
},
session: async ({ session, token }) => {
if (session.user) {
session.user.id = token.id
}
return session
}
},
}
const handler = NextAuth(authOptions)
export { handler as GET, handler as POST }
จาก code ตัวอย่างนี้แสดงวิธีการ setting NextAuth.js โดย code ได้กำหนดค่าเพื่อใช้การ authentication ผ่านผู้ใช้แบบ Custom Credentials (ใช้ อีเมล และ รหัสผ่าน) พร้อมเชื่อมต่อกับฐานข้อมูลผ่าน Prisma เข้ามา โดย
- มีการเรียกใช้ NextAuth จาก library
next-authสำหรับระบบยืนยันตัวตน (authentication) - CredentialsProvider จาก
next-auth/providers/credentialsเพื่อสร้างระบบยืนยันตัวตนแบบกำหนดเองโดยอ ้างอิงจากข้อมูลของผู้ใช้ (credentials) - PrismaClient จาก
@prisma/clientเพื่อใช้ติดต่อกับฐานข้อมูล และ bcrypt สำหรับการเข้ารหัส (hashing) และตรวจสอบความถูกต้องของรหัสผ่าน - PrismaAdapter จาก
@auth/prisma-adapterเพื่อเชื่อมต่อ NextAuth เข้ากับ Prisma ORM
โดย การตั้งค่า NextAuth นั้น มีการเรียกใช้ NextAuth พร้อมกับ object สำหรับการกำหนด config ต่างๆเอาไว้ โดยมี
- Providers array ที่ระบุวิธีการยืนยันตัวตน (authentication providers) โดยเคสนี้ เราใช้เพียง
CredentialsProviderซึ่งหมายถึงการเปิดให้ผู้ใช้สามารถเข้าสู่ระบบโดยใช้อีเมลและรหัสผ่าน - function
authorizeจะรับข้อมูลของผู้ใช้ (credentials) ที่ถูกส่งมา พร้อมกับ request object (user และ password) จากนั้นจะค้นหาผู้ใช้ในฐานข้อมูลโดยใช้อีเมล และใช้bcryptเทียบรหัสผ่านกับฐานข้อมูล หากข้อมูลทุกอย่างถูกต้องจะส่งกลับข้อมูลของผู้ใช้ (user object) กลับไป - Adapter ทำการเรียกใช้งาน
PrismaAdapterเพื่อเชื่อมต่อ NextAuth เข้ากับ Prisma ORM ซึ่งจะทำให้ NextAuth สามารถสื่อสารกับฐานข้อมูลผ่าน Prisma ได้ - Session ใช้สำหรับกำหนด รูปแบบของ session ซึ่งใน code นี้เราจะใช้เป็น
jwtหมายถึงจะใช้ JSON Web Tokens ในการจัดการ session - Callbacks หรือ function ที่จะทำการดำเนินการหลังจาก authentication เรียบร้อย โดยกำหนด callback 2 ตัวคือ
- jwt จะถูกเรียกใช้เมื่อใดก็ตามที่มีการสร้างหรือ update JWT โดยถ้ามีข้อมูล
userobject จะมีการเพิ่ม user ID เข้าไปใน token ด้วย - session จะถูกเรียกใช้เมื่อใดก็ตามที่มีการตรวจสอบ session หลัง authentication เสร็จ โดยจะเพิ่ม ID ของผู้ใช้ลงใน session object (ไว้สำหรับเป็น identity ของ user)
- jwt จะถูกเรียกใช้เมื่อใดก็ตามที่มีการสร้างหรือ update JWT โดยถ้ามีข้อมูล
- สุดท้าย เมื่อประกอบของทั้งหมดเรียบร้อย จะมีการส่งออก handler ของ NextAuth ซึ่งได้รับการตั้งค่าไว้เป็นทั้ง GET และ POST เพื่อรับมือกับ HTTP method สำหรับ routes ต่างๆที่ใช้ในการยืนยันตัวตน เนื่องจาก NextAuth.js จำเป็นต้องจัดการใช้ทั้ง GET (เช่น ตอนดึง session ปัจจุบัน) และ POST (เช่น การเข้าสู่ระบบ)
โดยสรุปทั้งหมดนั้น code ส่วนนี้ได้ทำการตั้งค่าระบบ authentication ด้วย NextAuth.js โดยทำการ custom credentials provider (ใช้เป็น email, password) ร่วมกับ Prisma และ bcrypt เพื่อจัดการการเข้าสู่ระบบของผู้ใช้ด้วยอีเมลและรหัสผ่าน นอกจากนี้ยังได้มีการกำหนดค่า JWT เพื่อใช้ในการจัดการ session และปรับแต่งการจัดการ token และ session ให้ตรงความต้องการด้วยการใช้ callbacks ออกมา
และเพื่อให้ใช้งานทั้งหมดออกมาได้จำเป็นต้องมีการเพิ่ม secret (สำหรับใช้งานใน jwt token ซึ่ง NextAuth จะเป็นคนนำไปใช้งาน) และ nextauth_url สำหรับกำหนด base url ของ ระบบ ดังนั้น เพิ่มเข้าไปใน .env ตามนี้
NEXTAUTH_SECRET=98E3B2CC28F61492C6934531C828C
NEXTAUTH_URL=http://localhost:3000/
โดยทั้ง 2 ส่วนนี้สามารถอ่านเพิ่มเติมจาก docs ต้นฉบับได้
- https://next-auth.js.org/providers/credentials = สำหรับ config credentials
- https://authjs.dev/reference/adapter/prisma = สำหรับ config adapter prisma
เพียงเท่านี้ ส่วนของการ login ก็เป็นการ implement เรียบร้อย step ต่อไปเราจะมาลองเชื่อม login เพื่อทดสอบการ login กัน
ทำ Frontend สำหรับ Login และดึง Profile
หลังจากที่ implement sign in เรียบร้อย เราจะลองนำ sign in ที่ implement ผ่าน NextAuth มาใช้กัน โดย file ที่เกี่ยวข้องจะเกี่ยวข้องกันทั้งหมด 2 หน้าใหญ่ๆคือ
- หน้า Login (หน้าแรกสุด) สำหรับการเชื่อมต่อ Login และเข้าสู่ระบบผ่าน NextAuth
- หน้า Profile (path: /profile) สำหรับการดึงข้อมูลผ่าน Session ที่เก็บไว้ผ่าน NextAuth
.
├── app
├── layout.js --> เพิ่ม Session Login
├── page.js --> สำหรับหน้าหลัก Login
└── profile --> สำหรับหน้าแสดง Profile หลัง Login
└── page.js
เริ่มต้นเราจะลองเพิ่มหน้า Login ออกมา ที่ app/page.js
// app/page.js
'use client'
import { useState } from 'react'
import { signIn } from 'next-auth/react'
import { useRouter } from 'next/navigation'
export default function SignIn() {
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const router = useRouter()
const handleSubmit = async (e) => {
e.preventDefault()
try {
const result = await signIn('credentials', {
redirect: false,
email,
password,
})
if (result.error) {
console.error(result.error)
} else {
router.push('/profile')
}
} catch (error) {
console.log('error', error)
}
}
return (
<div className="flex h-screen items-center justify-center">
<form
onSubmit={handleSubmit}
className="bg-white p-6 rounded-md shadow-md"
>
<div className="mb-4">
<label htmlFor="email">Email</label>
<input
id="email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
className="w-full border border-gray-300 px-3 py-2 rounded" // Added border
/>
</div>
<div className="mb-4">
<label htmlFor="password">Password</label>
<input
id="password"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
className="w-full border border-gray-300 px-3 py-2 rounded" // Added border
/>
</div>
<button
type="submit"
className="w-full bg-blue-500 text-white py-2 rounded mb-4"
>
Sign In
</button>{' '}
</form>
</div>
)
}
และที่หน้า Profile app/profile/page.js
'use client'
import { useSession, signOut } from 'next-auth/react'
import { useRouter } from 'next/navigation'
import { useEffect } from 'react'
export default function Profile() {
const { data: session, status } = useSession()
const router = useRouter()
useEffect(() => {
if (status === 'unauthenticated') {
router.push('/')
}
}, [status, router])
// When after loading success and have session, show profile
return (
status === 'authenticated' &&
session.user && (
<div className="flex h-screen items-center justify-center">
<div className="bg-white p-6 rounded-md shadow-md">
<p>
Welcome, <b>{session.user.name}!</b>
</p>
<p>Email: {session.user.email}</p>
<p>Role: {session.user.role}</p>
<button
onClick={() => signOut({ callbackUrl: '/' })}
className="w-full bg-blue-500 text-white py-2 rounded"
>
Logout
</button>
</div>
</div>
)
)
}
สุดท้ายเพื่อให้เรียกใช้งาน Session ได้จากทุกจุด เรียกใช้งาน SessionProvider จาก Layout แต่เพื่อให้ Layout นั้นไม่ต้องแปลงออกมาเป็น Client component (เนื่องจาก SessionProvider ใช้ Context API ที่ใช ้งานได้เฉพาะ React บน Client เท่านั้น) เราจึงมีการแยกเป็น client component ออกมา และเรียกใช้งานผ่านการ import จาก component เข้ามาแทน
ดังนั้นที่ app/components/SessionProvider.jsx ทำการ import SessionProvider และทำการ export ออกไป
'use client'
import { SessionProvider } from 'next-auth/react'
export default SessionProvider
หลังจากนั้นที่ app/layout.js ทำการเรียกใช้งาน SessionProvider เข้ามา
import { Inter } from 'next/font/google'
import { getServerSession } from 'next-auth'
import SessionProvider from './components/SessionProvider'
import './globals.css'
const inter = Inter({ subsets: ['latin'] })
export const metadata = {
title: 'Create Next App',
description: 'Generated by create next app',
}
export default async function RootLayout({ children}) {
const session = await getServerSession()
return (
<html lang="en">
<body className={inter.className}>
<SessionProvider session={session}>{children}</SessionProvider>
</body>
</html>
)
}
ทีนี้ เมื่อรวมทุกอย่างเรียบร้อย ลองทดสอบ Login ดู
2. เพิ่ม Role เข้า Token
โจทย์ต่อมา เราจะลองเพิ่มความสามารถให้ NextAuth สามารถใช้งานร่วมกับ Role ได้ ดังนั้น สิ่งที่เราจะทำ เราจะทำทั้งหมด 3 Step คือ
- เพิ่ม field
roleเข้า database - ทำการใส่
roleเข้าไปใน token และ session - เรียกใช้งานผ่าน Session ในแต่ละจุด
เราจะมาลองไล่ทำทีละ step กัน
เพิ่ม role เข้า database
step แรกให้ทำการเพิ่ม column role เข้า User เพื่อเพิ่มการใช้งาน role โดยทำการกำหนดเอาไว้ว่า ทุกคนที่สมัครเข้ามาใหม่ จะต้องเป็น role member ก่อนเสมอ
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id Int @id @default(autoincrement())
name String?
email String @unique
password String
image String?
role String @default("member") // Add a role field
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
หลังจากนั้น ให้ทำการ migrate database เพื่อเพิ่ม role เข้า database เข้าไป
npx prisma migrate dev --name add-role
และนี่คือ ผลลัพธ์ผ่าน database ก็จะเจอว่า table User มี column ใหม่ โผล่มาเรียบร้อย