Next.js Route Protection Sinhala | NextAuth.js Tutorial

Next.js Route Protection Sinhala | NextAuth.js Tutorial

කොහොමද යාලුවනේ! 👋 අද අපි කතා කරන්න යන්නේ web applications හදනකොට අනිවාර්යයෙන්ම අවධානය යොමු කරන්න ඕන වැදගත් මාතෘකාවක් ගැන – ඒ තමයි Route Protection. විශේෂයෙන්ම Next.js වගේ framework එකක් පාවිච්චි කරනකොට, අපේ application එකේ sensitive pages ආරක්ෂා කරගන්න එක අත්‍යවශ්‍යයි. අනවසර පුද්ගලයන්ට වැදගත් තොරතුරු තියෙන pages වලට access වෙන්න නොදී, අපේ usersලගේ privacy එක maintain කරන එක තමයි මෙහි මූලික අරමුණ. ඊට උදව් වෙන්න එන library එකක් තමයි NextAuth.js කියන්නේ.

NextAuth.js කියන්නේ Next.js applications වල authentication ක්‍රියාත්මක කරන්න පුළුවන් powerfull සහ flexible solution එකක්. Google, Facebook වගේ third-party providersලාව වගේම, email/password වගේ credential authentication එකත් NextAuth.js වලින් ලේසියෙන්ම handle කරන්න පුළුවන්. අද අපි මේ මාර්ගෝපදේශය තුළින් NextAuth.js භාවිතයෙන් Next.js routes ආරක්ෂා කරගන්නා ආකාරය, client-side වගේම server-side වලදීත්, step-by-step බලමු. මේ tutorial එක ඉවර වෙනකොට ඔයාට NextAuth.js භාවිතයෙන් fully protected Next.js application එකක් හදන්න අවශ්‍ය දැනුම ලැබිලා තියේවි!

NextAuth.js සහ Route ආරක්ෂාව (NextAuth.js and Route Protection)

ඕනෑම application එකක user authentication කියන්නේ essential feature එකක්. User කෙනෙක් login වුනාට පස්සේ, සමහර pages එයාට විතරක් පෙන්නන්න ඕනේ, තවත් සමහර pages හැමෝටම පෙන්වන්න පුළුවන්. මේක තමයි Route Protection කියන්නේ. NextAuth.js මේ වැඩේට අපිට ගොඩක් උදව් කරනවා. NextAuth.js session එකක් මගින් user කෙනෙක් login වෙලාද නැද්ද කියලා අපිට දැනගන්න පුළුවන්.

Client-side සහ Server-side ආරක්ෂාව

Next.js වලදී routes protect කරනකොට අපිට ප්‍රධාන ක්‍රම දෙකක් තියෙනවා:

  1. Client-side Protection: මේක වෙන්නේ user ගේ browser එකේදී. user session එක check කරලා, user authenticate වෙලා නැත්නම් වෙන page එකකට redirect කරනවා. මේක user experience (UX) එක improve කරන්න හොඳයි, නමුත් මේකෙන් විතරක් security එක සම්පූර්ණයෙන් සපයන්න බෑ. මොකද client-side code වලට ඕනෑම කෙනෙක්ට access වෙන්න පුළුවන්.
  2. Server-side Protection: මේක තමයි ඇත්තටම වැදගත්ම දේ. server එකට request එක එනකොටම user session එක check කරලා, user authenticate වෙලා නැත්නම් page එක load කරන එක නවත්වනවා හෝ redirect කරනවා. Sensitive data handle කරන හැම තැනකදීම server-side protection අනිවාර්යයි.

Client-side Route ආරක්ෂාව: getSession() භාවිතය

Client-side protection වලට අපි NextAuth.js වල useSession() hook එක භාවිත කරනවා. මේකෙන් දැනට login වෙලා ඉන්න user කෙනෙක්ගේ session data එකයි, authentication status එකයි අපිට ගන්න පුළුවන්. User authenticated ද නැද්ද කියලා බලාගන්න status property එක ගොඩක් ප්‍රයෝජනවත්.

useSession() hook එක භාවිත කරන්නේ කොහොමද?

මුලින්ම, අපේ application එකේ _app.js file එක SessionProvider එකකින් wrap කරගන්න ඕනේ, NextAuth.js session එක application පුරාවටම access කරන්න පුළුවන් වෙන්න.

// 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;

ඊට පස්සේ, ඔයාට protect කරන්න ඕන component එකේදී useSession() hook එක පාවිච්චි කරන්න පුළුවන්.

// components/ProtectedClientPage.jsx
import { useSession } from 'next-auth/react';
import { useRouter } from 'next/router';
import { useEffect } from 'react';

const ProtectedClientPage = () => {
  const { data: session, status } = useSession();
  const router = useRouter();

  useEffect(() => {
    if (status === 'loading') return; // Session is still loading, wait for it

    if (status === 'unauthenticated') {
      router.push('/api/auth/signin'); // Redirect to login page if not authenticated
    }
  }, [session, status, router]);

  if (status === 'loading') {
    return <p>Loading session...</p>;
  }

  if (status === 'authenticated') {
    return (
      <div>
        <h1>Welcome to the Protected Client-Side Dashboard!</h1>
        <p>You are logged in as {session.user.email}</p>
      </div>
    );
  }

  return null; // Or a loading spinner, or some fallback UI
};

export default ProtectedClientPage;

මේ code එකේ useEffect hook එක ඇතුලේ අපි status එක check කරනවා. 'unauthenticated' නම්, userව login page එකට redirect කරනවා. 'authenticated' නම්, protected content එක පෙන්නනවා. 'loading' වෙලාවේදී loading message එකක් පෙන්නනවා.

මතක තියාගන්න: Client-side protection කියන්නේ user experience එක හොඳ කරන්න මිසක්, සත්‍ය වශයෙන්ම security එක සපයන්න නෙවෙයි. ඕනෑම කෙනෙකුට browser එකේ JavaScript disable කරලා හෝ API calls කෙලින්ම කරලා මේ client-side check එක මගහරින්න පුළුවන්. ඒ නිසා critical data වලට server-side protection අනිවාර්යයි.

Server-side Route ආරක්ෂාව: getServerSession() සහ Middleware භාවිතය

Next.js වලදී server-side protection කියන්නේ essential part එකක්. මේකට ප්‍රධාන ක්‍රම දෙකක් තියෙනවා:

  1. getServerSession() භාවිතය: getServerSideProps වගේ server-side functions ඇතුලේ user session එක check කරන්න.
  2. Next.js Middleware භාවිතය: ගෝලීය වශයෙන් routes protect කරන්න.

getServerSession() භාවිතය

getServerSession() කියන්නේ NextAuth.js වලින් දෙන server-side function එකක්. මේක අපිට getServerSideProps වගේ functions ඇතුලේ පාවිච්චි කරන්න පුළුවන්, request එක server එකට එනකොටම user authenticate වෙලාද කියලා බලන්න.

// pages/protected-server-page.js
import { getServerSession } from 'next-auth';
import { authOptions } from './api/auth/[...nextauth]'; // NextAuth.js config file

export default function ProtectedServerPage({ userEmail }) {
  return (
    <div>
      <h1>Welcome to the Server-Side Protected Dashboard!</h1>
      <p>You are logged in as {userEmail}</p>
      <p>This page is rendered only if you are authenticated.</p>
    </div>
  );
}

export async function getServerSideProps(context) {
  const session = await getServerSession(context.req, context.res, authOptions);

  if (!session) {
    return {
      redirect: {
        destination: '/api/auth/signin',
        permanent: false,
      },
    };
  }

  return {
    props: {
      userEmail: session.user.email,
    },
  };
}

මේ උදාහරණයේදී, getServerSideProps function එක ඇතුලේ getServerSession() call කරනවා. session object එකක් නැත්නම් (user login වෙලා නැත්නම්), userව /api/auth/signin page එකට redirect කරනවා. එහෙම නැත්නම්, session එකෙන් userගේ email එක extract කරලා page එකට props විදිහට යවනවා. මේක ඉතාමත් ආරක්ෂිත ක්‍රමයක්, මොකද page එකේ content browser එකට යවන්න කලින්ම authentication check කරන නිසා.

Note: authOptions import කරද්දී, [...nextauth].js file එකේ export const authOptions = {...} විදිහට NextAuth config එක export කරලා තියෙන්න ඕනේ.

Next.js Middleware භාවිතය

Next.js Middleware කියන්නේ server-side route protection වලට තියෙන තවත් powerful ක්‍රමයක්. මේකෙන් පුළුවන් requests users' devices වලින් server එකට එනකොටම, එනම් pages load වෙන්න කලින්ම, ඒවා intercept කරලා logic run කරන්න. මේකෙන් අපි specify කරන routes වලට global protection එකක් දෙන්න පුළුවන්.

ඔයාගේ project එකේ root directory එකේ middleware.js (හෝ middleware.ts) file එකක් හදන්න.

// middleware.js
import { withAuth } from 'next-auth/middleware';

export default withAuth(
  // `withAuth` will redirect unauthenticated users to the configured sign-in page
  function middleware(req) {
    // console.log(req.nextUrl.pathname, req.token);
    // You can add more granular authorization logic here if needed
    // E.g., if (req.nextUrl.pathname === "/admin" && req.token?.role !== "admin") return NextResponse.redirect("/not-authorized");
  },
  {
    callbacks: {
      authorized: ({ req, token }) => {
        // `token` is present if the user is authenticated
        // We want to protect all paths that start with /dashboard
        if (req.nextUrl.pathname.startsWith('/dashboard')) {
          return !!token; // Return true if token exists (user is authenticated)
        }
        return true; // Allow access to all other paths by default
      },
    },
  }
);

export const config = {
  matcher: [
    '/dashboard/:path*',
    // Add other protected paths here as needed, e.g., '/profile', '/settings'
  ],
};

මේ middleware.js file එකෙන් වෙන්නේ, config.matcher එකේ specify කරපු routes වලට request එකක් එනකොට, withAuth function එක ඇතුලේ තියෙන authorized callback එක run වෙන එක. authorized callback එක true return කරනවා නම් request එකට ඉඩ දෙනවා, false return කරනවා නම් userව NextAuth.js වලින් configure කරලා තියෙන login page එකට redirect කරනවා.

මෙතනදී අපි /dashboard/:path* විදිහට දීලා තියෙන්නේ /dashboard කියන path එකෙන් පටන් ගන්න හැම route එකක්ම protect කරන්න.

ප්‍රායෝගික උදාහරණයක්: Protected Dashboard එකක් හදමු

අපි දැන් NextAuth.js භාවිතයෙන් Protected Dashboard page එකක් හදමු. මේකට අපි client-side වගේම server-side protection දෙකම පාවිච්චි කරනවා.

Step 1: Next.js Project Setup සහ NextAuth.js Configuration

මුලින්ම Next.js project එකක් හදාගෙන NextAuth.js install කරගන්න.

npx create-next-app my-protected-app
cd my-protected-app
npm install next-auth

ඊට පස්සේ, .env.local file එකක් හදාගෙන මේ variables add කරගන්න:

NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=ඔබේ_ඉතාමත්_සංකීර්ණ_රහසක්_මෙහි_ලියන්න # openssl rand -base64 32 from terminal is a good option

ඊට පස්සේ, NextAuth.js API route එක හදන්න. pages/api/auth/[...nextauth].js කියන file එක හදලා මේ code එක add කරන්න. අපි මෙතනදී Google Provider එකක් සහ Credentials Provider එකක් පාවිච්චි කරමු. (Google credentials ලබාගැනීමට Google Cloud Console එක භාවිතා කරන්න.)

// pages/api/auth/[...nextauth].js
import NextAuth from 'next-auth';
import GoogleProvider from 'next-auth/providers/google';
import CredentialsProvider from 'next-auth/providers/credentials';

export const authOptions = {
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
    }),
    CredentialsProvider({
      name: 'Credentials',
      credentials: {
        email: { label: 'Email', type: 'text', placeholder: '[email protected]' },
        password: { label: 'Password', type: 'password' }
      },
      async authorize(credentials, req) {
        // Add your own logic here to validate credentials
        // For demo purposes, we'll use a hardcoded user
        if (credentials.email === '[email protected]' && credentials.password === 'password123') {
          return { id: '1', name: 'Demo User', email: '[email protected]' };
        }
        return null; // Return null if user cannot be found
      }
    })
  ],
  pages: {
    signIn: '/api/auth/signin',
    // You can customize the sign-in page path here if you have a custom login page
  },
  session: {
    strategy: 'jwt',
  },
  callbacks: {
    async jwt({ token, user }) {
      if (user) {
        token.id = user.id;
        token.email = user.email;
      }
      return token;
    },
    async session({ session, token }) {
      session.user.id = token.id;
      return session;
    },
  },
};

export default NextAuth(authOptions);

ඔබේ .env.local file එකට GOOGLE_CLIENT_ID සහ GOOGLE_CLIENT_SECRET එකතු කරන්න.


# For Google OAuth
GOOGLE_CLIENT_ID=YOUR_GOOGLE_CLIENT_ID
GOOGLE_CLIENT_SECRET=YOUR_GOOGLE_CLIENT_SECRET

Step 2: Protected Dashboard Page එකක් හදමු (/pages/dashboard.js)

දැන් අපි /pages/dashboard.js කියන protected page එක හදමු. මේකෙදි අපි getServerSideProps එකෙන් server-side check එකක් දානවා, ඒ වගේම client-side quick redirect එකක් දාන්න useSession එකත් පාවිච්චි කරනවා.

// pages/dashboard.js
import { useSession, signOut } from 'next-auth/react';
import { getServerSession } from 'next-auth';
import { authOptions } from './api/auth/[...nextauth]';
import { useRouter } from 'next/router';
import { useEffect } from 'react';

export default function Dashboard({ userEmail }) {
  const { data: session, status } = useSession();
  const router = useRouter();

  // Client-side quick check for better UX (optional, but good practice)
  useEffect(() => {
    if (status === 'loading') return; // Wait for session to load
    if (status === 'unauthenticated') {
      // This redirect should ideally be handled by middleware or getServerSideProps
      // but is a good fallback for initial client-side render if those somehow fail.
      router.replace('/api/auth/signin');
    }
  }, [status, router]);

  if (status === 'loading') {
    return <div>Loading dashboard...</div>;
  }

  if (status === 'authenticated') {
    return (
      <div>
        <h1>පරිපාලක මණ්ඩලයට සාදරයෙන් පිළිගනිමු, {userEmail}!</h1>
        <p>මෙය පරිශීලකයින්ට පමණක් ප්‍රවේශ විය හැකි පිටුවකි.</p>
        <button onClick={() => signOut()}>Logout</button>
      </div>
    );
  }

  return null; // If unauthenticated, will be redirected by getServerSideProps or middleware
}

export async function getServerSideProps(context) {
  const session = await getServerSession(context.req, context.res, authOptions);

  if (!session) {
    return {
      redirect: {
        destination: '/api/auth/signin',
        permanent: false,
      },
    };
  }

  // If user is authenticated, pass their email as a prop
  return {
    props: {
      userEmail: session.user.email,
    },
  };
}

මේ page එකට request එකක් ආවම මුලින්ම getServerSideProps එක run වෙනවා. එතනින් user session එකක් නැත්නම් redirect කරනවා. user login වෙලා නම් විතරයි page එක render වෙන්නේ. Client-side useEffect එකෙන් getServerSideProps එකෙන් redirect නොවී මොකක් හරි හේතුවකට page එක load වුනොත් (ඒක වෙන්න තියෙන ඉඩ අඩුයි) quick redirect එකක් කරනවා.

Step 3: Unprotected Home Page (/pages/index.js)

අපි දැන් unprotected home page එකක් හදමු, ඒකෙන් login වෙන්න සහ dashboard එකට යන්න links දාමු.

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

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

  return (
    <div>
      <h1>Next.js Route Protection Demo</h1>
      {!session ? (
        <div>
          <p>ඔබව සාදරයෙන් පිළිගනිමු! <strong>Dashboard</strong> පිටුවට යාමට Login වන්න.</p>
          <button onClick={() => signIn('google')}>Sign in with Google</button>
          <br />
          <button onClick={() => signIn('credentials')}>Sign in with Credentials</button>
        </div>
      ) : (
        <div>
          <p>ඔබ දැනටමත් Login වෙලා ඉන්නේ {session.user.email} විදිහට.</p>
          <Link href="/dashboard">
            <button>Go to Dashboard</button>
          </Link>
          <button onClick={() => signOut()}>Logout</button>
        </div>
      )}
    </div>
  );
}

Step 4: Middleware Implementation (/middleware.js)

අපි කලින් කතා කරපු middleware.js file එක project root එකේ හදලා මේ code එක add කරන්න.

// middleware.js
import { withAuth } from 'next-auth/middleware';
import { NextResponse } from 'next/server';

export default withAuth(
  async function middleware(req) {
    const token = req.nextauth.token;
    // console.log("Middleware token:", token);

    // Example of more granular authorization
    // If you need to check user roles, you'd get the role from `token`
    // if (req.nextUrl.pathname.startsWith('/admin') && token?.role !== 'admin') {
    //   return NextResponse.redirect(new URL('/access-denied', req.url));
    // }
  },
  {
    callbacks: {
      authorized: ({ req, token }) => {
        // Protect /dashboard and all its sub-paths
        if (req.nextUrl.pathname.startsWith('/dashboard')) {
          return !!token; // Only allow if a token exists (user is authenticated)
        }
        return true; // Allow access to all other paths by default
      },
    },
    pages: {
        signIn: '/api/auth/signin', // Specifies the sign-in page to redirect to
    },
  }
);

export const config = {
  matcher: [
    '/dashboard/:path*', // Protects /dashboard and all its sub-paths
    // Add any other paths you want to protect here
  ],
};

දැන් ඔයාට project එක run කරලා බලන්න පුළුවන්.

npm run dev

http://localhost:3000 එකට ගිහින් බලන්න. /dashboard එකට කෙලින්ම යන්න හැදුවොත් Login page එකට redirect වෙනවා. Login වුනාට පස්සේ Dashboard එකට access වෙන්න පුළුවන්.

ගැටළු නිරාකරණය සහ හොඳම භාවිතයන් (Troubleshooting and Best Practices)

ගැටළු නිරාකරණය (Troubleshooting)

  • Redirection Loops: මේක ගොඩක් වෙලාවට වෙන්නේ middleware.js එකේ හෝ getServerSideProps එකේ redirect logic එක වැරදිලා. උදාහරණයක් විදිහට, login page එකත් protected කරොත්, userව login page එකට redirect කරද්දී, middleware එක ආයෙත් userව login page එකට redirect කරන්න හදනවා. මේක වලක්වන්න matcher එකේ login page එක protect කරන්න එපා, නැත්නම් authorized callback එක ඇතුලේ logic එක හරියට ලියන්න.
  • Environment Variables (.env.local) Not Loaded: NEXTAUTH_SECRET, GOOGLE_CLIENT_ID වගේ variables හරියට load වෙලා නැත්නම් NextAuth.js වැඩ කරන්නේ නෑ. Server එක restart කරලා බලන්න, variables name හරියට තියෙනවද කියලා බලන්න. Production වලදී Next.js build එක ගහන්න කලින් මේවා deploy කරන්න ඕනේ.
  • authOptions Import Error: getServerSession වලට authOptions import කරද්දී path එක හරියට තියෙනවද කියලා බලන්න.
  • Client-side Redirects Flashing Content: client-side useEffect එකෙන් redirect කරද්දී redirect වෙන්න කලින් තත්පරයකට වගේ unprotected content එකක් flash වෙනවා නම්, getServerSideProps හෝ middleware එකෙන් server-side redirect එකක් කරන එක වඩාත් හොඳයි.

හොඳම භාවිතයන් (Best Practices)

  • සෑම විටම Server-side ආරක්ෂාව භාවිතා කරන්න: Critical data සහ actions වලට සෑම විටම server-side authentication සහ authorization check කරන්න. Client-side check එකක් UX එකට හොඳ වුණත්, security එකට server-side check එක අත්‍යවශ්‍යයි.
  • getServerSideProps vs. Middleware:
    • getServerSideProps: එක් එක් page එකට විශේෂ වූ authentication logic එකක් තියෙනවා නම් හෝ page එකේ content එකට අනුව session data එක pass කරන්න අවශ්‍ය නම් මේක හොඳයි.
    • Middleware: ගෝලීය වශයෙන් routes ගණනාවක් protect කරන්න හෝ role-based access control (RBAC) වගේ දේවල් implement කරන්න මේක ගොඩක් පහසුයි. Middleware මගින් page render වෙන්න කලින්ම redirect කරන නිසා කාර්යක්ෂමතාවය වැඩියි.
  • ප්‍රවේශ වීම ප්‍රතික්ෂේප කළ පිටුවක් (Access Denied Page): user කෙනෙක්ට access නැති page එකකට යන්න හැදුවොත්, login page එකට redirect කරනවා වෙනුවට, /access-denied වගේ page එකකට redirect කරන්න පුළුවන්. ඒකෙන් userට clear message එකක් දෙනවා.
  • Role-Based Access Control (RBAC): ඔයාගේ application එකේ different user roles (e.g., admin, editor, subscriber) තියෙනවා නම්, NextAuth.js callbacks භාවිතයෙන් user session එකට role එක add කරලා, middleware එකේදී හෝ getServerSideProps එකේදී ඒ role එක check කරලා වඩාත් granular access control එකක් දෙන්න පුළුවන්.
  • Error Handling: Authentication process එකේදී errors handle කරන්න පුරුදු වෙන්න. User experience එක හොඳ වෙන්න clear error messages දෙන්න.

අවසන් වශයෙන් (Conclusion)

ඉතින් යාලුවනේ, Next.js applications වල route protection කියන්නේ හරිම වැදගත් දෙයක්. අද අපි NextAuth.js භාවිතයෙන් client-side (useSession) වගේම server-side (getServerSession සහ Middleware) routes protect කරගන්නා ආකාරය පැහැදිලිව ඉගෙන ගත්තා. Client-side protection එක user experience එකට හොඳ වුනත්, සැබෑ security එකට server-side protection අත්‍යවශ්‍ය බවත්, critical data හැමවිටම server-side validate කරන්න ඕන බවත් අපි කතා කළා.

දැන් ඔයාට NextAuth.js භාවිතයෙන් ඔයාගේම Next.js project වල ආරක්ෂිත routes implement කරන්න පුළුවන්. මේ tutorial එකේ තියෙන code examples අරන් ඔයාගේම project එකක මේක implement කරලා බලන්න. මොකද practice කරන තරමට තමයි ඕනෑම දෙයක් හොඳින් තේරෙන්නේ!

මේ ගැන ඔයාට මොනවා හරි ප්‍රශ්න තියෙනවා නම්, පහලින් comment කරන්න. ඔයාගේ අත්දැකීම් අපිත් එක්ක බෙදාගන්නත් අමතක කරන්න එපා. Happy Coding! 🎉