Dalam artikel blog ini, kami akan memandu Anda melalui proses langkah demi langkah dalam membuat autentikasi role-based untuk aplikasi Next.js Anda menggunakan Next Auth dan Prisma adapter. Anda akan memiliki fondasi yang kuat untuk membangun sistem kontrol akses pengguna yang fleksibel dan dapat diskalakan pada akhirnya.
Dalam project ini, kita akan menggunakan Next.js App Directory Seperti yang Anda ketahui, setelah Next.js 13, kita membuat rute API menggunakan App Directory dan file route.
Mari kita instal library autentikasi dan mulai membuat auth API route.
npm install next-auth
Untuk menambahkan Next Auth.js ke dalam project, buatlah sebuah file bernama route.ts di folder app/api/auth/[…nextauth].
Anda dapat langsung menambahkan opsi auth Anda di file ini, tetapi saya lebih suka menggunakan folder yang berbeda agar dapat menggunakan kembali opsi tersebut nantinya.
Mari buat file auth.ts di direktori src/utils dan tambahkan provider.
import { NextAuthOptions } from "next-auth";
import GoogleProvider from "next-auth/providers/google";
export const authOptions: NextAuthOptions = {
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_ID!,
clientSecret: process.env.GOOGLE_SECRET!,
}),
],
};
Jika Anda ingin menambahkan provider lain seperti GitHub, Facebook.
Kita sekarang siap untuk membuat route handler dan menambahkan beberapa opsi ini. Buka file route dan tambahkan ini.
app/api/auth/[…nextauth]/route.ts
import { authOptions } from "@/utils/auth";
const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };
Sekarang, kita bisa masuk menggunakan akun Google. Tetapi kita tidak menyimpan pengguna dalam basis data. Jadi, mari kita instal Prisma dan buat skema autentikasi kita.
npm install prisma @prisma/client @next-auth/prisma-adapter
untuk menginisialisasinya:
npx prisma init
Buka file schema.prisma di folder prisma dan tambahkan blok kode ini:
datasource db {
//You can use any database provider. It doesn't have to be PostgreSQL
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model Account {
id String @id @default(cuid())
userId String
type String
provider String
providerAccountId String
refresh_token String? @db.Text
access_token String? @db.Text
expires_at Int?
token_type String?
scope String?
id_token String? @db.Text
session_state String?
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([provider, providerAccountId])
}
model Session {
id String @id @default(cuid())
sessionToken String @unique
userId String
expires DateTime
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}
model User {
id String @id @default(cuid())
name String?
email String @unique
image String?
isAdmin Boolean @default(false)
emailVerified DateTime?
accounts Account[]
sessions Session[]
}
model VerificationToken {
identifier String
token String @unique
expires DateTime
@@unique([identifier, token])
}
Kami menggunakan skema standar ini untuk Prisma adapter; Anda dapat menemukannya di dokumentasi resmi.
Saya hanya menambahkan field isAdmin pada model pengguna untuk memberikan role kepada user. Saya menggunakan boolean karena aplikasi saya hanya memiliki dua role (admin/user reguler). Tapi jika Anda ingin, Anda dapat membuat lebih banyak role dengan mengubah field tersebut.
Ini akan membuat file migrasi SQL dan menjalankannya:
npx prisma migrate dev
Mari kita kembali ke opsi auth dan menambahkan prisma adapter.
src/utils/auth.ts
const prisma = new PrismaClient();
export const authOptions: NextAuthOptions = {
adapter: PrismaAdapter(prisma),
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_ID!,
clientSecret: process.env.GOOGLE_SECRET!,
}),
],
};
Mulai sekarang, adaptor akan menangani autentikasi dan secara otomatis menambahkan user, session, dan account baru ke dalam database.
Tetapi ketika kita mencoba untuk bereaksi terhadap pengguna menggunakan session (hook useSession untuk sisi klien dan getServerSession untuk sisi server), ia hanya akan mengembalikan name, email, dan image. Tetapi kita membutuhkan properti isAdmin untuk autorisasi.
Untuk melakukan itu, pertama-tama kita harus menemukan user dalam database dan menambahkan nilai isAdmin ke session. Mari tambahkan kode berikut ini:
export const authOptions: NextAuthOptions = {
adapter: PrismaAdapter(prisma),
session: {
strategy: 'jwt',
},
providers: [{
clientId: process.env.GOOGLE_ID!,
clientSecret: process.env.GOOGLE_SECRET!,
},
],
callbacks: {
async session({ token, session }) {
if (token) {
session.user.isAdmin = token.isAdmin;
}
return session;
},
async jwt({ token }) {
const dbUser = await prisma.user.findUnique({
where: {
email: token.email!,
},
});
token.isAdmin = Boolean(dbUser?.isAdmin);
return token;
},
},
};
Pada callback jwt, kita mengambil nilai isAdmin dari database dan menyembunyikannya di JWT. Dan dalam callback session, kita mengambil token, mencari nilai isAdmin yang telah kita tetapkan, dan menambahkannya ke dalam session. Dan perhatikan ketika kita mencoba mengakses session, session akan mengembalikan properti isAdmin bersama dengan properti pengguna lainnya.
Jika Anda mengalami masalah pada titik ini, coba bersihkan cache di folder .next dan cookie di browser.