Authentication (සත්යාපනය) යනු පරිශීලකයෙකු තමන් කියන පුද්ගලයාම බවට තහවුරු කර ගැනීමේ ක්රියාවලියයි. සරලවම කිවහොත්, "ඔබ කවුද?" යන ප්රශ්නයට පිළිතුරු සෙවීමයි. Web applications වලදී මෙය සාමාන්යයෙන් සිදු වන්නේ username (or email) සහ password එකක් ලබා දීමෙනි.
මෙය ඉතා වැදගත් වන්නේ, application එකේ ඇති සංවේදී දත්ත සහ ක්රියාකාරීත්වයන්, නිසි අවසරය ඇති පුද්ගලයින්ට පමණක් ප්රවේශ වීමට ඉඩ දීම සඳහාය. උදාහරණයක් ලෙස, ඔබේ Facebook ගිණුමේ ඡායාරූප වෙනස් කිරීමට ඔබට පමණක් හැකි විය යුතුය. ඒ සඳහා ඔබව හඳුනාගැනීමට authentication අවශ්ය වේ.
පරිශීලකයෙකු ලියාපදිංචි කිරීමේදී, ඔවුන්ගේ password එක ඒ ආකාරයෙන්ම (plain text) database එකේ ගබඩා කිරීම ඉතාම භයානක සහ නොකළ යුතු දෙයකි. Database එකකට යම් හෙයකින් අනවසරයෙන් කවුරුන් හෝ ඇතුළු වුවහොත්, සියලුම පරිශීලකයින්ගේ passwords ඔවුන් අතට පත් වේ.
ඒ වෙනුවට, අපි password එක **hash** කර, එම hash අගය database එකේ ගබඩා කළ යුතුය. Hashing යනු දත්ත (password) එකක්, ආපසු හැරවිය නොහැකි (irreversible) අකුරු සහ ඉලක්කම් සමූහයක් බවට පත් කරන ගණිතමය ක්රියාවලියකි.
මේ සඳහා අපි bcrypt නමැති ඉතා ජනප්රිය සහ සුරක්ෂිත library එක භාවිතා කරමු.
npm install bcryptjs
`models/User.js` file එක:
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
email: {
type: String,
required: true,
unique: true, // එකම email එකෙන් accounts දෙකක් සෑදීම වැළැක්වීම
lowercase: true
},
password: {
type: String,
required: true
}
});
module.exports = mongoose.model('User', userSchema);
Registration Route එක (`routes/auth.js`):
const express = require('express');
const router = express.Router();
const bcrypt = require('bcryptjs');
const User = require('../models/User'); // User model එක import කිරීම
// POST /api/auth/register
router.post('/register', async (req, res) => {
try {
const { email, password } = req.body;
// 1. පරිශීලකයා දැනටමත් ලියාපදිංචි වී ඇත්දැයි පරීක්ෂා කිරීම
let user = await User.findOne({ email });
if (user) {
return res.status(400).json({ msg: 'User already exists' });
}
// 2. නව පරිශීලකයෙක් නිර්මාණය කිරීම
user = new User({
email,
password
});
// 3. Password එක hash කිරීම
const salt = await bcrypt.genSalt(10); // Salt යනු hash එක වඩාත් සුරක්ෂිත කරන random text එකකි.
user.password = await bcrypt.hash(password, salt);
// 4. පරිශීලකයා database එකේ save කිරීම
await user.save();
res.status(201).send('User registered successfully');
} catch (err) {
console.error(err.message);
res.status(500).send('Server Error');
}
});
module.exports = router;
පරිශීලකයෙකු ලොග් වීමේදී, ඔවුන් ලබා දෙන plain text password එක, database එකේ ඇති hash කළ password එක සමඟ සැසඳිය යුතුය. `bcrypt.compare()` function එක මගින් මෙම කාර්යය අපට පහසුවෙන් කරගත හැක.
Login Route එක (`routes/auth.js`):
// POST /api/auth/login
router.post('/login', async (req, res) => {
try {
const { email, password } = req.body;
// 1. Email එකට අදාළ පරිශීලකයා සිටීදැයි පරීක්ෂා කිරීම
const user = await User.findOne({ email });
if (!user) {
return res.status(400).json({ msg: 'Invalid Credentials' });
}
// 2. ලබාදුන් password එක, database එකේ ඇති hash එක සමඟ සැසඳීම
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) {
return res.status(400).json({ msg: 'Invalid Credentials' });
}
// --- Password is correct, now create a JWT ---
res.json({ msg: 'Logged in successfully' });
} catch (err) {
console.error(err.message);
res.status(500).send('Server Error');
}
});
පරිශීලකයෙකු සාර්ථකව ලොග් වූ පසු, ඔවුන්ව "logged in" ලෙස හඳුනාගන්නේ කෙසේද? පැරණි ක්රමය වූයේ server-side sessions භාවිතා කිරීමයි. නමුත් නවීන APIs සඳහා වඩාත් සුදුසු, stateless ක්රමය වන්නේ **JWT** භාවිතා කිරීමයි.
JWT යනු පාර්ශව දෙකක් (server සහ client) අතර JSON object එකක් ලෙස තොරතුරු ආරක්ෂිතව හුවමාරු කර ගැනීමට ඇති සම්මතයකි. පරිශීලකයෙකු ලොග් වූ විට, server එක JWT එකක් නිර්මාණය කර client එකට යවයි. Client එක, ඉන්පසු server එකට යවන සෑම request එකකම header එකේ මෙම token එක අමුණා යවයි. Server එක එම token එක verify කර, පරිශීලකයා හඳුනා ගනී.
npm install jsonwebtoken
npm install config
Login Route එක (JWT සමඟ):
const jwt = require('jsonwebtoken');
// ... (previous login route code)
router.post('/login', async (req, res) => {
// ... (email and password checks)
// Password is correct, now create JWT
// 1. Payload: Token එකේ ගබඩා කළ යුතු දත්ත
const payload = {
user: {
id: user.id // user's database ID
}
};
// 2. Token එක sign කිරීම
jwt.sign(
payload,
'mysecretkey', // This should be in a config file!
{ expiresIn: 3600 }, // Token එක පැයකින් expire වේ
(err, token) => {
if (err) throw err;
res.json({ token }); // Client එකට token එක යැවීම
}
);
});
දැන් අපිට JWT එකක් තිබෙනවා. නමුත්, නිශ්චිත routes (උදා: get user profile, add a new note) වලට පිවිසීමට ලොග් වී සිටීම අනිවාර්ය කරන්නේ කෙසේද? ඒ සඳහා අපි Middleware එකක් භාවිතා කරමු.
මෙම middleware එක, protected route එකකට request එකක් පැමිණි විට, request header එකේ ඇති JWT එක verify කරයි. Token එක වලංගු නම්, request එක ඉදිරියට route handler එකට යවයි. වලංගු නැතිනම්, "Unauthorized" error එකක් යවයි.
`middleware/auth.js` file එක:
const jwt = require('jsonwebtoken');
module.exports = function(req, res, next) {
// 1. Request header එකෙන් token එක ලබා ගැනීම
const token = req.header('x-auth-token');
// 2. Token එකක් නොමැති නම්
if (!token) {
return res.status(401).json({ msg: 'No token, authorization denied' });
}
// 3. Token එක verify කිරීම
try {
const decoded = jwt.verify(token, 'mysecretkey');
req.user = decoded.user; // decode කළ user තොරතුරු request object එකට එකතු කිරීම
next(); // ඊළඟ middleware එකට හෝ route handler එකට pass කිරීම
} catch (err) {
res.status(401).json({ msg: 'Token is not valid' });
}
};
දැන් අපි `notes` route එකකට පිවිසීමට ලොග් වීම අනිවාර්ය කරමු.
`routes/notes.js` file එක:
const express = require('express');
const router = express.Router();
const authMiddleware = require('../middleware/auth'); // අපේ auth middleware එක
// GET /api/notes
// This is now a protected route
router.get('/', authMiddleware, async (req, res) => {
try {
// middleware එක මගින් req.user එකට user's ID එක එකතු කර ඇති නිසා,
// අපිට එම user ට අදාළ notes පමණක් fetch කළ හැක.
// const notes = await Note.find({ user: req.user.id });
res.json({ msg: `Welcome user with ID: ${req.user.id}. Here are your notes.` });
} catch (err) {
console.error(err.message);
res.status(500).send('Server Error');
}
});
module.exports = router;
මෙහිදී, `GET` request එකේ path එකට පසුව, route handler එකට පෙර, අපි `authMiddleware` එක ඇතුළත් කර ඇත. මේ නිසා, `/api/notes` වෙත පිවිසීමට පෙර, client විසින් header එකේ වලංගු JWT එකක් ලබා දීම අනිවාර්ය වේ.