ReactJS User Authentication & Authorization: Best Practices | Zenesys Blogs

REACTJS USER AUTHENTICATION AND AUTHORIZATION: BEST PRACTICES

4 min Dec 06, 2021 Manish Kumar WEB Views : 5977

The more our personal and business data flows online, the more concerned people are about internet security. Users desire application integration without having to provide user login information all the time. They also desire security without being aware that it is present. 

Authentication is concerned with identifying an entity (i.e user, server, or device). A web application may request a username and password when identifying a user. If a cloud service wants to locate a server, it may request the IP address. Finally, devices may be recognised by their unique MAC addresses. 

Similarly, authorisation permits one party to get access to another (or resource). A home key is a good example of how permission varies from authentication. A door lock is unconcerned about who is using the key to enter the residence. It merely recognises that persons in possession of the home key have permission to enter. In this article, we’ll be discussing how to curate a ReactJS application while implementing  API authorization and authentication. 

 

Methods of Putting API Authorization & Authentication In React js

ReactJS is a Javascript front-end framework for creating user interfaces. Furthermore, the framework is frequently used to create SPAs (single page applications). Single-page apps are loaded into the browser. This indicates that none of the information in the SPA is secure. 

As a result, React apps that use the SPA paradigm cannot store API keys on the front end for permitted access. This limits the application's ability to authorise itself for third-party API requests. This, however, does not prevent it from authenticating. 

Creating a React App that Utilizes Authentication and Api Authorization

In this article, we will show the distinctions between authentication and permission in a React project. We can do this by emulating the authentication principles of a React app and building an OAuth application via Github that allows our application to retrieve Github user data over the Github API. 

The Next.js framework will be used to construct the application. We’re choosing Next.js because it offers the following features: 

●    A simple page-based routing scheme (with support for dynamic routes). 
●    On a per-page basis, pre-rendering, static generation (SSG), and server-side rendering (SSR) are available.
●    Splitting code automatically for quicker page loading. 
●    Routing on the client-side with efficient prefetching. 
●    CSS and Sass support is built-in, as is support for any CSS-in-JS library.
●    API routes are supported in the development environment with Fast Refresh to construct API endpoints with Serverless Functions.
●    It is completely extensible.

The most essential aspect of Next.js is that it "generates HTML for each page in advance, rather than having it all done by client-side JavaScript." Pre-rendering can improve speed and SEO.” Furthermore, Next.js allows us to construct API routes within the folder of our application project. 

This is ideal for backend programming or code that deals with sensitive data. Next.js comes with a slew of capabilities out of the box and is suggested by ReactJS.org as one of the technologies to utilise when developing a react project. We've got a lot to cover in this application, so let's get started!

 

REQUIREMENTS FOR CURATING A REACTJS APP WITH AUTHENTICATION AND API AUTHORIZATION 

●    NodeJS is already installed locally (10.13 or later). 
●    Understanding of how to utilize a command-line program. 
●    Download Git for Windows and use the Git BASH program that comes with it if you're on Windows.
●    Knowledge of NodeJS and React is required.
●    Code editor with internet connectivity (Sublime, VSCode). 
●    Account on Github. 

Create the Next JS Project

Navigate to the location where you want to create the app in a terminal program (BASH, Git BASH, Terminal, Powershell, etc.). The command cd may be used at the terminal to do this. Run the command npx create-next-app in the terminal. When prompted, give the project a name (for example, react-authentication).

import Head from 'next/head'
import styles from '../styles/Home.module.css'
export default function Account({ query }) {
 React.useEffect(() => {
  // Call the Github API route to fetch user data
 }, [])
 return (
   <div className={styles.container}>
     <Head>
       <title>Account</title>
       <link rel="icon" href="/favicon.ico" />
     </Head>
     {/* Add logout button */}
     <main className={styles.main}>
       <h1>Authenticated Account Page</h1>
       <section className={styles.data}>
             <h2>Basic User Information</h2>
             <small>Since we know it's you.. here's your information!</small>
             {/* Display user information */}
       </section>
       <section className={styles.data}>
         <h2>Github OAuth</h2>
         <small>Authorize this application to acces your Github information.</small>
         {/* Add Github component */}
       </section>
     </main>
     <footer className={styles.footer}>
       <a
         href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
         target="_blank"
         rel="noopener noreferrer"
       >
         Powered by{' '}
         <img src="/vercel.svg" alt="Vercel Logo" className={styles.logo} />
       </a>
     </footer>
   </div>
 )
}


Individual pages are produced from the Javascript scripts in the pages directory. In this directory, create a file called account.js and paste the following code into it. In the preceding file, we generated a page with the same styles and structure as index.js. Let's fire up the development server and examine what we've got so far.

Run npm run dev at the root of your project directory (react-authentication). This will launch a development server at http://localhost:3000. In your browser, navigate to the webpage. You should see the Next.js project's default beginning page. Change the URL in the browser to http://localhost:3000/account. The page that we generated in pages should now be visible.

 

API ROUTES ARE STORED UNDER THE PAGES DIRECTORY 

The names of the API routes that our client application may call at /api/[route name] are stored in the api folder under the pages directory. The sample API route provided by the starting project is http://localhost:3000/api/hello. If you go to that URL, you'll see:

'name': "John Doe"

If we look at the pages/api/hello file, we can see that this is precisely what this method should return if an HTTP request is received.

Delete the file pages/api/hello and replace them with four new files in the pages/api folder:

auth.js
logout.js
user.js
github.js

Insert the following code into each of the new files.

const allowedMethods = []
export default (req, res) => {
   res.setHeader('Allow', allowedMethods)
   if (!allowedMethods.includes(req.method)) {
       return res.status(405).end()
   }
   return res.status(200).end()


CSS files in the styles directory might be global or modular. In the Home.module.css file, we may define CSS classes and then import that file as an object into a component file. Then, on the imported styles object, we can access the defined class names.

The globals.css file is used to style all elements such as li>, body>, and so forth.

Insert the following styles into styles/Home.module.css. Do not remove any of the existing styles from the file.

....
.input {
 display: block;
 margin: 5px;
 border-radius: 5px;
 border: 1px solid #CCC;
 font-size: 1.2em;
 line-height: 1.7em;
 -webkit-border-radius: 5px;
 -moz-border-radius: 5px;
 -ms-border-radius: 5px;
 -o-border-radius: 5px;
}
.button {
 font-size: 1.2em;
 color: white;
 border: none;
 padding: .4rem .7rem;
 background: #0070f3;
 border-radius: 2px;
 margin-right: auto;
 -webkit-border-radius: 2px;
 -moz-border-radius: 2px;
 -ms-border-radius: 2px;
 -o-border-radius: 2px;
}
.formGroup {
 margin: 10px;
}
.data {
 border: 1px solid #CCC;
 margin: 1rem auto;
 padding: 1rem;
 border-radius: 5px;
 -webkit-border-radius: 5px;
 -moz-border-radius: 5px;
 -ms-border-radius: 5px;
 -o-border-radius: 5px;
}
.github {
 margin: 1rem auto;
}
.github > a {
 color: blue;
}

....

You now have an understanding of how a Next.js project should be structured. Taking this into consideration,  we will now proceed with adding a login component, user data, and user authentication as well.

 

IMPLEMENT USER AUTHENTICATION

To begin, add a form to the index.js file that will take a user's email and password. Replace all of the code within the main> HTML tag with the following:
...
      <h1 className={styles.title}>
         React API Authorization
       </h1>
       <div className={styles.grid}>
         <div className={styles.card}>
           <h3>Login &rarr;</h3>
           <form>
             <div className={styles.formGroup}>
               <label htmlFor="email">Email</label>
               <input
                 onChange={(e) => setUsername(e.target.value)}
                 className={styles.input}
                 autoComplete='on'
                 type="email"
                 id="email"
                 name="email" />
             </div>
             <div className={styles.formGroup}>
               <label htmlFor="password">Password</label>
               <input
                 onChange={(e) => setPassword(e.target.value)}
                 className={styles.input}
                 type="password"
                 id="password"
                 name="password" />
             </div>
             <button onClick={(e) => login(e)} className={styles.button}>Login</button>
           </form>
           <p>
             Authenticate as a user via the mock server
             </p>
         </div>
       </div>

The preceding code generates a form that accepts two React state variables (set up using React Hooks). The submit button causes a functioning login to be executed. Next, we must define this function as well as our state variables.

Add; above the return statement in index.js.

...
 const router = useRouter();
 let [username, setUsername] = React.useState('')
 let [password, setPassword] = React.useState('')
 const login = async (e) => {
   e.preventDefault()
   try {
     await api.post('/api/auth', { username, password })
     router.push('/account')
   } catch (e) {
     setPassword('')
     console.log(e)
   }
 }

...

In index.js, add; after the return statement.

import React from 'react'
import api from '../api'
import { useRouter } from 'next/router'

...

Because we haven't yet built the api module, saving the file will result in an error. But, before we do so, let's have a look at the component code. 

Next's useRouter hook was imported. This allows us to manage the user's navigation while also utilising Next's routing functionalities. Then we supplied our React state hooks for username and password. 

Finally, we developed an asynchronous function that invokes the /api/auth API route (sending the user credentials). If the request to that route is successful, the user is routed to the /account page (through the router). If it fails, the password is cleared and an error message is logged to the console.

Saving the file will result in an error because we haven't yet constructed the api module. But first, let's have a look at the component code. The useRouter hook from Next was imported. This allows us to manage the user's navigation while simultaneously taking use of Next's routing capabilities. Then we supplied our React state hooks for username and password.

 

MAKE AN API FILE

An Axios instance is contained in the api module, which is a distinct file. Create the api.js file in the project's root directory. In that file, paste the following code:

import axios from 'axios'
const api = axios.create({
   baseURL: 'http://localhost:3000',
   headers: {
       'Accept': 'application/json',
       'Content-Type': 'application/json'
   }
});

export default api;

Save the document once done. We're almost done with the auth.js file, but we need to use npm to import Axios into our project. Return to your command prompt and type npm install axios in the project folder. It's possible that the development server will need to be restarted. 

After you have saved all of the new file changes, your home page should appear like this. We will make a file to simulate a database with user data. Create the file data.js in the project root (same directory as the api.js file) and add the following code.

export default [
   {
       id: 1,
       name: 'Jarrett',
       email: 'jarrett@app.org',
       password: 'react-authentication123',
   }
]


Of course, you can change the user object's contents or manually add more user objects.

 

You May Also Like: 5 Best Front End Frameworks to Build High-Quality Websites or Apps


When a user presses the Login button, the login function is called, which calls the /api/auth route. This path must include the following features:

●    Verify that the request method is permitted.
●    Check to see if the user already exists.
●    Verify that the password is accurate.
●    Set an authorization cookie and return the HTTP status code 200 if the credentials are found.

 

AUTHENTICATION THROUGH COOKIES

Add 'POST' to the allowed Methods array in /api/auth. Import data.js with the line import data from '../../data' at the top of the code. Then, from the request, extract the credentials and look for a user. Underneath the if statement, add the following code to check for authorised HTTP methods.
...
 const {
   body
 } = req
  
 const {
   username,
   password
 } = body
// Validate credentials middleware
 const user = data.find(user => user.email === username)
...


Insert the following IF statements to verify whether the user exists and if the password is correct. In case either fails, we return a 404 Not Found status code.

...
 if (!user) {
   return res.status(404).end()
 }
  if (!user.password !== 'password') {
   return res.status(404).end()
 }
...


Finally, we set a cookie on the res object if the credentials match. The cookie module is a simple way to serialise cookies. Run npm install cookie in the project root to add this module to the project. Add this code underneath the if block containing the password check and import cookie at the top of the file.

...
 res.setHeader('Set-Cookie', cookie.serialize('authorization', user.name, {
   httpOnly: true, // Javascript can't access value
   secure: process.env.NODE_ENV === 'development' ? false : true, // Only use HTTPS
   sameSite: 'strict', // Only send cookie to this site
   maxAge: 600, // In seconds
   path: '/' // Should be set to avoid problems later
 }));
...


This cookie is only delivered to our website and cannot be accessed (hijacked) by malicious Javascript. It has an expiration date, and may be used only with HTTPS. The value of this cookie is usually a session ID that is stored in the database or controlled by a session library/database. 

Alternatively, a JWT can be used. Set important information as the cookie value if it is not encrypted. Regardless, we will make an exception for this example and set the user's name as the value. This makes it easier to locate the user in later functions. You may now sign into the application using the credentials saved in data.js. You will be routed to the account page if the sign-in was successful. 

 

MAKING A LOGOUT FUNCTION

Let's add a logout mechanism to the account page that sets a new cookie and redirects the user back to the home page after the authorisation cookie expires. Import the cookie module at the top of the /api/logout file. cookie from the 'cookie' folder. Then, add 'GET' as one of the authorised methods. 

Once done, before the return statement, insert this set cookie code.

...
 res.setHeader('Set-Cookie', cookie.serialize('authorization', '', {
   httpOnly: true,
   secure: process.env.NODE_ENV === 'development' ? false : true,
   sameSite: 'strict',
   maxAge: new Date(),
   path: '/'
 }));
...


Now, add the logout function to pages/account,

..
 const logout = () => {
   api.get('/api/logout').then(() => {
     router.push('/')
   })
 }
...


below the Head component, add a button that calls the method.
...
     <button className={styles.button} style={{ background: 'red', margin: 'none' }} onClick={() => logout()}>&larr; Logout</button>
...


Similarly, below the Head component, add a button that executes the function.

...
 const router = useRouter();

Bottom Line

In this article, not only do we see how to implement authentication and authorization in ReactJS, but we also understand the various requirements needed for curating a ReactJS application along with API Authorization. 

We have also shed some light on how to implement user authentication and how you can curate an API file accordingly. Since knowing authentication through cookies and a logout function is crucial, we have discussed these as well.