npm install bcryptjs jsonwebtoken
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
app.post('/api/auth/register',
async (req, res) => {
try {
const { name, email, password } = req.body;
// Check if user exists
const existingUser =
await User.findOne({ email });
if (existingUser) return res.status(400)
.json({ error: 'Email already registered' });
// Hash password (NEVER store plain text!)
const salt = await bcrypt.genSalt(10);
const hashedPassword =
await bcrypt.hash(password, salt);
const user = await User.create({
name, email, password: hashedPassword
});
// Generate JWT
const token = jwt.sign(
{ userId: user._id },
process.env.JWT_SECRET,
{ expiresIn: '7d' }
);
res.status(201).json({
token,
user: { id: user._id, name, email }
});
} catch (error) {
res.status(500)
.json({ error: 'Registration failed' });
}
});
app.post('/api/auth/login',
async (req, res) => {
const { email, password } = req.body;
const user = await User.findOne({ email });
if (!user) return res.status(401)
.json({ error: 'Invalid credentials' });
const validPassword = await bcrypt.compare(
password, user.password
);
if (!validPassword) return res.status(401)
.json({ error: 'Invalid credentials' });
const token = jwt.sign(
{ userId: user._id },
process.env.JWT_SECRET,
{ expiresIn: '7d' }
);
res.json({
token,
user: {
id: user._id,
name: user.name, email
}
});
});
function authenticate(req, res, next) {
const authHeader = req.headers.authorization;
if (!authHeader?.startsWith('Bearer ')) {
return res.status(401)
.json({ error: 'No token provided' });
}
try {
const token = authHeader.split(' ')[1];
const decoded = jwt.verify(
token, process.env.JWT_SECRET
);
req.userId = decoded.userId;
next();
} catch (error) {
res.status(401)
.json({ error: 'Invalid token' });
}
}
app.get('/api/images', authenticate,
async (req, res) => {
const images = await Image.find({
owner: req.userId
});
res.json(images);
});
const login = async (email, password) => {
const res = await fetch('/api/auth/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ email, password }),
});
const data = await res.json();
localStorage.setItem('token', data.token);
setUser(data.user);
};
git switch -c feature/PIXELCRAFT-047-auth
git add server/ src/
git commit -m "Add JWT auth with register, login, protected routes (PIXELCRAFT-047)"
git push origin feature/PIXELCRAFT-047-auth
# PR → Review → Merge → Close ticket ✅
Cryptographic hashing.
bcrypt produces a one-way hash. You cannot reverse it. This is how EVERY secure system stores passwords.