JWT کا استعمال کرتے ہوئے Node.js REST API میں رول پر مبنی رسائی کنٹرول کو کیسے نافذ کیا جائے۔

جب میں نے رولز کے بارے میں سوچے بغیر اپنا API بنایا تو میں نے تمام لاگ ان صارفین کو یکساں رسائی دی۔ اس نے اس وقت تک ٹھیک کام کیا جب تک کہ ایک عام صارف غلطی سے ڈیلیٹ اینڈ پوائنٹ کو نہیں مارتا اور ٹیسٹ ڈیٹا کو مٹا دیتا ہے۔ یہ وہ دن تھا جب میں واقعتاً بیٹھ گیا اور RBAC کو صحیح طریقے سے سیکھا۔

کردار پر مبنی رسائی کنٹرول خیالی لگتا ہے، لیکن خیال آسان ہے۔ آپ کیا کر سکتے ہیں اس پر منحصر ہے: تم کون ہو؟ہاں کہ آپ نے لاگ ان کیا ہے۔. منتظم صارف کو حذف کرتا ہے۔ ایڈیٹرز پوسٹ لکھتے ہیں۔ عام صارفین صرف پڑھتے ہیں۔ ایک ہی ایپ، بالکل مختلف تجربہ اس بات پر منحصر ہے کہ کون پوچھتا ہے۔

یہ وہی ہے جو ہم یہاں تخلیق کر رہے ہیں۔ ایک REST API جس میں تین کردار ہوتے ہیں: ایک JWT جو ہر درخواست کے لیے اس کردار کو انجام دیتا ہے، اور مڈل ویئر فنکشنز کا ایک جوڑا جو روٹ ہینڈلر پر عمل درآمد سے پہلے اجازتوں کی جانچ کرتا ہے۔ فی درخواست کوئی ڈیٹا بیس ہٹ نہیں ہے اور آپ کی کاروباری منطق میں کوئی if/else سوپ نہیں ہے۔

آخر میں، تین کام کے کردار (admin، editor، user) ہر ایک اپنے اختتامی نقطہ پر بند ہے۔ زیادہ اہم بات یہ ہے کہ پیٹرن قابل منتقلی ہے۔ ایک بار جب آپ اس پر کلک کرتے ہیں، تو آپ بغیر کسی ٹیوٹوریل کے اپنے اگلے پروجیکٹ سے جڑ سکتے ہیں۔

GitHub پر مکمل سورس کوڈ: github.com/ziaongit/nodejs-rbac-jwt-api

انڈیکس

جو آپ سیکھیں گے۔

  • RBAC کیا ہے اور یہ بنیادی تصدیق سے کیسے مختلف ہے؟

  • JWT پے لوڈ میں کردار کیسے شامل کریں۔

  • ٹوکن کی تصدیق اور کردار کی تصدیق کے لیے دوبارہ قابل استعمال ایکسپریس مڈل ویئر کیسے لکھیں۔

  • صارف کے کردار کی بنیاد پر API روٹس کو کیسے محفوظ بنایا جائے۔

شرطیں

جو ہم تعمیر کریں گے۔

آئیے صارف کے تین کرداروں کے ساتھ ایک سادہ مواد کے انتظام کے نظام کے لیے ایک REST API بنائیں۔

کردار اتھارٹی
user مواد پڑھیں
editor پڑھیں + مواد بنائیں
admin مکمل رسائی — پڑھیں، تخلیق کریں، مواد حذف کریں، صارفین کا نظم کریں۔

API مندرجہ ذیل اختتامی نکات کو بے نقاب کرتا ہے:

طریقہ اختتامی نقطہ پوزیشن
میل /api/authenticate/register عوامی
میل /api/authenticate/login عوامی
حاصل کریں /api/content صارفین، ایڈیٹرز، منتظمین
میل /api/content ایڈیٹر، ایڈمنسٹریٹر
حذف کریں /api/content/:id صرف ایڈمنسٹریٹر
حاصل کریں /api/administrator/user صرف ایڈمنسٹریٹر

پروجیکٹ کی ترتیبات

ایک نیا فولڈر بنائیں اور پروجیکٹ کو شروع کریں۔

mkdir nodejs-rbac-jwt-api
cd nodejs-rbac-jwt-api
npm init -y

انحصار انسٹال کریں۔

npm install express jsonwebtoken bcryptjs dotenv
npm install --save-dev nodemon

ہر پیکج کی خصوصیات درج ذیل ہیں:

  • اظہار: API کی تعمیر کے لیے ویب فریم ورک

  • jsonwebtoken: JWT بنائیں اور چیک کریں۔

  • bcryptjs: پاس ورڈ کو محفوظ طریقے سے ہیش کریں۔

  • Dotenb: اپنا مواد پڑھیں .env فائل کو محفوظ کریں تاکہ آپ اپنے سورس کوڈ میں راز کو ہارڈ کوڈ نہ کریں۔

اپ ڈیٹ package.json اسٹارٹ اپ اسکرپٹ شامل کرنے کے لیے:

"scripts": {
  "start": "node src/app.js",
  "dev": "nodemon src/app.js"
}

پروجیکٹ کا ڈھانچہ بنائیں۔

nodejs-rbac-jwt-api/
├── src/
│   ├── middleware/
│   │   └── auth.js
│   ├── routes/
│   │   ├── auth.js
│   │   ├── content.js
│   │   └── admin.js
│   ├── data/
│   │   └── users.js
│   └── app.js
├── .env
├── .env.example
└── package.json

آپ کا .env فائل:

JWT_SECRET=your_super_secret_key_change_this_in_production
PORT=3000

اہم: کبھی عہد نہ کریں۔ .env فائل کو ورژن کنٹرول پر بھیجیں۔ اسے اگلی بار شامل کریں۔ .gitignore.

ان میموری ڈیٹا اسٹوریج کو ترتیب دینا

یہاں کوئی ڈیٹا بیس نہیں ہے، صرف میموری میں ایک صف ہے۔ نقطہ ڈیٹا بیس کی ترتیب پر نصف ٹیوٹوریل خرچ کرنے کے بجائے RBAC پر توجہ مرکوز کرنا تھا۔ اصلی پروجیکٹس میں، صف کو اس ڈیٹا بیس سے تبدیل کریں جسے آپ پہلے سے استعمال کر رہے ہیں۔

بنانا src/data/users.js:

// In-memory users store
// In production, replace this with a real database (MongoDB, PostgreSQL, etc.)
const users = [];

const findUserByEmail = (email) => users.find((u) => u.email === email);
const findUserById = (id) => users.find((u) => u.id === id);
const createUser = (user) => {
  users.push(user);
  return user;
};
const getAllUsers = () => users.map(({ password, ...user }) => user);

module.exports = { findUserByEmail, findUserById, createUser, getAllUsers };

ایک بات قابل غور ہے: getAllUsers کچھ بھی واپس کرنے سے پہلے، ہم پاس ورڈ کو ضائع کرنے کے لیے ڈیسٹرکچرنگ کا استعمال کرتے ہیں۔ API کے جوابات میں پاس ورڈ فیلڈز نہ بھیجیں، حتیٰ کہ ہیشڈ جوابات بھی۔

سرٹیفیکیشن کا راستہ بنانا

تصدیق کا راستہ رجسٹریشن اور لاگ ان کو سنبھالتا ہے۔ لاگ ان وہ جگہ ہے جہاں پہلے کردار ادا ہوتے ہیں۔ JWT پے لوڈ میں براہ راست صارف کے کردار کو شامل کریں۔

بنانا src/routes/auth.js:

const express = require('express');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const { findUserByEmail, createUser } = require('../data/users');

const router = express.Router();

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

  if (!name || !email || !password) {
    return res.status(400).json({ message: 'Name, email, and password are required' });
  }

  if (findUserByEmail(email)) {
    return res.status(409).json({ message: 'Email already registered' });
  }

  // Only allow valid roles — default to 'user' if none provided
  const validRoles = ['user', 'editor', 'admin'];
  const assignedRole = validRoles.includes(role) ? role : 'user';

  const hashedPassword = await bcrypt.hash(password, 10);

  const newUser = {
    id: Date.now().toString(),
    name,
    email,
    password: hashedPassword,
    role: assignedRole,
  };

  createUser(newUser);

  res.status(201).json({
    message: 'User registered successfully',
    user: {
      id: newUser.id,
      name: newUser.name,
      email: newUser.email,
      role: newUser.role,
    },
  });
});

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

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

  const user = findUserByEmail(email);
  if (!user) {
    return res.status(401).json({ message: 'Invalid credentials' });
  }

  const isMatch = await bcrypt.compare(password, user.password);
  if (!isMatch) {
    return res.status(401).json({ message: 'Invalid credentials' });
  }

  // Issue JWT — embed role in the payload
  const token = jwt.sign(
    {
      id: user.id,
      email: user.email,
      role: user.role,   // ← This is the key part for RBAC
    },
    process.env.JWT_SECRET,
    { expiresIn: '24h' }
  );

  res.json({
    message: 'Login successful',
    token,
  });
});

module.exports = router;

سب سے اہم لائن JWT پے لوڈ ہے۔

jwt.sign({ id, email, role }, process.env.JWT_SECRET, { expiresIn: '24h' })

ڈال کر role ٹوکن سے آنے والی تمام درخواستیں ڈیٹا بیس کی تلاش کی ضرورت کے بغیر صارف کی اجازت لے کر جاتی ہیں۔ سرور ٹوکن کی تصدیق کرتا ہے اور پے لوڈ سے رول پڑھتا ہے۔

RBAC مڈل ویئر کی تعمیر

یہ نظام کا بنیادی حصہ ہے۔ مڈل ویئر کے دو الگ الگ فنکشنز درکار ہیں۔

  1. verifyToken چیک کریں کہ آیا JWT درست ہے اور ڈی کوڈ شدہ پے لوڈ بھیجیں۔ req.user

  2. checkRole تصدیق کریں کہ صارف کے پاس مخصوص راستے کے لیے مطلوبہ کردار ہیں۔

انہیں الگ رکھنا لچک فراہم کرتا ہے۔ کچھ راستوں کو صرف تصدیق کی ضرورت ہوتی ہے۔ دوسروں کو توثیق اور مخصوص کردار دونوں کی ضرورت ہوتی ہے۔

بنانا src/middleware/auth.js:

const jwt = require('jsonwebtoken');

// Middleware 1: Verify the JWT token
const verifyToken = (req, res, next) => {
  const authHeader = req.headers['authorization'];
  const token = authHeader && authHeader.split(' ')[1]; // Expects: Bearer 

  if (!token) {
    return res.status(401).json({ message: 'Access denied. No token provided.' });
  }

  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    req.user = decoded; // Attach decoded payload (including role) to request
    next();
  } catch (err) {
    return res.status(403).json({ message: 'Invalid or expired token.' });
  }
};

// Middleware 2: Check if user has one of the required roles
const checkRole = (...allowedRoles) => {
  return (req, res, next) => {
    if (!req.user) {
      return res.status(401).json({ message: 'Not authenticated.' });
    }

    if (!allowedRoles.includes(req.user.role)) {
      return res.status(403).json({
        message: `Access denied. Required role: ${allowedRoles.join(' or ')}. Your role: ${req.user.role}`,
      });
    }

    next();
  };
};

module.exports = { verifyToken, checkRole };

checkRole باقی پیرامیٹرز استعمال کریں (...allowedRoles) ایک سے زیادہ کردار پاس کر سکتے ہیں۔

checkRole('admin')                  // only admin
checkRole('editor', 'admin')        // editor or admin
checkRole('user', 'editor', 'admin') // all roles

یہ راستے کی تعریف کو صاف اور پڑھنے میں آسان بناتا ہے۔ اس کا مطلب ہے کہ اجازتیں راستے کی سطح پر ظاہر ہوتی ہیں۔

ایک محفوظ راستہ بنانا

اب آئیے ان راستوں کو جوڑتے ہیں جو مڈل ویئر استعمال کرتے ہیں۔

بنانا src/routes/content.js:

const express = require('express');
const { verifyToken, checkRole } = require('../middleware/auth');

const router = express.Router();

// In-memory content store
const content = [
  { id: '1', title: 'Getting Started with Node.js', author: 'admin' },
  { id: '2', title: 'Express Middleware Explained', author: 'editor' },
];

// GET /api/content — all authenticated users
router.get('/', verifyToken, checkRole('user', 'editor', 'admin'), (req, res) => {
  res.json({ content });
});

// POST /api/content — editors and admins only
router.post('/', verifyToken, checkRole('editor', 'admin'), (req, res) => {
  const { title } = req.body;

  if (!title) {
    return res.status(400).json({ message: 'Title is required' });
  }

  const newItem = {
    id: Date.now().toString(),
    title,
    author: req.user.email,
  };

  content.push(newItem);
  res.status(201).json({ message: 'Content created', item: newItem });
});

// DELETE /api/content/:id — admin only
router.delete('/:id', verifyToken, checkRole('admin'), (req, res) => {
  const index = content.findIndex((c) => c.id === req.params.id);

  if (index === -1) {
    return res.status(404).json({ message: 'Content not found' });
  }

  content.splice(index, 1);
  res.json({ message: 'Content deleted successfully' });
});

module.exports = router;

غور کریں کہ ہر راستے کو پڑھنا کتنا آسان ہے۔

router.delete('/:id', verifyToken, checkRole('admin'), handler)

آپ ہینڈلر باڈی کو پڑھے بغیر رسائی کنٹرول کو سمجھ سکتے ہیں۔ یہ مڈل ویئر پر مبنی RBAC کے اہم فوائد میں سے ایک ہے۔ اجازتیں کاروباری منطق میں دفن ہونے کے بجائے روٹنگ پرت میں موجود ہیں۔

بنانا src/routes/admin.js:

const express = require('express');
const { verifyToken, checkRole } = require('../middleware/auth');
const { getAllUsers } = require('../data/users');

const router = express.Router();

// GET /api/admin/users — admin only
router.get('/users', verifyToken, checkRole('admin'), (req, res) => {
  res.json({ users: getAllUsers() });
});

module.exports = router;

یہ سب ایک ساتھ ڈالنا

بنانا src/app.js:

require('dotenv').config();
const express = require('express');

const authRoutes = require('./routes/auth');
const contentRoutes = require('./routes/content');
const adminRoutes = require('./routes/admin');

const app = express();

app.use(express.json());

// Routes
app.use('/api/auth', authRoutes);
app.use('/api/content', contentRoutes);
app.use('/api/admin', adminRoutes);

// Health check
app.get('/', (req, res) => {
  res.json({ message: 'RBAC API is running' });
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

API ٹیسٹنگ

سرور شروع کریں:

npm run dev

مرحلہ 1: مختلف کرداروں کے ساتھ صارفین کو رجسٹر کریں۔

رجسٹر ایڈمنسٹریٹر:

curl -X POST http://localhost:3000/api/auth/register \
  -H "Content-Type: application/json" \
  -d '{"name": "Admin User", "email": "admin@example.com", "password": "password123", "role": "admin"}'

بطور ایڈیٹر رجسٹر کریں:

curl -X POST http://localhost:3000/api/auth/register \
  -H "Content-Type: application/json" \
  -d '{"name": "Editor User", "email": "editor@example.com", "password": "password123", "role": "editor"}'

باقاعدہ صارف کے طور پر رجسٹر ہوں (کوئی کردار تفویض نہیں کیا گیا ہے – پہلے سے طے شدہ ہے۔ user):

curl -X POST http://localhost:3000/api/auth/register \
  -H "Content-Type: application/json" \
  -d '{"name": "Regular User", "email": "user@example.com", "password": "password123"}'

مرحلہ 2: لاگ ان کریں اور ٹوکن حاصل کریں۔

curl -X POST http://localhost:3000/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email": "user@example.com", "password": "password123"}'

آپ کو درج ذیل جواب ملے گا:

{
  "message": "Login successful",
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

ٹوکن کاپی کریں۔

مرحلہ 3: رول پر مبنی رسائی کی جانچ کریں۔

مواد کو ایک عام صارف کے طور پر پڑھیں (کامیاب ہونا ضروری ہے):

curl http://localhost:3000/api/content \
  -H "Authorization: Bearer YOUR_TOKEN_HERE"

ایک عام صارف کے طور پر مواد بنانے کی کوشش کریں (ناکام ہونا چاہئے – 403)۔

curl -X POST http://localhost:3000/api/content \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -d '{"title": "New Article"}'

جواب:

{
  "message": "Access denied. Required role: editor or admin. Your role: user"
}

اب ایڈیٹر کے طور پر لاگ ان ہوں اور اسی POST کی درخواست کو آزمائیں۔ کامیابی ایڈمنسٹریٹر کے طور پر لاگ ان کریں اور DELETE روٹ کو آزمائیں۔ صرف ایڈمن ٹوکن کام کرتے ہیں۔

مرحلہ 4: کردار کو چیک کرنے کے لیے JWT کو ڈی کوڈ کریں۔

آپ پے لوڈ کا معائنہ کرنے کے لیے کوئی بھی ٹوکن jwt.io میں چسپاں کر سکتے ہیں۔ آپ کو کچھ اس طرح نظر آئے گا:

{
  "id": "1720300000000",
  "email": "admin@example.com",
  "role": "admin",
  "iat": 1720300000,
  "exp": 1720386400
}

کہ role وہ میدان ہے۔ checkRole تمام محفوظ شدہ درخواستیں پڑھی جاتی ہیں۔

کلیدی ٹیک ویز

کردار JWT پے لوڈ میں ہے۔ رولز ٹوکن کے ساتھ سفر کرتے ہیں، لہذا جب بھی کوئی شخص محفوظ راستے سے ٹکراتا ہے تو اضافی DB کالز کی ضرورت نہیں ہوتی ہے۔ لاگ ان ہونے پر اسے انجکشن لگایا جاتا ہے اور ہر درخواست کے ساتھ خفیہ طور پر تصدیق کی جاتی ہے۔

مڈل ویئر قابل ترتیب ہے۔ verifyToken اور checkRole یہ ایک علیحدہ، دوبارہ قابل استعمال فنکشن ہے۔ آپ کسی بھی راستے اور کسی بھی امتزاج سے جڑ سکتے ہیں۔

اجازتیں راستے کی سطح پر ظاہر ہوتی ہیں۔ router.delete('/:id', verifyToken, checkRole('admin'), handler) ہینڈلر کو پڑھنے سے پہلے یہ آپ کو رسائی کنٹرول کے بارے میں سب کچھ بتاتا ہے۔

پیداوار میں بھیجنے سے پہلے:

  • ان میموری سرنی کا مقصد اس ٹیوٹوریل کا مرکز رہنا تھا۔ پیداوار کے قریب پہنچنے سے پہلے اسے حقیقی ڈیٹا بیس سے بدل دیں۔ سرور کو دوبارہ شروع کرنے سے تمام صارفین ابھی حذف ہو جائیں گے۔

  • 24 گھنٹے ٹوکن کی میعاد ختم ہونے کی مدت بہت طویل ہے۔ اسے 15 منٹ تک کم کریں اور ریفریش ٹوکن روٹیشن شامل کریں۔ چوری شدہ ٹوکن تیزی سے بیکار ہو جاتے ہیں۔

  • حساس کارروائیوں کے لیے، DB میں کرداروں کی دوبارہ توثیق کریں۔ موجودہ ٹوکنز میں رول کی تبدیلیاں اس وقت تک ظاہر نہیں ہوتی جب تک کہ ان کی میعاد ختم نہ ہو جائے۔

  • HTTPS، ہمیشہ

  • اگر آپ کی اجازت کی منطق "رول چیکنگ” سے آگے بڑھتی ہے، تو casl پر ایک نظر ڈالیں۔ انتساب کی سطح کے قواعد کو صاف طور پر ہینڈل کرتا ہے۔

نتیجہ

اس کے مرکز میں، یہ دو مڈل ویئر فنکشنز اور ایک JWT پے لوڈ میں فٹ بیٹھتا ہے۔ میں نے کئی منصوبوں میں اسی طرز کا استعمال کیا ہے۔ اور ایک بار جب آپ اسے خود بناتے ہیں، تو آپ اسے ہر جگہ تلاش کرنا شروع کر دیں گے، کیونکہ تقریباً ہر ملٹی یوزر ایپ کو ایک مخصوص ورژن کی ضرورت ہوتی ہے۔

GitHub پر مکمل سورس کوڈ: github.com/ziaongit/nodejs-rbac-jwt-api

اوپر تک سکرول کریں۔