{"id":8271,"date":"2022-10-04T19:47:21","date_gmt":"2022-10-04T19:47:21","guid":{"rendered":"https:\/\/codevoweb.com\/?p=8271"},"modified":"2023-08-16T09:59:39","modified_gmt":"2023-08-16T09:59:39","slug":"two-factor-authentication-2fa-in-nodejs","status":"publish","type":"post","link":"https:\/\/codevoweb.com\/two-factor-authentication-2fa-in-nodejs\/","title":{"rendered":"How to Implement Two-factor Authentication (2FA) in Node.js"},"content":{"rendered":"\n<p>This article will teach you how to secure a Node.js API by implementing <strong>two-factor authentication<\/strong> (2FA) system using tokens generated by <a href=\"https:\/\/play.google.com\/store\/apps\/details?id=com.google.android.apps.authenticator2\" target=\"_blank\" rel=\"noreferrer noopener\">Google Authenticator<\/a> or <a href=\"https:\/\/authy.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">Authy<\/a>. The <strong>one-time passcode<\/strong> (OTP) can be delivered via different methods like SMS but we will use Google Authenticator or Authy to reduce the complexity of the project.<\/p>\n\n\n\n<p>API security should be a top priority for developers. Why? Because there are more than 90,000 attacks on APIs every minute.  Instead of using only the traditional method of authentication &#8211; email and password, it&#8217;s recommended to add an extra layer of security by implementing social login with Google, GitHub, or Facebook, Two-Factor Authentication (2FA), Multi-factor, Facial recognition, Biometrics, Single sign-on, and many more.<\/p>\n\n\n\n<span id=\"ezoic-pub-video-placeholder-107\"><\/span>\n\n\n\n<p>Let&#8217;s talk about the main focus of this article. We will be using the <code>otpauth<\/code> library to implement the <strong>time-based one-time password<\/strong>&nbsp;(TOTP) authentication in Node.js. The API will run on an Express server and use Prisma ORM to store data in an SQLite database.<\/p>\n\n\n\n<p>When I initially wrote this article, we utilized the <a href=\"https:\/\/github.com\/speakeasyjs\/speakeasy\" target=\"_blank\" rel=\"noreferrer noopener\">Speakeasy<\/a> library to implement TOTP generation and validation. However, due to the lack of recent updates and maintenance for Speakeasy (last updated 7 years ago), many users have expressed a need for an alternative library. <\/p>\n\n\n\n<p>Consequently, I updated the article and the accompanying source code to utilize the <a href=\"https:\/\/www.npmjs.com\/package\/otpauth\" target=\"_blank\" rel=\"noreferrer noopener\">otpauth<\/a> library instead. This library has gained popularity as a reliable alternative, with a substantial number of weekly downloads, approximately 63,207. By making this switch, we can ensure a more dependable and actively supported solution for TOTP implementation.<\/p>\n\n\n\n<p>Related article:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"\/two-factor-authentication-2fa-in-reactjs\">How to Implement Two-factor Authentication (2FA) in React.js<\/a><\/li>\n\n\n\n<li><a href=\"\/two-factor-authentication-2fa-in-golang\">How to Implement (2FA) Two-factor Authentication in Golang<\/a><\/li>\n\n\n\n<li><a href=\"\/two-factor-authentication-2fa-in-fastapi-and-python\">Two-factor Authentication (2FA) in FastAPI and Python<\/a><\/li>\n\n\n\n<li><a href=\"\/django-implement-2fa-two-factor-authentication\">Django &#8211; Implement (2FA) Two-Factor Authentication<\/a><\/li>\n\n\n\n<li><a href=\"\/rust-implement-2fa-two-factor-authentication\/\">Rust &#8211; Implement (2FA) Two-Factor Authentication<\/a><\/li>\n<\/ul>\n\n\n\n<p>More practice:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"\/setup-and-use-mongodb-with-deno\">How to Setup and Use MongoDB with Deno<\/a><\/li>\n\n\n\n<li><a href=\"\/setup-deno-restful-crud-project-with-mongodb\">How to Set up Deno RESTful CRUD Project with MongoDB<\/a><\/li>\n\n\n\n<li><a href=\"\/authentication-with-bcrypt-jwt-and-cookies-in-deno\">Authentication with Bcrypt, JWT, and Cookies in Deno<\/a><\/li>\n\n\n\n<li><a href=\"\/deno-crud-restful-api-with-mongodb\">Complete Deno CRUD RESTful API with MongoDB<\/a><\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"850\" height=\"478\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/How-to-Implement-2FA-Two-factor-Authentication-in-Node.js.png\" alt=\"How to Implement (2FA) Two-factor Authentication in Node.js\" class=\"wp-image-8380\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/How-to-Implement-2FA-Two-factor-Authentication-in-Node.js.png 850w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/How-to-Implement-2FA-Two-factor-Authentication-in-Node.js-300x169.png 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/How-to-Implement-2FA-Two-factor-Authentication-in-Node.js-768x432.png 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/How-to-Implement-2FA-Two-factor-Authentication-in-Node.js-100x56.png 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/How-to-Implement-2FA-Two-factor-Authentication-in-Node.js-700x394.png 700w\" sizes=\"auto, (max-width: 850px) 100vw, 850px\" \/><\/figure>\n\n\n<style>.kb-table-of-content-nav.kb-table-of-content-id8271_91e8d5-1c .kb-table-of-content-wrap{padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;border-top:1px solid #abb8c3;border-right:1px solid #abb8c3;border-bottom:1px solid #abb8c3;border-left:1px solid #abb8c3;}.kb-table-of-content-nav.kb-table-of-content-id8271_91e8d5-1c .kb-table-of-contents-title-wrap{padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px;}.kb-table-of-content-nav.kb-table-of-content-id8271_91e8d5-1c .kb-table-of-contents-title-wrap{color:#ffffff;}.kb-table-of-content-nav.kb-table-of-content-id8271_91e8d5-1c .kb-table-of-contents-title{color:#ffffff;font-weight:regular;font-style:normal;}.kb-table-of-content-nav.kb-table-of-content-id8271_91e8d5-1c .kb-table-of-content-wrap .kb-table-of-content-list{color:#ffffff;font-weight:regular;font-style:normal;margin-top:10px;margin-right:0px;margin-bottom:0px;margin-left:-5px;}@media all and (max-width: 1024px){.kb-table-of-content-nav.kb-table-of-content-id8271_91e8d5-1c .kb-table-of-content-wrap{border-top:1px solid #abb8c3;border-right:1px solid #abb8c3;border-bottom:1px solid #abb8c3;border-left:1px solid #abb8c3;}}@media all and (max-width: 767px){.kb-table-of-content-nav.kb-table-of-content-id8271_91e8d5-1c .kb-table-of-content-wrap{border-top:1px solid #abb8c3;border-right:1px solid #abb8c3;border-bottom:1px solid #abb8c3;border-left:1px solid #abb8c3;}}<\/style>\n\n\n<h2 class=\"wp-block-heading\">What is Two-Factor Authentication?<\/h2>\n\n\n\n<p>Two-Factor Authentication, also commonly referred to as 2FA, is a specific type of multi-factor authentication (MFA) that requires two forms of identification (also referred to as authentication factors) to grant access to a resource or data. These factors include the traditional method of authentication like username\/email and password &#8211; plus something you have &#8211; like a smartphone &#8211; to approve the requests.<\/p>\n\n\n\n<p>Businesses use two-factor authentication (2FA) to protect against many security threats that target user accounts and passwords like credential exploitation, social engineering, password brute-force attacks, phishing, and many more.<\/p>\n\n\n\n<p>There are many authentication methods when using two-factor authentication (2FA). Some of the most popular options include:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>SMS verification<\/li>\n\n\n\n<li>Voice-based authentication<\/li>\n\n\n\n<li>Hardware tokens<\/li>\n\n\n\n<li>Push notifications<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Advantages of Two-Factor Authentication (2FA)<\/h2>\n\n\n\n<p>The advantages of using two-factor authentication are endless and am only going to list four of them.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>It adds an extra layer of security by protecting against cybercriminals who target user passwords and accounts.<\/li>\n\n\n\n<li>It does not add extra costs on the part of the user since most websites and apps use your mobile device to text, call, or use personalized 2FA methods to verify your identity.<\/li>\n\n\n\n<li>Setting up 2FA is relatively easy and user-friendly. In most cases, all that the user will do is enable the 2FA feature and scan a QR code or enter the phone number to view or receive OTP codes.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Prerequisites<\/h2>\n\n\n\n<p>To get the most out of this tutorial, you should have:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Basic knowledge of JavaScript and TypeScript<\/li>\n\n\n\n<li>Basic knowledge of API design and CRUD patterns<\/li>\n\n\n\n<li>Familiarity with Node.js and Express.js will be beneficial<\/li>\n\n\n\n<li>The <a href=\"https:\/\/nodejs.org\/\" target=\"_blank\" rel=\"noreferrer noopener\">latest version of Node.js installed<\/a> on your system<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Run the Node.js 2FA App Locally<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li>If you don&#8217;t have Node.js installed, visit <a href=\"https:\/\/nodejs.org\/\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/nodejs.org\/<\/a> to download the respective binary for your operating system.<\/li>\n\n\n\n<li>Download or clone the Node.js two-factor authentication source code from <a href=\"https:\/\/github.com\/wpcodevo\/2fa-nodejs\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/github.com\/wpcodevo\/2fa-nodejs<\/a><\/li>\n\n\n\n<li>Run <code>yarn<\/code> or <code>yarn install<\/code> to install all the necessary dependencies<\/li>\n\n\n\n<li>Migrate the Prisma schema to the SQLite database by running <code>yarn db:migrate<\/code> and <code>yarn db:push<\/code> .<\/li>\n\n\n\n<li>Start the Node.js Express server by running <code>yarn start<\/code> <\/li>\n\n\n\n<li>Open any API testing software like Postman to test the API endpoints<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">Run the Frontend Built with React.js<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Download or clone the React.js two-factor authentication source code from <a href=\"https:\/\/github.com\/wpcodevo\/two_factor_reactjs\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/github.com\/wpcodevo\/two_factor_reactjs<\/a>.<\/li>\n\n\n\n<li>Install all the necessary dependencies by running <code>yarn<\/code> or <code>yarn install<\/code> from the terminal of the root project folder.<\/li>\n\n\n\n<li>Start the Vite development server by running <code>yarn dev<\/code> .<\/li>\n\n\n\n<li>Open a new tab in your browser and visit <strong>http:\/\/localhost:3000<\/strong> to start testing the two-factor authentication system.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Two-factor Authentication in Node.js Flow<\/h2>\n\n\n\n<p>The Node.js API will have the following endpoints:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>METHOD<\/th><th>ENDPOINT<\/th><th>DESCRIPTION<\/th><\/tr><\/thead><tbody><tr><td>POST<\/td><td><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-purple-color\">\/api\/auth\/register<\/mark><\/td><td>Register New User<\/td><\/tr><tr><td>POST<\/td><td><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-purple-color\">\/api\/auth\/login<\/mark><\/td><td>Login User<\/td><\/tr><tr><td>POST<\/td><td><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-purple-color\">\/api\/auth\/otp\/generate<\/mark><\/td><td>Generate the OTP Secret<\/td><\/tr><tr><td>POST<\/td><td><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-purple-color\">\/api\/auth\/otp\/verify<\/mark><\/td><td>Verify the OTP Secret<\/td><\/tr><tr><td>POST<\/td><td><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-purple-color\">\/api\/auth\/otp\/validate<\/mark><\/td><td>Validate the OTP Token<\/td><\/tr><tr><td>POST<\/td><td><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-purple-color\">\/api\/auth\/otp\/disable<\/mark><\/td><td>Disable the OTP Feature<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>The API endpoints can be tested with any API testing tool like <a href=\"https:\/\/www.postman.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">Postman<\/a>, <a href=\"https:\/\/insomnia.rest\/\" target=\"_blank\" rel=\"noreferrer noopener\">Insomnia<\/a>, <a href=\"https:\/\/marketplace.visualstudio.com\/items?itemName=rangav.vscode-thunder-client\" target=\"_blank\" rel=\"noreferrer noopener\">VS Code Thunder Client extension<\/a>, and more &#8211; but since we humans understand concepts better with visual demonstrations, we will use a frontend app built with React.js to interact with the Node.js API.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Setup the 2FA feature<\/h3>\n\n\n\n<p>To reduce the steps involved in the 2FA process, I will skip the account registration and login steps. However, you need to create an account and log in with your credentials before you can access the profile page.<\/p>\n\n\n\n<p>When you click on the <strong>Setup 2FA<\/strong> button on the profile page, an HTTP <strong>POST<\/strong> request will be fired to the <code>\/api\/auth\/otp\/generate<\/code> endpoint where the Node.js API will generate the one-time passcode and return the OTP URL to the frontend app.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1011\" height=\"681\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-2fa-authenticate.webp\" alt=\"nodejs 2fa authenticate\" class=\"wp-image-8386\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-2fa-authenticate.webp 1011w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-2fa-authenticate-300x202.webp 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-2fa-authenticate-768x517.webp 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-2fa-authenticate-100x67.webp 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-2fa-authenticate-668x450.webp 668w\" sizes=\"auto, (max-width: 1011px) 100vw, 1011px\" \/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Scan the QRCode<\/h3>\n\n\n\n<p>The React app will then generate the QRCode from the OTP URL sent by the Node.js API and render a component to display the QRCode.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"958\" height=\"1024\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-enable-the-2fa-feature-958x1024.webp\" alt=\"nodejs enable the 2fa feature\" class=\"wp-image-8384\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-enable-the-2fa-feature-958x1024.webp 958w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-enable-the-2fa-feature-281x300.webp 281w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-enable-the-2fa-feature-768x821.webp 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-enable-the-2fa-feature-94x100.webp 94w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-enable-the-2fa-feature-421x450.webp 421w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-enable-the-2fa-feature.webp 1010w\" sizes=\"auto, (max-width: 958px) 100vw, 958px\" \/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Verify the OTP token<\/h3>\n\n\n\n<p>To see the OTP token, you can do the following:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Use the <a href=\"https:\/\/play.google.com\/store\/apps\/details?id=com.google.android.apps.authenticator2\" target=\"_blank\" rel=\"noreferrer noopener\">Google Authenticator <\/a>app to scan the QR Code<\/li>\n\n\n\n<li>Use the <a href=\"https:\/\/play.google.com\/store\/apps\/details?id=com.authy.authy\" target=\"_blank\" rel=\"noreferrer noopener\">Authy<\/a> app to scan the QR Code<\/li>\n\n\n\n<li>Use the <a href=\"https:\/\/chrome.google.com\/webstore\/detail\/authenticator\/bhghoamapcdpbohphigoooaddinpkbai\" target=\"_blank\" rel=\"noreferrer noopener\">Authenticator<\/a> extension in chrome to scan the QR Code<\/li>\n<\/ul>\n\n\n\n<p>In my case, I will use the <a href=\"https:\/\/chrome.google.com\/webstore\/detail\/authenticator\/bhghoamapcdpbohphigoooaddinpkbai\" target=\"_blank\" rel=\"noreferrer noopener\">Chrome Authenticator<\/a> extension to scan the QR Code since am in a development environment.<\/p>\n\n\n\n<p>To view the OTP token with the <a href=\"https:\/\/chrome.google.com\/webstore\/detail\/authenticator\/bhghoamapcdpbohphigoooaddinpkbai\" target=\"_blank\" rel=\"noreferrer noopener\">Chrome Authenticator<\/a> extension, click on the <strong>Scan QR Code<\/strong> icon adjacent to the <strong>pencil<\/strong> icon, and drag the scanner over the QR Code.<\/p>\n\n\n\n<p>After that, go back to the <a href=\"https:\/\/chrome.google.com\/webstore\/detail\/authenticator\/bhghoamapcdpbohphigoooaddinpkbai\" target=\"_blank\" rel=\"noreferrer noopener\">Chrome Authenticator<\/a> extension and you should see the OTP token.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"959\" height=\"1024\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-scanned-the-otp-qrcode-959x1024.webp\" alt=\"nodejs scanned the otp qrcode\" class=\"wp-image-8383\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-scanned-the-otp-qrcode-959x1024.webp 959w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-scanned-the-otp-qrcode-281x300.webp 281w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-scanned-the-otp-qrcode-768x820.webp 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-scanned-the-otp-qrcode-94x100.webp 94w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-scanned-the-otp-qrcode-422x450.webp 422w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-scanned-the-otp-qrcode.webp 1011w\" sizes=\"auto, (max-width: 959px) 100vw, 959px\" \/><\/figure>\n\n\n\n<p>Click on the OTP token to copy and paste it into the input field on the modal. When the <strong>Verify &amp; Activate<\/strong> button is clicked, React will make an HTTP <strong>POST<\/strong> request to the <code>\/api\/auth\/otp\/verify<\/code> endpoint where the Node.js API will enable the 2FA feature and return a success message to the frontend app.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Validate the OTP token<\/h3>\n\n\n\n<p>After enabling the 2FA feature, refresh the page to log out. When you successfully log in with your credentials, React will automatically redirect you to the OTP verification page where you will be required to provide the OTP token to verify your identity.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"975\" height=\"1024\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-verify-the-2fa-otp-token-975x1024.webp\" alt=\"nodejs verify the 2fa otp token\" class=\"wp-image-8387\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-verify-the-2fa-otp-token-975x1024.webp 975w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-verify-the-2fa-otp-token-286x300.webp 286w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-verify-the-2fa-otp-token-768x807.webp 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-verify-the-2fa-otp-token-95x100.webp 95w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-verify-the-2fa-otp-token-428x450.webp 428w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-verify-the-2fa-otp-token.webp 1028w\" sizes=\"auto, (max-width: 975px) 100vw, 975px\" \/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Disable the 2FA Feature<\/h3>\n\n\n\n<p>To disable the two-factor authentication (2FA) feature on your account, you can click the <strong>Disable 2FA<\/strong> button available on the profile page. Once the <strong>Disable 2FA<\/strong> button is clicked, React will make an HTTP <strong>POST<\/strong> request to the <code>\/api\/auth\/otp\/disable<\/code> endpoint.<\/p>\n\n\n\n<p>One success, the 2FA feature will be disabled and the user will not be presented with the 2FA verification page during the authentication process.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1013\" height=\"679\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-2fa-enabled.webp\" alt=\"nodejs 2fa enabled\" class=\"wp-image-8385\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-2fa-enabled.webp 1013w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-2fa-enabled-300x201.webp 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-2fa-enabled-768x515.webp 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-2fa-enabled-100x67.webp 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-2fa-enabled-671x450.webp 671w\" sizes=\"auto, (max-width: 1013px) 100vw, 1013px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Step 1 &#8211; Setup the Node.js Project<\/h2>\n\n\n\n<p>To begin, navigate to where you would like to set up the project and create a new directory to contain the source code. You can name the project <code>2fa_NodeJs<\/code> .  Once you are done, open the project with an IDE or text editor. In my case, I will be using VS Code.<\/p>\n\n\n\n<pre class=\"line-numbers language-shell\"><code>\nmkdir 2fa_nodejs &amp;&amp; cd 2fa_nodejs &amp;&amp; code .\n<\/code>\n<\/pre>\n\n\n\n<p>Next, you need to initialize the Node.js project with <strong>Yarn<\/strong> or <strong>NPM<\/strong> to manage the dependencies. Also, we will use TypeScript in this project to help us get better IntelliSense in VS Code.<\/p>\n\n\n\n<pre class=\"line-numbers language-shell\"><code>\nyarn init -y &amp;&amp; yarn add -D typescript @types\/node\n# or\nnpm init -y &amp;&amp; npm install -D typescript @types\/node\n<\/code>\n<\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code><a href=\"https:\/\/www.npmjs.com\/package\/typescript\" target=\"_blank\" rel=\"noreferrer noopener\">typescript<\/a><\/code> &#8211; A superset of JavaScript<\/li>\n\n\n\n<li><code><a href=\"https:\/\/www.npmjs.com\/package\/@types\/node\" target=\"_blank\" rel=\"noreferrer noopener\">@types\/node<\/a><\/code> &#8211; Contains the TypeScript definitions for Node.js<\/li>\n<\/ul>\n\n\n\n<p>This will create a&nbsp;<strong>package.json<\/strong> file&nbsp;with an initial setup for the Node.js TypeScript app. Once the project initialization is complete, you will have a <strong>package.json<\/strong> file that might look something like this:<\/p>\n\n\n\n<pre class=\"line-numbers language-json\"><code>\n{\n  \"name\": \"2fa_nodejs\",\n  \"version\": \"1.0.0\",\n  \"main\": \"index.js\",\n  \"license\": \"MIT\"\n}\n\n<\/code>\n<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Step 2 &#8211; Setup Prisma<\/h2>\n\n\n\n<p>The project will use Prisma ORM to store data in an SQLite database.  What is Prisma? Prisma is a next-generation ORM for Node.js&nbsp;and TypeScript. It supports a lot of database servers like:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>MongoDB<\/li>\n\n\n\n<li>PostgreSQL<\/li>\n\n\n\n<li>SQLite<\/li>\n\n\n\n<li>SQL Server, and many more.<\/li>\n<\/ul>\n\n\n\n<p>Since the main focus of this tutorial is how two-factor authentication (2FA) can be implemented in Node.js, we will use Prisma with SQLite because it only requires a few steps to get it up and running.<\/p>\n\n\n\n<p>To get started, install these Prisma dependencies:<\/p>\n\n\n\n<pre class=\"line-numbers language-shell\"><code>\nyarn add -D prisma &amp;&amp; yarn add @prisma\/client\n# or\nnpm install -D prisma &amp;&amp; npm install @prisma\/client\n<\/code>\n<\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code><a href=\"https:\/\/www.npmjs.com\/package\/prisma\" target=\"_blank\" rel=\"noreferrer noopener\">prisma<\/a><\/code> &#8211; A Prisma CLI that helps you interact with your Prisma project from the command line.<\/li>\n\n\n\n<li><code><a href=\"https:\/\/www.npmjs.com\/package\/@prisma\/client\" target=\"_blank\" rel=\"noreferrer noopener\">@prisma\/client<\/a><\/code> &#8211; An <strong>auto-generated query builder<\/strong>&nbsp;that enables&nbsp;<strong>type-safe<\/strong>&nbsp;database access.<\/li>\n<\/ul>\n\n\n\n<p>After that, run the Prisma <strong>init<\/strong> command to initialize the Prisma assets in the project:<\/p>\n\n\n\n<pre class=\"line-numbers language-shell\"><code>\nyarn prisma init --datasource-provider sqlite\n# or \nnpx prisma init --datasource-provider sqlite\n<\/code>\n<\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>--datasource-provider<\/code> &#8211; this flag specifies the default database provider<\/li>\n\n\n\n<li><code>sqlite<\/code> &#8211; is the database<\/li>\n<\/ul>\n\n\n\n<p>The above command will create a new&nbsp;<code>prisma<\/code>&nbsp;directory with your Prisma schema file and configure SQLite as your database. You&#8217;re now ready to define the data model and migrate the schema to the database.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 3 &#8211; Create the Prisma Database Model<\/h2>\n\n\n\n<p>The Prisma schema provides a simpler interface to model data. The model in the Prisma schema represents the SQL table in the underlying database. <\/p>\n\n\n\n<p>Now open the <code>prisma\/schema.prisma<\/code> file and replace its content with the following:<\/p>\n\n\n\n<p><strong>prisma\/schema.prisma<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-js\"><code>\ngenerator client {\n  provider = \"prisma-client-js\"\n}\n\ndatasource db {\n  provider = \"sqlite\"\n  url      = env(\"DATABASE_URL\")\n}\n\nmodel User {\n  id       String @id @default(uuid())\n  email    String @unique\n  name     String\n  password String\n\n  otp_enabled  Boolean @default(false)\n  otp_verified Boolean @default(false)\n\n  otp_ascii    String?\n  otp_hex      String?\n  otp_base32   String?\n  otp_auth_url String?\n\n  @@map(name: \"users\")\n}\n\n<\/code>\n<\/pre>\n\n\n\n<p>In the above, we defined a&nbsp;<strong>User<\/strong>&nbsp;model with some fields. Each field has a field name followed by a datatype and&nbsp;<a href=\"https:\/\/www.prisma.io\/docs\/concepts\/components\/prisma-schema\/data-model#defining-fields\" target=\"_blank\" rel=\"noreferrer noopener\">optional field attributes<\/a>. We used the <code>@default(uuid())<\/code> attribute to generate a UUID for the <strong>id<\/strong> column instead of using incremental integers.<\/p>\n\n\n\n<p>Now add the following commands to the <strong>package.json<\/strong> file:<\/p>\n\n\n\n<p><strong>package.json<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-json\"><code>\n{\n  \"name\": \"2fa_nodejs\",\n  \"version\": \"1.0.0\",\n  \"main\": \"index.js\",\n  \"license\": \"MIT\",\n  \"scripts\": {\n    \"start\": \"ts-node-dev --respawn --transpile-only --exit-child server.ts\",\n    \"db:migrate\": \"npx prisma migrate dev --name user-entity --create-only &amp;&amp; npx prisma generate\",\n    \"db:push\": \"npx prisma db push\"\n  },\n  \"devDependencies\": {\n    \"@types\/node\": \"^18.7.23\",\n    \"prisma\": \"^4.4.0\",\n    \"typescript\": \"^4.8.4\"\n  },\n  \"dependencies\": {\n    \"@prisma\/client\": \"^4.4.0\"\n  }\n}\n<\/code>\n<\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>start<\/code> &#8211; Starts the Node.js server<\/li>\n\n\n\n<li><code>db:migrate<\/code> &#8211; Generates the migration file and Prisma Client<\/li>\n\n\n\n<li><code>db:push<\/code> &#8211; Pushes the Prisma schema to the database<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Step 4 &#8211; Database Migration with Prisma<\/h2>\n\n\n\n<p>At this point, you have a Prisma model but no database yet. Open your terminal and run the following command to create the SQLite database and the &#8220;<strong>users<\/strong>&#8221; table represented by the model.<\/p>\n\n\n\n<p>Run this command to generate the database migration file and the Prisma Client:<\/p>\n\n\n\n<pre class=\"line-numbers language-shell\"><code>\nnpx prisma migrate dev --name user-entity --create-only &amp;&amp; npx prisma generate\n<\/code>\n<\/pre>\n\n\n\n<p>Push the Prisma schema to the database:<\/p>\n\n\n\n<pre class=\"line-numbers language-shell\"><code>\nnpx prisma db push\n<\/code>\n<\/pre>\n\n\n\n<p>Alternatively, you can run <code>yarn db:migrate<\/code> and <code>yarn db:push<\/code> to execute the above commands.<\/p>\n\n\n\n<p>After synching the SQLite database with the Prisma schema, run <code>npx prisma studio<\/code> to open the Prisma GUI tool in the browser. Prisma studio allows you to view and mutate the data stored in the database.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 5 &#8211; Setup the Node.js Express App<\/h2>\n\n\n\n<p>With all the above configurations, we are ready to set up the Node.js HTTP server. We will use the Express framework to set up the HTTP server and use SQLite for data storage.<\/p>\n\n\n\n<p>Before the implementation, install the following dependencies:<\/p>\n\n\n\n<pre class=\"line-numbers language-ts\"><code>\nyarn add express cors otpauth hi-base32 &amp;&amp; yarn add -D morgan ts-node-dev @types\/cors @types\/express @types\/morgan\n# or \nnpm install express cors otpauth hi-base32 &amp;&amp; npm install -D morgan ts-node-dev @types\/cors @types\/express @types\/morgan\n<\/code>\n<\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code><a href=\"https:\/\/www.npmjs.com\/package\/express\" target=\"_blank\" rel=\"noreferrer noopener\">express<\/a><\/code> &#8211; A Node.js web framework<\/li>\n\n\n\n<li><code><a href=\"https:\/\/www.npmjs.com\/package\/cors\" target=\"_blank\" rel=\"noreferrer noopener\">cors<\/a><\/code> &#8211; Enable CORS in Express<\/li>\n\n\n\n<li><code><a href=\"https:\/\/www.npmjs.com\/package\/otpauth\" target=\"_blank\" rel=\"noreferrer noopener\">otpauth<\/a><\/code> &#8211; A library for generating and validating TOTP<\/li>\n\n\n\n<li><code><a href=\"https:\/\/www.npmjs.com\/package\/hi-base32\" target=\"_blank\" rel=\"noreferrer noopener\">hi-base32<\/a><\/code> &#8211; This library provides encoding and decoding functions for converting data to and from the Base32 format. We&#8217;ll use the base32-encoded string to generate the TOTP in an Authenticator app.<\/li>\n\n\n\n<li><code><a href=\"https:\/\/www.npmjs.com\/package\/morgan\" target=\"_blank\" rel=\"noreferrer noopener\">morgan<\/a><\/code> &#8211; An HTTP request logger middleware for node.js<\/li>\n\n\n\n<li><code><a href=\"https:\/\/www.npmjs.com\/package\/ts-node-dev\" target=\"_blank\" rel=\"noreferrer noopener\">ts-node-dev<\/a><\/code> &#8211; Run the Express.js server<\/li>\n<\/ul>\n\n\n\n<p>After the installations, your <strong>package.json<\/strong> file should look like this:<\/p>\n\n\n\n<pre class=\"line-numbers language-json\"><code>\n{\n  \"name\": \"2fa_nodejs\",\n  \"version\": \"1.0.0\",\n  \"main\": \"index.js\",\n  \"license\": \"MIT\",\n  \"scripts\": {\n    \"start\": \"ts-node-dev --respawn --transpile-only --exit-child server.ts\",\n    \"db:migrate\": \"npx prisma migrate dev --name user-entity --create-only && npx prisma generate\",\n    \"db:push\": \"npx prisma db push\"\n  },\n  \"devDependencies\": {\n    \"@types\/cors\": \"^2.8.12\",\n    \"@types\/express\": \"^4.17.14\",\n    \"@types\/morgan\": \"^1.9.3\",\n    \"@types\/node\": \"^18.7.23\",\n    \"morgan\": \"^1.10.0\",\n    \"prisma\": \"^4.4.0\",\n    \"ts-node-dev\": \"^2.0.0\",\n    \"typescript\": \"^4.8.4\"\n  },\n  \"dependencies\": {\n    \"@prisma\/client\": \"^4.4.0\",\n    \"cors\": \"^2.8.5\",\n    \"express\": \"^4.18.1\",\n    \"hi-base32\": \"^0.5.1\",\n    \"otpauth\": \"^9.1.2\"\n  }\n}\n<\/code>\n<\/pre>\n\n\n\n<p>With that out of the way, create a <code>server.ts<\/code> file and add the following code:<\/p>\n\n\n\n<p><strong>server.ts<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-ts\"><code>\nimport { PrismaClient } from \"@prisma\/client\";\nimport express, { Request, Response } from \"express\";\nimport cors from \"cors\";\nimport morgan from \"morgan\";\n\nexport const prisma = new PrismaClient();\nconst app = express();\n\nasync function main() {\n  \/\/ Middleware\n  app.use(morgan(\"dev\"));\n  app.use(\n    cors({\n      origin: [\"http:\/\/localhost:3000\"],\n      credentials: true,\n    })\n  );\n  app.use(express.json());\n\n  \/\/   Health Checker\n  app.get(\"\/api\/healthchecker\", (req: Request, res: Response) =&gt; {\n    res.status(200).json({\n      status: \"success\",\n      message: \"Welcome to Two-Factor Authentication with Node.js\",\n    });\n  });\n\n  \/\/ Register the API Routes\n\n  \/\/ Catch All\n  app.all(\"*\", (req: Request, res: Response) =&gt; {\n    return res.status(404).json({\n      status: \"fail\",\n      message: `Route: ${req.originalUrl} not found`,\n    });\n  });\n\n  const PORT = 8000;\n  app.listen(PORT, () =&gt; {\n    console.info(`Server started on port: ${PORT}`);\n  });\n}\n\nmain()\n  .then(async () =&gt; {\n    await prisma.$disconnect();\n  })\n  .catch(async (e) =&gt; {\n    console.error(e);\n    await prisma.$disconnect();\n    process.exit(1);\n  });\n<\/code>\n<\/pre>\n\n\n\n<p>Quite a lot is happening in the above, let&#8217;s break it down:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>First, we created instances of the Prisma Client and Express. Next, we created a <code>main<\/code> function to contain all the server code. <\/li>\n\n\n\n<li>The <code>morgan(\"dev\")<\/code> middleware will configure Express to log the HTTP request information in the terminal.<\/li>\n\n\n\n<li>The <code>cors()<\/code> middleware will configure the Express server to accept requests from cross-origin domains.<\/li>\n\n\n\n<li> The <code>express.json()<\/code> middleware will parse the JSON payload of the incoming HTTP POST request and expose it as&nbsp;<code>req.body<\/code>.<\/li>\n<\/ul>\n\n\n\n<p>Now start the Express.js server by running <code>yarn start<\/code> or<\/p>\n\n\n\n<pre class=\"line-numbers language-ts\"><code>\nyarn ts-node-dev --respawn --transpile-only --exit-child server.ts\n# or \nnpx ts-node-dev --respawn --transpile-only --exit-child server.ts\n<\/code>\n<\/pre>\n\n\n\n<p>This will start the Express server on port <strong>8000<\/strong>. Open a new tab in the browser and navigate to <strong>http:\/\/localhost:8000\/api\/healthchecker<\/strong>. You should get the JSON response sent from the Express server.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"776\" height=\"268\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/two-factor-authentication-2fa-express-server-initial-set-up.webp\" alt=\"two factor authentication 2fa express server initial set up\" class=\"wp-image-8392\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/two-factor-authentication-2fa-express-server-initial-set-up.webp 776w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/two-factor-authentication-2fa-express-server-initial-set-up-300x104.webp 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/two-factor-authentication-2fa-express-server-initial-set-up-768x265.webp 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/two-factor-authentication-2fa-express-server-initial-set-up-100x35.webp 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/two-factor-authentication-2fa-express-server-initial-set-up-700x242.webp 700w\" sizes=\"auto, (max-width: 776px) 100vw, 776px\" \/><\/figure>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\">Step 6 &#8211; Create the Node.js Route Controllers<\/h2>\n\n\n\n<p>In this step, we will create six route controllers responsible for various tasks including user registration, login, generating the TOTP authentication URL and base32 string, verifying the TOTP, validating the TOTP, and disabling the TOTP. Prior to that, let&#8217;s begin by creating a new directory named <code>controllers<\/code> at the root level. Inside the newly created <code>controllers<\/code> directory, generate a file called <code>auth.controller.ts<\/code> and proceed to include the following import statements.<\/p>\n\n\n\n<p><strong>controllers\/auth.controller.ts<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-ts\"><code>\nimport crypto from \"crypto\";\nimport { Prisma } from \"@prisma\/client\";\nimport { Request, Response, NextFunction } from \"express\";\nimport { prisma } from \"..\/server\";\nimport * as OTPAuth from \"otpauth\";\nimport { encode } from \"hi-base32\";\n<\/code>\n<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Register User<\/h3>\n\n\n\n<p>The first step in every authentication flow is user registration. We will create a route handler that will add the new user to the database. <\/p>\n\n\n\n<p><strong>controllers\/auth.controller.ts<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-ts\"><code>\n\/\/ [...] Register user\nconst RegisterUser = async (\n  req: Request,\n  res: Response,\n  next: NextFunction\n) =&gt; {\n  try {\n    const { name, email, password } = req.body;\n\n    await prisma.user.create({\n      data: {\n        name,\n        email,\n        password: crypto.createHash(\"sha256\").update(password).digest(\"hex\"),\n      },\n    });\n\n    res.status(201).json({\n      status: \"success\",\n      message: \"Registered successfully, please login\",\n    });\n  } catch (error) {\n    if (error instanceof Prisma.PrismaClientKnownRequestError) {\n      if (error.code === \"P2002\") {\n        return res.status(409).json({\n          status: \"fail\",\n          message: \"Email already exist, please use another email address\",\n        });\n      }\n    }\n    res.status(500).json({\n      status: \"error\",\n      message: error.message,\n    });\n  }\n};\n<\/code>\n<\/pre>\n\n\n\n<p>Let&#8217;s evaluate the above code:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>First, we extracted the credentials from the request body<\/li>\n\n\n\n<li>We called Prisma&#8217;s <code>.create()<\/code> method to add the new record to the database.<\/li>\n\n\n\n<li>Then, we returned a success message to the client<\/li>\n\n\n\n<li>Lastly, we used a <strong>catch<\/strong> block to handle any possible errors.<\/li>\n<\/ul>\n\n\n\n<p>When a request is made to the <code>\/api\/auth\/register<\/code> endpoint with the credentials included in the request body, the <strong>RegisterUser<\/strong> handler will be evoked to register the user.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1007\" height=\"738\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-api-2fa-register-user.webp\" alt=\"nodejs api 2fa register user\" class=\"wp-image-8396\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-api-2fa-register-user.webp 1007w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-api-2fa-register-user-300x220.webp 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-api-2fa-register-user-768x563.webp 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-api-2fa-register-user-100x73.webp 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-api-2fa-register-user-614x450.webp 614w\" sizes=\"auto, (max-width: 1007px) 100vw, 1007px\" \/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Sign-in User<\/h3>\n\n\n\n<p>Here, let&#8217;s create the route handler that will be called to log the user into the API. To simplify the authentication process, we will ignore other authentication methods and return the user&#8217;s data to the client. <\/p>\n\n\n\n<p>The API will assume the user is authenticated when the client includes the <strong>user_id<\/strong> in the request body.<\/p>\n\n\n\n<p><strong>controllers\/auth.controller.ts<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-ts\"><code>\n\/\/ [...] Register user\n\n\/\/ [...] Login user\nconst LoginUser = async (req: Request, res: Response, next: NextFunction) =&gt; {\n  try {\n    const { email, password } = req.body;\n\n    const user = await prisma.user.findUnique({ where: { email } });\n\n    if (!user) {\n      return res.status(404).json({\n        status: \"fail\",\n        message: \"No user with that email exists\",\n      });\n    }\n\n    res.status(200).json({\n      status: \"success\",\n      user: {\n        id: user.id,\n        name: user.name,\n        email: user.email,\n        otp_enabled: user.otp_enabled,\n      },\n    });\n  } catch (error) {\n    res.status(500).json({\n      status: \"error\",\n      message: error.message,\n    });\n  }\n};\n<\/code>\n<\/pre>\n\n\n\n<p>This route controller will query the database to check if the user exists and return the found record to the client.<\/p>\n\n\n\n<p>When you make a request to the <code>\/api\/auth\/login<\/code> endpoint, the <strong>LoginUser<\/strong> function will be evoked to sign the user into the API.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1011\" height=\"853\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-api-2fa-signin-user.webp\" alt=\"nodejs api 2fa signin user\" class=\"wp-image-8395\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-api-2fa-signin-user.webp 1011w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-api-2fa-signin-user-300x253.webp 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-api-2fa-signin-user-768x648.webp 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-api-2fa-signin-user-100x84.webp 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-api-2fa-signin-user-533x450.webp 533w\" sizes=\"auto, (max-width: 1011px) 100vw, 1011px\" \/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Generate the OTP<\/h3>\n\n\n\n<p>It&#8217;s now time to create the route handler function that will generate the secret key and the OTP Auth URL. The secret key will link the server and the application that will generate the two-factor authentication tokens. <\/p>\n\n\n\n<p>To generate the base32 encoded secret key, we&#8217;ll implement a utility function named <code>generateRandomBase32()<\/code>. This function leverages the power of Node.js Crypto module and the <strong>hi-base32<\/strong> library to generate a 24-character base32-encoded string. Although a 32-character string can also be generated, a 24-character string suffices for our purposes.<\/p>\n\n\n\n<p>If you require the secret key in different formats such as utf8, hex, buffer, base32, or latin1, the <code>OTPAuth.Secret.fromBase32()<\/code> method can be utilized to generate the corresponding representations from the base32 string. Here&#8217;s an example:<\/p>\n\n\n\n<pre class=\"line-numbers language-ts\"><code>\nconst {base32, hex, utf8,buffer, latin1} =OTPAuth.Secret.fromBase32(generateRandomBase32());\n<\/code>\n<\/pre>\n\n\n\n<p>Moving forward, we&#8217;ll invoke the <code>totp.toString()<\/code> method to obtain the OTP Auth URL. This URL encapsulates the encoded secrets as part of the URL structure, alongside other essential configurations required for generating the QR Code. Users can also utilize the base32 string directly to generate the QR Code, providing them with multiple options for setting up their two-factor authentication.<\/p>\n\n\n\n<p><strong>controllers\/auth.controller.ts<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-ts\"><code>\n\/\/ [...] Register user\n\n\/\/ [...] Login user\n\n\/\/ [...] Generate OTP\nconst generateRandomBase32 = () =&gt; {\n  const buffer = crypto.randomBytes(15);\n  const base32 = encode(buffer).replace(\/=\/g, &quot;&quot;).substring(0, 24);\n  return base32;\n};\n\nconst GenerateOTP = async (req: Request, res: Response) =&gt; {\n  try {\n    const { user_id } = req.body;\n\n    const user = await prisma.user.findUnique({ where: { id: user_id } });\n\n    if (!user) {\n      return res.status(404).json({\n        status: &quot;fail&quot;,\n        message: &quot;No user with that email exists&quot;,\n      });\n    }\n\n    const base32_secret = generateRandomBase32();\n\n    let totp = new OTPAuth.TOTP({\n      issuer: &quot;codevoweb.com&quot;,\n      label: &quot;CodevoWeb&quot;,\n      algorithm: &quot;SHA1&quot;,\n      digits: 6,\n      secret: base32_secret,\n    });\n\n    let otpauth_url = totp.toString();\n\n    await prisma.user.update({\n      where: { id: user_id },\n      data: {\n        otp_auth_url: otpauth_url,\n        otp_base32: base32_secret,\n      },\n    });\n\n    res.status(200).json({\n      base32: base32_secret,\n      otpauth_url,\n    });\n  } catch (error) {\n    res.status(500).json({\n      status: &quot;error&quot;,\n      message: error.message,\n    });\n  }\n};\n<\/code>\n<\/pre>\n\n\n\n<p>In the provided code snippet, we created the <code>generateRandomBase32()<\/code> function, responsible for generating a base32-encoded secret key. This key serves as the foundation for the TOTP generation process. Furthermore, we instantiated the <code>OTPAuth.TOTP<\/code> class with the necessary parameters, assigning it to the <code>totp<\/code> variable.<\/p>\n\n\n\n<p>Next, we obtained the TOTP Auth URL by invoking the <code>totp.toString()<\/code> method. This URL contains the encoded secrets and relevant configurations required for generating the QR code or setting up the TOTP authentication in an application. Moreover, we persisted the base32 string and the TOTP Auth URL in the database, ensuring they are readily available for future usage.<\/p>\n\n\n\n<p>Finally, we included the base32 string and the TOTP Auth URL in the JSON response. By returning these values, the client can choose to utilize either the base32 string or the URL to generate the corresponding TOTP tokens, providing flexibility and convenience for the client-side implementation.<\/p>\n\n\n\n<p>When a request is made to the <code>\/api\/auth\/otp\/generate<\/code> endpoint, the <strong>GenerateOTP<\/strong> handler will be evoked to generate the two-factor authentication secrets and return them.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"906\" height=\"764\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/Generate-the-TOTP-Auth-URL-for-the-two-factor-authentication-process.webp\" alt=\"Generate the TOTP Auth URL for the two-factor authentication process\" class=\"wp-image-11531\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/Generate-the-TOTP-Auth-URL-for-the-two-factor-authentication-process.webp 906w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/Generate-the-TOTP-Auth-URL-for-the-two-factor-authentication-process-300x253.webp 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/Generate-the-TOTP-Auth-URL-for-the-two-factor-authentication-process-768x648.webp 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/Generate-the-TOTP-Auth-URL-for-the-two-factor-authentication-process-100x84.webp 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/Generate-the-TOTP-Auth-URL-for-the-two-factor-authentication-process-534x450.webp 534w\" sizes=\"auto, (max-width: 906px) 100vw, 906px\" \/><\/figure>\n\n\n\n<p>Open the Google Authenticator app or Chrome Authenticator extension and enter the <strong>base64<\/strong> string sent by the API.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"373\" height=\"537\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/view-the-otp-token-with-chrome-authenticator-extension.webp\" alt=\"view the otp token with chrome authenticator extension\" class=\"wp-image-8391\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/view-the-otp-token-with-chrome-authenticator-extension.webp 373w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/view-the-otp-token-with-chrome-authenticator-extension-208x300.webp 208w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/view-the-otp-token-with-chrome-authenticator-extension-69x100.webp 69w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/view-the-otp-token-with-chrome-authenticator-extension-313x450.webp 313w\" sizes=\"auto, (max-width: 373px) 100vw, 373px\" \/><\/figure>\n<\/div>\n\n\n<p>You&#8217;re not quite finished yet! There&#8217;s another essential step to ensure reliable TOTP verification without any pesky invalid errors. It&#8217;s crucial to follow this next instruction attentively. Open the Advanced settings, and in the Period field, set the time period to <strong>15<\/strong> seconds. This value must align perfectly with the server configuration for accurate TOTP validation. Once you&#8217;ve made this adjustment, simply click the Ok button to proceed confidently.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"332\" height=\"496\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/Change-the-TOTP-period-to-15-to-avoid-validation-errors.webp\" alt=\"Change the TOTP period to 15 to avoid validation errors\" class=\"wp-image-11532\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/Change-the-TOTP-period-to-15-to-avoid-validation-errors.webp 332w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/Change-the-TOTP-period-to-15-to-avoid-validation-errors-201x300.webp 201w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/Change-the-TOTP-period-to-15-to-avoid-validation-errors-67x100.webp 67w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/Change-the-TOTP-period-to-15-to-avoid-validation-errors-301x450.webp 301w\" sizes=\"auto, (max-width: 332px) 100vw, 332px\" \/><\/figure>\n<\/div>\n\n\n<h3 class=\"wp-block-heading\">Verify the OTP<\/h3>\n\n\n\n<p>After entering the secret key, you should see the TOTP displayed in the authenticator app or extension. It&#8217;s now time to verify the TOTP code to enable the 2FA feature on the API.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"368\" height=\"536\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-viewing-the-otp-tokens.webp\" alt=\"nodejs viewing the otp tokens\" class=\"wp-image-8393\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-viewing-the-otp-tokens.webp 368w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-viewing-the-otp-tokens-206x300.webp 206w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-viewing-the-otp-tokens-69x100.webp 69w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-viewing-the-otp-tokens-309x450.webp 309w\" sizes=\"auto, (max-width: 368px) 100vw, 368px\" \/><\/figure>\n<\/div>\n\n\n<p>You will notice that the <code>otp_enabled<\/code> column in the &#8220;<strong>users<\/strong>&#8221; table has a default value of <strong>false<\/strong>. After the TOTP token has been verified, we will go ahead and set the value of the <code>otp_enabled<\/code> column to <strong>true<\/strong>.<\/p>\n\n\n\n<p>To perform the verification, we need to create a route handler that extracts the <code>user_id<\/code> and the <code>token<\/code> generated by the authenticator app from the request body.<\/p>\n\n\n\n<p><strong>controllers\/auth.controller.ts<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-ts\"><code>\n\/\/ [...] Register user\n\n\/\/ [...] Login user\n\n\/\/ [...] Generate OTP\n\n\/\/ [...] Verify OTP\nconst VerifyOTP = async (req: Request, res: Response) =&gt; {\n  try {\n    const { user_id, token } = req.body;\n\n    const user = await prisma.user.findUnique({ where: { id: user_id } });\n    const message = &quot;Token is invalid or user doesn&#039;t exist&quot;;\n    if (!user) {\n      return res.status(401).json({\n        status: &quot;fail&quot;,\n        message,\n      });\n    }\n\n    let totp = new OTPAuth.TOTP({\n      issuer: &quot;codevoweb.com&quot;,\n      label: &quot;CodevoWeb&quot;,\n      algorithm: &quot;SHA1&quot;,\n      digits: 6,\n      secret: user.otp_base32!,\n    });\n\n    let delta = totp.validate({ token });\n\n    if (delta === null) {\n      return res.status(401).json({\n        status: &quot;fail&quot;,\n        message,\n      });\n    }\n\n    const updatedUser = await prisma.user.update({\n      where: { id: user_id },\n      data: {\n        otp_enabled: true,\n        otp_verified: true,\n      },\n    });\n\n    res.status(200).json({\n      otp_verified: true,\n      user: {\n        id: updatedUser.id,\n        name: updatedUser.name,\n        email: updatedUser.email,\n        otp_enabled: updatedUser.otp_enabled,\n      },\n    });\n  } catch (error) {\n    res.status(500).json({\n      status: &quot;error&quot;,\n      message: error.message,\n    });\n  }\n};\n<\/code>\n<\/pre>\n\n\n\n<p>Let&#8217;s evaluate the above code snippet to gain a better understanding of its functionality. Here&#8217;s a detailed analysis:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>We begin by extracting the user ID and the TOTP token from the request body. This allows us to retrieve the necessary information for validation.<\/li>\n\n\n\n<li>Using Prisma, we search for a user with the provided ID. If no matching user is found, we return a 401 Unauthorized response to the client, indicating that the token is invalid or the user does not exist.<\/li>\n\n\n\n<li>Next, we instantiate a new instance of the <code>OTPAuth.TOTP<\/code> class. This class provides functionality for generating and validating TOTP tokens. We initialize it with the base32-encoded key retrieved from the database as the secret key for verification.<\/li>\n\n\n\n<li>We then call the <code>totp.validate()<\/code> method to validate the provided token. This method compares the token against the expected value generated by the TOTP algorithm. If the token is not found within the valid time window, the method returns <code>null<\/code>, indicating that the token is invalid.<\/li>\n\n\n\n<li>Upon successful verification of the token, we update the user&#8217;s credentials in the database. By setting both the <code>otp_verified<\/code> and <code>otp_enabled<\/code> fields to <strong>true<\/strong>, we indicate that the user&#8217;s TOTP authentication is verified and enabled.<\/li>\n<\/ol>\n\n\n\n<p>To verify the TOTP token, make a <strong>POST<\/strong> request to the <code>\/api\/auth\/otp\/verify<\/code> endpoint with the <strong>user_id<\/strong> and <strong>token<\/strong> included in the request body.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1011\" height=\"840\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-api-2fa-verify-the-otp-token.webp\" alt=\"nodejs api 2fa verify the otp token\" class=\"wp-image-8399\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-api-2fa-verify-the-otp-token.webp 1011w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-api-2fa-verify-the-otp-token-300x249.webp 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-api-2fa-verify-the-otp-token-768x638.webp 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-api-2fa-verify-the-otp-token-100x83.webp 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-api-2fa-verify-the-otp-token-542x450.webp 542w\" sizes=\"auto, (max-width: 1011px) 100vw, 1011px\" \/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Validate the OTP<\/h3>\n\n\n\n<p>To validate the TOTP token entered by the user from their authenticator app during subsequent logins, we will create a dedicated route handler named <code>ValidateOTP<\/code>. This function is designed to verify the token against the secret key stored in the database.<\/p>\n\n\n\n<p>The <code>ValidateOTP<\/code> function shares a similar logic with the <strong>VerifyOTP<\/strong> function mentioned earlier, but with a distinct purpose. Unlike <code>VerifyOTP<\/code>, the <code>ValidateOTP<\/code> function does not update the user&#8217;s credentials in the database. Its primary objective is to validate the provided token and return the result indicating whether it is valid or not.<\/p>\n\n\n\n<p><strong>controllers\/auth.controller.ts<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-ts\"><code>\n\/\/ [...] Register user\n\n\/\/ [...] Login user\n\n\/\/ [...] Generate OTP\n\n\/\/ [...] Verify OTP\n\n\/\/ [...] Validate OTP\nconst ValidateOTP = async (req: Request, res: Response) =&gt; {\n  try {\n    const { user_id, token } = req.body;\n    const user = await prisma.user.findUnique({ where: { id: user_id } });\n\n    const message = &quot;Token is invalid or user doesn&#039;t exist&quot;;\n    if (!user) {\n      return res.status(401).json({\n        status: &quot;fail&quot;,\n        message,\n      });\n    }\n    let totp = new OTPAuth.TOTP({\n      issuer: &quot;codevoweb.com&quot;,\n      label: &quot;CodevoWeb&quot;,\n      algorithm: &quot;SHA1&quot;,\n      digits: 6,\n      secret: user.otp_base32!,\n    });\n\n    let delta = totp.validate({ token, window: 1 });\n\n    if (delta === null) {\n      return res.status(401).json({\n        status: &quot;fail&quot;,\n        message,\n      });\n    }\n\n    res.status(200).json({\n      otp_valid: true,\n    });\n  } catch (error) {\n    res.status(500).json({\n      status: &quot;error&quot;,\n      message: error.message,\n    });\n  }\n};\n<\/code>\n<\/pre>\n\n\n\n<p>As you can see in the code snippet above, we instantiate a new object of the <code>OTPAuth.TOTP<\/code> class, passing in the base32-encoded string retrieved from the database. This object represents the TOTP configuration associated with the user.<\/p>\n\n\n\n<p>Next, we use the <code>totp.validate()<\/code> method to compare the token provided by the user with the expected value generated by the TOTP algorithm. If the validation fails, indicating an incorrect or expired token, we send a 401 UnAuthorized response back to the client, signaling that the token is invalid.<\/p>\n\n\n\n<p>On the other hand, if the validation succeeds, we include <code>otp_valid: true<\/code> in the JSON response, indicating that the token is valid and authentication is successful.<\/p>\n\n\n\n<p>If you&#8217;d like to learn more about the <code>window<\/code> parameter and its role, you can check out the ReadMe on the Speakeasy library&#8217;s GitHub page at <a href=\"https:\/\/github.com\/speakeasyjs\/speakeasy\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/github.com\/speakeasyjs\/speakeasy<\/a>. Even though we&#8217;re not using that library in our project, the ReadMe provides a detailed guide on how TOTP generation and validation work. It&#8217;s worth reading and understanding to get a clearer picture of the concepts involved, even if you&#8217;re not directly using the library itself.<\/p>\n\n\n\n<p>Now make a <strong>POST<\/strong> request to the <code>\/api\/auth\/otp\/validate<\/code> endpoint with the <strong>user_id<\/strong> and <strong>token<\/strong> provided in the request body to validate the token.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1014\" height=\"666\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-api-2fa-validate-the-otp-code.webp\" alt=\"nodejs api 2fa validate the otp code\" class=\"wp-image-8394\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-api-2fa-validate-the-otp-code.webp 1014w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-api-2fa-validate-the-otp-code-300x197.webp 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-api-2fa-validate-the-otp-code-768x504.webp 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-api-2fa-validate-the-otp-code-100x66.webp 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-api-2fa-validate-the-otp-code-685x450.webp 685w\" sizes=\"auto, (max-width: 1014px) 100vw, 1014px\" \/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Disable the OTP Feature<\/h3>\n\n\n\n<p>The final step in the two-factor (2FA) authentication is to create a controller that will be evoked to disable the 2FA feature. When this function is called, it will update the <strong>otp_enabled<\/strong> column to <strong>false<\/strong> in the database.<\/p>\n\n\n\n<p><strong>controllers\/auth.controller.ts<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-ts\"><code>\n\/\/ [...] Register user\n\n\/\/ [...] Login user\n\n\/\/ [...] Generate OTP\n\n\/\/ [...] Verify OTP\n\n\/\/ [...] Validate OTP\n\n\/\/ [...] Disable OTP\nconst DisableOTP = async (req: Request, res: Response) =&gt; {\n  try {\n    const { user_id } = req.body;\n\n    const user = await prisma.user.findUnique({ where: { id: user_id } });\n    if (!user) {\n      return res.status(401).json({\n        status: \"fail\",\n        message: \"User doesn't exist\",\n      });\n    }\n\n    const updatedUser = await prisma.user.update({\n      where: { id: user_id },\n      data: {\n        otp_enabled: false,\n      },\n    });\n\n    res.status(200).json({\n      otp_disabled: true,\n      user: {\n        id: updatedUser.id,\n        name: updatedUser.name,\n        email: updatedUser.email,\n        otp_enabled: updatedUser.otp_enabled,\n      },\n    });\n  } catch (error) {\n    res.status(500).json({\n      status: \"error\",\n      message: error.message,\n    });\n  }\n};\n<\/code>\n<\/pre>\n\n\n\n<p>To disable the 2FA feature, make a <strong>POST<\/strong> request to the <code>\/api\/auth\/otp\/disable<\/code> endpoint with the <strong>TOTP<\/strong> token and <strong>user_id<\/strong> provided in the request body.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1004\" height=\"809\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-api-disable-2fa.webp\" alt=\"nodejs api disable 2fa\" class=\"wp-image-8398\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-api-disable-2fa.webp 1004w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-api-disable-2fa-300x242.webp 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-api-disable-2fa-768x619.webp 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-api-disable-2fa-100x81.webp 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/10\/nodejs-api-disable-2fa-558x450.webp 558w\" sizes=\"auto, (max-width: 1004px) 100vw, 1004px\" \/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Complete Code of the Route Handlers<\/h3>\n\n\n\n<p><strong>controllers\/auth.controller.ts<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-ts\"><code>\nimport crypto from &quot;crypto&quot;;\nimport { Prisma } from &quot;@prisma\/client&quot;;\nimport { Request, Response, NextFunction } from &quot;express&quot;;\nimport { prisma } from &quot;..\/server&quot;;\nimport * as OTPAuth from &quot;otpauth&quot;;\nimport { encode } from &quot;hi-base32&quot;;\n\nconst RegisterUser = async (\n  req: Request,\n  res: Response,\n  next: NextFunction\n) =&gt; {\n  try {\n    const { name, email, password } = req.body;\n\n    await prisma.user.create({\n      data: {\n        name,\n        email,\n        password: crypto.createHash(&quot;sha256&quot;).update(password).digest(&quot;hex&quot;),\n      },\n    });\n\n    res.status(201).json({\n      status: &quot;success&quot;,\n      message: &quot;Registered successfully, please login&quot;,\n    });\n  } catch (error) {\n    if (error instanceof Prisma.PrismaClientKnownRequestError) {\n      if (error.code === &quot;P2002&quot;) {\n        return res.status(409).json({\n          status: &quot;fail&quot;,\n          message: &quot;Email already exist, please use another email address&quot;,\n        });\n      }\n    }\n    res.status(500).json({\n      status: &quot;error&quot;,\n      message: error.message,\n    });\n  }\n};\n\nconst LoginUser = async (req: Request, res: Response, next: NextFunction) =&gt; {\n  try {\n    const { email, password } = req.body;\n\n    const user = await prisma.user.findUnique({ where: { email } });\n\n    if (!user) {\n      return res.status(404).json({\n        status: &quot;fail&quot;,\n        message: &quot;No user with that email exists&quot;,\n      });\n    }\n\n    res.status(200).json({\n      status: &quot;success&quot;,\n      user: {\n        id: user.id,\n        name: user.name,\n        email: user.email,\n        otp_enabled: user.otp_enabled,\n      },\n    });\n  } catch (error) {\n    res.status(500).json({\n      status: &quot;error&quot;,\n      message: error.message,\n    });\n  }\n};\n\nconst generateRandomBase32 = () =&gt; {\n  const buffer = crypto.randomBytes(15);\n  const base32 = encode(buffer).replace(\/=\/g, &quot;&quot;).substring(0, 24);\n  return base32;\n};\n\nconst GenerateOTP = async (req: Request, res: Response) =&gt; {\n  try {\n    const { user_id } = req.body;\n\n    const user = await prisma.user.findUnique({ where: { id: user_id } });\n\n    if (!user) {\n      return res.status(404).json({\n        status: &quot;fail&quot;,\n        message: &quot;No user with that email exists&quot;,\n      });\n    }\n\n    const base32_secret = generateRandomBase32();\n\n    let totp = new OTPAuth.TOTP({\n      issuer: &quot;codevoweb.com&quot;,\n      label: &quot;CodevoWeb&quot;,\n      algorithm: &quot;SHA1&quot;,\n      digits: 6,\n      secret: base32_secret,\n    });\n\n    let otpauth_url = totp.toString();\n\n    await prisma.user.update({\n      where: { id: user_id },\n      data: {\n        otp_auth_url: otpauth_url,\n        otp_base32: base32_secret,\n      },\n    });\n\n    res.status(200).json({\n      base32: base32_secret,\n      otpauth_url,\n    });\n  } catch (error) {\n    res.status(500).json({\n      status: &quot;error&quot;,\n      message: error.message,\n    });\n  }\n};\n\nconst VerifyOTP = async (req: Request, res: Response) =&gt; {\n  try {\n    const { user_id, token } = req.body;\n\n    const user = await prisma.user.findUnique({ where: { id: user_id } });\n    const message = &quot;Token is invalid or user doesn&#039;t exist&quot;;\n    if (!user) {\n      return res.status(401).json({\n        status: &quot;fail&quot;,\n        message,\n      });\n    }\n\n    let totp = new OTPAuth.TOTP({\n      issuer: &quot;codevoweb.com&quot;,\n      label: &quot;CodevoWeb&quot;,\n      algorithm: &quot;SHA1&quot;,\n      digits: 6,\n      secret: user.otp_base32!,\n    });\n\n    let delta = totp.validate({ token });\n\n    if (delta === null) {\n      return res.status(401).json({\n        status: &quot;fail&quot;,\n        message,\n      });\n    }\n\n    const updatedUser = await prisma.user.update({\n      where: { id: user_id },\n      data: {\n        otp_enabled: true,\n        otp_verified: true,\n      },\n    });\n\n    res.status(200).json({\n      otp_verified: true,\n      user: {\n        id: updatedUser.id,\n        name: updatedUser.name,\n        email: updatedUser.email,\n        otp_enabled: updatedUser.otp_enabled,\n      },\n    });\n  } catch (error) {\n    res.status(500).json({\n      status: &quot;error&quot;,\n      message: error.message,\n    });\n  }\n};\n\nconst ValidateOTP = async (req: Request, res: Response) =&gt; {\n  try {\n    const { user_id, token } = req.body;\n    const user = await prisma.user.findUnique({ where: { id: user_id } });\n\n    const message = &quot;Token is invalid or user doesn&#039;t exist&quot;;\n    if (!user) {\n      return res.status(401).json({\n        status: &quot;fail&quot;,\n        message,\n      });\n    }\n    let totp = new OTPAuth.TOTP({\n      issuer: &quot;codevoweb.com&quot;,\n      label: &quot;CodevoWeb&quot;,\n      algorithm: &quot;SHA1&quot;,\n      digits: 6,\n      secret: user.otp_base32!,\n    });\n\n    let delta = totp.validate({ token, window: 1 });\n\n    if (delta === null) {\n      return res.status(401).json({\n        status: &quot;fail&quot;,\n        message,\n      });\n    }\n\n    res.status(200).json({\n      otp_valid: true,\n    });\n  } catch (error) {\n    res.status(500).json({\n      status: &quot;error&quot;,\n      message: error.message,\n    });\n  }\n};\n\nconst DisableOTP = async (req: Request, res: Response) =&gt; {\n  try {\n    const { user_id } = req.body;\n\n    const user = await prisma.user.findUnique({ where: { id: user_id } });\n    if (!user) {\n      return res.status(401).json({\n        status: &quot;fail&quot;,\n        message: &quot;User doesn&#039;t exist&quot;,\n      });\n    }\n\n    const updatedUser = await prisma.user.update({\n      where: { id: user_id },\n      data: {\n        otp_enabled: false,\n      },\n    });\n\n    res.status(200).json({\n      otp_disabled: true,\n      user: {\n        id: updatedUser.id,\n        name: updatedUser.name,\n        email: updatedUser.email,\n        otp_enabled: updatedUser.otp_enabled,\n      },\n    });\n  } catch (error) {\n    res.status(500).json({\n      status: &quot;error&quot;,\n      message: error.message,\n    });\n  }\n};\n\nexport default {\n  RegisterUser,\n  LoginUser,\n  GenerateOTP,\n  VerifyOTP,\n  ValidateOTP,\n  DisableOTP,\n};\n<\/code>\n<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Step 7 &#8211; Create the Express API Routes<\/h2>\n\n\n\n<p>Now that we have all the route handlers defined, let&#8217;s create routes to evoke them. To do that, create a <code>routes\/auth.route.ts<\/code> file and add the following code.<\/p>\n\n\n\n<p><strong>routes\/auth.route.ts<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-ts\"><code>\nimport express from \"express\";\nimport authController from \"..\/controllers\/auth.controller\";\n\nconst router = express.Router();\n\nrouter.post(\"\/register\", authController.RegisterUser);\nrouter.post(\"\/login\", authController.LoginUser);\nrouter.post(\"\/otp\/generate\", authController.GenerateOTP);\nrouter.post(\"\/otp\/verify\", authController.VerifyOTP);\nrouter.post(\"\/otp\/validate\", authController.ValidateOTP);\nrouter.post(\"\/otp\/disable\", authController.DisableOTP);\n\nexport default router;\n<\/code>\n<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Step 8 &#8211; Add the Routes to the Middleware Stack<\/h2>\n\n\n\n<p>Finally, add the Express router we defined above to the middleware pipeline.<\/p>\n\n\n\n<p><strong>server.ts<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-ts\"><code>\nimport { PrismaClient } from \"@prisma\/client\";\nimport express, { Request, Response } from \"express\";\nimport cors from \"cors\";\nimport authRouter from \".\/routes\/auth.route\";\nimport morgan from \"morgan\";\n\nexport const prisma = new PrismaClient();\nconst app = express();\n\nasync function main() {\n  \/\/ Middleware\n  app.use(morgan(\"dev\"));\n  app.use(\n    cors({\n      origin: [\"http:\/\/localhost:3000\"],\n      credentials: true,\n    })\n  );\n  app.use(express.json());\n\n  \/\/   Health Checker\n  app.get(\"\/api\/healthchecker\", (res: Response) =&gt; {\n    res.status(200).json({\n      status: \"success\",\n      message: \"Welcome to Two-Factor Authentication with Node.js\",\n    });\n  });\n\n  app.use(\"\/api\/auth\", authRouter);\n\n  app.all(\"*\", (req: Request, res: Response) =&gt; {\n    return res.status(404).json({\n      status: \"fail\",\n      message: `Route: ${req.originalUrl} not found`,\n    });\n  });\n\n  const PORT = 8000;\n  app.listen(PORT, () =&gt; {\n    console.info(`Server started on port: ${PORT}`);\n  });\n}\n\nmain()\n  .then(async () =&gt; {\n    await prisma.$disconnect();\n  })\n  .catch(async (e) =&gt; {\n    console.error(e);\n    await prisma.$disconnect();\n    process.exit(1);\n  });\n<\/code>\n<\/pre>\n\n\n\n<p>Start the Node.js server by running:<\/p>\n\n\n\n<pre class=\"line-numbers language-shell\"><code>\nyarn ts-node-dev --respawn --transpile-only --exit-child server.ts\n# or \nnpx ts-node-dev --respawn --transpile-only --exit-child server.ts\n<\/code>\n<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>In this article, you learned how to implement two-factor (2FA) authentication in Node.js using the <code>hi-base32<\/code> and <code>otpauth<\/code> libraries. You also learned how to use the authenticator extension in chrome to generate the TOTP tokens.<\/p>\n\n\n\n<p>You can find the frontend and backend source code on this GitHub repository:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/github.com\/wpcodevo\/two_factor_reactjs\" target=\"_blank\" rel=\"noreferrer noopener\">React.js 2FA Source Code<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/wpcodevo\/2fa-nodejs\" target=\"_blank\" rel=\"noreferrer noopener\">Node.js 2FA Source Code<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>This article will teach you how to secure a Node.js API by implementing two-factor authentication (2FA) system using tokens generated by Google Authenticator or Authy&#8230;.<\/p>\n","protected":false},"author":1,"featured_media":8380,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[47],"tags":[42,43],"class_list":["post-8271","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-nodejs","tag-nodejs-api","tag-pure-nodejs-api"],"acf":[],"_links":{"self":[{"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/posts\/8271","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/comments?post=8271"}],"version-history":[{"count":3,"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/posts\/8271\/revisions"}],"predecessor-version":[{"id":11976,"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/posts\/8271\/revisions\/11976"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/media\/8380"}],"wp:attachment":[{"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/media?parent=8271"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/categories?post=8271"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/tags?post=8271"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}