A Simple Guide to User Authentication: Sign Up, Log In, and Log Out Flows

Introduction:

User authentication is a critical aspect of any web application, ensuring that only authorized users can access protected resources. In this guide, we'll go into the detailed processes involved in user authentication; covering sign-up, log-in, and log-out flows.

NB: The code sample attached below each flow, shows the basic implementation to that flow using React, ReactHooks for form handling and Axios for handling HTTP requests.

  1. Sign-Up Flow:

    • User Initiation: The sign-up process begins when a user navigates to the sign-up page or initiates the registration process.

    • Form Submission: The user fills out the sign-up form with required information, such as email, password, username, etc.

    • Client-Side Validation: Before submitting the form, client-side validation ensures that the provided information meets certain criteria (e.g., valid email format, strong password).

    • Server-Side Handling: Upon form submission, the client sends a POST request to the server's sign-up endpoint.

    • Server-Side Validation: The server-side endpoint receives the request, validates the user's data (e.g., checks for duplicate emails), and processes the registration if everything is valid.

    • Account Creation: If validation succeeds, the server creates a new user account in the database, typically encrypting the password for security.

    • Response Handling: The server responds to the client with an appropriate status code (e.g., 201 Created) and possibly a success message.

    • Redirect or Notification: Upon successful registration, the user may be redirected to the login page or notified of successful registration.

  •   // SignUp.js
      import React, { useState } from "react";
      import { useForm } from "react-hook-form";
      import Button from "../components/Button";
      import axiosInstance from "../helperFunctions/axios.util";
      import Spinner from "../components/Spinner/Spinner";
      import { toast } from "react-toastify";
    
      const SignUp = () => {
        const [isLoading, setIsLoading] = useState(false);
        const { register, handleSubmit, formState: { errors } } = useForm();
    
        const onSubmit = async (data) => {
          try {
            setIsLoading(true);
            const response = await axiosInstance.post("/user/register", data);
            setIsLoading(false);
    
            toast.success("Sign up successful");
            navigate("/login");
          } catch (error) {
            setIsLoading(false);
            toast.error(error.response.data.message);
          }
        };
    
        return (
          <div>
            <form onSubmit={handleSubmit(onSubmit)}>
              <input type="text" {...register("firstName", { required: "First name is required" })} />
              <input type="text" {...register("lastName", { required: "Last name is required" })} />
              {/* Other form fields */}
              <Button type="submit">{isLoading ? <Spinner /> : "Sign Up"}</Button>
            </form>
          </div>
        );
      };
    
      export default SignUp;
    
  1. Log-In Flow:

    • User Initiation: The log-in process starts when a user navigates to the login page or attempts to access a protected resource without authentication.

    • Credentials Submission: The user enters their credentials (e.g., email and password) into the login form.

    • Client-Side Validation: Similar to sign-up, the client performs validation on the entered credentials to ensure completeness.

    • Server-Side Authentication: Upon form submission, the client sends a POST request to the server's login endpoint.

    • Credential Verification: The server validates the user's credentials, typically by comparing the provided email and password with those stored in the database.

    • Token Generation: If the credentials are valid, the server generates an authentication token (e.g., JWT) and includes it in the response.

    • Token Storage: The client-side application stores the authentication token securely, often in local storage or a cookie, for subsequent authenticated requests.

    • Redirection or Access Grant: After successful authentication, the user is typically redirected to the home page or granted access to the requested resource.

    •   // LogIn.js
        import React, { useState } from "react";
        import { useForm } from "react-hook-form";
        import Button from "../components/Button";
        import axiosInstance from "../helperFunctions/axios.util";
        import Spinner from "../components/Spinner/Spinner";
        import { useNavigate } from "react-router-dom";
        import { toast } from "react-toastify";
      
        const LogIn = () => {
          const [isLoading, setIsLoading] = useState(false);
          const navigate = useNavigate();
          const { register, handleSubmit, formState: { errors } } = useForm();
      
          const onSubmit = async (data) => {
            try {
              setIsLoading(true);
              const response = await axiosInstance.post("/auth/login", data);
              setIsLoading(false);
      
              toast.success("Login successful");
              localStorage.setItem("access_token", response.data.data.accessToken);
              navigate("/");
            } catch (error) {
              setIsLoading(false);
              toast.error(error.response.data.message);
            }
          };
      
          return (
            <div>
              <form onSubmit={handleSubmit(onSubmit)}>
                <input type="email" {...register("email", { required: "Email is required" })} />
                <input type="password" {...register("password", { required: "Password is required" })} />
                <Button type="submit">{isLoading ? <Spinner /> : "Log In"}</Button>
              </form>
            </div>
          );
        };
      
        export default LogIn;
      
  2. Log-Out Flow:

    • User Initiation: Log-out is triggered when the user explicitly clicks a "Log Out" button or link.

    • Token Removal: The client-side application removes the authentication token from local storage or a cookie, effectively logging the user out.

    • Optional Server Interaction: Optionally, the client may send a request to the server to invalidate the token or perform additional cleanup tasks.

    • Redirect or Notification: Upon successful log-out, the user may be redirected to the login page or notified of successful log-out.

    •   // Home.js
        import React from "react";
        import Button from "../components/Button";
        import { useLocation, useNavigate } from "react-router-dom";
        import { toast } from "react-toastify";
      
        const Home = () => {
          const navigate = useNavigate();
          const { state } = useLocation();
          const firstName = state?.firstName;
      
          const handleLogout = () => {
            toast.info("You are now logged out.");
            localStorage.removeItem("access_token");
            navigate("/login");
          };
      
          return (
            <div>
              <p>{firstName ? `Welcome ${firstName}` : "Welcome"}</p>
              <Button onClick={handleLogout}>Log Out</Button>
            </div>
          );
        };
      
        export default Home;
      

Concepts Used in Code Samples:

  1. useState: This is a React Hook used to manage state variables in functional components.

  2. useForm: Another React Hook from the react-hook-form library, used for managing form state and validation.

  3. axiosInstance.post: This is a function call using the Axios library to make a POST request to the server.

  4. try...catch: This is a JavaScript construct used for error handling. Code inside the try block is executed, and if any errors occur, they're caught and handled in the catch block.

  5. localStorage.setItem: This is a browser API used to store data in the browser's local storage.

  6. localStorage.removeItem: Another browser API used to remove data from local storage.

  7. React Router DOM hooks: useNavigate, useLocation are hooks provided by React Router DOM for navigation and accessing the current location in the app.

  8. Optional Chaining (?.): Used to safely access nested properties without causing errors if any intermediate property is null or undefined.

  9. Arrow Functions: These are used extensively throughout the code for defining functions, especially for event handlers and callbacks.

  10. Conditional Rendering: This concept is used to conditionally render different UI elements based on certain conditions, like showing a spinner when data is loading or displaying an error message if something goes wrong.

  11. Template Literals: Used for string interpolation, allowing variables and expressions to be embedded within a string using ${} syntax.

Conclusion:

User authentication is super important for keeping web apps safe and making sure only the right people can use certain parts of the app. By understanding how signing up, logging in, and logging out work, you can implement robust authentication systems. By thinking about both the stuff that happens on the user's device (client-side) and what happens on the application's server (server-side), you can make logging in and out smooth and easy for every user.