🌐 Detecting your location…
📢 Advertisement — Configure AdSense in Appearance → Customize → AdSense Settings

Express.js-এ JWT প্রমাণীকরণ কীভাবে প্রয়োগ করবেন: ধাপে ধাপে 2026 গাইড

⏱️4 min read  ·  697 words

JWT (JSON ওয়েব টোকেন) প্রমাণীকরণ হল Node.js অ্যাপ্লিকেশনগুলিতে REST APIগুলি সুরক্ষিত করার জন্য মানক। 2026 সালে, বাস্তবায়নের ধরণগুলি সু-প্রতিষ্ঠিত – কিন্তু অনেক টিউটোরিয়াল টোকেন রিফ্রেশ, সঠিক গোপনীয় ব্যবস্থাপনা, এবং টোকেন অবৈধকরণের মতো গুরুত্বপূর্ণ সুরক্ষা বিবরণ এড়িয়ে যায়। এই নির্দেশিকা সম্পূর্ণ, উৎপাদন-প্রস্তুত বাস্তবায়ন কভার করে।

কিভাবে JWT প্রমাণীকরণ কাজ করে

  1. ব্যবহারকারী শংসাপত্রের সাথে লগ ইন করে → সার্ভার তাদের যাচাই করে
  2. সার্ভার দুটি টোকেন ইস্যু করে:অ্যাক্সেস টোকেন (স্বল্পস্থায়ী, 15 মিনিট) এবংরিফ্রেশ টোকেন (দীর্ঘদিন, 7 দিন)
  3. ক্লায়েন্ট প্রতিটি অনুরোধের জন্য অনুমোদন হেডারে অ্যাক্সেস টোকেন পাঠায়
  4. অ্যাক্সেস টোকেনের মেয়াদ শেষ হলে, ক্লায়েন্ট একটি নতুন অ্যাক্সেস টোকেন পেতে রিফ্রেশ টোকেন পাঠায়
  5. লগআউটে, রিফ্রেশ টোকেন সার্ভার-সাইডে অবৈধ হয়ে যায়

সেটআপ

npm install express jsonwebtoken bcryptjs cookie-parser ioredis
npm install -D @types/jsonwebtoken @types/bcryptjs

টোকেন পরিষেবা

// services/tokenService.js
const jwt = require('jsonwebtoken');
const Redis = require('ioredis');

const redis = new Redis(process.env.REDIS_URL);

const ACCESS_SECRET  = process.env.JWT_ACCESS_SECRET;
const REFRESH_SECRET = process.env.JWT_REFRESH_SECRET;
// CRITICAL: Use different secrets for access and refresh tokens

function generateAccessToken(payload) {
  return jwt.sign(payload, ACCESS_SECRET, {
    expiresIn:  '15m',
    issuer:     'myapp',
    audience:   'myapp-users',
  });
}

function generateRefreshToken(payload) {
  return jwt.sign(payload, REFRESH_SECRET, {
    expiresIn: '7d',
    issuer:    'myapp',
    audience:  'myapp-users',
    jwtid:     crypto.randomUUID(),  // unique ID for invalidation
  });
}

function verifyAccessToken(token) {
  return jwt.verify(token, ACCESS_SECRET, {
    issuer:   'myapp',
    audience: 'myapp-users',
  });
}

async function verifyRefreshToken(token) {
  const payload = jwt.verify(token, REFRESH_SECRET, {
    issuer:   'myapp',
    audience: 'myapp-users',
  });
  // Check token hasn't been revoked
  const isRevoked = await redis.get(`revoked:${payload.jti}`);
  if (isRevoked) throw new Error('Token revoked');
  return payload;
}

async function revokeRefreshToken(token) {
  try {
    const payload = jwt.decode(token);
    if (payload?.jti && payload?.exp) {
      const ttl = payload.exp - Math.floor(Date.now() / 1000);
      if (ttl > 0) {
        await redis.setex(`revoked:${payload.jti}`, ttl, '1');
      }
    }
  } catch (err) {
    // Ignore decode errors on logout
  }
}

module.exports = {
  generateAccessToken, generateRefreshToken,
  verifyAccessToken, verifyRefreshToken, revokeRefreshToken
};

প্রমাণীকরণ রুট

// routes/auth.js
const express = require('express');
const bcrypt  = require('bcryptjs');
const router  = express.Router();
const { generateAccessToken, generateRefreshToken,
        verifyRefreshToken, revokeRefreshToken } = require('../services/tokenService');
const { findUserByEmail } = require('../db/users');

// POST /auth/login
router.post('/login', async (req, res) => {
  const { email, password } = req.body;

  if (!email || !password) {
    return res.status(400).json({ error: 'Email and password required' });
  }

  const user = await findUserByEmail(email);
  if (!user || !(await bcrypt.compare(password, user.hashedPassword))) {
    // Same error for missing user and wrong password (prevent user enumeration)
    return res.status(401).json({ error: 'Invalid credentials' });
  }

  const payload = { sub: user.id, email: user.email, role: user.role };
  const accessToken  = generateAccessToken(payload);
  const refreshToken = generateRefreshToken(payload);

  // Store refresh token in httpOnly cookie (not accessible to JS)
  res.cookie('refreshToken', refreshToken, {
    httpOnly: true,
    secure:   process.env.NODE_ENV === 'production',
    sameSite: 'strict',
    maxAge:   7 * 24 * 60 * 60 * 1000,  // 7 days in ms
  });

  res.json({ accessToken, user: { id: user.id, email: user.email, role: user.role } });
});

// POST /auth/refresh
router.post('/refresh', async (req, res) => {
  const refreshToken = req.cookies.refreshToken;
  if (!refreshToken) return res.status(401).json({ error: 'No refresh token' });

  try {
    const payload = await verifyRefreshToken(refreshToken);

    // Refresh token rotation: revoke old, issue new
    await revokeRefreshToken(refreshToken);
    const newRefreshToken = generateRefreshToken({
      sub: payload.sub, email: payload.email, role: payload.role
    });
    const newAccessToken = generateAccessToken({
      sub: payload.sub, email: payload.email, role: payload.role
    });

    res.cookie('refreshToken', newRefreshToken, {
      httpOnly: true,
      secure:   process.env.NODE_ENV === 'production',
      sameSite: 'strict',
      maxAge:   7 * 24 * 60 * 60 * 1000,
    });
    res.json({ accessToken: newAccessToken });
  } catch (err) {
    res.clearCookie('refreshToken');
    res.status(401).json({ error: 'Invalid or expired refresh token' });
  }
});

// POST /auth/logout
router.post('/logout', async (req, res) => {
  const refreshToken = req.cookies.refreshToken;
  if (refreshToken) await revokeRefreshToken(refreshToken);
  res.clearCookie('refreshToken');
  res.json({ message: 'Logged out successfully' });
});

module.exports = router;

প্রমাণীকরণ মিডলওয়্যার

// middleware/authenticate.js
const { verifyAccessToken } = require('../services/tokenService');

function authenticate(req, res, next) {
  const authHeader = req.headers.authorization;
  if (!authHeader?.startsWith('Bearer ')) {
    return res.status(401).json({ error: 'Access token required' });
  }

  const token = authHeader.split(' ')[1];
  try {
    const payload = verifyAccessToken(token);
    req.user = payload;  // { sub, email, role, iat, exp }
    next();
  } catch (err) {
    if (err.name === 'TokenExpiredError') {
      return res.status(401).json({ error: 'Access token expired', code: 'TOKEN_EXPIRED' });
    }
    res.status(401).json({ error: 'Invalid access token' });
  }
}

// Role-based authorization
function authorize(...roles) {
  return (req, res, next) => {
    if (!roles.includes(req.user?.role)) {
      return res.status(403).json({ error: 'Insufficient permissions' });
    }
    next();
  };
}

module.exports = { authenticate, authorize };

রুট রক্ষা

const { authenticate, authorize } = require('./middleware/authenticate');

// Public route
app.get('/health', (req, res) => res.json({ status: 'ok' }));

// Authenticated route
app.get('/api/profile', authenticate, (req, res) => {
  res.json({ userId: req.user.sub, email: req.user.email });
});

// Role-restricted route
app.delete('/api/users/:id', authenticate, authorize('admin'), async (req, res) => {
  await deleteUser(req.params.id);
  res.json({ deleted: true });
});

ক্রিটিক্যাল সিকিউরিটি চেকলিস্ট

  • লোকাল স্টোরেজ এ কখনই টোকেন সঞ্চয় করবেন না — XSS-এর জন্য ঝুঁকিপূর্ণ। রিফ্রেশ টোকেনের জন্য httpOnly কুকিজ ব্যবহার করুন।
  • পৃথক গোপন ব্যবহার করুন অ্যাক্সেস এবং রিফ্রেশ টোকেন জন্য. অ্যাক্সেস গোপন লিক হলে, রিফ্রেশ টোকেন নিরাপদ থাকে।
  • সংক্ষিপ্ত অ্যাক্সেস টোকেন মেয়াদ শেষ (15 মিনিট সর্বোচ্চ)। দীর্ঘস্থায়ী অ্যাক্সেস টোকেন একটি অস্বীকারকারী ছাড়া অবৈধ করা যাবে না.
  • রিফ্রেশ টোকেন ঘূর্ণন – প্রতিটি রিফ্রেশ একটি নতুন রিফ্রেশ টোকেন জারি করে এবং পুরানোটিকে প্রত্যাহার করে। টোকেন চুরি শনাক্ত করে।
  • শুধুমাত্র HTTPS উৎপাদনে — ট্রানজিটে JWT গুলিকে এনক্রিপ্ট করা আবশ্যক।
  • আইএসএস এবং অড দাবিগুলি যাচাই করুন – একটি পরিষেবা থেকে অন্য পরিষেবাতে টোকেন ব্যবহার করা প্রতিরোধ করে।

প্রায়শই জিজ্ঞাসিত প্রশ্ন

প্রশ্ন: সেশন বনাম JWT – কোনটি ভাল?
উত্তর: সেশন (সার্ভার-সাইড স্টেট) অবিলম্বে বাতিল করা সহজ। JWT গুলি স্টেটলেস এবং শেয়ার্ড সেশন স্টোর ছাড়াই অনুভূমিকভাবে স্কেল। সহজ অ্যাপের জন্য সেশন ব্যবহার করুন; মোবাইল/এসপিএ ক্লায়েন্ট বা মাইক্রোসার্ভিস দ্বারা খাওয়া APIগুলির জন্য JWT।

প্রশ্ন: মেয়াদ শেষ হওয়ার আগে আমি কীভাবে একটি JWT বাতিল করব?
উত্তর: দুটি পন্থা: (1) প্রত্যাহার করা টোকেন JTI-এর একটি রেডিস অস্বীকৃতির তালিকা বজায় রাখুন — প্রতিটি অনুরোধে পরীক্ষা করা হয়। (2) অ্যাক্সেস টোকেনের মেয়াদ সংক্ষিপ্ত করুন যাতে ক্ষতির উইন্ডোটি ছোট হয়। বিকল্প 1 আরো নিরাপদ; বিকল্প 2 সহজ।

প্রশ্ন: ক্লায়েন্টে অ্যাক্সেস টোকেন কোথায় সংরক্ষণ করা উচিত?
উত্তর: মেমরিতে (জাভাস্ক্রিপ্ট পরিবর্তনশীল) — লোকাল স্টোরেজ বা সেশন স্টোরেজ নয়। মেমরি স্টোরেজ মানে পৃষ্ঠা রিফ্রেশ করার সময় এটি হারিয়ে গেছে, একটি নতুন পেতে একটি রিফ্রেশ টোকেন কল প্রয়োজন – এটিই উদ্দেশ্যমূলক আচরণ।

প্রশ্নঃ আমার কি RS256 বা HS256 ব্যবহার করা উচিত?
A: HS256 (প্রতিসম, ভাগ করা গোপন) পরিষেবাগুলির জন্য যা আপনি সম্পূর্ণরূপে নিয়ন্ত্রণ করেন৷ RS256 (অসমমিতিক, পাবলিক/প্রাইভেট কী) যখন তৃতীয় পক্ষকে আপনার গোপনীয়তা না জেনে টোকেন যাচাই করতে হবে — যেমন মাইক্রোসার্ভিস বা অংশীদার।

প্রশ্ন: আমি কিভাবে ফ্রন্টএন্ডে “টোকেন মেয়াদোত্তীর্ণ” হ্যান্ডেল করব?
A: ইন্টারসেপ্ট 401 এর সাথে একটিcode: "TOKEN_EXPIRED" হেডার, স্বয়ংক্রিয়ভাবে কল/auth/refresh, একটি নতুন অ্যাক্সেস টোকেন পান, এবং মূল অনুরোধটি পুনরায় চেষ্টা করুন। বেশিরভাগ HTTP ক্লায়েন্ট (axios, ky) এই প্যাটার্নের জন্য অনুরোধ ইন্টারসেপ্টর সমর্থন করে।

উপসংহার

উৎপাদন JWT প্রমাণীকরণের জন্য শুধুjwt.sign() এবংjwt.verify(). সম্পূর্ণ বাস্তবায়ন কভার করে: রিফ্রেশ টোকেনগুলির জন্য শুধুমাত্র http কুকিজ, প্রতিটি রিফ্রেশে টোকেন ঘূর্ণন, লগআউটের জন্য রিডিস-ব্যাকড প্রত্যাহার, প্রতিটি টোকেন প্রকারের জন্য পৃথক গোপনীয়তা, এবং ভূমিকা-ভিত্তিক অনুমোদন মিডলওয়্যার। এই আর্কিটেকচারটি অনুভূমিকভাবে স্কেল করে (স্টেটলেস অ্যাক্সেস টোকেন), টোকেন চুরি পরিচালনা করে (ঘূর্ণন পুনরায় ব্যবহার শনাক্ত করে), এবং সঠিক লগআউট (প্রত্যাহার তালিকা) সমর্থন করে। এই ফাউন্ডেশন দিয়ে শুরু করুন এবং আপনাকে পরবর্তীতে আপনার প্রমাণীকরণ সিস্টেম রিফ্যাক্টর করতে হবে না।

✍️ Leave a Comment

Your email address will not be published. Required fields are marked *

🌐 Read in:🇩🇪 Deutsch🇧🇷 Português🇸🇦 العربية🇮🇳 हिन्दी🇧🇩 বাংলা