You are browsing as a guest. Sign up (or log in) to start making projects!

3h 3m 18s logged

Devlog: Frontend Login Flow and Session Handling with Cookies

In this update, I built the entire frontend authentication flow for xbServerHub in React. The main goals were to handle regular login, two-factor authentication (2FA), and secure session persistence using cookies from the ASP.NET Core Identity backend.


What Was Done

1. Project Structure and Clean Components

Instead of keeping all the code in one giant file, I split the authentication page into smaller, reusable components:

  • AuthPage.tsx – Manages which form to show.
  • LoginForm.tsx – Handles the initial email and password submission.
  • TwoFactorForm.tsx – Handles the 2FA code verification if required by the backend.

2. React Router and Route Protection

I installed react-router-dom and configured the routing for the app:

  • Public route: /auth for logging in.
  • Protected route: /dashboard which can only be accessed by authenticated users. If an unauthenticated user tries to visit it, they are automatically redirected to login.

3. Axios Client and Cookie Sessions

Since the ASP.NET backend uses cookie-based authentication, I set up an Axios instance with withCredentials: true. This ensures that the browser automatically stores and sends the session cookie with every single request.

4. Session Persistence (No Logout on Refresh)

To fix the issue where refreshing the page logs the user out, I added an initialization check in App.tsx. When the app loads, it sends a quick request to GET user/me. If the server responds with success, the user stays logged in and lands straight on the dashboard.


Code Reference: authService.ts

Here is the complete service managing the API calls using Axios:

import api from "./api";
import { AxiosError } from "axios";

export interface LoginDto {
    email: string;
    password: string;
}

export interface AuthResult {
    isSuccess: boolean;
    message: string;
    requiresTwoFactor: boolean;
}

export interface LoginWith2FADto {
    code: string;
    rememberDevice: boolean;
}

export const loginUser = async (credentials: LoginDto): Promise<AuthResult> => {
    try {
        const response = await api.post("auth/login", credentials);
        return response.data as AuthResult;
    }
    catch (error: unknown) {
        let errorMessage = "An error occurred during login.";
        if ((error as AxiosError)?.response?.data) {
            errorMessage = (error as AxiosError).response!.data as string;
        }
        return {
            isSuccess: false,
            message: errorMessage,
            requiresTwoFactor: false,
        };
    }
}

export const loginWith2FA = async (data: LoginWith2FADto): Promise<AuthResult> => {
    try {
        const response = await api.post("auth/login-with-authenticator", data);
        return response.data as AuthResult;
    }
    catch (error: unknown) {
        let errorMessage = "An error occurred during 2FA login.";
        if ((error as AxiosError)?.response?.data) {
            errorMessage = (error as AxiosError).response!.data as string;
        }
        return {
            isSuccess: false,
            message: errorMessage,
            requiresTwoFactor: false,
        };
    }
}

export const checkAuthStatus = async (): Promise<boolean> => {
    try {
        await api.get("user/me");
        return true;
    }
    catch {
        return false;
    }
}

export const logoutUser = async (): Promise<void> => {
    await api.post("auth/logout");
}
0
0

Comments 0

No comments yet. Be the first!