How to implement reset password on a rest api with jsonwebtoken in a express app
Implement reset password in a rest-api with stateless jsonwebtoken approach
1 Overview
- Add password reset functionality on top of a rest-api that has authentication and authorization implemented. If you want to know the basics of how jsonwebtoken is used for authorization, you can read here
- Prisma ORM is used with SQLite. So you don’t have to setup any database in your system
- This approach is stateless and requires the least amount of db calls, therefore prioritizing performance
- The code is available on github
1.1 Flow
sequenceDiagram participant c as Client participant s as Server c ->> s: body = { email } Note over c,s: GET /pass-reset s -> s : find user and generate token s -> s : send reset link in email c ->> s: body = { password } Note over c,s : POST /pass-reset/:userId/:token s -> s : verify token and update password
2 Token Generation
utils/genToken.ts:
|
|
- Generate a token with a 2 minute expiration time (lesser the better)
- Use an environment variable as secret, this should be large and random
|
|
2.1 Why keep the validity duration small ?
As we are using stateless jwt, we can not invalidate the token. To prevent a user from using the same reset link again and again, we must set the expiration time to a lower value
2.2 Why not just use regular accessToken ?
A regular access token provides access to protected resources which we don’t want at all. The user only provides a email address, they may or may not be a legitimate user. Thats why we can’t just hand over an accessToken on a password reset request
3 Sending Email
There are multiple ways to send email from a server.
Here is a sample code to setup a gmail account to send an email using nodemailer.
utils/sendEmail.ts:
|
|
3.1 How to enable app password in google account
- Go to google account page
- Navigate to security and enable 2-step verification
- Search “App Passwords” in the search bar and click on the matched result
- Chose app as “Mail” and device as “Other” and put anything there (such as “linux”)
- Click on “GENERATE” button and save the password as
serviceEmailPassword
in .env file. (NOTE: you only get one chance to save the password)
3.2 Sending the reset link
controllers/passReset.ts:
|
|
4 Resetting password
4.1 Password Reset Form
- The frontend should have a route “PASS_RESET_URL/:userId/:token” that takes an email in the body which sends a POST request to
/api/pass-reset/:userId/:token
on submit - Here, I have created the most basic html form to keep this article backend focused
utils/passReset.ts:
|
|
4.2 Updating password
Route : router.post("/api/pass-reset/:userId/:token", verifyPasswordResetToken, passReset)
- verifyPasswordResetToken middleware verifies the token with the secret PASS_RESET_TOKEN_SECRET
utils/passReset.ts:
|
|
- IMPORTANT: Hash the password before saving it in the database
- Ensure that you use the same hash function for password hashing in every cases such as user creation, change password, reset password
db/users.ts:
|
|