Welcome back! In the previous lesson, you built a secure registration system that hashed passwords before saving them. Now it’s time to let users log in and access protected areas of your API.
But how do we remember users once they log in? That’s where JWT (JSON Web Token) comes in.
- When a user logs in successfully, the server issues a signed JWT.
- The client includes this token in the
Authorizationheader of future requests. - The server verifies the token to confirm the user’s identity and role.
By the end of this lesson, you’ll have:
- A login endpoint that validates credentials.
- A system that issues JWTs for authenticated users.
- A way for clients to access protected routes using those tokens.
At registration time, you hashed the user’s password with bcrypt. Now during login, bcrypt needs to check whether the plain-text password the user entered matches the stored hash.
Here’s the interesting part: bcrypt hashes aren’t deterministic in the way you might expect. Even if two users both use "password123", their stored hashes will look completely different. That’s because each hash contains:
- The salt (a random string generated when hashing).
- The cost factor (how many rounds of hashing).
- The final hash itself.
When verifying:
- bcrypt extracts the salt and cost factor from the stored hash string.
- It applies the same algorithm to the candidate password.
- If the result matches the stored hash, the password is correct.
This design means you don’t need to store salts in a separate column — everything is self-contained in the hash string.
That’s why verifyPassword works reliably:
Here, the hash itself tells bcrypt how to verify it, making the process both secure and convenient.
The login method in AuthService is a small but critical piece of the authentication puzzle. Let’s break down the important design choices:
Key points:
- Fail fast → If either username or password is missing, the method immediately rejects.
- Constant response → Notice how it throws the same error message whether the username is wrong or the password is wrong. This prevents attackers from learning which part was incorrect.
- JWT payload → The token contains just the user ID (
sub) and role, not sensitive info like the full user object. - Token signing → Uses
JwtService.signwith the secret key fromAuthModule, ensuring the token can later be verified.
This design balances security, performance, and simplicity.
