























Authentication is a crucial aspect of any web application that requires users to sign in and manage their accounts.
In this article, we'll be exploring how to implement a basic authentication system using Express as well as a signup and login form in React.js. You'll learn the difference between the JWT- and session-based authentication and some associated best practices. You'll then learn how to implement session authentication step by step using a real-world demo that's before getting access to a ready-to-use React login page template based on the steps outlined in this guide.
By the end of this tutorial, you'll have a solid understanding of how to create a login page using React, handle user registration, and protect routes from unauthenticated users.
There are different ways to authenticate users, but the primary methods today are JWT-based authentication and session-based authentication.
Session-based authentication is a method of tracking a user's login state by creating a unique session for each authenticated user. When a user logs in, the server generates a session ID and stores session information server-side, typically in memory or a database. This session ID is then sent to the client, usually as a cookie, which the client includes with subsequent requests to prove authentication.
Cookies are used since they are sent back to the server with each request. When the server receives the session ID, it can look up the session in the database to both confirm its validity, as well as reference the associated user.
This article covers how to implement session-based authentication into a React application.
JSON Web Token (JWT) authentication is an authentication method where a signed, encoded token is generated upon user login and returned to the client.
The JWT contains information such as the user ID, issue date, expiration date, etc. Once verified by the server using a private key, the server can trust that the information within the JWT correctly identifies the party that made the request.
Unlike session-based authentication, JWTs are self-contained and eliminate the need for server-side session storage.
Implementing session-based authentication includes several key requirements.
A storage mechanism is required to hold information about the users and the sessions. Using a database would require two tables, one for each entity. The following table diagram outlines the minimum requirements to implement session-based authentication.

The users table will store general information about the user such as their name, email address, and a hashed version of the password. The sessions table will store details about each session.
Notice how the sessions table contains a userId field which is a foreign key of the users table. This is used to associate that session with a specific user.
Signing up the user will require a form to be created on the front end that is accessible if the user is not already authenticated. This typically includes, at minimum, inputs for username and password.
The server needs a route handler that can accept the user’s credentials when they click “Sign up”. The simplest implementation of this will create a user record and store the username and a hashed version of the password.
It’s also a best practice to implement validation on both the form the user interacts with and the route handler on the server. This ensures that any requirements for the inputs (ex: password complexity) are met before any records are created.

A login form is also required to allow the user to sign in. Again, the simplest implementation is at least a username and password.
As with handling sign-up, a route handler also needs to be created on the server to handle the credentials when they are submitted. Instead of creating a new user record, the credentials are verified with the existing user record.
If the React login page contains the proper credentials and they match when checked with the database, a session is created in the database. The token is added to a cookie that is sent back to the client.
Cookies are used for several reasons:

Finally, routes on both the front end and back end need to be configured to be protected. In a typical React application using react-router-dom, Routes can be wrapped in a parent component which will check the authorization status with the server before rendering them. When the React login page is used to authenticate a user, the <ProtectedRoute /> component will automatically permit access to those views.
On the server, the session ID that is sent to the server will be checked with the sessions table in the database to make sure the sessions is valid before processing the request. In Express, middleware can be created that wraps routes or route collections to make sure this is done automatically on the proper requests.
To demonstrate how session-based authentication can be implemented, we’ll use a realistic project called Qulllmate.
Quillmate is an open-source web application where writers can create articles with the help of an AI assistant. The core entity of Quillmate is an article, and each article has its own AI assistant that understands what has already been written and helps when asked questions about the material.
The following video demonstrates the finished product, where the user can sign up, sign in, create articles, and ask AI for assistance:
Quillmate is built with React and uses Express to both serve the application, as well as handle backend requests. The project uses Open AI to ask questions about articles, and Neon to store the data.
Quillmate is built with the following tech stack:
/api will be handled by various API routes, whereas any other requests will serve up the React app.Let’s start by adding the users table and sessions table. This can be done by modifying the Prisma schema and running a script to apply the changes to the Neon database.
Modify the prisma/schema.prisma file and add the User and Session models:
Next, update the Article model to include a relationship with the user:
Finally, push the schema changes to Neon and update the local client by running the following commands in your terminal:
Next, you’ll need to update the backend to support creating accounts, creating sessions, and protecting the necessary routes.
The following dependencies are required:
bcryptjs - Used for salting and hashing passwords before saving them to the database.cookie-parser - An Express middleware that makes working with cookies easier.zod - Used for type validation.Run the following commands to install those packages and their types:
Next, create the Express middleware that will be used to protect routes. This will be used by protected routes to make sure that the request is authorized, returning a 401 status if it is not. It will also handle removing the session from the database if it’s expired.
Create a file at server/middleware/auth.ts and paste in the following:
If your editor is complaining about setting the req.user and req.session values, create a file at server/types/express.d.ts and paste in the following:
Now let’s add the routes required to enable sign-up and sign-in. The route file will also include validation from There will be four routes in total:
/api/signin - Used by the React login page to create a session and return it back to the user./api/signup - Used to create an account, which we’ll use to also create a session right away./api/signout - Used to sign the user out, which deletes the session. This route is protected by the middleware./api/me - Used by the front end to verify that the session is valid before loading the articles. This route is also protected by the middleware.Create a file at server/routes/auth.ts and paste in the following:
/api/articles route to filter by userNext, we’ll need to update the /api/articles routes to ensure that when database records are created, the user information is saved with the record as well. This will allow queries to return only the articles created by that user.
Update server/routes/articles.ts like so:
The last thing to do on the server is update server/index.ts to set up cookie-parser, register the /api/auth routes, and protect the /api/articles and /api/ai routes.
Update server/index.ts as follows:
With the backend updated to support authentication, let’s update the frontend to do the same.
Since the authentication state can affect the way the entire application behaves, we’ll create a React Context and Provider so we can check if the user is logged in from anywhere. We’ll also add the logic to sign up, sign in, and sign out from the context, making it easy to call those functions from various points of the app.
Create the src/hooks/use-auth.tsx file and add the following code:
Next, update the src/App.tsx file to wrap the entire application with the provider:
Next, let’s add the sign-up and sign-in views. These views will both be very similar, each containing form elements where the user can enter their credentials. Both also use zod for client-side validation, which prevents an unnecessary network trip to the server if the user does not populate the fields properly. If validation fails, errors will be shown on the respective fields.
The key difference between them is what happens when the user submits the form, specifically, the method called from the useAuth hook created earlier.
Create the src/views/sign-in.tsx page and populate it with the following code:
Do the same with src/views/sign-up.tsx:
Now register the views in src/App.tsx:
To protect routes, we’ll create a separate component that will use the context & provider and check the authentication state before allowing the user to proceed. If the authentication state is being checked by the provider, a “Loading…” message will be rendered for the user. If the user is not logged in, they will be redirected to the sign-in view.
Create the src/components/protected-route.tsx file and add the following:
Update src/App.tsx and wrap the element for the /app route in the <ProtectedRoute> component:
The last thing to add is a sign-out button that lets users log out of the app once they are finished. This will use the signOut function of the useAuth hook to remove the session from the database and clear the cookie set in the browser.
Update src/components/article-list.tsx with the following:
With all the changes in place, you may test the application! Run the application with the following command in your terminal:
Now open localhost:3000 in your browser and try creating a user to use the application, create an article, and experiment with the AI functionality! I encourage you to also log into Neon and check the contents of the database, specifically the users and sessions tables as they contain the records needed to support authentication.
Clerk is a user management and authentication platform, so it might surprise you that we’re publishing an article walking you through how to implement authentication yourself.
This article covers how to implement a single method of authentication, but in reality, user management is much more than just session-based authentication. For example, it’s commonplace in modern web applications to also support social login providers like Google or Apple. A password reset flow is also a critical requirement for supporting your own authentication setup.
These are just a few of the many features Clerk supports out of the box, oftentimes with a single line of code.
Using Clerk, you can easily create a sign-in page by just importing and rendering the <SignIn /> component like so:
If you want to learn how easy it is to get Clerk working in a React application, check out the quickstart on our docs.
In conclusion, this article has walked you through how to implement a basic authentication system using React and Express. By setting up a session-based authentication flow, we've covered how to create a sign-in page, handle user registration, log users in and out, as well as protect routes with a simple ProtectedRoute component.
While implementing a custom authentication system can be a great learning experience, it's also important to consider the larger picture of what user management really entails. In reality, you'll often need to support features like social login providers, password reset flows, and more.
That's where Clerk comes in - a user management and authentication platform that simplifies the process of implementing these features with just a few lines of code. If you're interested in learning more about how easy it is to get Clerk working in a React application, check out the quickstart guide on our documentation site.
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。