Two-factor authentication can seem like a very banal process on the user's part. Many have long been accustomed to the fact that when using many applications, it is not enough to enter a familiar login and password combination. An additional verification factor is introduced for added security and to prevent unauthorized access. In most cases, it is a unique code that can be received by e-mail or SMS.
In this tutorial, we will learn how you can implement two-factor authentication in AppMaster. As an example, sending the code to the mail will be used. We will analyze which modules need to be connected, what settings to make, what business process to create, and how to check the finished result in practice.
First, let's take a look at the general plan. What steps need to be implemented in our business process?
- Login verification (make sure the user is actually registered).
- Checking if the password is correct.
- Sending a confirmation code by mail or SMS.
- Checking the relevance of the code.
- Checking the correctness of the code.
- Сompletion of authentication.
Preparatory stage
Even before you start designing two-factor authentication, at the user registration stage, you need to be sure that:
- The user data must contain an e-mail. It will be used to send the verification code. E-mail is often used as a login, and we will look at just such an example.
- The login is unique. Two different users with the same login cannot exist in the system.
For the convenience of solving these (and many other) tasks, the Auth module is installed by default in each AppMaster project. It contains the necessary database models, business process blocks, and endpoints for their use.
Login verification
Login, password, and verification code are used as input parameters of the business process. But at the first stage, we only need a login. It would help to make sure that this user exists. He has been registered, and information about it is stored in the database.
To do this, the DB: Search User block searches for a user with the given login in the database. Be sure to set the SearchExact = True parameter to search for an exact match.
The received data is passed to the Array Element block with index 0 (counting starts from zero, and the only value found will always correspond to index 0 in the array).
The Is Null block checks whether the user was actually found. And depending on the result (True/False), the If-Else block will either interrupt the business process with an error message (Raise Error block) or direct it further.
Password check
At this stage, you need to make sure that the user password is entered correctly.
And here, it is essential to understand that passwords are not stored explicitly in the database. The Bcrypt function hashes them, and only the resulting hash is stored in the database. Therefore, even if we assume that a data leak will occur, attackers will still not be able to find user passwords; they are securely encrypted.
For this reason, you cannot simply get the password from the database and compare it with the entered password. You need to get the hash of the password and use it for comparison. To solve this problem, the Auth: Probe Password block is used. As input parameters, it takes the password entered by the user (password) and the hash of the password stored in the database (hashed_password) and converts it to the String data type (the To String block).
Depending on the result, as in the previous step, using the If-Else block, we either display an error message (Raise Error, Message = Password is wrong) or direct the process to the next stage.
User model extension
For the next step, you need to make a small change in the user model and add information about the code that needs to be confirmed.
In general, the User model initially contains fields that can be used for this task - Confirmed, Confirmation code, Confirmation code expires at. But in this example, we will assume that these fields are used only for registration and initial account verification.
For greater clarity of the process, let's create a separate twofa (two-factor authentication) model, associate the User model with it (1-to-1 relationship, has one), and add one field - code (String type).
Preparing to send emails
To send e-mails with confirmation codes, one should prepare preliminary. One of the most accessible options is to use the Custom SMTP module, which you need to install and configure.
When using Gmail, most settings are already set by default, and you need to add your username and password. When using other mail servers, it would help if you referred to their documentation to obtain the necessary data.
In this case, you may need to change the mail server's security settings slightly. For example, Gmail may block connections using third-party applications by default, and you need to remove this restriction in the settings.
Sending a verification code
We checked the login and password and completed all the necessary preparations, so now you can proceed to send a letter with a confirmation code.
The Custom SMTP: Send Email block uses an array of addresses as a destination. Therefore, even though you need to send a letter to only one address, it should be added to the array. For this, the Append Array block is used.
The next step is to generate a verification code. The Random string block is suitable for this. We will send a code consisting of 6 random numbers and make the appropriate settings. Length = 6, With 0-9 = True, all other parameters = False.
Next, you need to create the text of the letter. To do this, use the Concat Strings block to add explanatory text to the generated code (First = Verification code: ), convert the result to the Text data type (To Text block), and connect the result to the body parameter of the email sending block.
For the final sending, it remains only to specify the subject of the letter (subject) and the sender (from_name).
But it is not enough just to send the code; it must also be stored in the user's data. After all, you need to verify its correctness when the user receives the code and sends it back as confirmation.
To do this, we will use the twofa model, which we prudently created earlier. If this is the first code submission, you must create it with information about which user it belongs to. In case of reuse, it is necessary to patch the existing entry, indicating its ID and new code.
The final step of the stage is to use the Raise Error block to return a message about sending the code to the e-mail.
Code relevance check
It is worth taking care of additional security and protecting the code from a banal enumeration. It would be wise to limit the number of input attempts, their frequency, and the submitted code's lifetime. We will not analyze all these examples; security requirements are individual for each project and may include many different conditions. We restrict ourselves to checking the relevance of the code by its validity period.
Let's use the Current date & time block to get the current time. Connect it to the B parameter of the Date & time difference block. We will use the UpdatedAt field of the twofa model as the parameter A.
As a result, we get Time span - the difference between two time points. It remains only to check whether this difference exceeds a certain selected value. In our example, this is 5 minutes, which we will set as the static value B of the Greater block.
The If-Else block will use the comparison result to guide the process further. If True (the difference exceeds 5 minutes), the process will return to the step of sending the letter; the user will receive an updated code. In the case of False (the code is fresh and up-to-date), it will be possible to proceed to check this code.
Code review
The final stage of authentication is checking the received code.
The Equal block must validate that the code passed in by the user matches the code stored in the twofa model associated with the user. If this is not the case and the code is specified incorrectly (If-Else -> False), then you need to display an error message (Raise Error, Message = Code is wrong). If the comparison confirms the match, you can proceed to the final authentication stage.
To do this, we use the Auth: Authentication block and get information about the user with his authorization token. We pass them to the End block as a result of successful authentication.
Creating an endpoint
The business process has been created but is not yet available for use. You need to create an endpoint that will run this business process.
A reasonable solution would be to use the default authentication endpoint (POST /Auth). It will be enough to replace its business process and install the one that was just created. Thus, simple authentication will be disabled, and two-factor authentication will be used instead.
Publication and testing
This completes the creation of two-factor authentication. You can publish the result and check it in action. For this, it is convenient to use Swagger, which can be accessed by clicking on the name of the Deploy plan in the Project API section of the Preview button.
It remains only to find the desired endpoint in the list, enter user data and start the execution with the Execute button. If any errors are found during testing, then return to the business process and make the necessary changes.