Session Got Sent and Again Went Missing React

The author selected Artistic Commons to receive a donation as part of the Write for DOnations program.

Introduction

Many spider web applications are a mix of public and individual pages. Public pages are available to anyone, while a private folio requires a user login. You can utilize authentication to manage which users have access to which pages. Your React application will need to handle situations where a user tries to access a private folio before they are logged in, and you volition need to relieve the login data once they have successfully authenticated.

In this tutorial, you'll create a React awarding using a token-based authentication system. You'll create a mock API that will return a user token, build a login folio that will fetch the token, and check for authentication without rerouting a user. If a user is not authenticated, you'll provide an opportunity for them to log in and and so allow them to continue without navigating to a dedicated login page. As you build the application, you'll explore different methods for storing tokens and will acquire the security and experience merchandise-offs for each approach. This tutorial volition focus on storing tokens in localStorage and sessionStorage.

Past the stop of this tutorial, you'll exist able to add authentication to a React awarding and integrate the login and token storage strategies into a consummate user workflow.

Prerequisites

  • You will need a evolution surroundings running Node.js; this tutorial was tested on Node.js version 10.22.0 and npm version 6.14.6. To install this on macOS or Ubuntu 18.04, follow the steps in How to Install Node.js and Create a Local Development Environment on macOS or the Installing Using a PPA section of How To Install Node.js on Ubuntu 18.04.

  • A React development environment set with Create React App, with the non-essential boilerplate removed. To gear up this upwardly, follow Pace one — Creating an Empty Projection of the How To Manage State on React Grade Components tutorial. This tutorial will use auth-tutorial every bit the projection proper name.

  • You will exist fetching information from APIs using React. Yous can larn almost working with APIs in How To Telephone call Web APIs with the useEffect Claw in React.

  • You will also need a bones knowledge of JavaScript, HTML, and CSS, which y'all can observe in our How To Build a Website With HTML series, How To Style HTML with CSS, and in How To Code in JavaScript.

Step 1 — Building a Login Page

In this step, you'll create a login page for your application. You'll start past installing React Router and creating components to correspond a full application. Then you'll render the login page on any route and so that your users can login to the awarding without existence redirected to a new folio.

By the stop of this footstep, you lot'll have a basic application that will render a login page when a user is not logged into the awarding.

To begin, install react router with npm. There are two different versions: a web version and a native version for use with React Native. Install the spider web version:

                      
  1. npm install react-router-dom

The package will install and you'll receive a message when the installation is complete. Your message may vary slightly:

                      

Output

... + react-router-dom@v.2.0 added eleven packages from 6 contributors, removed 10 packages and audited 1945 packages in 12.794s ...

Next, create ii components called Dashboard and Preferences to act as private pages. These will represent components that a user should not see until they have successfully logged into the awarding.

First, create the directories:

                      
  1. mkdir src/components/Dashboard
  2. mkdir src/components/Preferences

Then open up Dashboard.js in a text editor. This tutorial will employ nano:

                      
  1. nano src/components/Dashboard/Dashboard.js

Within of Dashboard.js, add an <h2> tag with the content of Dashboard:

auth-tutorial/src/components/Dashboard/Dashboard.js

                      import            React            from            'react'            ;            export            default            function            Dashboard            (            )            {            render            (            <h2>Dashboard<            /h2>            )            ;            }                  

Save and close the file.

Repeat the aforementioned steps for Preferences. Open up the component:

                      
  1. nano src/components/Preferences/Preferences.js

Add the content:

auth-tutorial/src/components/Preferences/Preferences.js

                      import            React            from            'react'            ;            export            default            function            Preferences            (            )            {            render            (            <h2>Preferences<            /h2>            )            ;            }                  

Save and close the file.

Now that you lot have some components, y'all demand to import the components and create routes inside of App.js. Bank check out the tutorial How To Handle Routing in React Apps with React Router for a total introduction to routing in React applications.

To begin, open App.js:

                      
  1. nano src/components/App/App.js

Then import Dashboard and Preferences by calculation the following highlighted code:

auth-tutorial/src/components/App/App.js

                      import            React            from            'react'            ;            import            './App.css'            ;                          import              Dashboard              from              '../Dashboard/Dashboard'              ;                                      import              Preferences              from              '../Preferences/Preferences'              ;                        function            App            (            )            {            return            (            <            >            <            /            >            )            ;            }            consign            default            App;                  

Adjacent, import BrowserRouter, Switch, and Route from react-router-dom:

auth-tutorial/src/components/App/App.js

                      import            React            from            'react'            ;            import            './App.css'            ;                          import              {              BrowserRouter,              Route,              Switch              }              from              'react-router-dom'              ;                        import            Dashboard            from            '../Dashboard/Dashboard'            ;            import            Preferences            from            '../Preferences/Preferences'            ;            function            App            (            )            {            return            (            <            >            <            /            >            )            ;            }            export            default            App;                  

Add a surrounding <div> with a className of wrapper and an <h1> tag to serve as a template for the application. Be certain that yous are importing App.css then that yous can employ the styles.

Next, create routes for the Dashboard and Preferences components. Add BrowserRouter, then add a Switch component as a child. Inside of the Switch, add a Route with a path for each component:

tutorial/src/components/App/App.js

                      import            React            from            'react'            ;            import            './App.css'            ;            import            {            BrowserRouter,            Road,            Switch            }            from            'react-router-dom'            ;            import            Dashboard            from            '../Dashboard/Dashboard'            ;            import            Preferences            from            '../Preferences/Preferences'            ;            office            App            (            )            {            return                          (                                      <div className=              "wrapper"              >                                      <h1>Application<              /h1>                                      <BrowserRouter>                                      <Switch>                                      <Route path=              "/dashboard"              >                                      <Dashboard              /              >                                      <              /Route>                                      <Route path=              "/preferences"              >                                      <Preferences              /              >                                      <              /Route>                                      <              /Switch>                                      <              /BrowserRouter>                                      <              /div>                                      )              ;                        }            consign            default            App;                  

Salve and close the file.

The last step is to add some padding to the main <div> and then your component is non directly at the edge of the browser. To practice this, you lot will change the CSS.

Open App.css:

                      
  1. nano src/components/App/App.css

Replace the contents with a class of .wrapper with padding of 20px:

auth-tutorial/src/components/App/App.css

                      .wrapper            {            padding            :            20px;            }                  

Relieve and close the file. When you practice, the browser will reload and you'll observe your basic components:

Basic component

Check each of the routes. If you lot visit http://localhost:3000/dashboard, you'll find the dashboard page:

Dashboard Component

Your routes are working as expected, but in that location is a slight problem. The route /dashboard should exist a protected folio and should not be viewable past an unauthenticated user. In that location are dissimilar ways to handle a private folio. For instance, yous can create a new route for a login folio and utilise React Router to redirect if the user is not logged in. This is a fine approach, but the user would lose their route and accept to navigate back to the folio they originally wanted to view.

A less intrusive option is to generate the login page regardless of the route. With this arroyo, y'all'll return a login folio if there is non a stored user token and when the user logs in, they'll be on the aforementioned road that they initially visited. That means if a user visits /dashboard, they will notwithstanding be on the /dashboard route after login.

To begin, make a new directory for the Login component:

                      
  1. mkdir src/components/Login

Next, open Login.js in a text editor:

                      
  1. nano src/components/Login/Login.js

Create a basic form with a submit <button> and an <input> for the username and the countersign. Be certain to set the input type for the countersign to password:

auth-tutorial/src/components/Login/Login.js

                      import            React            from            'react'            ;            consign            default            office            Login            (            )            {            return            (            <grade>            <characterization>            <p>Username<            /p>            <input type=            "text"            /            >            <            /label>            <label>            <p>Password<            /p>            <input blazon=            "password"            /            >            <            /label>            <div>            <button type=            "submit"            >Submit<            /push>            <            /div>            <            /class>            )            }                  

For more than on forms in React, check out the tutorial How To Build Forms in React.

Adjacent, add an <h1> tag asking the user to log in. Wrap the <class> and the <h1> in a <div> with a className of login-wrapper. Finally, import Login.css:

auth-tutorial/src/components/Login/Login.js

                      import            React            from            'react'            ;                          import              './Login.css'              ;                        export            default            function            Login            (            )            {            render            (                          <div className=              "login-wrapper"              >                                      <h1>Delight Log In<              /h1>                        <form>            <label>            <p>Username<            /p>            <input type=            "text"            /            >            <            /label>            <label>            <p>Password<            /p>            <input blazon=            "password"            /            >            <            /label>            <div>            <button type=            "submit"            >Submit<            /button>            <            /div>            <            /course>                          <              /div>                        )            }                  

Save and shut the file.

Now that you take a basic Login component, yous'll need to add some styling. Open Login.css:

                      
  1. nano src/components/Login/Login.css

Middle the component on the page past adding a display of flex, and then setting the flex-direction to cavalcade to marshal the elements vertically and adding align-items to middle to make the component centered in the browser:

auth-tutorial/src/components/Login/Login.css

                      .login-wrapper            {            display            :            flex;            flex-direction            :            column;            align-items            :            eye;            }                  

For more information on using Flexbox, see our CSS Flexbox Cheatsheet

Salvage and close the file.

Finally, you lot'll need to return it within of App.js if in that location is no user token. Open App.js:

                      
  1. nano src/components/App/App.js

In Footstep 3, you'll explore options for storing the token. For now, you tin can store the token in memory using the useState Hook.

Import useState from react, then phone call useState and set return values to token and setToken:

auth-tutorial/src/components/App/App.js

                      import            React              ,              {              useState              }                        from            'react'            ;            import            {            BrowserRouter,            Route,            Switch            }            from            'react-router-dom'            ;            import            './App.css'            ;            import            Dashboard            from            '../Dashboard/Dashboard'            ;            import            Preferences            from            '../Preferences/Preferences'            ;            function            App            (            )            {                          const              [token,              setToken]              =              useState              (              )              ;                        render            (            <div className=            "wrapper"            >            <h1>Awarding<            /h1>            <BrowserRouter>            <Switch>            <Route path=            "/dashboard"            >            <Dashboard            /            >            <            /Road>            <Route path=            "/preferences"            >            <Preferences            /            >            <            /Road>            <            /Switch>            <            /BrowserRouter>            <            /div>            )            ;            }            export            default            App;                  

Import the Login component. Add together a conditional statement to display Login if the token is falsy.

Pass the setToken function to the Login component:

auth-tutorial/src/components/App/App.js

                      import            React,            {            useState            }            from            'react'            ;            import            {            BrowserRouter,            Route,            Switch            }            from            'react-router-dom'            ;            import            './App.css'            ;            import            Dashboard            from            '../Dashboard/Dashboard'            ;                          import              Login              from              '../Login/Login'              ;                        import            Preferences            from            '../Preferences/Preferences'            ;            function            App            (            )            {            const            [token,            setToken]            =            useState            (            )            ;                          if              (              !token)              {                                      return              <Login setToken=              {setToken}              /              >                                      }                        render            (            <div className=            "wrapper"            >            <h1>Application<            /h1>            <BrowserRouter>            <Switch>            <Route path=            "/dashboard"            >            <Dashboard            /            >            <            /Route>            <Route path=            "/preferences"            >            <Preferences            /            >            <            /Route>            <            /Switch>            <            /BrowserRouter>            <            /div>            )            ;            }            consign            default            App;                  

For at present, in that location is no token; in the next step, you'll telephone call an API and ready the token with the return value.

Salvage and close the file. When yous exercise, the browser will reload and you'll encounter the login page. Notice that if you visit http://localhost:3000/dashboard, you'll nonetheless notice the login page since the token has not yet been set up:

Login page

In this step, y'all created an application with individual components and a login component that will display until you ready a token. You too configured routes to brandish the pages and added a check to display the Login component on every route if the user is not however logged into the application.

In the next pace, you'll create a local API that volition return a user token. Yous'll call the API from the Login component and save the token to memory on success.

Footstep ii — Creating a Token API

In this stride, you'll create a local API to fetch a user token. You'll build a mock API using Node.js that will return a user token. You'll then call that API from your login page and return the component after you successfully call up the token. By the terminate of this step, y'all'll take an awarding with a working login page and protected pages that will only be accessible after login.

You are going to need a server to act every bit a backend that will return the token. You can create a server quickly using Node.js and the Express web framework. For a detailed introduction to creating an Limited server, see the tutorial Basic Express Server in Node.js.

To start, install express. Since the server is not a requirement of the final build, exist sure to install equally a devDependency.

Y'all'll also need to install cors. This library volition enable cross origin resources sharing for all routes.

Warning: Do non enable CORS for all routes in a production application. This can lead to security vulnerabilities.

                      
  1. npm install --salvage-dev express cors

When the installation is complete, you lot'll receive a success message:

                      

Output

... + cors@2.8.five + express@4.17.i removed 10 packages, updated 2 packages and audited 2059 packages in 12.597s ...

Next, open a new file called server.js in the root of your application. Practice not add this file to the /src directory since you do non want it to be function of the final build.

                      
  1. nano server.js

Import limited, then initialize a new app past calling limited() and saving the result to a variable called app:

auth-tutorial/server.js

                      const            express            =            crave            (            'express'            )            ;            const            app            =            express            (            )            ;                  

After creating the app, add cors as a middleware. Starting time, import cors, then add it to the application by calling the use method on app:

auth-tutorial/server.js

                      const            express            =            require            (            'express'            )            ;                          const              cors              =              crave              (              'cors'              )              ;                        const            app            =            express            (            )            ;            app.              utilise              (              cors              (              )              )              ;                              

Next, listen to a specific road with app.use. The first argument is the path the application volition listen to and the second statement is a callback function that volition run when the awarding serves the path. The callback takes a req statement, which contains the request data and a res statement that handles the consequence.

Add together in a handler for the /login path. Call res.transport with a JavaScript object containing a token:

auth-tutorial/server.js

                      const            limited            =            require            (            'express'            )            ;            const            cors            =            crave            (            'cors'            )            const            app            =            express            (            )            ;            app.            employ            (            cors            (            )            )            ;            app.              use              (              '/login'              ,              (              req,                res              )              =>              {                        res.              send              (              {                                      token              :              'test123'                                      }              )              ;                                      }              )              ;                              

Finally, run the server on port 8080 using app.heed:

auth-tutorial/server.js

                      const            express            =            require            (            'express'            )            ;            const            cors            =            crave            (            'cors'            )            const            app            =            limited            (            )            ;            app.            use            (            cors            (            )            )            ;            app.            employ            (            '/login'            ,            (            req,              res            )            =>            {            res.            send            (            {            token            :            'test123'            }            )            ;            }            )            ;            app.              listen              (              8080              ,              (              )              =>              console.              log              (              'API is running on http://localhost:8080/login'              )              )              ;                              

Save and close the file. In a new terminal window or tab, get-go the server:

                      
  1. node server.js

You volition receive a response indicating that the server is starting:

                      

Output

API is running on http://localhost:8080/login

Visit http://localhost:8080/login and you lot'll observe your JSON object.

Token response

When y'all fetch the token in your browser, you are making a GET asking, merely when yous submit the login form y'all will be making a Post request. That'south not a problem. When you set up your route with app.utilise, Express volition handle all requests the same. In a product awarding, you should be more specific and just allow certain request methods for each route.

At present that y'all accept a running API server, you need to make a request from your login page. Open Login.js:

                      
  1. nano src/components/Login/Login.js

In the previous step, you passed a new prop called setToken to the Login component. Add in the PropType from the new prop and destructure the props object to pull out the setToken prop.

auth-tutorial/src/components/Login/Login.js

                      import            React            from            'react'            ;                          import              PropTypes              from              'prop-types'              ;                        import            './Login.css'            ;            export            default            role            Login            (                                          {                setToken                }                                      )            {            render            (            <div className=            "login-wrapper"            >            <h1>Please Log In<            /h1>            <form>            <label>            <p>Username<            /p>            <input blazon=            "text"            /            >            <            /label>            <label>            <p>Password<            /p>            <input type=            "password"            /            >            <            /label>            <div>            <push button type=            "submit"            >Submit<            /button>            <            /div>            <            /class>            <            /div>            )            }            Login.propTypes              =              {                                      setToken              :              PropTypes.func.isRequired                          }                              

Next, create a local state to capture the Username and Password. Since you do not need to manually set data, make the <inputs> uncontrolled components. You tin find detailed information about uncontrolled components in How To Build Forms in React.

auth-tutorial/src/components/Login/Login.js

                      import            React              ,              {              useState              }                        from            'react'            ;            import            PropTypes            from            'prop-types'            ;            import            './Login.css'            ;            export            default            function            Login            (                          {              setToken              }                        )            {                          const              [username,              setUserName]              =              useState              (              )              ;                                      const              [password,              setPassword]              =              useState              (              )              ;                        return            (            <div className=            "login-wrapper"            >            <h1>Please Log In<            /h1>            <form>            <characterization>            <p>Username<            /p>            <input type=            "text"            onChange=              {              e              =>              setUserName              (e.target.value)              }                        /            >            <            /label>            <label>            <p>Password<            /p>            <input blazon=            "password"            onChange=              {              e              =>              setPassword              (e.target.value)              }                        /            >            <            /characterization>            <div>            <push button blazon=            "submit"            >Submit<            /push>            <            /div>            <            /form>            <            /div>            )            }            Login.propTypes            =            {            setToken            :            PropTypes.func.isRequired            }            ;                  

Next, create a part to make a POST asking to the server. In a large application, you would add together these to a separate directory. In this example, you'll add together the service straight to the component. Check out the tutorial How To Call Web APIs with the useEffect Hook in React for a detailed expect at calling APIs in React components.

Create an async function called loginUser. The function will take credentials every bit an argument, then it will call the fetch method using the Mail option:

auth-tutorial/src/components/Login/Login.js

                      import            React,            {            useState            }            from            'react'            ;            import            PropTypes            from            'prop-types'            ;            import            './Login.css'            ;                          async              function              loginUser              (              credentials              )              {                                      return              fetch              (              'http://localhost:8080/login'              ,              {                                      method              :              'POST'              ,                                      headers              :              {                                      'Content-Type'              :              'application/json'                                      }              ,                                      body              :              JSON              .              stringify              (credentials)                                      }              )                                      .              then              (              information              =>              information.              json              (              )              )                                      }                        export            default            role            Login            (                          {              setToken              }                        )            {            ...                  

Finally, create a form submit handler called handleSubmit that will call loginUser with the username and countersign. Call setToken with a successful event. Call handleSubmit using the onSubmit event handler on the <form>:

auth-tutorial/src/components/Login/Login.js

                      import            React,            {            useState            }            from            'react'            ;            import            PropTypes            from            'prop-types'            ;            import            './Login.css'            ;            async            function            loginUser            (            credentials            )            {            render            fetch            (            'http://localhost:8080/login'            ,            {            method            :            'POST'            ,            headers            :            {            'Content-Blazon'            :            'application/json'            }            ,            trunk            :            JSON            .            stringify            (credentials)            }            )            .            and then            (            data            =>            information.            json            (            )            )            }            consign            default            role            Login            (                          {              setToken              }                        )            {            const            [username,            setUserName]            =            useState            (            )            ;            const            [password,            setPassword]            =            useState            (            )            ;                          const              handleSubmit              =              async              eastward              =>              {                        e.              preventDefault              (              )              ;                                      const              token              =              await              loginUser              (              {                        username,                        password                          }              )              ;                                      setToken              (token)              ;                                      }                        return            (            <div className=            "login-wrapper"            >            <h1>Please Log In<            /h1>            <course            onSubmit=              {handleSubmit}                        >            <label>            <p>Username<            /p>            <input type=            "text"            onChange=            {            eastward            =>            setUserName            (e.target.value)            }            /            >            <            /label>            <label>            <p>Password<            /p>            <input type=            "countersign"            onChange=            {            e            =>            setPassword            (e.target.value)            }            /            >            <            /label>            <div>            <button type=            "submit"            >Submit<            /push button>            <            /div>            <            /class>            <            /div>            )            }            Login.propTypes            =            {            setToken            :            PropTypes.func.isRequired            }            ;                  

Salvage and close the file. Make sure that your local API is withal running, then open up a browser to http://localhost:3000/dashboard.

Yous volition see the login page instead of the dashboard. Fill out and submit the grade and you lot will receive a web token then redirect to the page for the dashboard.

Login page

You lot now have a working local API and an application that requests a token using a username and password. But there is notwithstanding a problem. The token is currently stored using a local state, which means that information technology is stored in JavaScript retentivity. If y'all open a new window, tab, or even just refresh the folio, you will lose the token and the user will need to login once more. This volition exist addressed in the next pace.

In this step y'all created a local API and a login page for your application. You learned how to create a Node server to send a token and how to phone call the server and shop the token from a login component. In the next footstep, y'all'll larn how to shop the user token and then that a session will persist across folio refreshes or tabs.

Step 3 — Storing a User Token with sessionStorage and localStorage

In this step, you'll shop the user token. You lot'll implement different token storage options and larn the security implications of each approach. Finally, you lot'll learn how different approaches will modify the user feel as the user opens new tabs or closes a session.

By the cease of this step, you lot'll be able to choose a storage arroyo based on the goals for your application.

There are several options for storing tokens. Every option has costs and benefits. In cursory the options are: storing in JavaScript memory, storing in sessionStorage, storing in localStorage, and storing in a cookie. The principal trade-off is security. Whatever information that is stored exterior of the retention of the current application is vulnerable to Cross-Site Scripting (XSS) attacks. The danger is that if a malicious user is able to load code into your application, it can access localStorage, sessionStorage, and whatever cookie that is besides accessible to your application. The benefit of the non-memory storage methods is that you tin reduce the number of times a user will need to log in to create a better user experience.

This tutorial will cover sessionStorage and localStorage, since these are more modern than using cookies.

Session Storage

To test the benefits of storing outside of memory, catechumen the in-retentiveness storage to sessionStorage. Open App.js:

                      
  1. nano src/components/App/App.js

Remove the telephone call to useState and create two new functions called setToken and getToken. Then call getToken and assign the results to a variable called token:

auth-tutorial/src/components/App/App.js

                      import            React            from            'react'            ;            import            {            BrowserRouter,            Route,            Switch            }            from            'react-router-dom'            ;            import            './App.css'            ;            import            Dashboard            from            '../Dashboard/Dashboard'            ;            import            Login            from            '../Login/Login'            ;            import            Preferences            from            '../Preferences/Preferences'            ;                          function              setToken              (              userToken              )              {                                      }                                      office              getToken              (              )              {                                      }                        function            App            (            )            {                          const              token              =              getToken              (              )              ;                        if            (            !token)            {            return            <Login setToken=            {setToken}            /            >            }            return            (            <div className=            "wrapper"            >            ...            <            /div>            )            ;            }            export            default            App;                  

Since yous are using the same function and variable names, you lot volition non demand to change any code in the Login component or the remainder of the App component.

Inside of setToken, salvage the userToken statement to sessionStorage using the setItem method. This method takes a fundamental as a starting time statement and a cord as the second argument. That means you'll need to convert the userToken from an object to a string using the JSON.stringify function. Call setItem with a cardinal of token and the converted object.

auth-tutorial/src/components/App/App.js

                      import            React            from            'react'            ;            import            {            BrowserRouter,            Route,            Switch            }            from            'react-router-dom'            ;            import            './App.css'            ;            import            Dashboard            from            '../Dashboard/Dashboard'            ;            import            Login            from            '../Login/Login'            ;            import            Preferences            from            '../Preferences/Preferences'            ;            function            setToken            (            userToken            )            {            sessionStorage.              setItem              (              'token'              ,              JSON              .              stringify              (userToken)              )              ;                        }            function            getToken            (            )            {            }            function            App            (            )            {            const            token            =            getToken            (            )            ;            if            (            !token)            {            return            <Login setToken=            {setToken}            /            >            }            return            (            <div className=            "wrapper"            >            ...            <            /div>            )            ;            }            export            default            App;                  

Relieve the file. When you do the browser will reload. If you type in a username and countersign and submit, the browser will still render the login page, but if you look within your browser panel tools, you'll find the token is stored in sessionStorage. This epitome is from Firefox, merely y'all'll discover the aforementioned results in Chrome or other modern browsers.

Token in sessionStorage

Now y'all need to retrieve the token to render the correct page. Within the getToken function, call sessionStorage.getItem. This method takes a key every bit an argument and returns the string value. Catechumen the cord to an object using JSON.parse, so render the value of token:

auth-tutorial/src/components/App/App.js

                      import            React            from            'react'            ;            import            {            BrowserRouter,            Route,            Switch            }            from            'react-router-dom'            ;            import            './App.css'            ;            import            Dashboard            from            '../Dashboard/Dashboard'            ;            import            Login            from            '../Login/Login'            ;            import            Preferences            from            '../Preferences/Preferences'            ;            function            setToken            (            userToken            )            {            sessionStorage.            setItem            (            'token'            ,            JSON            .            stringify            (userToken)            )            ;            }            part            getToken            (            )            {                          const              tokenString              =              sessionStorage.              getItem              (              'token'              )              ;                                      const              userToken              =              JSON              .              parse              (tokenString)              ;                                      return              userToken?.token            }            function            App            (            )            {            const            token            =            getToken            (            )            ;            if            (            !token)            {            return            <Login setToken=            {setToken}            /            >            }            return            (            <div className=            "wrapper"            >            ...            <            /div>            )            ;            }            export            default            App;                  

You need to utilise the optional chaining operator—?.—when accessing the token property considering when you lot commencement access the application, the value of sessionStorage.getItem('token') will be undefined. If you endeavor to admission a property, you will generate an mistake.

Save and close the file. In this case, you already have a token stored, so when the browser refreshes, you volition navigate to the individual pages:

Dashboard

Clear out the token by either deleting the token in the Storage tab in your developer tools or past typing sessionStorage.clear() in your developer console.

At that place's a little problem at present. When you lot log in, the browser saves the token, just y'all still meet the login page.

Token stored still not logged in

The problem is your code never alerts React that the token retrieval was successful. You'll notwithstanding need to set some state that will trigger a re-render when the data changes. Similar about problems in React, there are multiple ways to solve it. One of the most elegant and reusable is to create a custom Hook.

Creating a Custom Token Hook

A custom Hook is a part that wraps custom logic. A custom Hook commonly wraps one or more born React Hooks along with custom implementations. The master advantage of a custom Hook is that yous can remove the implementation logic from the component and you lot can reuse it across multiple components.

By convention, custom Hooks start with the keyword use*.

Open a new file in the App directory called useToken.js:

                      
  1. nano src/components/App/useToken.js

This will be a minor Hook and would be fine if you defined it direct in App.js. Merely moving the custom Hook to a unlike file will show how Hooks work outside of a component. If you start to reuse this Hook beyond multiple components, yous might as well want to move it to a separate directory.

Inside useToken.js, import useState from react. Notice that you do not need to import React since you will have no JSX in the file. Create and consign a function chosen useToken. Inside this function, use the useState Hook to create a token state and a setToken role:

auth-tutorial/src/components/App/useToken.js

                      import            {            useState            }            from            'react'            ;            export            default            office            useToken            (            )            {            const            [token,            setToken]            =            useState            (            )            ;            }                  

Next, copy the getToken office to useHook and convert it to an pointer role, since you placed it inside useToken. You could get out the role as a standard, named function, but information technology can be easier to read when top-level functions are standard and internal functions are arrow functions. However, each squad volition be different. Choose one way and stick with it.

Place getToken earlier the state proclamation, then initialize useState with getToken. This volition fetch the token and ready it equally the initial state:

auth-tutorial/src/components/App/useToken.js

                      import            {            useState            }            from            'react'            ;            export            default            office            useToken            (            )            {                          const              getToken              =              (              )              =>              {                                      const              tokenString              =              sessionStorage.              getItem              (              'token'              )              ;                                      const              userToken              =              JSON              .              parse              (tokenString)              ;                                      return              userToken?.token                          }              ;                        const            [token,            setToken]            =            useState            (                          getToken              (              )                        )            ;            }                  

Next, copy the setToken function from App.js. Catechumen to an arrow function and name the new function saveToken. In addition to saving the token to sessionStorage, save the token to state by calling setToken:

auth-tutorial/src/components/App/useToken.js

                      import            {            useState            }            from            'react'            ;            export            default            office            useToken            (            )            {            const            getToken            =            (            )            =>            {            const            tokenString            =            sessionStorage.            getItem            (            'token'            )            ;            const            userToken            =            JSON            .            parse            (tokenString)            ;            return            userToken?.token            }            ;            const            [token,            setToken]            =            useState            (            getToken            (            )            )            ;                          const              saveToken              =              userToken              =>              {                        sessionStorage.              setItem              (              'token'              ,              JSON              .              stringify              (userToken)              )              ;                                      setToken              (userToken.token)              ;                                      }              ;                        }                  

Finally, render an object that contains the token and saveToken set to the setToken property name. This will give the component the same interface. Y'all can too return the values as an array, only an object will requite users a chance to destructure only the values they desire if you reuse this in another component.

auth-tutorial/src/components/App/useToken.js

                      import            {            useState            }            from            'react'            ;            export            default            function            useToken            (            )            {            const            getToken            =            (            )            =>            {            const            tokenString            =            sessionStorage.            getItem            (            'token'            )            ;            const            userToken            =            JSON            .            parse            (tokenString)            ;            return            userToken?.token            }            ;            const            [token,            setToken]            =            useState            (            getToken            (            )            )            ;            const            saveToken            =            userToken            =>            {            sessionStorage.            setItem            (            'token'            ,            JSON            .            stringify            (userToken)            )            ;            setToken            (userToken.token)            ;            }            ;                          return              {                                      setToken              :              saveToken,                        token                          }                        }                  

Save and close the file.

Next, open App.js:

                      
  1. nano src/components/App/App.js

Remove the getToken and setToken functions. Then import useToken and call the part destructuring the setToken and token values. You can besides remove the import of useState since you are no longer using the Hook:

auth-tutorial/src/components/App/App.js

                      import            React            from            'react'            ;            import            {            BrowserRouter,            Road,            Switch            }            from            'react-router-dom'            ;            import            './App.css'            ;            import            Dashboard            from            '../Dashboard/Dashboard'            ;            import            Login            from            '../Login/Login'            ;            import            Preferences            from            '../Preferences/Preferences'            ;                          import              useToken              from              './useToken'              ;                        office            App            (            )            {                          const              {              token,              setToken              }              =              useToken              (              )              ;                        if            (            !token)            {            return            <Login setToken=            {setToken}            /            >            }            render            (            <div className=            "wrapper"            >            <h1>Application<            /h1>            <BrowserRouter>            <Switch>            <Route path=            "/dashboard"            >            <Dashboard            /            >            <            /Route>            <Route path=            "/preferences"            >            <Preferences            /            >            <            /Road>            <            /Switch>            <            /BrowserRouter>            <            /div>            )            ;            }            export            default            App;                  

Save and close the file. When you do, the browser will refresh, and when you log in, you will immediately go to the folio. This is happening because you are calling useState in your custom Hook, which volition trigger a component re-return:

Login immediately

Yous at present have a custom Hook to store your token in sessionStorage. Now you can refresh your page and the user will remain logged in. But if you try to open up the application in another tab, the user will be logged out. sessionStorage belongs only to the specific window session. Any data will not exist bachelor in a new tab and will exist lost when the agile tab is closed. If you lot desire to save the token across tabs, yous'll need to convert to localStorage.

Using localStorage to Save Information Beyond Windows

Unlike sessionStorage, localStorage will salvage data even afterwards the session ends. This can exist more convenient, since information technology lets users open multiple windows and tabs without a new login, but it does have some security problems. If the user shares their estimator, they will remain logged in to the application even though they close the browser. It will exist the user'south responsibility to explicitly log out. The next user would have immediate admission to the application without a login. It's a risk, just the convenience may be worth it for some applications.

To convert to localStorage, open useToken.js:

                      
  1. nano src/components/App/useToken.js

And then modify every reference of sessionStorage to localStorage. The methods you telephone call will exist the same:

auth-tutorial/src/components/App/useToken.js

                      import            {            useState            }            from            'react'            ;            export            default            function            useToken            (            )            {            const            getToken            =            (            )            =>            {            const            tokenString            =            localStorage            .            getItem            (            'token'            )            ;            const            userToken            =            JSON            .            parse            (tokenString)            ;            return            userToken?.token            }            ;            const            [token,            setToken]            =            useState            (            getToken            (            )            )            ;            const            saveToken            =            userToken            =>            {            localStorage            .            setItem            (            'token'            ,            JSON            .            stringify            (userToken)            )            ;            setToken            (userToken.token)            ;            }            ;            return            {            setToken            :            saveToken,            token            }            }                  

Save the file. When you do, the browser volition refresh. Yous will need to log in again since there is no token all the same in localStorage, but after y'all do, you will remain logged in when you lot open a new tab.

Still logged into tab

In this step, y'all saved tokens with sessionStorage and localStorage. You also created a custom Hook to trigger a component re-render and to move component logic to a separate part. You besides learned about how sessionStorage and localStorage affect the user'due south ability to start new sessions without login.

Determination

Authentication is a crucial requirement of many applications. The mixture of security concerns and user experience tin can exist intimidating, but if you focus on validating data and rendering components at the right time, it can become a lightweight procedure.

Each storage solution offers singled-out advantages and disadvantages. Your choice may change as your application evolves. By moving your component logic into an abstract custom Hook, you give yourself the ability to refactor without disrupting existing components.

If you would like to read more React tutorials, bank check out our React Topic folio, or render to the How To Code in React.js serial folio.

gibbscous1961.blogspot.com

Source: https://www.digitalocean.com/community/tutorials/how-to-add-login-authentication-to-react-applications

0 Response to "Session Got Sent and Again Went Missing React"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel