Goal is to create a boilerplate project for users authentication. With the following functionality
-
Reguster a user
-
Verify email
-
Login
-
Forgot/Reset password
-
Logout
-
Get all users
-
Get single user
-
Show current user
-
Change password
-
Update user
-
Get user tokens
-
Create mongoDB and setup MongoDB
-
Clone the repo
git clone https://github.com/Delvoid/boilerplate-auth-express.git
-
Install NPM packages
npm install
-
Enter your MongoDB API's in .env
MONGO_URI = ENTER YOUR MONOGO API JWT_SECRET= YOUR SECRET JWT_LIFETIME= LIFETIME
Run the tests
npm test
Start the dev server
npm run dev
Setup Basic Express Server
- import express and assign to variable
- setup start port variable (5000) and start function
Connect To DB
- get connection string
- setup .env with MONGO_URI variable and assign the value
- import 'dotenv' and setup package
- import connectDB() and invoke in the starter
Basic Routes and Middleware
- setup /test GET Route
- setup express.json() middleware
- setup errorHandler middleware
- setup 404 route not found middleware
- import 'exress-async-errors' package
User Model
- create models folder and User.js file
- create schema with name,email, password, verificationToken (all type:String), isVerified - {type: Boolean, default: false}, verified type: Date
- export mongoose model
Auth Routes Structure
- create controllers folder
- add authController file
- export (register,login,logout) functions
- res.send('some string value')
- create routes folder
- setup authRoutes file
- import all controllers
- setup three routes
- post('/register') post('/login') get('/logout')
- import authRoutes as authRouter in the app.js
- setup app.use('/api/v1/auth', authRouter)
Register Controller
- create user
- setup fake verificationToken - 'fake token' - for verification functionality later
- check if email already in use (schema and controller)
- send response with entire user (only while testing)
- send back success message and token
Handle Password
- UserSchema.pre('save') - hook
- this points to User
- bcrypt.genSalt - number of rounds
- bcrypt.hash
JWT
- require 'jsonwebtoken' package
- create jwt - jwt.sign(payload,secret,options)
- verify jwt - jwt.verify(token,secret)
- add variables in .env JWT_SECRET=jwtSecret and JWT_LIFETIME=1d
- refactor code, create jwt functions in utils
- refactor cookie code
- setup func attachCookiesToResponse
- accept payload(res, tokenUser)
- create token, setup cookie
Login Route
- check if email and password exist, if one missing return 400
- find user, if no user return 401
- check password, if does not match return 401
- check if user.isVerified, if not 401
- if everything is correct, attach cookie
and send back the same response as in register
Logout Route
- set token cookie equal to some string value
- set expires:new Date(Date.now())
Verify Email Controller
- create verifyEmail in authController
- get verificationToken and email from req.body
- setup a '/verify-email' route in authRoutes
- check for user using email
- if no user 401
- if token does not match user token 401
- if correct set
- user.isVerified = true
- user.verified = Date.now()
- user.verificationToken = ''
- save use with instance method
- return msg:'email verified'
Email Setup
- ethereal credentials (create account/login)
- install nodemailer
- create (nodemailerConfig, sendEmail,
sendResetPasswordEmail, sendVerificationEmail) files in utils
Send Verification Link
- setup sendEmail
- setup sendVerificationEmail.js
- pass arguments
Refresh Token Model
- create Token.js in models
- refreshToken,ip,userAgent - all String and required
- isValid - Boolean, default:true
- ref user
- timestamps true
- attempts Number: default: 0
Setup Refresh Token in Login Controller
- create empty refreshToken
- check for existing token
- if existing token
- check token is valid, if not 401
- refreshToken = existingToken
- attachCookiesToResponse
- return status ok with tokenUser
- create new refreshToken
- get user-agent
- create userToken
- ip
- refreshToken
- userAgent
- user id
- attachCookiesToResponse
- return response ok with userToken
Send Multiple Cookies
- attachCookiesToResponse utils- jwt
- accessTokenJWT
- refreshTokenJWT
Create Auth Middleware - Access , Refresh Token
- create auth middleware
- create authenticateUser and authorizePermissions
- [x]add dashboard route to test auth middleware
Refactor Logout
- remove cookies when logging out
Forgot/Reset Password Functionality
- Update User Model
- passwordToken {type:String}
- passwordTokenExpirationDate {type:Date}
- Update authController
- forgotPassword and resetPassword
- Update authRoutes
- post '/forgot-password' '/reset-password'
- post '/forgot-password' '/reset-password'
Forgot Password Controller
- check valid email
- find user
- if valid user
- generate a passwordToken
- send password reset email
- set passwordTokenExpirationDate
- has password token
- save to user
- send ok status with msg
Reset Password Controller
- check for token, email and password, if not 404
- find user by email
- if user
- check if token has expired
- get current date
- If check password token matches &&
- If check token has not expired
- update users password
- set password token to null
- set passwordTokenExpirationDate to null
- save user
- return response password reset
User Routes Structure
- add userController file
- export (getAllUsers,getUserById,showCurrentUser,updateUser,updateUserPassword) functions
- res.send('some string value')
- setup userRoutes file
- import all controllers
- import userRoutes as userRouter in the app.js
- setup app.use('/api/v1/users', userRouter)
GetAllUsers and GetSingleUser
- Get all users and remove password
- Get Single User where id matches id param and remove password
- throw error if invalid id
- If no user 404
ShowCurrentUser
- get user from req
- send response with user
UpdateUserPassword
- almost identical to login user
- add authenticateUser middleware in the route
- check for oldPassword and newPassword in the body
- if one missing 400
- look for user with req.user.userId
- check if oldPassword matches with user.comparePassword
- if no match 401
- if everything good set user.password equal to newPassword
- await user.save()
CreateTokenUser in Utils
- create a file in utils (createTokenUser)
- setup a function that accepts user object and returns userToken object
- export as default
- setup all the correct imports/exports
updateUser with user.save()
- add authenticateUser middleware in the route
- check for name and email in the body
- if one is missing, send 400 (optional)
- use user.save() to trigger the UserSchema.pre('save') - hook
- create token user, attachCookiesToResponse and send back the tokenUser
get logged in user tokens
- create GetUserTokens
- if no id passed get logged in users tokens
- if id is passed find the tokens for that users id
Setup and Apply checkPermissions()
- first user created is a admin
- create checkPermissions in ultis
- create authorizePermissions middleware
- only admin can get all users list
- users can cannot get other users by ID unless they are admin
Setup
- install supertest
- install smtp-server
- create tests folder
- refacter app.js for supertest
- create package script test
Register User
- beforeAll connect to DB
- afterAll disconnect from DB
- afterEach delete users
- returns 200 on valid requests
- returns Success! Please check your email to verify account on valid requests
- returns badRequest is email already in use with message
- returns 400 if name email missing
- returns 400 if name name missing
- returns 400 if name password missing
- returns validationErrors field in response body when validation error occurs
- returns error for when name, password and email are null
- creates user unverified
- creates an activation token for user
- password is hashed
- [x]Password must have at least 1 uppercase, 1 lowercase letter and 1 number
- email sent with verificationToken
- returns 502 when sending email fails
- does not save user to dabase if activation email fails
- first user is created has a role of admin
- second user is created has a role of user
Verify Email
- verifies the email when correct token is sent
- removes the token from user table after successful verification
- sets the verified date after successful verification
- returns msg with Email verified on successful verification and 200 status
- does not verify email with incorrect token and returns 401
- returns unauthorised request when token is wrong
- returns unauthorised request when email is wrong
Error Model
- returns path, timestamp, message and validationErrors in response when validation failure
- returns path, timestamp and message in response when request fails other than validation error
- returns path in error body
- returns timestamp in milliseconds within 5 seconds value in error body
Login
- returns 200 when credentials are correct
- returns tokenUser: name, userId, role when login success
- attach cookie if login is successful
- returns 401 if user not exist
- returns correct error body when auth fails
- returns 401 when password does not match
- returns 401 when e-mail is not valid
- returns 401 when password is not valid
- returns 400 when e-mail is missing
- returns 400 when password is missing
- returns 401 when logging in with an unverified account
Logout
- returns 401 ok when unathorized request send for logout
- removes tokens from database
- removes stored cookies
Forgot password
- return 200 with msg
- creates password reset token on valid email and user
- creates passwordTokenExpirationDate on valid user
- return 400 if invalid email
- sends email if valid user is found
- returns 502 when bad gateway when sending email fails
Reset password
- returns 403 when password update request does not have the valid password reset token
- returns 400 when trying to update with invalid password and the reset token is valid
- returns 200 when valid password is sent with valid reset token
- updates the password in database when the request is valid
- clears the reset token in database when the request is valid
- verifys email if unverified after valid password reset
Get users
- returns 403 if not authorised
- returns 200 when valid auth and role admin
- returns users list when valid auth and role admin
Get single user
- returns 404 when user is not found
- returns correct error body when user not found
- returns 200 on valid user when admin
- returns 401 when basic user tries to get another user
Get current user
- returns 401 if not authorised
- returns 200 and logged in user object without password if authorised
Update password
- returns 200 with msg on valid request
- returns 400 if oldPassword is missing
- returns 400 if newPassword is missing
- returns 401 if passwords do not match
- returns 400 if password is not pass validation
- updates the password in database when the request is valid
Update User
- returns 401 when request without basic auth
- returns 200 with msg on valid request
- returns 400 if name is missing
- returns 400 if email is missing
Get user tokens
- returns 200 if valid auth
- returns logged in users tokens if no id is passed
- returns logged in users tokens if id is passed
- returns 400 if invalid id
- returns 403 if a user tries to get another users tokens
- returns 200 if a admin tries to get other users tokens
- [] returns 403 when refresh token is 30days old
- [] refreshes token if expiered and refresh token is valid
- [] should i add? - to think about it
- [] [Docgen Library] (https://github.com/thedevsaddam/docgen)
- [] Export Postman Collection
- [] docgen build -i fileName.json -o index.html
- [] create index.html in public
- [] express-rate-limiter - is this needed?
- helmet
- xss-clean
- cors (cookies!!!!)
- heroku account and heroku cli
- add dev command "nodemon app.js"
- change start to "node app.js"
- setup node version in package.json
- git commit latest changes
- heroku login
- heroku create "App Name"
- setup env vars in heroku
- git push heroku