{"id":6457,"date":"2026-07-02T23:21:10","date_gmt":"2026-07-02T16:21:10","guid":{"rendered":"https:\/\/daiilynews.cu.ma\/?p=6457"},"modified":"2026-07-02T23:21:10","modified_gmt":"2026-07-02T16:21:10","slug":"part-2-building-an-authentication-system-from-scratch-backend-setup","status":"publish","type":"post","link":"https:\/\/daiilynews.cu.ma\/?p=6457","title":{"rendered":"Part 2 Building an Authentication System from Scratch \u2013 Backend Setup"},"content":{"rendered":"<p> <br \/>\n<\/p>\n<p>  User Registration &#038; Secure Password Hashing with bcrypt<\/p>\n<p>In the previous article, we built the backend foundation by setting up Express.js, PostgreSQL, environment variables, and a clean layered architecture.<\/p>\n<p>With the backend ready, it&#8217;s time to implement the first authentication feature\u2014User Registration.<\/p>\n<p>Although registration appears straightforward, it involves much more than simply storing user details in a database. A secure registration system must validate user input, prevent duplicate accounts, protect passwords, and ensure that sensitive information is never exposed.<\/p>\n<p>In this article, we&#8217;ll build the complete registration workflow while following security best practices.<\/p>\n<p>The registration process follows a layered architecture, where each layer has a single responsibility.<\/p>\n<p>Client<br \/>\n   \u2502<br \/>\n   \u25bc<br \/>\nRoutes<br \/>\n   \u2502<br \/>\n   \u25bc<br \/>\nController<br \/>\n   \u2502<br \/>\n   \u25bc<br \/>\nService<br \/>\n   \u2502<br \/>\n   \u25bc<br \/>\nRepository<br \/>\n   \u2502<br \/>\n   \u25bc<br \/>\nPostgreSQL<\/p>\n<p>    Enter fullscreen mode<\/p>\n<p>    Exit fullscreen mode<\/p>\n<p>The overall workflow is:<\/p>\n<p>The client submits the registration form.<br \/>\nThe controller receives the request.<br \/>\nThe service validates the data.<br \/>\nThe repository checks whether the email already exists.<br \/>\nThe password is securely hashed using bcrypt.<br \/>\nThe user is stored in PostgreSQL.<br \/>\nA success response is returned to the client.<\/p>\n<p>Instead of placing all the registration logic inside the controller, I divided the implementation into three layers.<\/p>\n<p>  Controller<\/p>\n<p>Responsible only for:<\/p>\n<p>Receiving the HTTP request<br \/>\nCalling the service layer<br \/>\nReturning the HTTP response<\/p>\n<p>The controller should never contain business logic or database queries.<\/p>\n<p>  Service<\/p>\n<p>The service contains the application&#8217;s business logic.<\/p>\n<p>For registration, it is responsible for:<\/p>\n<p>Validating the request<br \/>\nChecking whether the email already exists<br \/>\nHashing the password<br \/>\nCalling the repository to save the user<\/p>\n<p>This layer acts as the brain of the application.<\/p>\n<p>  Repository<\/p>\n<p>The repository communicates directly with PostgreSQL.<\/p>\n<p>Its responsibilities include:<\/p>\n<p>Checking if a user already exists<br \/>\nCreating a new user<br \/>\nExecuting SQL queries<\/p>\n<p>Keeping SQL isolated inside repositories makes the application easier to maintain and test.<\/p>\n<p>The controller receives the registration request and forwards the data to the service layer.<\/p>\n<p>\/\/ Register Controller Screenshot Here<\/p>\n<p>    Enter fullscreen mode<\/p>\n<p>    Exit fullscreen mode<\/p>\n<p>The controller itself performs very little work.<\/p>\n<p>Its responsibility is simply to:<\/p>\n<p>Extract the request body<br \/>\nCall the service<br \/>\nReturn either a success or an error response<\/p>\n<p>This keeps controllers lightweight and easy to understand.<\/p>\n<p>The service contains the actual registration workflow.<\/p>\n<p>\/\/ Register Service Screenshot Here<\/p>\n<p>    Enter fullscreen mode<\/p>\n<p>    Exit fullscreen mode<\/p>\n<p>The registration service performs the following steps:<\/p>\n<p>Check whether the email already exists.<br \/>\nGenerate a secure password hash.<br \/>\nCreate the user in PostgreSQL.<br \/>\nReturn the newly created user.<\/p>\n<p>Because all business rules live inside the service layer, future changes become much easier.<\/p>\n<p>For example, adding email verification later would require changes only inside the service, without affecting controllers or repositories.<\/p>\n<p>The repository is responsible only for database communication.<\/p>\n<p>\/\/ Repository Screenshot Here<\/p>\n<p>    Enter fullscreen mode<\/p>\n<p>    Exit fullscreen mode<\/p>\n<p>Typical repository functions include:<\/p>\n<p>findByEmail()<br \/>\ncreateUser()<\/p>\n<p>Keeping SQL queries isolated improves readability and keeps the service layer database-agnostic.<\/p>\n<p>One of the biggest mistakes an application can make is storing passwords in plain text.<\/p>\n<p>Imagine a database leak.<\/p>\n<p>If passwords are stored as plain text, every user&#8217;s credentials become immediately visible.<\/p>\n<p>Instead, passwords should always be transformed into a secure one-way hash before being stored.<\/p>\n<p>This is exactly why we use bcrypt.<\/p>\n<p>bcrypt is one of the most trusted password hashing libraries available for Node.js.<\/p>\n<p>Unlike encryption, hashing is a one-way operation.<\/p>\n<p>This means:<\/p>\n<p>The original password cannot be recovered.<br \/>\nEven the application itself cannot view the user&#8217;s password.<br \/>\nOnly password verification is possible.<\/p>\n<p>When a user registers, bcrypt performs several operations internally.<\/p>\n<p>Password<br \/>\n    \u2502<br \/>\n    \u25bc<br \/>\nGenerate Random Salt<br \/>\n    \u2502<br \/>\n    \u25bc<br \/>\nPassword + Salt<br \/>\n    \u2502<br \/>\n    \u25bc<br \/>\nMultiple Hashing Rounds<br \/>\n    \u2502<br \/>\n    \u25bc<br \/>\nStore Hash in Database<\/p>\n<p>    Enter fullscreen mode<\/p>\n<p>    Exit fullscreen mode<\/p>\n<p>Each password receives its own randomly generated salt before hashing.<\/p>\n<p>Because of this:<\/p>\n<p>Two users with the same password will have completely different hashes.<br \/>\nRainbow table attacks become ineffective.<br \/>\nBrute-force attacks become significantly slower due to bcrypt&#8217;s configurable cost factor.<\/p>\n<p>During login, the user enters their password as plain text.<\/p>\n<p>bcrypt then:<\/p>\n<p>Reads the stored hash.<br \/>\nExtracts the embedded salt.<br \/>\nHashes the entered password using the same salt.<br \/>\nCompares the generated hash with the stored hash.<\/p>\n<p>If both hashes match, the user is successfully authenticated.<\/p>\n<p>const isMatch = await bcrypt.compare(<br \/>\n    enteredPassword,<br \/>\n    storedHash<br \/>\n);<\/p>\n<p>    Enter fullscreen mode<\/p>\n<p>    Exit fullscreen mode<\/p>\n<p>One of bcrypt&#8217;s biggest advantages is that developers never need to manually manage salts or compare hashes\u2014the library handles the entire verification process securely.<\/p>\n<p>Using bcrypt provides several important security advantages.<\/p>\n<p>\u2705 Passwords are never stored in plain text.<\/p>\n<p>\u2705 Every password uses a unique random salt.<\/p>\n<p>\u2705 Identical passwords generate different hashes.<\/p>\n<p>\u2705 Brute-force attacks become significantly slower.<\/p>\n<p>\u2705 Rainbow table attacks are mitigated.<\/p>\n<p>These features make bcrypt one of the industry standards for password protection.<\/p>\n<p>Once the backend implementation was complete, I verified the registration API using Postman.<\/p>\n<p>  Request<\/p>\n<p>POST \/api\/auth\/register<\/p>\n<p>    Enter fullscreen mode<\/p>\n<p>    Exit fullscreen mode<\/p>\n<p>{<br \/>\n  &#8220;username&#8221;: &#8220;Sriya&#8221;,<br \/>\n  &#8220;email&#8221;: &#8220;sriya@gmail.com&#8221;,<br \/>\n  &#8220;password&#8221;: &#8220;Password123&#8221;<br \/>\n}<\/p>\n<p>    Enter fullscreen mode<\/p>\n<p>    Exit fullscreen mode<\/p>\n<p>  Response<\/p>\n<p>{<br \/>\n  &#8220;success&#8221;: true,<br \/>\n  &#8220;user&#8221;: {<br \/>\n    &#8220;id&#8221;: 1,<br \/>\n    &#8220;username&#8221;: &#8220;Sriya&#8221;,<br \/>\n    &#8220;email&#8221;: &#8220;sriya@gmail.com&#8221;<br \/>\n  }<br \/>\n}<\/p>\n<p>    Enter fullscreen mode<\/p>\n<p>    Exit fullscreen mode<\/p>\n<p>Notice that the response never includes the password or its hash.<\/p>\n<p>Only non-sensitive user information is returned to the client.<\/p>\n<p>Now that users can securely register and their passwords are safely stored, the next step is allowing them to authenticate.<\/p>\n<p>In the next article, we&#8217;ll build the Login Flow, where we&#8217;ll:<\/p>\n<p>Verify user credentials<br \/>\nCompare passwords using bcrypt<br \/>\nGenerate JWT Access Tokens<br \/>\nGenerate Refresh Tokens<br \/>\nUnderstand how JWT authentication works internally<\/p>\n<p><br \/>\n<br \/><a href=\"https:\/\/dev.to\/t_sriya_2af6abc7e8d4e87da\/part-2building-an-authentication-system-from-scratch-backend-setup-59gg\">Source link <\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>User Registration &#038; Secure Password Hashing with bcrypt In the previous article, we built the backend foundation by setting up Express.js, PostgreSQL, environment variables, and a clean layered architecture. With the backend ready, it&#8217;s time to implement the first authentication feature\u2014User Registration. Although registration appears straightforward, it involves much more than simply storing user details [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":6458,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[676],"tags":[1143,761,765,762,763,764,1092,1086,760,795],"class_list":["post-6457","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-tech-ai","tag-backend","tag-coding","tag-community","tag-development","tag-engineering","tag-inclusive","tag-node","tag-security","tag-software","tag-tutorial"],"_links":{"self":[{"href":"https:\/\/daiilynews.cu.ma\/index.php?rest_route=\/wp\/v2\/posts\/6457","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/daiilynews.cu.ma\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/daiilynews.cu.ma\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/daiilynews.cu.ma\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/daiilynews.cu.ma\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=6457"}],"version-history":[{"count":0,"href":"https:\/\/daiilynews.cu.ma\/index.php?rest_route=\/wp\/v2\/posts\/6457\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/daiilynews.cu.ma\/index.php?rest_route=\/wp\/v2\/media\/6458"}],"wp:attachment":[{"href":"https:\/\/daiilynews.cu.ma\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=6457"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/daiilynews.cu.ma\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=6457"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/daiilynews.cu.ma\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=6457"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}