Implementing Authentication in Node.js
Learn how to implement secure authentication in Node.js using password hashing with bcrypt and stateless authentication with JSON Web Tokens (JWT).
Authentication is a fundamental aspect of any web application, ensuring that users can securely log in and access sensitive resources. In Node.js, authentication requires securely handling user credentials, validating access rights, and managing sessions effectively. To achieve this, developers commonly rely on two key techniques: hashing passwords for secure storage and using JSON Web Tokens (JWT) for stateless authentication.
Together, password hashing and JWTs form a robust foundation for building secure, efficient, and scalable authentication systems in Node.js applications.
Password hashing with bcrypt
Passwords should never be stored in plain text to ensure user security. If a database is compromised, plain-text passwords can be easily exploited, putting all accounts at risk. Instead, passwords should be hashed, transforming them into irreversible strings that cannot be converted back to their original form.
The bcrypt
library is a reliable tool for hashing passwords in Node.js. It adds a unique random string, called a salt, to each password before hashing, ensuring that even identical passwords produce different hashes. This approach prevents attackers from using precomputed hash dictionaries, making it much harder to crack passwords. In addition to hashing passwords during registration, bcrypt
allows for secure verification during login by comparing the entered password with the stored hash. This ensures user credentials are protected even if the database is compromised.
Here’s how we can use bcrypt
to hash a password and see the transformation from the original password to its hashed version:
import bcrypt from 'bcrypt'; const password = 'my_secure_password'; // Hash the password bcrypt.hash(password, 10) .then((hashedPassword) => { console.log(`Original Password: ${password}`); console.log(`Hashed Password: ${hashedPassword}`); // Compare the original password with the hashed password return bcrypt.compare(password, hashedPassword) .then((isMatch) => { console.log(`Password matches: ${isMatch}`); }); }) .catch((err) => console.error('Error:', err));
Explanation
Line 1: Import the
bcrypt
library, which is used for securely hashing and verifying passwords.Line 3: Define a plain-text password to simulate what a user might provide during registration.
Line 6: Use
bcrypt.hash(password, 10)
to hash the password. The number 10 represents the salt rounds, which determine how many times the hashing algorithm is applied. More rounds increase security but require more processing time.Lines 8–9: Log the original password and its hashed version to show the transformation from plain text to a secure, irreversible format.
Line 12: Use
bcrypt.compare(password, hashedPassword)
to verify whether the plain-text password matches the hashed password. This simulates the process used during login to validate user credentials.Line 13: Log the result of the comparison. If the plain-text password matches the hash,
isMatch
will betrue
. Otherwise, it will befalse
.
Run the node index.js
command multiple times to observe how the hashed password changes each time, even though the original password remains the same. This variation occurs because bcrypt
generates a unique salt for each hashing operation. The salt is embedded directly into the hash, along with other details, and processed through the bcrypt
algorithm. This design ensures that the resulting hash includes all the information necessary for secure password verification.
Applying bcrypt
to user registration
Now that we’ve seen how bcrypt
hashes passwords, let’s apply this knowledge to a real-world scenario: registering a new user. During user registration, securely handling passwords is essential. In a plain Node.js application, the ...