Auth service tutorial in react with example

 

Building an Authentication Service in React

In this tutorial, we’ll learn how to create a simple authentication service in a React application. This service will handle user login, registration, logout, and state management (whether the user is logged in or not). We’ll be using the following tools:

  1. React for the UI.
  2. React Context API for global state management (auth state).
  3. Axios for making API requests.
  4. Local Storage to persist the user's authentication state (to keep the user logged in after page refresh).
  5. React Router for navigating between pages.

Steps Involved:

  1. Setting up the React app.
  2. Creating an AuthContext for global state management.
  3. Implementing login, logout, and register functionality.
  4. Protecting certain routes based on authentication.

1. Setting Up the React App

If you don’t have a React app set up already, create a new React app:

bash
npx create-react-app auth-demo cd auth-demo npm install axios react-router-dom

We’ll be using Axios for API requests and React Router for navigation.

2. Creating the Auth Context

We'll use the Context API to store the user's authentication state globally so that it can be accessed from anywhere in the application.

Creating AuthContext

In the src folder, create a new folder called context and inside it, create a file called AuthContext.js.

javascript
// src/context/AuthContext.js import React, { createContext, useState, useEffect } from "react"; import axios from "axios"; const AuthContext = createContext(); const AuthProvider = ({ children }) => { const [user, setUser] = useState(null); const [loading, setLoading] = useState(true); // Check for authenticated user from local storage useEffect(() => { const storedUser = localStorage.getItem("user"); if (storedUser) { setUser(JSON.parse(storedUser)); } setLoading(false); }, []); const login = async (email, password) => { try { const response = await axios.post("https://example.com/api/login", { email, password }); const { token, user } = response.data; // Store the token and user in local storage localStorage.setItem("token", token); localStorage.setItem("user", JSON.stringify(user)); setUser(user); } catch (error) { console.error("Login error", error); } }; const register = async (email, password) => { try { const response = await axios.post("https://example.com/api/register", { email, password }); const { token, user } = response.data; // Store the token and user in local storage localStorage.setItem("token", token); localStorage.setItem("user", JSON.stringify(user)); setUser(user); } catch (error) { console.error("Register error", error); } }; const logout = () => { localStorage.removeItem("token"); localStorage.removeItem("user"); setUser(null); }; return ( <AuthContext.Provider value={{ user, login, register, logout, loading }}> {children} </AuthContext.Provider> ); }; const useAuth = () => { return React.useContext(AuthContext); }; export { AuthProvider, useAuth };

Explanation:

  • AuthContext: This context stores the user's authentication information (like the user object and login token).
  • AuthProvider: This component provides the authentication state and methods (login, register, logout) to the rest of the app.
  • useAuth: A custom hook to easily access the auth state and functions in any component.

3. Creating the Login and Register Components

Next, let’s create components for Login and Register pages.

Login Component:

javascript
// src/pages/Login.js import React, { useState } from "react"; import { useAuth } from "../context/AuthContext"; import { useNavigate } from "react-router-dom"; const Login = () => { const { login } = useAuth(); const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [error, setError] = useState(""); const navigate = useNavigate(); const handleSubmit = async (e) => { e.preventDefault(); try { await login(email, password); navigate("/dashboard"); // Navigate to the dashboard page } catch (err) { setError("Failed to login"); } }; return ( <div> <h1>Login</h1> <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> {error && <div>{error}</div>} <button type="submit">Login</button> </form> </div> ); }; export default Login;

Register Component:

javascript
// src/pages/Register.js import React, { useState } from "react"; import { useAuth } from "../context/AuthContext"; import { useNavigate } from "react-router-dom"; const Register = () => { const { register } = useAuth(); const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [error, setError] = useState(""); const navigate = useNavigate(); const handleSubmit = async (e) => { e.preventDefault(); try { await register(email, password); navigate("/dashboard"); // Navigate to the dashboard page } catch (err) { setError("Failed to register"); } }; return ( <div> <h1>Register</h1> <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> {error && <div>{error}</div>} <button type="submit">Register</button> </form> </div> ); }; export default Register;

Explanation:

  • Login and Register components manage the form input for the user's email and password.
  • They call login and register functions from the AuthContext to handle authentication.
  • On successful login or registration, they navigate to the /dashboard route.

4. Creating a Protected Route

Now, we’ll create a protected route for the Dashboard page, which should only be accessible if the user is logged in.

PrivateRoute Component:

javascript
// src/components/PrivateRoute.js import React from "react"; import { Navigate } from "react-router-dom"; import { useAuth } from "../context/AuthContext"; const PrivateRoute = ({ children }) => { const { user, loading } = useAuth(); if (loading) { return <div>Loading...</div>; // Show loading state while checking user authentication } if (!user) { return <Navigate to="/login" replace />; // Redirect to login if not authenticated } return children; // Render the protected content if authenticated }; export default PrivateRoute;

Explanation:

  • PrivateRoute: This component checks if the user is authenticated by looking at the user state from the AuthContext.
  • If the user is not logged in, they are redirected to the login page.
  • If the user is logged in, the requested protected page is rendered.

5. Setting Up React Router and Routing

Now we need to set up React Router to handle navigation between different pages, such as Login, Register, and the Dashboard.

App.js:

javascript
// src/App.js import React from "react"; import { BrowserRouter as Router, Route, Routes } from "react-router-dom"; import { AuthProvider } from "./context/AuthContext"; import Login from "./pages/Login"; import Register from "./pages/Register"; import Dashboard from "./pages/Dashboard"; import PrivateRoute from "./components/PrivateRoute"; const App = () => { return ( <AuthProvider> <Router> <Routes> <Route path="/login" element={<Login />} /> <Route path="/register" element={<Register />} /> {/* Protected Route */} <Route path="/dashboard" element={<PrivateRoute><Dashboard /></PrivateRoute>} /> </Routes> </Router> </AuthProvider> ); }; export default App;

Dashboard Component:

Dashboard Component:

javascript
// src/pages/Dashboard.js import React from "react"; import { useAuth } from "../context/AuthContext"; const Dashboard = () => { const { user, logout } = useAuth(); return ( <div> <h1>Welcome, {user?.name}</h1> <button onClick={logout}>Logout</button> </div> ); }; export default Dashboard;

Explanation:

  • In App.js, we define the routes for Login, Register, and Dashboard.
  • We wrap the Dashboard route inside the PrivateRoute component to ensure it is protected and accessible only to logged-in users.
  • The Dashboard component displays the user's name and a logout button.

6. Final Notes

  • LocalStorage: We are using localStorage to persist the user's authentication state. When the user logs in or registers, we store the authentication token and user info in localStorage. This ensures the user remains logged in even after page reloads.
  • API Endpoints: In this example, we've used placeholders like https://example.com/api/login. You should replace them with your actual API endpoints that handle authentication.

This is a simple but functional authentication service using React, Context API, and Axios. You can expand this service by adding features like password reset, email verification, role-based access, and more, depending on your application's needs.

Comments

Popular posts from this blog

PrimeNG tutorial with examples using frequently used classes

Docker and Kubernetes Tutorials and QnA

Building strong foundational knowledge in frontend development topics