Securing React Apps with JSON Web Tokens (JWT)

Securing React Apps with JSON Web Tokens (JWT)

JavaScript For Kids

$ 5,00

“JavaScript Explorers: Fun and Easy Coding for Kids” is a vibrant and interactive ebook designed to introduce children to the exciting world of JavaScript programming. Through colorful illustrations, step-by-step instructions, and hands-on coding exercises, young learners will embark on a fun-filled journey to discover the fundamentals of JavaScript in a playful and engaging way. With…

In modern web development, security is a critical aspect, especially when it comes to user authentication and data protection. JSON Web Tokens (JWT) offer a robust solution for securing web applications, including those built with React. This article will guide you through the process of securing a React application using JWT for authentication and authorization, with practical examples to illustrate each step.

What is JWT?

JWT is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA.

Why Use JWT?

  1. Stateless Authentication: JWTs are stateless, meaning the server doesn’t need to store session information.
  2. Compact: JWTs are compact and can be sent via URL, POST parameter, or inside an HTTP header.
  3. Secure: JWTs can be signed and encrypted to ensure the authenticity and integrity of the token.

Setting Up a React Application

First, let’s set up a basic React application. If you don’t have a React app yet, create one using Create React App:

npx create-react-app jwt-auth-example
cd jwt-auth-example
npm start

Installing Dependencies

You’ll need some additional libraries to handle JWT authentication:

npm install axios jwt-decode

  • axios is a promise-based HTTP client for making API requests.
  • jwt-decode is a library for decoding JWT tokens.

Creating an Authentication Service

Let’s create a service to handle authentication. Create a new file authService.js in the src directory:

import axios from 'axios';
import jwtDecode from 'jwt-decode';

const API_URL = 'http://localhost:4000/api/auth/';

class AuthService {
  login(username, password) {
    return axios
      .post(API_URL + 'login', { username, password })
      .then(response => {
        if (response.data.accessToken) {
          localStorage.setItem('user', JSON.stringify(response.data));
        }
        return response.data;
      });
  }

  logout() {
    localStorage.removeItem('user');
  }

  register(username, email, password) {
    return axios.post(API_URL + 'register', {
      username,
      email,
      password
    });
  }

  getCurrentUser() {
    return JSON.parse(localStorage.getItem('user'));
  }

  isAuthenticated() {
    const user = this.getCurrentUser();
    if (user && user.accessToken) {
      const decodedToken = jwtDecode(user.accessToken);
      const currentTime = Date.now() / 1000;
      if (decodedToken.exp < currentTime) {
        this.logout();
        return false;
      }
      return true;
    }
    return false;
  }
}

export default new AuthService();

Creating a Login Component

Create a Login component to handle user login:

import React, { useState } from 'react';
import AuthService from './authService';

const Login = (props) => {
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');
  const [message, setMessage] = useState('');

  const handleLogin = (e) => {
    e.preventDefault();

    AuthService.login(username, password).then(
      () => {
        props.history.push('/profile');
        window.location.reload();
      },
      (error) => {
        const resMessage =
          (error.response &&
            error.response.data &&
            error.response.data.message) ||
          error.message ||
          error.toString();

        setMessage(resMessage);
      }
    );
  };

  return (
    <div className="col-md-12">
      <div className="card card-container">
        <form onSubmit={handleLogin}>
          <div className="form-group">
            <label htmlFor="username">Username</label>
            <input
              type="text"
              className="form-control"
              name="username"
              value={username}
              onChange={(e) => setUsername(e.target.value)}
              required
            />
          </div>

          <div className="form-group">
            <label htmlFor="password">Password</label>
            <input
              type="password"
              className="form-control"
              name="password"
              value={password}
              onChange={(e) => setPassword(e.target.value)}
              required
            />
          </div>

          <div className="form-group">
            <button className="btn btn-primary btn-block">Login</button>
          </div>
        </form>

        {message && (
          <div className="form-group">
            <div className="alert alert-danger" role="alert">
              {message}
            </div>
          </div>
        )}
      </div>
    </div>
  );
};

export default Login;

Protecting Routes

To protect routes and ensure only authenticated users can access certain parts of your application, create a PrivateRoute component:

import React from 'react';
import { Route, Redirect } from 'react-router-dom';
import AuthService from './authService';

const PrivateRoute = ({ component: Component, ...rest }) => (
  <Route
    {...rest}
    render={(props) =>
      AuthService.isAuthenticated() ? (
        <Component {...props} />
      ) : (
        <Redirect to="/login" />
      )
    }
  />
);

export default PrivateRoute;

Creating a Profile Component

Create a Profile component to display user information:

import React from 'react';
import AuthService from './authService';

const Profile = () => {
  const currentUser = AuthService.getCurrentUser();

  return (
    <div className="container">
      <header className="jumbotron">
        <h3>
          <strong>{currentUser.username}</strong> Profile
        </h3>
      </header>
      <p>
        <strong>Token:</strong> {currentUser.accessToken.substring(0, 20)} ...{' '}
        {currentUser.accessToken.substr(currentUser.accessToken.length - 20)}
      </p>
      <p>
        <strong>Id:</strong> {currentUser.id}
      </p>
      <p>
        <strong>Email:</strong> {currentUser.email}
      </p>
    </div>
  );
};

export default Profile;

Configuring Routes

Finally, configure your routes to use the PrivateRoute component:

import React from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import Login from './Login';
import Profile from './Profile';
import PrivateRoute from './PrivateRoute';

const App = () => {
  return (
    <Router>
      <div className="container mt-3">
        <Switch>
          <Route exact path={['/', '/login']} component={Login} />
          <PrivateRoute path="/profile" component={Profile} />
        </Switch>
      </div>
    </Router>
  );
};

export default App;

Conclusion

Securing a React application with JWT involves setting up a robust authentication service, protecting sensitive routes, and ensuring secure communication with the backend. By following these steps and utilizing JWT for authentication and authorization, you can significantly enhance the security of your React applications.

Tags

#React #JWT #WebSecurity #Authentication #Authorization #ReactJS #JavaScript #WebDevelopment #FrontendDevelopment #Coding #Programming #TechBlog #SecureCoding #SoftwareDevelopment #API #TokenBasedAuthentication

Leave a Reply