Skip to content

Aleksandar15/Password-Manager-frontend

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Password Manager V2 using PERN Stack

-> Here is my server-side code

Visit my live website here: https://alek-password-manager.netlify.app

Test login user:

About my Full-Stack Password Manager app:

I created this project with one of the main goal being that to challenge my web security skills. In the process I learned that while you can't control which links the frontend user clicks or which apps they may install (that could be malicious), we as developers should focus to minimize those risks by maximizing security steps required to access sensitive data. All that must be achieved by finding a sweet spot between trying to not annoy our users and securing their data.

Special features I implemented:

On the frontend
  • With React I avoided any unnecessary re-renders & used strategic re-renders to my advantage for features like multi-device for example: user A logged on 'device A' modifies its "password vault", then the same user A but logged on 'device B' - when they try any CRUD operations on their (unrefreshed) "password vault" page - they will get the latest changes (made on device A) without any refresh on their device B. While also my goal was to use as minimum libraries as possible and to keep following the DRY principle by building reusable components myself & creating custom hooks.
On the backend
  • I implemented "refresh tokens" which are long-lived alongside "access tokens" which are short-lived JSON web tokens. However I gave the clients an option to stay signed-in until they manually log out in cases where they fully trust their device & network. The user requires a valid refresh token in order to request a new access token - on success they get both new accessToken & refreshToken - while on invalid or expired refresh token the said token is removed from the database and the user is alerted accordingly and redirected to the login page on the frontend.
  • Anti-hacks security: in case where the user's refreshToken is not inside the database -> it means the refreshToken was used by someone else (I suspect it's a hacker) and I alert the user about the potential threat.
    • Note: these features are commonly called "refresh token rotation" and "refresh token reuse detection".
    • However, in my multi-device logic the result of "missing refreshToken" could simply mean that a trusted family user - logged on another device - have just used the option "logout all devices" so I had to modify the alert to remind my users of such case scenario where they have to communicate it out with them and be assured whether they were hacked or not. (Read more info below)
The challenge
  • "Multi-device" feature allows the user A logged on device A to "log out all devices" which means: empties out the array of refreshToken's in the database; which will technically log-out the user A from both the device A and also logout the same user A but logged on device B & my "safety alert-message" about anti-hacks will get triggered, therefore, the message itself had to be modified to include more empathy about such a case scenario where some of their family members may have clicked the "logout all devices" button on another device as an example.
    • It was kind of like a Catch-22 where I couldn't have a separate message - for both #1 the attempt of the hacker to be re-using the same refreshToken that the legit-user already have used & #2 a trusted user has used "*logout all devices" feature -> which in both cases leads to removal of the refreshToken from database - and the solution was a guided-empathetic-message to make sure that such a user has asked for more info from his trusted user's actions first, so that I'm not misleading my users with the alert.
My PERN Stack technologies:
Postgres DB + ExpressJS + ReactJS + NodeJS + JWT + Redux

Run my frontend project

  • Clone this project.
  • Navigate (cd) into your project directory.
  • Run npm install in your command line.
  • Run npm start in your command line.
  • Visit http://localhost:3000 in your browser!
  • Optional: you may want to connect it with my backend project.

NOTES

1. In order for authentication cookies to work, the current server setup has secure: true property in its cookie creation so you might need to start local development with HTTPS protocol by modifying parts of package.json:

For Windows OS:

"scripts": {"start": "set HTTPS=true&&react-scripts start"} or just start the app with set HTTPS=true&&npm start command.

For MacOS:

"scripts": {"start": "HTTPS=true react-scripts start" or just start the app with HTTPS=true npm start command.

2. Make sure to modify BASE_URL to your server's localhost URL by heading to axios.js file directory: src/Utils/api/axios.js & also omit the /api path as its only suitable for production build with my current Netlify proxy setup.

HOW TO's

How to clone the project?
Clone with HTTPS URL: git clone https://github.com/Aleksandar15/Password-Manager-frontend.git
Clone with SSH URL: git clone [email protected]:Aleksandar15/Password-Manager-frontend.git
How to connect to the server?
Clone the server code from here & follow the instructions there.

More info:

  1. PersistLogin on frontend could be named "PersistLoading" because throughout development I modified it to persist "Loading" page always and to never give an 'empty skeleton-page' of a protected route (see ex. #1.1). I compared my app to instagram for inspiration and achieved exactly what I imagined. All the while auth-checks are handled in each component and they all have "Loading" as default state which is pretty cool.

#1.1 Example: to never give away an 'empty skeleton-page' of a protected route (ex. /manager) to unauthorized user - and the other way - to never visually show unauthenticated route like /login to authorized user and instead after "Loading" -> redirect them back to authorized route /manager.

  1. refreshTokenController on the server is rotating each valid non-expired refreshToken with a new one and I am passing the remaining 'expiryTime' from the old one which was now "invalidated" - meaning it was removed from database & replaced with newRefreshToken. That's a perfect security feature I implemented on my app.
  2. Axios frontend utils created using axios.create method by default parses my JSON data behind the scenes hence why I don't use JSON.parse on my backend. If I were to send a JSON I'd need the transformRequest function.

About

This is the frontend part of my full-stack password-manager app

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published