পর্ব ৯-এ Middleware সার্ভারে চলে আর Firebase Auth ব্রাউজারে — এই দ্বন্দ্বের কথা বলেছিলাম। এই পর্বে সেই সমস্যার সম্পূর্ণ সমাধান দেখব — Firebase Auth-এর সাথে সেশন কুকি জোড়া লাগানো, আর Server Component থেকে সরাসরি RTDB ব্যবহার করা।
দুটো ভিন্ন জগৎ — Client Auth vs Server Session
Firebase এর হাতেখড়ি পর্ব ২-৩-এ যা শিখেছিলাম (signInWithEmailAndPassword, onAuthStateChanged) সবই ব্রাউজারে চলে — Client Component-এর জগৎ। কিন্তু পর্ব ৪-এর Server Component-এ ডেটা আনতে বা পর্ব ৯-এর Middleware-এ Auth চেক করতে, সার্ভারেরও জানা দরকার ইউজার লগইন করা কিনা। এই দুটো জগৎ জোড়া লাগাতেই সেশন কুকি লাগে।
ধাপ ১ — লগইন (Client Component)
লগইন নিজেই এখনো ব্রাউজারে হয় — Firebase এর হাতেখড়ি পর্ব ২-এর কোডই এখানে ব্যবহার হচ্ছে, শুধু একটা অতিরিক্ত ধাপ যোগ হবে।
// app/login/LoginForm.js
"use client";
import { signInWithEmailAndPassword } from "firebase/auth";
import { auth } from "../../firebase";
import { useRouter } from "next/navigation";
export default function LoginForm() {
const router = useRouter();
async function handleLogin(email, password) {
const userCredential = await signInWithEmailAndPassword(auth, email, password);
const idToken = await userCredential.user.getIdToken(); // Firebase-এর ID Token
// ধাপ ২-এর API Route-এ পাঠানো, সেশন কুকি সেট করতে
await fetch("/api/session", {
method: "POST",
body: JSON.stringify({ idToken }),
});
router.push("/dashboard"); // useRouter দিয়ে নেভিগেশন
}
// ... ফর্ম JSX ...
}
useRouter এখানে নতুন — এটা Next.js-এর নিজস্ব Hook, Client Component থেকে প্রোগ্রাম্যাটিকভাবে পেজ বদলাতে ব্যবহার হয় (পর্ব ২-এর Link কম্পোনেন্ট যেখানে ইউজার ক্লিক করে, useRouter সেখানে কোড থেকে নেভিগেট করায়)।
ধাপ ২ — সেশন কুকি সেট করা (API Route)
এই API Route Firebase-এর ID Token যাচাই করে, একটা নিরাপদ কুকি সেট করে — এখানেই সার্ভার প্রথমবার জানে ইউজার আসলেই লগইন করা।
// app/api/session/route.js
import { cookies } from "next/headers";
export async function POST(request) {
const { idToken } = await request.json();
// বাস্তবে এখানে Firebase Admin SDK দিয়ে idToken যাচাই করা হয়
// (Firebase এর হাতেখড়ি পর্ব ১১-এর firebase-admin-এর মতোই)
cookies().set("session", idToken, {
httpOnly: true, // JavaScript দিয়ে পড়া যাবে না, শুধু সার্ভার
secure: true, // শুধু HTTPS-এ পাঠানো হবে
maxAge: 60 * 60 * 24 * 5, // ৫ দিন
});
return Response.json({ success: true });
}
httpOnly: true খুবই গুরুত্বপূর্ণ — এটা মানে এই কুকি ব্রাউজারের JavaScript দিয়ে পড়া যাবে না, শুধু সার্ভার-সাইড কোড (Middleware, Server Component) পড়তে পারবে। এটা XSS আক্রমণ থেকে সুরক্ষা দেয়।
ধাপ ৩ — Middleware-এ চেক (পর্ব ৯-এর প্যাটার্ন সম্পূর্ণ করা)
// middleware.js
import { NextResponse } from "next/server";
export function middleware(request) {
const sessionCookie = request.cookies.get("session");
if (!sessionCookie) {
return NextResponse.redirect(new URL("/login", request.url));
}
return NextResponse.next();
}
export const config = {
matcher: ["/dashboard/:path*"],
};
এখন পর্ব ৯-এ যে ফাঁক ছিল, সেটা পূরণ হয়ে গেলো — Firebase Auth (ব্রাউজার) → API Route (সেশন কুকি সেট) → Middleware (সেই কুকি চেক) — পুরো চক্র সম্পূর্ণ।
Server Component থেকে RTDB ব্যবহার
পর্ব ৪-এর প্যাটার্নেই Firebase এর হাতেখড়ি পর্ব ৭-এর REST API endpoint ব্যবহার করা যায় — Cloudflare এর হাতেখড়ি পর্ব ৬-এর Worker-থেকে-Firebase কলের মতোই ধারণা, শুধু এবার Next.js Server Component থেকে সরাসরি।
// app/dashboard/page.js — Server Component
import { cookies } from "next/headers";
async function getMyBooks(userId) {
const res = await fetch(
`https://my-first-app-default-rtdb.firebasedatabase.app/books.json?orderBy="ownerId"&equalTo="${userId}"`,
{ cache: "no-store" } // পর্ব ৫-এর SSR — ইউজার-নির্দিষ্ট ডেটা
);
return res.json();
}
export default async function DashboardPage() {
const sessionCookie = cookies().get("session");
// বাস্তবে এখানে sessionCookie থেকে userId বের করা হতো (decoded token থেকে)
const userId = "user123";
const books = await getMyBooks(userId);
return (
<div>
<h1>আমার বইয়ের তালিকা</h1>
<ul>
{Object.values(books || {}).map((book, i) => (
<li key={i}>{book.title}</li>
))}
</ul>
</div>
);
}
এখানে কোনো useEffect, কোনো onAuthStateChanged লিসেনার লাগেনি — Middleware ইতিমধ্যে নিশ্চিত করেছে ইউজার লগইন করা, আর Server Component সরাসরি ডেটা এনে HTML হিসেবে পাঠিয়ে দিচ্ছে।
লগআউট — কুকি মুছে ফেলা
// app/actions.js — Server Action (পর্ব ৮)
"use server";
import { cookies } from "next/headers";
import { redirect } from "next/navigation";
export async function logout() {
cookies().delete("session");
redirect("/login");
}
ইন্টারেক্টিভ — পুরো Auth ফ্লো
নিচে লগইন থেকে প্রোটেক্টেড ড্যাশবোর্ড দেখা পর্যন্ত পুরো প্রক্রিয়া ধাপে ধাপে দেখো।
ভালো অভ্যাস
- সবসময় httpOnly কুকি ব্যবহার করুন — সেশন টোকেন ব্রাউজার JavaScript-এ এক্সপোজ করলে XSS আক্রমণে চুরি হতে পারে
- প্রোডাকশনে অবশ্যই Firebase Admin SDK দিয়ে ID Token যাচাই করুন — উপরের উদাহরণে সরলীকরণের জন্য এই ধাপ বাদ দেওয়া হয়েছে, বাস্তবে এটা বাদ দিলে নিরাপত্তাহীন
- Middleware আর RTDB Security Rules দুটোই রাখুন — Middleware UI-level সুরক্ষা দেয়, কিন্তু আসল ডেটা সুরক্ষা Firebase এর হাতেখড়ি পর্ব ৮-এর Rules-এই থাকা উচিত
নিজে চেষ্টা করো
Exercise:
- নিজের Next.js প্রজেক্টে Firebase SDK ইনস্টল করো, পর্ব ১-এর প্যাটার্নে
firebase.jsকনফিগ ফাইল বানাও। - একটা লগইন ফর্ম বানাও, সফল লগইনে
/api/session-এ ID Token পাঠাও। /api/sessionroute বানাও, একটা httpOnly কুকি সেট করো।- Middleware দিয়ে
/dashboardপ্রোটেক্ট করো, লগইন ছাড়া অ্যাক্সেস করার চেষ্টা করে দেখো রিডাইরেক্ট হচ্ছে কিনা। - একটা লগআউট Server Action বানাও, কুকি মুছে ফেলে
/login-এ ফিরিয়ে আনো।
পরের পর্বে যা থাকছে
পরের পর্বে আমরা শিখব Image ও SEO Optimization — next/image দিয়ে স্বয়ংক্রিয় ছবি অপটিমাইজেশন, আর Metadata API দিয়ে প্রতিটা পেজের জন্য সঠিক title, description বসানো।