Day 3 of 5
⏱ ~60 minutes
Express.js in 5 Days — Day 3

Authentication with JWT

Register and login users, hash passwords with bcrypt, issue JWTs, and protect routes with auth middleware.

Password Hashing and JWT

Terminal
npm install bcryptjs jsonwebtoken
routes/auth.js
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const User = require('../models/User');

router.post('/register', async (req, res) => {
  const { name, email, password } = req.body;
  const hash = await bcrypt.hash(password, 12);
  const user = await User.create({ name, email, password: hash });
  const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, { expiresIn: '7d' });
  res.status(201).json({ token, user: { id: user._id, name, email } });
});

router.post('/login', async (req, res) => {
  const { email, password } = req.body;
  const user = await User.findOne({ email });
  if (!user || !await bcrypt.compare(password, user.password)) {
    return res.status(401).json({ error: 'Invalid credentials' });
  }
  const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, { expiresIn: '7d' });
  res.json({ token });
});
middleware/auth.js
const jwt = require('jsonwebtoken');
const User = require('../models/User');

module.exports = async (req, res, next) => {
  const authHeader = req.headers.authorization;
  if (!authHeader?.startsWith('Bearer ')) {
    return res.status(401).json({ error: 'No token' });
  }
  try {
    const token = authHeader.split(' ')[1];
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    req.user = await User.findById(decoded.id).select('-password');
    next();
  } catch {
    res.status(401).json({ error: 'Invalid token' });
  }
};
Protected route
const auth = require('../middleware/auth');
router.get('/me', auth, (req, res) => res.json(req.user));
📝 Day 3 Exercise
Add Auth to Your API
  1. I
  2. m
  3. p
  4. l
  5. e
  6. m
  7. e
  8. n
  9. t
  10. r
  11. e
  12. g
  13. i
  14. s
  15. t
  16. e
  17. r
  18. a
  19. n
  20. d
  21. l
  22. o
  23. g
  24. i
  25. n
  26. e
  27. n
  28. d
  29. p
  30. o
  31. i
  32. n
  33. t
  34. s
  35. .
  36. H
  37. a
  38. s
  39. h
  40. p
  41. a
  42. s
  43. s
  44. w
  45. o
  46. r
  47. d
  48. s
  49. w
  50. i
  51. t
  52. h
  53. b
  54. c
  55. r
  56. y
  57. p
  58. t
  59. (
  60. c
  61. o
  62. s
  63. t
  64. f
  65. a
  66. c
  67. t
  68. o
  69. r
  70. 1
  71. 2
  72. )
  73. .
  74. I
  75. s
  76. s
  77. u
  78. e
  79. J
  80. W
  81. T
  82. s
  83. w
  84. i
  85. t
  86. h
  87. 7
  88. -
  89. d
  90. a
  91. y
  92. e
  93. x
  94. p
  95. i
  96. r
  97. y
  98. .
  99. W
  100. r
  101. i
  102. t
  103. e
  104. t
  105. h
  106. e
  107. a
  108. u
  109. t
  110. h
  111. m
  112. i
  113. d
  114. d
  115. l
  116. e
  117. w
  118. a
  119. r
  120. e
  121. .
  122. P
  123. r
  124. o
  125. t
  126. e
  127. c
  128. t
  129. a
  130. /
  131. m
  132. e
  133. r
  134. o
  135. u
  136. t
  137. e
  138. .

Day 3 Summary

  • Hash passwords with bcrypt — never store plaintext. Cost factor 10-12 balances security and speed.
  • JWTs: jwt.sign(payload, secret, options) creates. jwt.verify(token, secret) validates.
  • Attach req.user in auth middleware so downstream handlers know who's making the request.
  • Send JWTs in the Authorization: Bearer <token> header. Never in query strings.
Finished this lesson?