NextAuth.js Custom Callbacks & Adapters | Prisma Integration | Sinhala Guide

NextAuth.js Custom Callbacks & Adapters | Prisma Integration | Sinhala Guide

ආයුබෝවන්! ✋ ඔන්න ඉතින් අදත් අපි අලුත්, ගොඩක් වැදගත් software engineering මාතෘකාවක් අරගෙන ආවා. Next.js එක්ක web applications හදන අයට user authentication කියන්නේ මගහරින්න බැරි දෙයක්. ඒ වගේම, ඒක ආරක්ෂිතව, පහසුවෙන් implement කරන එකත් අභියෝගයක්. මෙන්න මේකට නියම විසඳුමක් තමයි NextAuth.js කියන්නේ.

NextAuth.js කියන්නේ Next.js applications වලට authentication එක ගොඩක් පහසු කරන library එකක්. ඒකෙන් පුළුවන් Google, GitHub වගේ OAuth providers එක්ක වගේම email/password authentication (credential provider) පවා එකතු කරන්න. හැබැයි, සමහර වෙලාවට අපිට standard functionality එකට වඩා ටිකක් extra දේවල් ඕනේ වෙනවා. උදාහරණයක් විදිහට, user ගේ role එක JWT (JSON Web Token) එකට එකතු කරන්න, නැත්නම් user data database එකේ persist කරන්න ඕනේ වෙනවා. මෙන්න මේ වගේ අවස්ථාවලට තමයි Custom Callbacks සහ Database Adapters කියන concepts දෙක ගොඩක් වැදගත් වෙන්නේ.

මේ tutorial එකෙන් අපි බලමු NextAuth.js වල custom callbacks (signIn, jwt, session) කොහොමද පාවිච්චි කරන්නේ කියලා. ඒ වගේම, user data database එකේ store කරන්න Prisma adapter එක කොහොමද use කරන්නේ කියලාත් අපි ප්‍රායෝගික උදාහරණයක් එක්ක ඉගෙන ගමු. එහෙනම්, අපි පටන් ගමුද?

NextAuth.js කියන්නේ මොකක්ද? 🤔

සරලවම කිව්වොත්, NextAuth.js කියන්නේ Next.js applications වලට robust, secure authentication solutions ඉක්මනින් implement කරන්න පුළුවන් open-source library එකක්. මේකෙන් පුළුවන්:

  • විවිධ authentication providers (Google, Facebook, GitHub, Apple, Email, Credentials) පහසුවෙන් integrate කරන්න.
  • Secure session management කරන්න.
  • JWT support එකක් දෙනවා.
  • Database persistence සඳහා adapters support එකක් දෙනවා.

ඉතින්, Next.js project එකකට authentication එකක් දානවා නම්, NextAuth.js කියන්නේ ගොඩක් හොඳ option එකක්.

Callbacks: ඔබේ Authentication Process එක Personalize කරමු! 🛠️

Callbacks කියන්නේ මොනවද?

Callbacks කියන්නේ NextAuth.js වල authentication flow එකේ යම් යම් අවස්ථාවලදී අපිට custom logic add කරන්න පුළුවන් functions. User කෙනෙක් sign-in වෙනකොට, JWT එකක් generate වෙනකොට, නැත්නම් session එකක් retrieve කරනකොට මේ callbacks trigger වෙනවා. මේවා පාවිච්චි කරලා අපිට පුළුවන් user data modify කරන්න, authorization checks කරන්න, නැත්නම් JWT එකට අමතර data එකතු කරන්න.

NextAuth.js වල තියෙන ප්‍රධාන callbacks තුනක් තමයි signIn, jwt, සහ session.

signIn Callback එක

මේ callback එක trigger වෙන්නේ user කෙනෙක් sign-in වෙන්න හදන හැම වෙලාවකම. මේකෙන් පුළුවන් user කෙනෙක්ට sign-in වෙන්න අවසර දෙන්න (true return කරලා) නැත්නම් sign-in එක block කරන්න (false return කරලා). මේක authorization checks වලට ගොඩක් වැදගත්.

async signIn({ user, account, profile, email, credentials }) {
  // උදාහරණයක්: යම්කිසි email domain එකක අයට විතරක් sign-in වෙන්න අවසර දෙන්න
  if (user.email.endsWith("@yourdomain.com")) {
    return true;
  }
  return false; // අනිත් හැමෝටම sign-in block කරන්න
}

jwt Callback එක

jwt callback එක තමයි මේ tutorial එකේ අපිට ගොඩක්ම වැදගත් වෙන්නේ. මේක trigger වෙන්නේ JWT එකක් create කරනකොට (sign-in වුණාට පස්සේ) නැත්නම් JWT එක update කරන හැම වෙලාවකම. මේකෙන් පුළුවන් අපිට JWT එකට custom data (උදා: user role, permissions) add කරන්න.

මතක තියාගන්න: user object එක මේ callback එකට එන්නේ user කෙනෙක් sign-in වුණාට පස්සේ පළවෙනි පාරට විතරයි. ඊට පස්සේ එන හැම call එකකටම එන්නේ token object එක විතරයි. ඒ නිසා, user object එක තියෙන වෙලාවට database එකෙන් අවශ්‍ය data අරගෙන token එකට add කරගන්න එක තමයි හොඳම පුරුද්ද.

async jwt({ token, user, account, profile }) {
  if (user) {
    // user object එක තියෙන්නේ user කෙනෙක් sign-in වුණාට පස්සේ පළවෙනි පාරට විතරයි.
    // මෙතනදී ඔයාගේ database එකෙන් අමතර user data retrieve කරලා token එකට add කරන්න පුළුවන්.
    token.id = user.id;
    token.role = user.role || "user"; // උදාහරණයක් ලෙස, user ගේ role එක add කිරීම
    token.customData = "My Secret Value";
  }
  return token;
}

session Callback එක

session callback එක trigger වෙන්නේ client side එකේදී useSession() hook එකෙන් session data retrieve කරන හැම වෙලාවකම. මේකෙන් පුළුවන් JWT එකේ තියෙන custom data client side එකට expose කරන්න. ආරක්ෂිත හේතු නිසා, JWT එකේ තියෙන හැම දෙයක්ම client side එකට යවන්න ඕනේ නැහැ. අවශ්‍ය දේවල් විතරක් යවන එක තමයි හොඳ.

async session({ session, token }) {
  // jwt callback එකෙන් ආපු token එකේ තියෙන data session එකට add කරන්න.
  if (token) {
    session.user.id = token.id;
    session.user.role = token.role;
    session.user.customData = token.customData;
  }
  return session;
}

Database Adapters: User Data සුරක්ෂිතව තියාගමු! 💾

Adapters කියන්නේ මොනවද?

NextAuth.js වල adapters කියන්නේ user data (users, accounts, sessions, verification tokens) database එකේ persist කරන්න උදව් කරන abstractions. NextAuth.js වලට විවිධ databases (MongoDB, PostgreSQL, MySQL, SQLite) සඳහා adapters ගොඩක් තියෙනවා. මේවා පාවිච්චි කරලා අපිට පුළුවන් user accounts, sessions වගේ දේවල් database එකේ save කරලා තියාගන්න.

Adapters නැතුව වුණත් NextAuth.js JWT-based sessions එක්ක වැඩ කරනවා, හැබැයි database adapter එකක් පාවිච්චි කරන එක ගොඩක් වාසිදායකයි:

  • Data Persistence: User accounts, sessions, linked OAuth accounts වගේ දේවල් applications restarts වලදී වුණත් නැති වෙන්නේ නැහැ.
  • Account Linking: එක් user කෙනෙක්ට Google, GitHub වගේ providers කිහිපයකින් sign-in වෙන්න පුළුවන්.
  • Email Verification: Email based authentication වලදී email verification tokens manage කරන්න පුළුවන්.

Prisma Adapter එක පාවිච්චි කරමු

මේ tutorial එකේදී අපි database adapter එකක් විදිහට Prisma පාවිච්චි කරනවා. Prisma කියන්නේ Next.js project වලට ගොඩක්ම ජනප්‍රිය, powerful ORM (Object-Relational Mapper) එකක්. අපි SQLite database එකක් එක්ක Prisma පාවිච්චි කරමු. මේක development වලට ගොඩක් පහසුයි.

ප්‍රායෝගික උදාහරණය: Custom JWT සහ Prisma Integration 🧑‍💻

දැන් අපි බලමු මේ concepts දෙක Next.js project එකක කොහොමද implement කරන්නේ කියලා. අපි කරන්නේ user කෙනෙක් sign-in වුණාට පස්සේ user ගේ role එක database එකෙන් අරගෙන JWT එකටත්, ඒ වගේම client side එකේ session එකටත් එකතු කරන එක.

පියවර 1: ව්‍යාපෘතිය ආරම්භ කිරීම

මුලින්ම අපි Next.js project එකක් create කරගෙන අවශ්‍ය dependencies install කරගමු.

npx create-next-app nextauth-prisma-demo --typescript
cd nextauth-prisma-demo

npm install next-auth @prisma/client
npm install -D prisma

පියවර 2: Prisma Setup

ඊළඟට අපි Prisma initialize කරලා database schema එක හදමු. අපි SQLite database එකක් පාවිච්චි කරනවා.

npx prisma init --datasource-provider sqlite

මේකෙන් prisma/schema.prisma කියන file එක සහ .env file එකක් generate වෙනවා. .env file එකේ DATABASE_URL එක මේ විදිහට තියෙනවද කියලා බලන්න:

DATABASE_URL="file:./dev.db"

දැන්, prisma/schema.prisma file එක open කරලා NextAuth.js adapter එකට අවශ්‍ය models එකතු කරන්න. මේවා NextAuth.js docs වල තියෙන standard models:

// prisma/schema.prisma

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "sqlite"
  url      = env("DATABASE_URL")
}

model Account {
  id                String  @id @default(cuid())
  userId            String
  type              String
  provider          String
  providerAccountId String
  refresh_token     String?
  access_token      String?
  expires_at        Int?
  token_type        String?
  scope             String?
  id_token          String?
  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
  emailVerified DateTime?
  image         String?
  // Custom field for user role
  role          String?   @default("user") // <-- අපේ custom field එක
  accounts      Account[]
  sessions      Session[]
}

model VerificationToken {
  identifier String
  token      String   @unique
  expires    DateTime

  @@unique([identifier, token])
}

මෙහිදී, අපි User model එකට role String? @default("user") කියන field එක එකතු කරලා තියෙනවා. මේක තමයි අපේ custom data එක.

දැන් database migrations run කරලා Prisma client එක generate කරගමු:

npx prisma migrate dev --name init
npx prisma generate

පියවර 3: [...nextauth].js ගොනුව සකස් කිරීම

pages/api/auth/[...nextauth].js කියන file එක create කරලා NextAuth.js configure කරමු. අපි මෙතනදී GitHub Provider එකක් උදාහරණයක් විදිහට පාවිච්චි කරනවා. ඔබට කැමති provider එකක් use කරන්න පුළුවන්. ඒ වගේම අපි Prisma Adapter එකත් configure කරනවා.

.env.local file එකට පහත variables එකතු කරන්න. (ඔබට GitHub Developer settings වලින් මේවා ලබාගන්න පුළුවන්.) NEXTAUTH_SECRET එක security එකට අනිවාර්යයි, මේක strong, random string එකක් වෙන්න ඕනේ.

GITHUB_ID=YOUR_GITHUB_CLIENT_ID
GITHUB_SECRET=YOUR_GITHUB_CLIENT_SECRET
NEXTAUTH_SECRET=YOUR_SUPER_SECRET_KEY_FOR_NEXTAUTH

දැන් pages/api/auth/[...nextauth].js file එක මේ විදිහට modify කරමු:

// pages/api/auth/[...nextauth].js
import NextAuth from "next-auth";
import GithubProvider from "next-auth/providers/github";
import { PrismaAdapter } from "@next-auth/prisma-adapter";
import { PrismaClient } from "@prisma/client";

const prisma = new PrismaClient();

export default NextAuth({
  // Prisma Adapter එක configure කිරීම
  adapter: PrismaAdapter(prisma),
  providers: [
    GithubProvider({
      clientId: process.env.GITHUB_ID,
      clientSecret: process.env.GITHUB_SECRET,
    }),
    // ඔබට අවශ්‍ය නම් වෙනත් providers මෙතනට එකතු කරන්න පුළුවන්
  ],
  session: {
    strategy: "jwt", // JWT-based sessions use කරන්න
  },
  callbacks: {
    // JWT callback එක - user ගේ role එක JWT එකට එකතු කිරීම
    async jwt({ token, user, account, profile, isNewUser }) {
      // 'user' object එක තියෙන්නේ user කෙනෙක් sign-in වුණාට පස්සේ පළවෙනි පාරට විතරයි.
      // ඒ වෙලාවේදී database එකෙන් user ගේ role එක අරගෙන token එකට එකතු කරනවා.
      if (user) {
        // Database එකෙන් user ගේ data retrieve කිරීම.
        // මේකෙන් අපේ custom 'role' field එකත් ලැබෙනවා.
        const dbUser = await prisma.user.findUnique({
          where: { id: user.id },
        });

        // token එකට custom fields එකතු කිරීම
        token.userId = dbUser.id; // User ID එක token එකට එකතු කිරීම
        token.role = dbUser.role || "user"; // User role එක token එකට එකතු කිරීම
        token.customMessage = "Welcome to our app!"; // තවත් custom field එකක්
      }
      return token;
    },
    // Session callback එක - JWT එකේ තියෙන custom data client-side session එකට expose කිරීම
    async session({ session, token }) {
      // token එකේ තියෙන custom fields session එකට assign කිරීම
      if (token) {
        session.user.id = token.userId;
        session.user.role = token.role;
        session.user.customMessage = token.customMessage;
      }
      return session;
    },
    // SignIn callback එක - sign-in process එක control කිරීම (අත්‍යවශ්‍ය නැහැ)
    // මේකෙන් පුළුවන් යම් යම් කොන්දේසි මත sign-in එකට අවසර දෙන්න නැත්නම් block කරන්න.
    // async signIn({ user, account, profile, email, credentials }) {
    //   // උදාහරණයක්: යම්කිසි domain එකක email තියෙන අයට විතරක් අවසර දෙන්න
    //   if (user.email.endsWith("@example.com")) {
    //     return true; // Allow sign in
    //   }
    //   return false; // Prevent sign in
    // },
  },
  // Debugging වලට මේක enable කරන්න පුළුවන්.
  // debug: true,
});

මේ code එකේ jwt callback එක ඇතුලේ, අපි user කෙනෙක් sign-in වුණාට පස්සේ, database එකෙන් user ගේ full data එක අරගෙන (prisma.user.findUnique පාවිච්චි කරලා) token එකට userId, role, සහ customMessage වගේ custom fields එකතු කරනවා.

ඊට පස්සේ, session callback එක ඇතුලේ, අපි JWT එකේ තියෙන මේ custom fields client side එකේදී useSession() hook එකෙන් ලැබෙන session.user object එකට assign කරනවා.

පියවර 4: Client Side එකේ Custom Data පාවිච්චි කිරීම

දැන් අපි බලමු මේ custom data client side එකේදී කොහොමද access කරන්නේ කියලා. මුලින්ම, _app.js file එකේ SessionProvider එකෙන් app එක wrap කරන්න.

// pages/_app.js
import { SessionProvider } from "next-auth/react";

function MyApp({ Component, pageProps: { session, ...pageProps } }) {
  return (
    <SessionProvider session={session}>
      <Component {...pageProps} />
    </SessionProvider>
  );
}

export default MyApp;

දැන් pages/index.js file එකේ useSession() hook එක පාවිච්චි කරලා custom data access කරමු:

// pages/index.js
import { useSession, signIn, signOut } from "next-auth/react";

export default function Home() {
  const { data: session } = useSession();

  if (session) {
    return (
      <div style={{ padding: '20px' }}>
        <h1>ආයුබෝවන්, {session.user.name}! 👋</h1>
        <p>ඔබගේ Email එක: <strong>{session.user.email}</strong></p>
        <p>ඔබගේ User ID එක (Custom): <strong>{session.user.id}</strong></p>
        <p>ඔබගේ Role එක (Custom): <strong>{session.user.role}</strong></p>
        <p>අපේ Custom Message: <em>{session.user.customMessage}</em></p>
        <button onClick={() => signOut()}>Sign out</button>
      </div>
    );
  }
  return (
    <div style={{ padding: '20px' }}>
      <h1>ඔබ තාම Sign වෙලා නැහැ. 🙁</h1>
      <button onClick={() => signIn('github')}>Sign in with GitHub</button>
    </div>
  );
}

දැන් project එක run කරලා බලන්න:

npm run dev

GitHub එක්ක sign-in වුණාට පස්සේ ඔබට පෙනෙයි session.user.id, session.user.role, සහ session.user.customMessage කියන අපේ custom fields ටිකත් session එකට ඇවිල්ලා තියෙනවා කියලා. මේකෙන් අපිට පුළුවන් user ගේ role එක මත පදනම් වෙලා UI elements පෙන්වන්න, නැත්නම් certain routes වලට access control කරන්න.

ප්‍රශ්න නිරාකරණය සහ හොඳම පුරුදු (Troubleshooting & Best Practices) 💡

Debugging Callbacks

Callbacks වල logic debug කරන එක ටිකක් අමාරු වෙන්න පුළුවන්. console.log() statements පාවිච්චි කරලා token, session, user වගේ objects වල current state එක මොකක්ද කියලා බලන්න. ඒ වගේම debug: true කියන option එක NextAuth.js config එකට එකතු කිරීමෙන් terminal එකේදී NextAuth.js logs බලාගන්න පුළුවන්.

export default NextAuth({
  // ...other config
  debug: true, // මේක enable කරන්න
});

Data Security

JWT එකට හෝ session එකට sensitive data (උදා: passwords, private keys) කෙලින්ම එකතු නොකරන්න. JWT එකේ තියෙන දේවල් encode කරලා තියෙන්නේ මිසක් encrypt කරලා නැහැ. ඒ නිසා, ඒවා client side එකට expose වෙනවා. අවශ්‍යම දේවල් විතරක් session එකට හෝ JWT එකට දාන්න. නැත්නම්, back-end API calls හරහා sensitive data secure විදිහට retrieve කරන්න.

Adapter Choice

ඔබේ project එකේ අවශ්‍යතා අනුව නිවැරදි database adapter එක තෝරාගන්න. Production environments වලට SQLite වෙනුවට PostgreSQL, MySQL, MongoDB වගේ robust databases පාවිච්චි කරන්න. Prisma වගේ ORM එකක් පාවිච්චි කරන එකෙන් database interactions පහසු වෙනවා.

නිගමනය ✨

ඉතින්, NextAuth.js වල Custom Callbacks සහ Database Adapters කියන්නේ ඔබේ authentication process එකට flexibility එකක් සහ control එකක් ලබාදෙන ගොඩක් powerful features දෙකක්. මේවා පාවිච්චි කරලා ඔබට පුළුවන් user ගේ role එක මත පදනම් වෙලා access control කරන්න, JWT එකට custom data එකතු කරන්න, ඒ වගේම user accounts සහ sessions ආරක්ෂිතව database එකේ persist කරන්න.

අපි මේ tutorial එකේදී NextAuth.js කොහොමද Prisma එක්ක integrate කරන්නේ කියලා, ඒ වගේම jwt සහ session callbacks පාවිච්චි කරලා custom data manage කරන්නේ කොහොමද කියලා පැහැදිලිව ඉගෙන ගත්තා. මේක ඔබට ඔබේ Next.js projects වලදී ගොඩක් ප්‍රයෝජනවත් වෙයි කියලා මම විශ්වාස කරනවා.

මේක ඔයාගේ project එකට implement කරලා බලන්න! මොකද, theory එක ඉගෙන ගන්නවා වගේම practice කරන එකත් ගොඩක් වැදගත්. ඔබට මේ ගැන තියෙන අදහස්, ප්‍රශ්න, නැත්නම් මේක implement කරනකොට ආපු අත්දැකීම් පහතින් comment කරන්න. හැමෝටම ජය වේවා! 🚀