{"id":3495,"date":"2024-04-05T12:59:24","date_gmt":"2024-04-05T16:59:24","guid":{"rendered":"https:\/\/blog.logrocket.com\/?p=3495"},"modified":"2024-06-04T16:47:19","modified_gmt":"2024-06-04T20:47:19","slug":"adding-login-authentication-secure-react-apps","status":"publish","type":"post","link":"https:\/\/blog.logrocket.com\/adding-login-authentication-secure-react-apps\/","title":{"rendered":"Adding login authentication to secure React apps"},"content":{"rendered":"<!DOCTYPE html>\n<html><p><em><strong>Editor\u2019s note<\/strong>: This React and Express.js login authentication tutorial was last updated by <a href=\"https:\/\/blog.logrocket.com\/author\/davidomotayo\/\">David Omotayo<\/a> on 5 April 2024 to detail the creation of a login component using the React Context API and React Router DOM. Additionally, it covers the integration of form validation on both the client and server side, as well as how to implement role-based access controls. It was previously updated to evaluate the benefits of server-side login authentication in React apps, as well as to include information about logging out and how to connect to a MongoDB database.<\/em><\/p><img loading=\"lazy\" decoding=\"async\" width=\"730\" height=\"487\" src=\"https:\/\/blog.logrocket.com\/wp-content\/uploads\/2021\/05\/adding-login-authentication-secure-react-apps-nocdn.png\" class=\"attachment-full size-full wp-post-image\" alt=\"Adding Login Authentication to Secure React Apps\" srcset=\"https:\/\/blog.logrocket.com\/wp-content\/uploads\/2021\/05\/adding-login-authentication-secure-react-apps-nocdn.png 730w, https:\/\/blog.logrocket.com\/wp-content\/uploads\/2021\/05\/adding-login-authentication-secure-react-apps-nocdn-300x200.png 300w\" sizes=\"auto, (max-width: 730px) 100vw, 730px\">\n<p>It\u2019s well-known that the client side is unsafe due to its exposed nature. In your web application, you can conditionally render views to show different content to different users. But, if that information is already stored on the client side, it\u2019s no longer secure.<\/p>\n<p>To ensure that only users with a secure login can see the limited content, you should ship the content data from your server upon authentication.<\/p>\n<p>In this tutorial, we\u2019ll show you how to secure your React app by implementing basic server-side authentication for an Express.js server. Although the client side will be a React app, you can apply it to virtually any other type of client-side application. We\u2019ll also discuss some React authentication best practices for implementing secure login functionality on the server side.<\/p>\n<h2 id=\"what-server-side-login-authentication\">What is server-side login authentication?<\/h2>\n<p>Server-side login authentication is a <a href=\"https:\/\/blog.logrocket.com\/complete-guide-authentication-with-react-router-v6\/\">method of authenticating<\/a> the identity of a user attempting to log in to a server. This type of authentication typically involves the user providing a username and password, which are then sent to the server for verification. If the credentials are valid, the server allows the user to log in and access the resources on the server.<\/p>\n<h2 id=\"benefits-using-server-side-login-authentication\">Benefits of using server-side login authentication<\/h2>\n<p>There are several uses for and benefits to using server-side login authentication. First and foremost, it helps ensure the security of the server and the resources it contains. By requiring users to provide a username and password, the server can verify that the person attempting to log in is authorized to do so.<\/p>\n<p>Another benefit of server-side login authentication is that it can provide a centralized system for managing user accounts. This means that the server administrator can easily add, remove, or update user accounts, and the changes will be immediately reflected across the entire server. This can make it easier to manage user access and ensure that only authorized users have access to the server and its resources.<\/p>\n<p>In addition, server-side login authentication can provide a way for users to securely access resources from any device \u2014 as long as they have the necessary credentials. This can be especially useful for organizations with multiple locations or employees needing to access resources remotely.<\/p>\n<h2 id=\"react-authentication-server-side-login-setup\">React authentication server-side login setup<\/h2>\n<p>The easiest way to bootstrap a React project is to use the <a href=\"https:\/\/blog.logrocket.com\/build-react-typescript-app-vite\/\">Vite Create<\/a> package. When you create a project with this package and run <code>npm start<\/code>, you essentially start a Vite server. This works fine on your local machine, but when you want to deploy it to a remote server, you need your own server to serve your React application. This is basically a package of HTML, JavaScript, and CSS.<\/p>\n<p>We\u2019ll refer to the following folder structure for this React authentication example project:<\/p>\n<pre class=\"language-javascript hljs\">--- Project Folder\n |__ client (React App)\n |__ server.js\n |__ package.json\n<\/pre>\n<p>There is a <code>Project Folder<\/code> and, inside it, a <code>client<\/code> folder containing the React app. The folder also contains <code>server.js<\/code> and <code>package.json<\/code> files, which you can create by using the following commands on the terminal in the project directory:<\/p>\n<pre class=\"language-yarn hljs\">npm init -y\ntouch server.js\n<\/pre>\n<h2 id=\"serving-react-app\">Serving the React app<\/h2>\n<p>There are two primary approaches to serving the React application when building with the MERN stack:<\/p>\n<ul>\n<li><strong>Serving separately<\/strong>: This method involves running independent development servers for both the client-side React app and the server-side Express app. During production deployment, you would deploy the React and Express applications separately. The React app would then fetch data from the deployed Express server endpoints<\/li>\n<li><strong>Serving with Express<\/strong>: This approach involves running both the React application and the Express app within the same server environment (same origin). Here\u2019s the process breakdown:\n<ul>\n<li>You build your React application and generate a production-ready <code>build\/dist<\/code> folder containing static files (HTML, CSS, and JavaScript)<\/li>\n<li>You use the <code>express.static()<\/code> middleware within your Express app to serve these static files from the <code>build<\/code> folder. This allows you to access your React application through the Express server<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>The following code snippet is a basic Express.js server that serves the client\u2019s <code>dist<\/code> folder. As we progress, we\u2019ll add authentication and additional functionalities on top of this structure:<\/p>\n<pre class=\"language-javascript hljs\">const express = require('express');\nconst path = require('path');\nconst app = express();\n\nconst PORT = process.env.PORT || 5000;\n\napp\n  .use(express.static(path.join(__dirname, \"..\/client\", \"dist\")));\n  .listen(PORT, () =&gt; console.log(`Listening on ${PORT}`));\n\napp.get('\/', (req, res) =&gt; {\n  res.sendFile(path.join(__dirname, '\/client\/build\/index.html'));\n});\n<\/pre>\n<p>The choice between these methods depends on your specific needs and preferences. Serving separately offers greater flexibility but requires managing two separate processes. Serving with Express simplifies deployment and streamlines development, but can introduce limitations in complex routing scenarios.<\/p>\n<h3 id=\"proxy-react-app\">Proxy the React app<\/h3>\n<p>When serving your React application with Express, you\u2019re typically required to rebuild the app every time there\u2019s a change. To avoid this, you can serve the application using the built-in Vite server with the <code>npm run dev<\/code> command and <a href=\"https:\/\/blog.logrocket.com\/why-you-should-use-proxy-server-create-react-app\/\">proxy your React app<\/a> to a specific port.<\/p>\n<p>That way, you can run the React app locally and avoid CORS errors when communicating with the Express server.<\/p>\n<p>Add the following code to the <code>vite.config.js<\/code> file in your <code>client<\/code> directory, assuming that the Express server will be serving on <code>port 5000<\/code>:<\/p>\n<pre class=\"language-javascript hljs\">import { defineConfig } from 'vite';\nimport react from '@vitejs\/plugin-react';\n\nexport default defineConfig({\n  plugins: [react()],\n  server: {\n      proxy: {\n        '\/api': {\n          target: 'http:\/\/localhost:5000',\n          changeOrigin: true,\n        },\n      },\n    },\n});\n<\/pre>\n<p>This configuration will re-route all requests with the <code>\/api<\/code> suffix to the backend URL <code>http:\/\/localhost:5000<\/code>.<\/p>\n<h3 id=\"run-express-js-server-locally\">Run the Express.js server locally<\/h3>\n<p>As mentioned earlier, the React app will still use the Vite server and proxy to <code>port 5000<\/code>. However, we still have to run the Express server separately.<\/p>\n<p>The Nodemon package is very handy for running and listening for changes. You can install it globally:<\/p>\n<pre class=\"language-yarn hljs\">npm i -g nodemon\n<\/pre>\n<p>Then run the server by simply running <code>nodemon server.js<\/code> in the main directory of the project folder.<\/p>\n<h3 id=\"how-to-run-remote-server\">How to run on a remote server<\/h3>\n<p>Although this is an optional step, it\u2019s important to mention. Let\u2019s assume we want to deploy our application to a <a href=\"https:\/\/www.heroku.com\">Heroku<\/a> dyno.<\/p>\n<p>Heroku detects a Node.js application, installs dependencies, and runs it automatically. But you still have to tell it to go into the specific folder, install dependencies, and build the React app for production. In our case, this is going into <code>\/client<\/code> running <code>npm install<\/code> and then <code>npm run build<\/code>, respectively.<\/p>\n<p>For this purpose, Heroku has a <code>post-build<\/code> command: <code>\"heroku-postbuild\": \"cd client &amp;&amp; npm install &amp;&amp; npm run build\"<\/code>.<\/p>\n<p>Add this under the <code>\"scripts\"<\/code> key inside the <code>package.json<\/code> of the server. Also, make sure your entry point for the Node.js application is <code>server.js<\/code> in the <code>package.json<\/code> file.<\/p>\n<p>This is likely to be <code>index.js<\/code> if you initialized your npm package with <code>-y<\/code> flag as <code>npm init -y<\/code> with <code>\"main\": \"server.js\"<\/code>.<\/p>\n<h2 id=\"basic-authentication-react-express-js\">Basic authentication in React and Express.js<\/h2>\n<p>As the name suggests, <a href=\"https:\/\/www.npmjs.com\/package\/express-basic-auth\">express-basic-auth<\/a> is a convenient and easy-to-use package for basic authentication purposes.<\/p>\n<p>First, install the package and then require it at the top of your <code>server.js<\/code>. We\u2019ll define the secure login credentials by using the instance of the package:<\/p>\n<pre class=\"language-javascript hljs\">const basicAuth = require('express-basic-auth');\n\nconst auth = basicAuth({\n  users: {\n    admin: '123',\n    user: '456',\n  },\n});\n<\/pre>\n<p>Now, when the <code>auth<\/code> variable is used as an endpoint parameter, the endpoint\u2019s response reaches back to the client only if the credentials were sent along with the request match.<\/p>\n<p>In the code below, you\u2019ll see both the <code>\/authenticate<\/code> endpoint on the server side and the <code>GET<\/code> request sent from the client, along with the <code>auth<\/code> object, which contains the credentials:<\/p>\n<pre class=\"language-javascript hljs\">\/\/ End-point on Server\n\napp.get('\/api\/authenticate', auth, (req, res) =&gt; {\n  if (req.auth.user === 'admin') {\n    res.send('admin');\n  } else if (req.auth.user === 'user') {\n    res.send('user');\n  }\n});\n\n\/\/ Request on Client\n\nconst auth = async () =&gt; {\n  try {\n    const res = await axios.get('\/api\/authenticate', { auth: { username: 'admin', password: '123' } });\n    console.log(res.data);\n  } catch (e) {\n    console.log(e);\n  }\n};\n<\/pre>\n<p>In the example above, passing the correct credentials sends back either <code>admin<\/code> or <code>user<\/code> as a string response, depending on the username used. Wrong login credentials will simply return a response of <code>401 (Unauthorized)<\/code>.<\/p>\n<h2 id=\"react-authentication-using-http-cookies\">React authentication using HTTP cookies<\/h2>\n<p>Now that we\u2019ve figured out how to send data from server to client if the credentials are correct, the next step is to persist that authentication through a <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTTP\/Cookies\">cookie session<\/a>.<\/p>\n<p>Instead of sending a response from the <code>authenticate<\/code> endpoint, we can set a cookie on the client from the server. By deploying another endpoint, we can check for the cookie and send the data to populate the view.<\/p>\n<p>Once the user is authenticated, this information should be stored somewhere on the client side so that the user does not need to authenticate every time. The common practice is to use cookies to store this session information. Cookies are safe as long as the correct flags are set.<\/p>\n<h3 id=\"react-httponly-cookie\">The React <code>httpOnly<\/code> cookie<\/h3>\n<p>When using a cookie session to persist authentication in React, the <code>httpOnly<\/code> flag ensures that no client-side script can access the cookie other than the server.<\/p>\n<p>The <code>secure<\/code> flag ensures that cookie information is sent to the server with an encrypted request over the HTTPS protocol. When using the <code>secure<\/code> flag, you also need a key to sign the cookie. For this purpose, we use <code>cookie-parser<\/code> middleware for the Express.js server.<\/p>\n<p>A cookie simply has a <code>name<\/code> and a <code>value<\/code>. Even with the aforementioned flags, never disclose any vulnerable information within cookie parameters.<\/p>\n<h3 id=\"how-to-create-user-session-using-cookies-react\">How to create a user session using cookies in React<\/h3>\n<p>In the code example below, <code>server.js<\/code> sets a unique cookie upon authentication. As you can see, after setting the cookie, the response is also sending an object with the <code>screen:admin<\/code> or <code>screen:user<\/code> key-value pair.<\/p>\n<p>This response will later be used in the React application on the client side:<\/p>\n<pre class=\"language-javascript hljs\">const cookieParser = require('cookie-parser');\n\n\/\/ A random key for signing the cookie\napp.use(cookieParser('82e4e438a0705fabf61f9854e3b575af'));\n\napp.get('\/api\/authenticate', auth, (req, res) =&gt; {\n  const options = {\n    httpOnly: true,\n    signed: true,\n  };\n\n  if (req.auth.user === 'admin') {\n    res.cookie('name', 'admin', options).send({ screen: 'admin' });\n  } else if (req.auth.user === 'user') {\n    res.cookie('name', 'user', options).send({ screen: 'user' });\n  }\n});\n<\/pre>\n<p>As the cookie has an <code>httpOnly<\/code> flag, we can neither read nor delete it on the client side. Therefore, we need two more endpoints to read and delete the cookie and send back a response accordingly.<\/p>\n<h3 id=\"reading-cookie-from-server\">Reading a cookie from the server<\/h3>\n<p>Reading a cookie from a server is quite straightforward, but you should keep in mind that the endpoint for this functionality should not have the <code>auth<\/code> variable because authentication for this endpoint should not be required.<\/p>\n<p>Below the log-out user section, we have two endpoints: <code>\/read-cookie<\/code> and <code>\/clear-cookie<\/code>. The <code>signedCookies<\/code> object with the <code>res<\/code> contains the <code>name:value<\/code> pair that we set for the cookie:<\/p>\n<pre class=\"language-javascript hljs\">res.cookie('name', 'admin', options)\n<\/pre>\n<p>Depending on the value of the cookie name, we\u2019ll send a response.<\/p>\n<h3 id=\"log-out-user\">Log out user<\/h3>\n<p>As for the <code>\/clear-cookie<\/code> endpoint, deleting the cookie is done by referring to the name of the cookie, which is <code>name<\/code>. This, in turn, performs a simple <code>logout<\/code> functionality, as it clears out the users\u2019 session:<\/p>\n<pre class=\"language-javascript hljs\">app.get('\/api\/read-cookie', (req, res) =&gt; {\n  if (req.signedCookies.name === 'admin') {\n    res.send({ screen: 'admin' });\n  } else if (req.signedCookies.name === 'user') {\n    res.send({ screen: 'user' });\n  } else {\n    res.send({ screen: 'auth' });\n  }\n});\n\napp.get('\/clear-cookie', (req, res) =&gt; {\n  res.clearCookie('name').end();\n});\n<\/pre>\n<p>By following this logic, you can create several endpoints to send different types of data depending on the nature of your application. All you need to do is check the cookie and send the response accordingly.<\/p>\n<p>Below, you can find the complete <code>server.js<\/code> file, which serves the client-side React application that we\u2019ll cover in the next section:<\/p>\n<pre class=\"language-javascript hljs\">const express = require('express');\nconst basicAuth = require('express-basic-auth');\nconst cookieParser = require('cookie-parser');\nconst path = require('path');\n\nconst app = express();\n\nconst auth = basicAuth({\n  users: {\n    admin: '123',\n    user: '456',\n  },\n});\n\nconst PORT = process.env.PORT || 5000;\n\napp.use(cookieParser('82e4e438a0705fabf61f9854e3b575af'));\n\napp\n  .use(express.static(path.join(__dirname, '\/client\/build')))\n  .listen(PORT, () =&gt; console.log(`Listening on ${PORT}`));\n\napp.get('\/', (req, res) =&gt; {\n  res.sendFile(path.join(__dirname, '\/client\/build\/index.html'));\n});\n\napp.get('\/api\/authenticate', auth, (req, res) =&gt; {\n  const options = {\n    httpOnly: true,\n    signed: true,\n  };\n\n  console.log(req.auth.user);\n\n  if (req.auth.user === 'admin') {\n    res.cookie('name', 'admin', options).send({ screen: 'admin' });\n  } else if (req.auth.user === 'user') {\n    res.cookie('name', 'user', options).send({ screen: 'user' });\n  }\n});\n\napp.get('\/api\/read-cookie', (req, res) =&gt; {\n  console.log(req.signedCookies);\n  if (req.signedCookies.name === 'admin') {\n    res.send({ screen: 'admin' });\n  } else if (req.signedCookies.name === 'user') {\n    res.send({ screen: 'user' });\n  } else {\n    res.send({ screen: 'auth' });\n  }\n});\n\napp.get('\/api\/clear-cookie', (req, res) =&gt; {\n  res.clearCookie('name').end();\n});\n\napp.get('\/api\/get-data', (req, res) =&gt; {\n  if (req.signedCookies.name === 'admin') {\n    res.send('This is admin panel');\n  } else if (req.signedCookies.name === 'user') {\n    res.send('This is user data');\n  } else {\n    res.end();\n  }\n});\n<\/pre>\n<h2 id=\"practical-react-authentication-example\">A practical React authentication example<\/h2>\n<p>Assume you have an admin screen and a regular user screen, in which you show different contents. The first thing we need is an authentication request, which sends the credentials to the server. We also need another request that we send from the <a href=\"https:\/\/blog.logrocket.com\/react-lifecycle-methods-tutorial-examples\/#componentDidMount\"><code>componentDidMount<\/code> lifecycle Hook<\/a> to check whether there is already a cookie so that we can log in automatically.<\/p>\n<p>Then, we might need some other requests to get extra data. Eventually, we need to be able to send a request to clear the cookie so that the session does not persist anymore.<\/p>\n<p>You can find the complete client-side code on <a href=\"https:\/\/codesandbox.io\/s\/basic-auth-example-o73bh?from-embed\">CodeSandbox<\/a>. However, to get it working, you should run it alongside the server.<\/p>\n<h2 id=\"creating-login-component\">Creating a login component<\/h2>\n<p>Ideally, for cleaner and more maintainable code, it\u2019s recommended to create a dedicated login component that encapsulates all the authentication logic. This helps with the separation of concerns within your application.<\/p>\n<p>However, creating a login component is just the first step. We also need to create protected routes. These routes will verify a user\u2019s authentication status before granting access. Unauthenticated users attempting to access protected pages would be redirected to the login page.<\/p>\n<p>To do this, we\u2019ll use two key tools:<\/p>\n<ul>\n<li><strong>React Context API<\/strong>: This is a built-in state management system in React. We\u2019ll use it to make authentication data accessible throughout relevant components in the application<\/li>\n<li><strong>React Router DOM<\/strong>: This library adds routing functionalities to React applications. We\u2019ll use it to create the protected routes and handle user redirection based on authentication status<\/li>\n<\/ul>\n<h3 id=\"creating-authcontext-authprovider\">Creating an <code>AuthContext<\/code> and <code>AuthProvider<\/code><\/h3>\n<p>To get started, navigate to the <code>client<\/code> path in your terminal and run the following command to install react-router-dom as a dependency:<\/p>\n<pre class=\"language-yarn hljs\">npm i react-router-dom\n<\/pre>\n<p>Next, we need to create a context called <code>AuthContext<\/code> that manages and shares authentication-related data across our application and an <code>AuthProvider<\/code> component responsible for providing the authentication context to its children.<\/p>\n<p>Go to the <code>client<\/code> folder and create an <code>AuthProvider.jsx<\/code> file within the <code>src<\/code> folder and add the following code:<\/p>\n<pre class=\"language-javascript hljs\">import { useContext, createContext, useState } from \"react\";\nimport axios from \"axios\";\nimport { useNavigate } from \"react-router-dom\";\nconst AuthContext = createContext();\n\nconst AuthProvider = ({ children }) =&gt; {\n  const navigate = useNavigate();\n  const [screen, setScreen] = useState(\"auth\");\n\n  const loginAuth = async (data) =&gt; {\n    try {\n      const res = await axios.get(\"\/api\/authenticate\", {\n        auth: data,\n      });\n      if (res.data.screen !== undefined) {\n        readCookie();\n        return;\n      }\n    } catch (e) {\n      console.log(e);\n    }\n  };\n\n  const readCookie = async () =&gt; {\n    try {\n      const res = await axios.get(\"\/api\/read-cookie\");\n      if (res.data.screen !== undefined) {\n        setScreen(res.data.screen);\n        navigate(\"\/view\");\n        return;\n      }\n    } catch (e) {\n      setScreen(\"auth\");\n      console.log(e);\n    }\n  };\n\n  const logoutAuth = async () =&gt; {\n    try {\n      await axios.get(\"\/api\/clear-cookie\");\n      setScreen(\"auth\");\n      navigate(\"\/login\");\n      return;\n    } catch (e) {\n      console.log(e);\n    }\n  };\n\n  return (\n    &lt;AuthContext.Provider value={{ screen, loginAuth, logoutAuth }}&gt;\n      {children}\n    &lt;\/AuthContext.Provider&gt;\n  );\n};\nexport default AuthProvider;\nexport const useAuth = () =&gt; {\n  return useContext(AuthContext);\n};\n<\/pre>\n<p>Here, we\u2019ve defined three context methods: <code>loginAuth<\/code>, <code>readCookie<\/code>, and <code>logoutAuth<\/code>, which handle the login, cookie reading, and logout functionalities, respectively. Additionally, we define a <code>screen<\/code> state variable that stores the screen response retrieved from the backend.<\/p>\n<p>The <code>loginAuth<\/code> method handles user login by sending credentials (passed as the <code>data<\/code> parameter) to the <code>\/authenticate<\/code> endpoint. Upon successful login, it triggers the <code>readCookie<\/code> function.<\/p>\n<p>The <code>readCookie<\/code>, when invoked by the <code>loginAuth<\/code> method, retrieves the current <code>screen<\/code> string value from the <code>\/read-cookie<\/code> endpoint. It then updates the screen state and navigates the user to the <code>\/view<\/code> page using <code>navigate(\"\/view\")<\/code>.<\/p>\n<p>The <code>logoutAuth<\/code> method handles user logout. It clears the cookie by querying the <code>\/clear-cookie<\/code> endpoint, updates the screen state to <code>auth<\/code>, and redirects the user back to the login page with <code>navigate(\"\/login\")<\/code>.<\/p>\n<p>Now that we have the <code>AuthProvider<\/code> set up, let\u2019s integrate it into our application. We can achieve this by wrapping the application\u2019s root component with the <code>AuthProvider<\/code>. Because we\u2019ll also define our routes in the same component, we might as well set everything up there.<\/p>\n<p>Go to the <code>App.jsx<\/code> file and update it with the following:<\/p>\n<pre class=\"language-javascript hljs\">import \".\/App.css\";\nimport AuthProvider from \".\/AuthProvider.jsx\";\nimport ProtectedRoute from \"..\/components\/protectedRoute.jsx\";\nimport Login from \"..\/components\/login\";\nimport View from \"..\/components\/view.jsx\";\nimport { Routes, Route, BrowserRouter as Router } from \"react-router-dom\";\nfunction App() {\n  return (\n    &lt;div className=\"App\"&gt;\n      &lt;Router&gt;\n        &lt;AuthProvider&gt;\n          &lt;Routes&gt;\n            &lt;Route path=\"\/login\" element={&lt;Login \/&gt;} \/&gt;\n            &lt;Route element={&lt;ProtectedRoute \/&gt;}&gt;\n              &lt;Route path=\"\/view\" element={&lt;View \/&gt;} \/&gt;\n            &lt;\/Route&gt;\n          &lt;\/Routes&gt;\n        &lt;\/AuthProvider&gt;\n      &lt;\/Router&gt;\n    &lt;\/div&gt;\n  );\n}\nexport default App;\n<\/pre>\n<p>In the code above, we define a <code>\/login<\/code> and a <code>protectedRoute<\/code> route containing a nested <code>\/view<\/code> route. These routes are wrapped with the <code>AuthProvider<\/code> component, which allows us to access the <code>AuthContext<\/code> within these components using the <code>useAuth<\/code> Hook.<\/p>\n<h3 id=\"creating-view-protected-routes\">Creating the view and protected routes<\/h3>\n<p>The <code>ProtectedRoute<\/code> component acts as a gatekeeper for our protected content, which, in this case, is the <code>\/view<\/code> component. So our main task in the <code>protectedRoute<\/code> component is to verify whether the current value of <code>screen<\/code> state is not <code>auth<\/code>, and then render the <code>\/view<\/code> component; otherwise, we render the <code>\/login<\/code> component.<\/p>\n<p>To do this, create a <code>component<\/code> folder in the client\u2019s root and add a <code>ProtectedRoute.jsx<\/code> file with the following content:<\/p>\n<pre class=\"language-javascript hljs\">import React from \"react\";\nimport { Navigate, Outlet } from \"react-router-dom\";\nimport { useAuth } from \"..\/src\/AuthProvider\";\n\nconst ProtectedRoute = () =&gt; {\n  const auth = useAuth();\n  if (auth.screen == \"auth\") return &lt;Navigate to=\"\/login\" \/&gt;;\n  return &lt;Outlet \/&gt;;\n};\n\nexport default ProtectedRoute;\n<\/pre>\n<p>Next, create another file within <code>component<\/code>, name it <code>View.jsx<\/code>, and add the following code:<\/p>\n<pre class=\"language-javascript hljs\">import { useState } from \"react\";\nimport { useAuth } from \"..\/src\/AuthProvider\";\nimport axios from \"axios\";\nexport default function View() {\n  const { logoutAuth, screen } = useAuth();\n  const [data, setData] = useState();\n\n  const getData = async () =&gt; {\n    try {\n      const res = await axios.get(\"\/api\/get-data\");\n      console.log(res);\n      setData(res.data);\n    } catch (e) {\n      console.log(e);\n    }\n  };\n  return (\n    &lt;div&gt;\n      &lt;p&gt;{screen}&lt;\/p&gt;\n      &lt;p&gt;{data}&lt;\/p&gt;\n      &lt;button onClick={getData}&gt;Get Data&lt;\/button&gt;\n      &lt;button onClick={logoutAuth}&gt;Logout&lt;\/button&gt;\n    &lt;\/div&gt;\n  );\n}\n<\/pre>\n<p>In the code above, we fetch data from the <code>\/get-data<\/code> endpoint and display the response alongside the current value of the <code>screen<\/code> state. Additionally, we use the <code>logoutAuth<\/code> context method to log out the current user when the <code>Logout<\/code> button is clicked.<\/p>\n<h3 id=\"creating-login-page\">Creating the login page<\/h3>\n<p>The login component is the final piece of the puzzle. In this component, we\u2019ll obtain the data (username and password) needed to initiate the authentication process.<\/p>\n<p>Create the <code>Login<\/code> component by adding a <code>Login.jsx<\/code> file within the <code>components<\/code> folder and populating it with the following code:<\/p>\n<pre class=\"language-javascript hljs\">import React, { useState } from \"react\";\nimport { useAuth } from \"..\/src\/AuthProvider\";\n\nexport default function () {\n  const [input, setInput] = useState({\n    username: \"\",\n    password: \"\",\n  });\n  const auth = useAuth();\n  const handleSubmit = (e) =&gt; {\n    e.preventDefault();\n    if (input.username !== \"\" &amp;&amp; input.password !== \"\") {\n      auth.loginAuth(input);\n      return;\n    }\n    alert(\"please provide a valid input\");\n  };\n  return (\n\n    &lt;form onSubmit={handleSubmit}&gt;\n      &lt;label&gt;Username: &lt;\/label&gt;\n      &lt;br \/&gt;\n      &lt;input\n        type=\"text\"\n        onChange={(e) =&gt; setInput({ ...input, username: e.target.value })}\n      \/&gt;\n      &lt;br \/&gt;\n      &lt;label&gt;Password: &lt;\/label&gt;\n      &lt;br \/&gt;\n      &lt;input\n        type=\"password\"\n        onChange={(e) =&gt; setInput({ ...input, password: e.target.value })}\n      \/&gt;\n      &lt;br \/&gt;\n      &lt;button type=\"submit\"&gt;Login&lt;\/button&gt;\n    &lt;\/form&gt;\n  );\n}\n<\/pre>\n<p>The key aspect of this code is the <code>handleSubmit<\/code> function, which transfers the <code>data<\/code> argument (containing the username and password) to the <code>loginAuth<\/code> context method for authentication.<\/p>\n<p>Now, if you start your Express server and React\u2019s development server, your application should work as expected, with the added security of protected routes:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-189140\" src=\"https:\/\/blog.logrocket.com\/wp-content\/uploads\/2022\/11\/demo-login-page.gif\" alt=\"Demo Login Page\" width=\"895\" height=\"441\"><\/p>\n<h2 id=\"integrating-form-validation\">Integrating form validation<\/h2>\n<p>Form validation is essential when handling authentication; it not only helps the users provide the right input, but it also protects your application from malicious users who may want to inject malicious scripts into your backend.<\/p>\n<p>When validating a form for authentication purposes, you are required to implement validation on the client and the server. The client\u2019s validation will check if all the required fields are filled and the user\u2019s input, while the server validation will check if the credentials provided match an existing account.<\/p>\n<h3 id=\"client-validation\">Client validation<\/h3>\n<p>HTML5 provides a powerful yet straightforward <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Learn\/Forms\/Form_validation\">native approach to validating form inputs<\/a>. It offers built-in attributes, such as <code>min<\/code> and <code>max<\/code>, <code>required<\/code>, <code>minLength<\/code>, <code>maxLength<\/code>, <code>type<\/code>, and <code>pattern<\/code>, which enable complex validations using regular expressions:<\/p>\n<pre class=\"language-javascript hljs\">form onSubmit={handleSubmit}&gt;\n      &lt;label&gt;Username: &lt;\/label&gt;\n      &lt;input\n        required\n        type=\"text\"\n        minLength={4}\n        ...\n      \/&gt;\n      &lt;br \/&gt;\n      &lt;label&gt;Password: &lt;\/label&gt;\n      &lt;input\n        required\n        type=\"password\"\n        minLength={3}\n        ...\n      \/&gt;\n      &lt;br \/&gt;\n      &lt;button type=\"submit\"&gt;Login&lt;\/button&gt;\n    &lt;\/form&gt;\n<\/pre>\n<p>These attributes will provide the required validation for our application. However, in cases where we want to take full control of the validation and render customized error messages or implement more complex validation, we can opt for one of many <a href=\"https:\/\/blog.logrocket.com\/comparing-schema-validation-libraries-zod-vs-yup\/\">JavaScript validation libraries<\/a>, such as Yup, ValidatorJS, Formik, etc.<\/p>\n<p>These libraries offer sophisticated client-side validation in a simplified manner, or we can simply use a similar function as follows:<\/p>\n<pre class=\"language-javascript hljs\">  const validateForm = (input) =&gt; {\n    let errors = {username: \"\", password: \"\"};\n    if (input.username.length &lt; 5) {\n      errors.username = \"username is too short\";\n    }\n    if (input.password.length &lt; 3) {\n      errors.password = \"Password is too short\";\n    }\n    return errors;\n  };\n<\/pre>\n<p>We can update the <code>handleSubmit<\/code> function in our login component to work with the function above like this:<\/p>\n<pre class=\"language-javascript hljs\">  const handleSubmit = (e) =&gt; {\n    e.preventDefault();\n    const error = validateForm(input);\n    if (error.username === \"\" &amp;&amp; error.password === \"\") {\n      auth.loginAuth(input);\n    }\n  };\n<\/pre>\n<h3 id=\"server-validation\">Server validation<\/h3>\n<p>Server-side validation comes in two forms: validating payloads (post data) and validating if the payload corresponds with existing data.<\/p>\n<p>Payloads can be validated using a similar method as we used during client validation, i.e., creating a function that checks the payload and sends a response if successful. However, several JavaScript libraries are more efficient and simplify payload validation, including Joi, Hapi, and Yup.<\/p>\n<p>When validating using existing data, we can send a response back to the client if the validation fails. We can then display customized error messages based on that response. In our case, we\u2019ve already implemented this validation using the following code snippet within the <code>\/authenticate<\/code> route in our Express server:<\/p>\n<pre class=\"language-javascript hljs\">app.get(\"\/api\/authenticate\", auth, (req, res) =&gt; {\n  const options = {\n    httpOnly: true,\n    signed: true,\n  };\n  if (req.auth.user === \"admin\") {\n    res.cookie(\"name\", \"admin\", options).send({ screen: \"admin\" });\n  } else if (req.auth.user === \"user\") {\n    res.cookie(\"name\", \"user\", options).send({ screen: \"user\" });\n  }\n});\n<\/pre>\n<p>In a real-world application where the user\u2019s credentials are stored in a database, you\u2019d query the database and compare the payload with the existing data in the database.<\/p>\n<h2 id=\"role-based-access-control\">Role-based access control<\/h2>\n<p>Role-based access control is an authentication approach that restricts data access based on user roles. A classic example is a dashboard with an admin who has complete control over the dashboard and a user who has limited access.<\/p>\n<p>Implementing role-based access control features involves creating a check on the server or client that verifies a user\u2019s role after authentication and routes them to the appropriate page or route.<\/p>\n<p>Luckily for us, we\u2019ve implemented this into our Express server with the following code:<\/p>\n<pre class=\"language-javascript hljs\">app.get(\"\/api\/authenticate\", auth, (req, res) =&gt; {\n  ...\n  if (req.auth.user === \"admin\") {\n    res.cookie(\"name\", \"admin\", options).send({ screen: \"admin\" });\n  } else if (req.auth.user === \"user\") {\n    res.cookie(\"name\", \"user\", options).send({ screen: \"user\" });\n  }\n});\n<\/pre>\n<p>The <code>if<\/code> statement in the code snippet above checks if the authentication request\u2019s username is either <code>admin<\/code> or <code>user<\/code>, and then sends back a <code>screen<\/code> response with either <code>\"admin\"<\/code> or <code>\"user\"<\/code> string values.<\/p>\n<p>We can then use the protected route we created earlier to render the appropriate components\/page based on the logged-in user. However, in our case, the endpoint that sends the data, <code>\/read-data<\/code>, checks the role of the currently logged-in user before sending the appropriate data:<\/p>\n<pre class=\"language-javascript hljs\">app.get(\"\/api\/get-data\", (req, res) =&gt; {\n  if (req.signedCookies.name === \"admin\") {\n    res.send(\"This is admin panel\");\n  } else if (req.signedCookies.name === \"user\") {\n    res.send(\"This is user data\");\n  } else {\n    res.end();\n  }\n});\n<\/pre>\n<p>Therefore, we don\u2019t have to do much work in the <code>protectedRoute<\/code> component. We only need to verify if the current screen is not <code>auth<\/code>. If so, the <code>\/view<\/code> route is rendered, where the appropriate data can be fetched:<\/p>\n<pre class=\"language-javascript hljs\">const ProtectedRoute = () =&gt; {\n  const auth = useAuth();\n  if (auth.screen == \"auth\") return &lt;Navigate to=\"\/login\" \/&gt;;\n  return &lt;Outlet \/&gt;;\n};\n<\/pre>\n<p>The following shows the data returned based on the currently logged-in user\u2019s role:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-189141\" src=\"https:\/\/blog.logrocket.com\/wp-content\/uploads\/2022\/11\/role-based-login.gif\" alt=\"Role-Based Login\" width=\"895\" height=\"439\"><\/p>\n<p>You can find the complete code for this project in this GitHub repository.<\/p>\n<h2 id=\"connecting-mongodb\">Connecting to MongoDB<\/h2>\n<p>To connect a Node.js application to a <a href=\"https:\/\/www.mongodb.com\/atlas\/database\">MongoDB Atlas database<\/a>, you will need to perform the following steps.<\/p>\n<p>Install the MongoDB driver for Node.js, which will allow you to connect and interact with a MongoDB database from a Node.js application. You can do this by running <code>npm install mongodb<\/code> in your terminal.<\/p>\n<p>Create a <a href=\"https:\/\/account.mongodb.com\/account\/register\">MongoDB Atlas account<\/a>. This will give you a URL that you can use to connect to your database. After successfully creating an account, click the <strong>Build a Database<\/strong> button on your dashboard.<\/p>\n<p>Select the free <strong>Shared<\/strong> option. Then, create a Cluster by clicking the <strong>Create Cluster<\/strong> button at the bottom of the page. You can edit and make changes to your Cluster on this page. For now, simply set a username and password for your Cluster. Then, scroll down to the bottom of the page, click <strong>Connect to My Local Environment,<\/strong> and add your IP address.<\/p>\n<p>If you followed the above steps correctly, you should be able to see the page shown in the image below:<\/p>\n<p><img decoding=\"async\" class=\"aligncenter\" src=\"https:\/\/blog.logrocket.com\/wp-content\/uploads\/2021\/05\/deploy-react-database-mongodb.png\" alt=\"Connecting to MongoDB Step Five\"><\/p>\n<p>From there, click the <strong>Menu<\/strong> button highlighted in red, then click <strong>Load Sample Dataset<\/strong>. MongoDB provides us with a mock dataset that we can quickly use to test the connection from our application to the DB.<\/p>\n<p>Next, select the <strong>Connect<\/strong> button on the far left. You should see a pop-up modal. Select the <strong>Connect Your Application<\/strong> option. This will help us connect to our application using one of MongoDB\u2019s Native Drivers, and in our case, it\u2019s the <a href=\"https:\/\/mongodb.github.io\/node-mongodb-native\/\">Node Native Driver<\/a>.<\/p>\n<h3>Using the MongoDB Driver<\/h3>\n<p>Copy your connection string from the next pop-up modal that appears on the screen. We\u2019ll add this to our <code>server.js<\/code> file.<\/p>\n<p>In your Node.js application, use the MongoDB Driver to connect to your database using the URL provided by MongoDB Atlas. You can do this using the following code:<\/p>\n<pre class=\"language-javascript hljs\">const MongoClient = require('mongodb').MongoClient;\nconst url = 'mongodb+srv:\/\/&lt;username&gt;:&lt;password&gt;@cluster0.mongodb.net\/test?retryWrites=true&amp;w=majority';\n\nMongoClient.connect(url, { useNewUrlParser: true, useUnifiedTopology: true }, (err, client) =&gt; {\n  if (err) {\n    console.log(err);\n  } else {\n    console.log('Connected to MongoDB Atlas');\n    \/\/ do something with your database here\n  }\n});\n<\/pre>\n<p>In this code, you will need to replace <code>&lt;username&gt;<\/code> and <code>&lt;password&gt;<\/code> with your MongoDB Atlas username and password.<\/p>\n<p>Once your Node.js application is connected to your MongoDB Atlas database, you can use the MongoDB Driver to perform <code>CRUD<\/code> operations on your database. For example, you can use the <code>insertOne()<\/code> method to insert a document into a collection or the <code>find()<\/code> method to retrieve documents from a collection.<\/p>\n<h2 id=\"key-takeaways\">Key takeaways<\/h2>\n<p>Let\u2019s review the most important steps of securing a React app with login authentication.<\/p>\n<p>We have three different state variables: <code>screen<\/code>, <code>username<\/code>, <code>password<\/code>. <code>username<\/code> and <code>password<\/code> are for storing the input field data and sending it to the server over the <code>\/authenticate<\/code> endpoint through the <code>auth<\/code> function. Therefore, the <a href=\"https:\/\/blog.logrocket.com\/a-guide-to-react-onclick-event-handlers-d411943b14dd\/\"><code>onClick<\/code> event<\/a> of the login button calls the <code>auth<\/code> function. This is only required if the user is authenticating initially.<\/p>\n<p>To check whether the user has already logged in, we use the <code>\/read-cookie<\/code> endpoint in the <code>readCookie<\/code> context method. This function is only called when the user is authenticated. The response from this endpoint sets the <code>screen<\/code> state to change the view to the <code>admin<\/code> or <code>user<\/code> screen.<\/p>\n<p>In this React authentication example, both the <code>admin<\/code> and <code>user<\/code> screens are the same component. However, because the response from the server changes depending on the authentication, the same component renders different contents.<\/p>\n<p>Additionally, the <code>\/get-data<\/code> endpoint demonstrates another use case for cookie-specific responses from the server.<\/p>\n<p>Lastly, <code>\/clear-cookie<\/code> is used with the <code>onClick<\/code> event of the logout button to clear the cookie and set the <code>screen<\/code> state variable back to its initial state.<\/p>\n<h2>Conclusion<\/h2>\n<p>This tutorial aims to give you a foundational understanding of basic server-side React authentication on an Express.js server with the express-basic-auth npm package. The potential use cases for such a simple authentication system range from small personal projects to a secured page for an interface with a fixed number of users.<\/p>\n<p>The need to protect data behind a secure login mechanism is nearly universal. If you know how to implement secure login authentication in React and Express.js, you can achieve the same in virtually any other type of client-side application.<\/p>\n<p>If you\u2019re interested in how to further your web application security in Node.js, check out our guide to <a href=\"https:\/\/blog.logrocket.com\/password-hashing-node-js-bcrypt\/\">password hashing in Node.js with bcrypt<\/a>.<\/p>\n<\/html>\n","protected":false},"excerpt":{"rendered":"<p>Learn how to secure your React app by implementing basic server-side login authentication for an Express.js server.<\/p>\n","protected":false},"author":156415253,"featured_media":149096,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2147999,1],"tags":[96530],"class_list":["post-3495","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dev","category-uncategorized","tag-react"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.1.1 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Adding login authentication to secure React apps - LogRocket Blog<\/title>\n<meta name=\"description\" content=\"Explore the benefits of basic server-side login authentication, and role-based access control, and implement them in your React app.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/blog.logrocket.com\/adding-login-authentication-secure-react-apps\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Adding login authentication to secure React apps - LogRocket Blog\" \/>\n<meta property=\"og:description\" content=\"Explore the benefits of basic server-side login authentication, and role-based access control, and implement them in your React app.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/blog.logrocket.com\/adding-login-authentication-secure-react-apps\/\" \/>\n<meta property=\"og:site_name\" content=\"LogRocket Blog\" \/>\n<meta property=\"article:published_time\" content=\"2024-04-05T16:59:24+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2024-06-04T20:47:19+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/blog.logrocket.com\/wp-content\/uploads\/2021\/05\/adding-login-authentication-secure-react-apps-nocdn.png\" \/>\n\t<meta property=\"og:image:width\" content=\"730\" \/>\n\t<meta property=\"og:image:height\" content=\"487\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Do\u011facan Bilgili\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Do\u011facan Bilgili\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"17 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/blog.logrocket.com\/adding-login-authentication-secure-react-apps\/\",\"url\":\"https:\/\/blog.logrocket.com\/adding-login-authentication-secure-react-apps\/\",\"name\":\"Adding login authentication to secure React apps - LogRocket Blog\",\"isPartOf\":{\"@id\":\"https:\/\/blog.logrocket.com\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/blog.logrocket.com\/adding-login-authentication-secure-react-apps\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/blog.logrocket.com\/adding-login-authentication-secure-react-apps\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/blog.logrocket.com\/wp-content\/uploads\/2021\/05\/adding-login-authentication-secure-react-apps-nocdn.png\",\"datePublished\":\"2024-04-05T16:59:24+00:00\",\"dateModified\":\"2024-06-04T20:47:19+00:00\",\"author\":{\"@id\":\"https:\/\/blog.logrocket.com\/#\/schema\/person\/4d43ca5998b9a5466ed1b5ddf9fd1d7e\"},\"description\":\"Explore the benefits of basic server-side login authentication, and role-based access control, and implement them in your React app.\",\"breadcrumb\":{\"@id\":\"https:\/\/blog.logrocket.com\/adding-login-authentication-secure-react-apps\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/blog.logrocket.com\/adding-login-authentication-secure-react-apps\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/blog.logrocket.com\/adding-login-authentication-secure-react-apps\/#primaryimage\",\"url\":\"https:\/\/blog.logrocket.com\/wp-content\/uploads\/2021\/05\/adding-login-authentication-secure-react-apps-nocdn.png\",\"contentUrl\":\"https:\/\/blog.logrocket.com\/wp-content\/uploads\/2021\/05\/adding-login-authentication-secure-react-apps-nocdn.png\",\"width\":730,\"height\":487,\"caption\":\"Adding Login Authentication to Secure React Apps\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/blog.logrocket.com\/adding-login-authentication-secure-react-apps\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/blog.logrocket.com\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Adding login authentication to secure React apps\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/blog.logrocket.com\/#website\",\"url\":\"https:\/\/blog.logrocket.com\/\",\"name\":\"LogRocket Blog\",\"description\":\"Resources to Help Product Teams Ship Amazing Digital Experiences\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/blog.logrocket.com\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/blog.logrocket.com\/#\/schema\/person\/4d43ca5998b9a5466ed1b5ddf9fd1d7e\",\"name\":\"Do\u011facan Bilgili\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/blog.logrocket.com\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/3be01d09a21d02d556598020ed4ada97145a1dead1edecc084045501cadccde1?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/3be01d09a21d02d556598020ed4ada97145a1dead1edecc084045501cadccde1?s=96&d=mm&r=g\",\"caption\":\"Do\u011facan Bilgili\"},\"description\":\"A software developer who is also into 3D-modeling and animation.\",\"url\":\"https:\/\/blog.logrocket.com\/author\/dbilgili\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Adding login authentication to secure React apps - LogRocket Blog","description":"Explore the benefits of basic server-side login authentication, and role-based access control, and implement them in your React app.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/blog.logrocket.com\/adding-login-authentication-secure-react-apps\/","og_locale":"en_US","og_type":"article","og_title":"Adding login authentication to secure React apps - LogRocket Blog","og_description":"Explore the benefits of basic server-side login authentication, and role-based access control, and implement them in your React app.","og_url":"https:\/\/blog.logrocket.com\/adding-login-authentication-secure-react-apps\/","og_site_name":"LogRocket Blog","article_published_time":"2024-04-05T16:59:24+00:00","article_modified_time":"2024-06-04T20:47:19+00:00","og_image":[{"width":730,"height":487,"url":"https:\/\/blog.logrocket.com\/wp-content\/uploads\/2021\/05\/adding-login-authentication-secure-react-apps-nocdn.png","type":"image\/png"}],"author":"Do\u011facan Bilgili","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Do\u011facan Bilgili","Est. reading time":"17 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/blog.logrocket.com\/adding-login-authentication-secure-react-apps\/","url":"https:\/\/blog.logrocket.com\/adding-login-authentication-secure-react-apps\/","name":"Adding login authentication to secure React apps - LogRocket Blog","isPartOf":{"@id":"https:\/\/blog.logrocket.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/blog.logrocket.com\/adding-login-authentication-secure-react-apps\/#primaryimage"},"image":{"@id":"https:\/\/blog.logrocket.com\/adding-login-authentication-secure-react-apps\/#primaryimage"},"thumbnailUrl":"https:\/\/blog.logrocket.com\/wp-content\/uploads\/2021\/05\/adding-login-authentication-secure-react-apps-nocdn.png","datePublished":"2024-04-05T16:59:24+00:00","dateModified":"2024-06-04T20:47:19+00:00","author":{"@id":"https:\/\/blog.logrocket.com\/#\/schema\/person\/4d43ca5998b9a5466ed1b5ddf9fd1d7e"},"description":"Explore the benefits of basic server-side login authentication, and role-based access control, and implement them in your React app.","breadcrumb":{"@id":"https:\/\/blog.logrocket.com\/adding-login-authentication-secure-react-apps\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/blog.logrocket.com\/adding-login-authentication-secure-react-apps\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/blog.logrocket.com\/adding-login-authentication-secure-react-apps\/#primaryimage","url":"https:\/\/blog.logrocket.com\/wp-content\/uploads\/2021\/05\/adding-login-authentication-secure-react-apps-nocdn.png","contentUrl":"https:\/\/blog.logrocket.com\/wp-content\/uploads\/2021\/05\/adding-login-authentication-secure-react-apps-nocdn.png","width":730,"height":487,"caption":"Adding Login Authentication to Secure React Apps"},{"@type":"BreadcrumbList","@id":"https:\/\/blog.logrocket.com\/adding-login-authentication-secure-react-apps\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/blog.logrocket.com\/"},{"@type":"ListItem","position":2,"name":"Adding login authentication to secure React apps"}]},{"@type":"WebSite","@id":"https:\/\/blog.logrocket.com\/#website","url":"https:\/\/blog.logrocket.com\/","name":"LogRocket Blog","description":"Resources to Help Product Teams Ship Amazing Digital Experiences","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/blog.logrocket.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/blog.logrocket.com\/#\/schema\/person\/4d43ca5998b9a5466ed1b5ddf9fd1d7e","name":"Do\u011facan Bilgili","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/blog.logrocket.com\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/3be01d09a21d02d556598020ed4ada97145a1dead1edecc084045501cadccde1?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/3be01d09a21d02d556598020ed4ada97145a1dead1edecc084045501cadccde1?s=96&d=mm&r=g","caption":"Do\u011facan Bilgili"},"description":"A software developer who is also into 3D-modeling and animation.","url":"https:\/\/blog.logrocket.com\/author\/dbilgili\/"}]}},"yoast_description":"Explore the benefits of basic server-side login authentication, and role-based access control, and implement them in your React app.","_links":{"self":[{"href":"https:\/\/blog.logrocket.com\/wp-json\/wp\/v2\/posts\/3495","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.logrocket.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.logrocket.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.logrocket.com\/wp-json\/wp\/v2\/users\/156415253"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.logrocket.com\/wp-json\/wp\/v2\/comments?post=3495"}],"version-history":[{"count":29,"href":"https:\/\/blog.logrocket.com\/wp-json\/wp\/v2\/posts\/3495\/revisions"}],"predecessor-version":[{"id":189139,"href":"https:\/\/blog.logrocket.com\/wp-json\/wp\/v2\/posts\/3495\/revisions\/189139"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.logrocket.com\/wp-json\/wp\/v2\/media\/149096"}],"wp:attachment":[{"href":"https:\/\/blog.logrocket.com\/wp-json\/wp\/v2\/media?parent=3495"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.logrocket.com\/wp-json\/wp\/v2\/categories?post=3495"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.logrocket.com\/wp-json\/wp\/v2\/tags?post=3495"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}