Allow user to log in as long as the account is on the database
Upon logging in, the user will now be asked to confirm their 2FA if they have it enabled
Implementation
Added an extra field called otp_base32. This will hold the seed of the user's randomly generated base 32 for their One Time Password (OTP)
Added a services.py file to handle function services related to the account view
For 2FA generation/verification, the package pyotp is used. The package also allows this implementation to be further extended for MFA implementation.
Added 3 Views: Set2FAView, Verify2FAView, and LoginView
Created a basic web page to input the 6-digit (OTP) at \meshapp\src\home\otp.tsx. The input field relies on a package: react-otp-input
To transfer some needed data from page to the other, the package: react-cookie is used, which implements the cookie functionality
How It Should Work
The user first will make a login request from the front end, filling in the required field of both email and password
Upon clicking the login button, a post request to the login view from the backend is made
Backend will verify if the request is valid, verifying if the user's data and password. If successful, backend will give both the accountID that ties to this user and the information if the user has 2FA enabled
If 2FA is enabled, the page will be navigated to /otp where the user will be asked to input a 6 digit pin code, otherwise they will be directed to the logged_in_home page
Simultaneously, a cookie holding the user's accountID will be created with a time limit. It will also call another post request to the backend, calling Set2FAView.
Set2FAView will check if the user has 2FA enabled or not. If it doesn't then backend will generate a new seed and store it on the otp_base32 field for that user. Finally, backend will send an email to the corresponding user's email which holds the otp. NOTE Set2FAView might need a separate new view or function to handle the user's first-time setup, especially if MFA uses an authenticator to be implemented.
Once the user input the 6 digit code and click the submit button, a post request is called for Verify2FAView, which take the cookie holding the user's accountID to find the correct OTP seed, and then verify if the 6 digit code is correct.
Upon verification, the user will be redirected to the logged_in_home page.
Issues/Bugs/ToDos
[x] BUG: upon first login request and the cookie was generated, there is a synchronization error between the cookie data and the request. This causes the user to be verified and their user ID is still saved as a cookie, but upon requesting OTP verification, the user's ID is somehow still undefined. This does not occur on a subsequent request, and the possible explanation is that the previous cookie from a previous attempt is still saved. SOLVED: 3d188d8
[x] There needs to be a timeout when the user is on the otp page, since the otp that is sent to the user will expire on a given period of time. On this point, it might also be needed for the otp that was sent to the user to be semi-constantly checked until it no longer valid on the backend, and then synchronized with the timeout on the front end, guaranteeing a possible better consistency for the otp period. Somewhat solved by having session interval: 82aa946
[ ] Email from backend to the user has not been setup and therefore currently the otp is printed out to the console
[ ] While the code for sending the email is there, the body message for the email itself need to be further extended
[ ] In order to organize the files, most of the new implemented backend functionality is stored at services.py. But to verify user's login request, the decrypt function needs to be copied to this file. To avoid copying of the same code, it is possible to move some functions from views.py to services.py such as both encrypt and decrypt.
Improvement
[ ] Permission/authentication needs to be added for all of the newly added views. For example, requesting an OTP verification does not make sense if the user is not verified to be logged in.
[ ] Similarly, the /otp web page should not be accessible if the user is not properly verified
[ ] Some extra security need to be implemented. Some detail for the basic security requirement: https://pypi.org/project/pyotp/. For example, login requests should be throttled in order to avoid brute-force attack
[ ] The web page implementation is pretty barebones. Some more styling can be added such as handling error responses with better UI instead of just displaying an error alert
Note
While packages added to the project frontend are saved on package.json, I am not sure if that is also the case for the backend. Therefore to test the changes these packages needed to be installed:
pyotp
django-cors-headers
django-cors-headers was used for testing calls made from front end to the back end. To do this, I run the front end using npm start and the backend using manage.py runserver. At the same time on django settings, both field, CORS_ORIGIN_WHITELIST and CORS_ALLOWED_ORIGINS are set as my localhost for the front end, which is "http://localhost:3000"
Solving issue: #77
Features
Implementation
How It Should Work
Issues/Bugs/ToDos
BUG: upon first login request and the cookie was generated, there is a synchronization error between the cookie data and the request. This causes the user to be verified and their user ID is still saved as a cookie, but upon requesting OTP verification, the user's ID is somehow still undefined. This does not occur on a subsequent request, and the possible explanation is that the previous cookie from a previous attempt is still saved.SOLVED: 3d188d8There needs to be a timeout when the user is on the otp page, since the otp that is sent to the user will expire on a given period of time. On this point, it might also be needed for the otp that was sent to the user to be semi-constantly checked until it no longer valid on the backend, and then synchronized with the timeout on the front end, guaranteeing a possible better consistency for the otp period.Somewhat solved by having session interval: 82aa946Improvement
Note
While packages added to the project frontend are saved on package.json, I am not sure if that is also the case for the backend. Therefore to test the changes these packages needed to be installed:
django-cors-headers was used for testing calls made from front end to the back end. To do this, I run the front end using npm start and the backend using manage.py runserver. At the same time on django settings, both field, CORS_ORIGIN_WHITELIST and CORS_ALLOWED_ORIGINS are set as my localhost for the front end, which is "http://localhost:3000"