Build a Secure Login Page with React

In the ever-evolving world of web development, creating a secure and user-friendly login page is a fundamental skill. Recently, I embarked on a project to develop a login page for user authentication, and along the way, I learned about protected routes in React Router, used the Navigate component for redirecting users, and even managed to solve a few problems on Codeforces. I’ll walk you through my journey, sharing insights, code snippets, and lessons learned.

Building a Login Page for User Authentication

The first step in my project was to create a login page that would allow users to authenticate themselves before accessing protected areas of the application. Here’s how I approached it:

Setting Up the Login Page

The login page needed to be simple yet functional. I used React to build the UI, with a form that collects the user’s email and password. Here’s a snippet of the code:

import React, { useState } from 'react';

const LoginPage = () => {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');

  const handleSubmit = (e) => {
    e.preventDefault();
    // Add authentication logic here
    console.log('Email:', email, 'Password:', password);
  };

  return (
    <div className="login-container">
      <h2>Login</h2>
      <form onSubmit={handleSubmit}>
        <div>
          <label>Email:</label>
          <input
            type="email"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
            required
          />
        </div>
        <div>
          <label>Password:</label>
          <input
            type="password"
            value={password}
            onChange={(e) => setPassword(e.target.value)}
            required
          />
        </div>
        <button type="submit">Login</button>
      </form>
    </div>
  );
};

export default LoginPage;

This code sets up a basic login form with state management using React’s useState hook. When the form is submitted, the handleSubmit function is triggered, which (for now) simply logs the email and password to the console.

Implementing User Authentication

To handle user authentication, I integrated a mock authentication system. In a real-world scenario, this would involve communicating with a backend server to verify user credentials. For simplicity, I used a hardcoded check:

const authenticateUser = (email, password) => {
  // Mock authentication
  const validEmail = 'user@example.com';
  const validPassword = 'password123';
  return email === validEmail && password === validPassword;
};

In the handleSubmit function, I added logic to check if the user’s credentials are valid:

const handleSubmit = (e) => {
  e.preventDefault();
  if (authenticateUser(email, password)) {
    // Redirect to dashboard or protected route
    console.log('Authentication successful!');
  } else {
    alert('Invalid email or password');
  }
};

Learning About Protected Routes in React Router

Once the login page was functional, I needed to ensure that only authenticated users could access certain parts of the application. This is where protected routes come into play.

Creating a Protected Route Component

I created a ProtectedRoute component that checks if the user is authenticated. If not, it redirects them to the login page. Here’s how I implemented it:

import { Navigate, Outlet } from 'react-router-dom';

const ProtectedRoute = ({ isAuthenticated }) => {
  return isAuthenticated ? <Outlet /> : <Navigate to="/login" />;
};

export default ProtectedRoute;
  • Outlet is used to render the child components of the protected route.
  • Navigate redirects the user to the login page if they are not authenticated.

Integrating Protected Routes

I then wrapped the protected routes in my application with the ProtectedRoute component:

import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import LoginPage from './LoginPage';
import Dashboard from './Dashboard';
import ProtectedRoute from './ProtectedRoute';

const App = () => {
  const isAuthenticated = false; // This would come from your auth state

  return (
    <Router>
      <Routes>
        <Route path="/login" element={<LoginPage />} />
        <Route element={<ProtectedRoute isAuthenticated={isAuthenticated} />}>
          <Route path="/dashboard" element={<Dashboard />} />
        </Route>
      </Routes>
    </Router>
  );
};

export default App;

This setup ensures that the /dashboard route is only accessible to authenticated users.

Using the Navigate Component for Redirecting Users

The Navigate component from React Router is incredibly useful for programmatically redirecting users. In my project, I used it in two scenarios:

  1. Redirecting unauthenticated users to the login page.
  2. Redirecting authenticated users to the dashboard after a successful login.

Here’s how I implemented the second scenario:

import { useState } from 'react';
import { useNavigate } from 'react-router-dom';

const LoginPage = () => {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const navigate = useNavigate();

  const handleSubmit = (e) => {
    e.preventDefault();
    if (authenticateUser(email, password)) {
      navigate('/dashboard'); // Redirect to dashboard
    } else {
      alert('Invalid email or password');
    }
  };

  return (
    // Login form JSX
  );
};

export default LoginPage;

The useNavigate hook provides a function that allows you to navigate to a different route programmatically.

Final Thoughts

This project was a great learning experience. I not only built a functional login page but also gained a deeper understanding of protected routes and navigation in React Router. Additionally, solving problems on Codeforces reminded me of the importance of continuous learning and practice.

Related blog posts