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

xbServerHub

  • 3 Devlogs
  • 8 Total hours

I build Server Hub where you can see details about your server/computer. For example CPU and RAM usage.

Open comments for this post

3h 12m 3s logged

Devlog: Real-Time Server Metrics with Glances and SignalR

In this update, I added live server monitoring to xbServerHub. The goal was to collect system metrics (CPU, memory, network) from a Glances instance running in Docker, and stream them to the frontend in real time using SignalR — all while keeping the backend organized around Clean Architecture (Domain, Application, Persistence, API).


What Was Done

1. Metrics Collection via Glances

Glances exposes a REST API with system stats. I added a GlancesClient in the Persistence layer that polls the /cpu, /mem, and /network endpoints in parallel and maps the raw JSON responses into clean domain models (ServerMetrics, CpuStats, MemoryStats, NetworkInterfaceStats).

A BackgroundService using PeriodicTimer polls Glances every 2 seconds and passes the result to a handler (CollectSystemMetricsHandler) in the Application layer, which doesn’t know or care that the data originally came from an HTTP call to Glances — it just receives a ServerMetrics object.

2. Broadcasting Metrics with SignalR

To get this data to the frontend without constant polling from the client, I introduced an IMetricsBroadcaster interface in Application. This follows the same pattern as IGlancesClient: Application defines what it needs (a way to send metrics somewhere), without knowing how that’s actually done.

The implementation, SignalRMetricsBroadcaster, lives in the API layer and uses a MetricsHub to push data to all connected clients via Clients.All.SendAsync. The handler now calls both the Glances client and the broadcaster on every tick, so every 2 seconds connected clients get a fresh snapshot pushed to them automatically.

3. Securing the Hub and Fixing Auth Routes

Since metrics shouldn’t be visible to anyone who isn’t logged in, I added [Authorize] to MetricsHub. Because the app uses cookie-based authentication (SignInManager), this works without needing any token handling on the SignalR connection — the existing session cookie is enough, as long as the connection is treated as same-origin.

This is also where I cleaned up and reorganized the authentication API routes to make sure they’re consistent with how the frontend (and now SignalR) expects to reach them, and wired up the Hub endpoint (MapHub) alongside the rest of the API configuration.


Current State

The backend now collects CPU, memory, and network stats every 2 seconds and pushes them live to any authenticated, connected client. The frontend isn’t consuming this yet — that’s the next step, along with finalizing the dev proxy setup so cookies and WebSocket connections behave the same in development as they will once frontend and backend are deployed together in a single container.

0
0
0
Open comments for this post

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
0
Open comments for this post

1h 15m 57s logged

Devlog: Implementing 2FA (TOTP) Authentication in ASP.NET

This update focuses on authentication security. The core login flow has been refactored, and full support for Two-Factor Authentication (2FA) using authenticator apps (TOTP) has been implemented.

The entire feature has been split into a business logic layer (AuthService) and an API layer (AuthController) using ASP.NET Core Identity.


Architecture and Implementation Details

1. Updated Login Flow

The standard login mechanism now detects whether a user has enabled two-factor authentication. If 2FA is active, the API blocks immediate access and returns a response containing RequiresTwoFactor = true, signaling that further verification is required.

2. QR Code Secret Generation

To allow users to enable 2FA, the system securely generates a shared secret key and formats it into an industry-standard otpauth:// URI. This URI includes the issuer name (xbServerHub) and the user’s email, making it ready to be converted into a QR code on the frontend.

3. Input Sanitization

Users often format verification codes with spaces or hyphens. To prevent validation failures due to formatting, the service automatically sanitizes the input string using .Replace(" ", string.Empty).Replace("-", string.Empty) before passing it to the token provider.

4. Account Management Enforced by Authorization

New endpoints have been exposed to allow users to manually enable, verify, and disable their authenticator settings. All management endpoints are protected by the [Authorize] attribute, ensuring that only authenticated users can modify their security preferences.


Code Reference: Verification and Activation

The following method handles the final verification step. It cleans the user input, validates the token against the ASP.NET Identity token provider, and toggles the TwoFactorEnabled flag on the user record.

public async Task<VerifyAuthenticatorResponseDto> VerifyAuthenticatorAsync(string userId, string code)
{
    var user = await userManager.FindByIdAsync(userId);
    if (user is null)
    {
        return new()
        {
            IsSuccess = false,
            Message = "User not found"
        };
    }

    var sanitizedCode = code.Replace(" ", string.Empty).Replace("-", string.Empty);
    var isValid = await userManager.VerifyTwoFactorTokenAsync(user, userManager.Options.Tokens.AuthenticatorTokenProvider, sanitizedCode);

    if (!isValid)
    {
        _logger.LogWarning("Invalid authenticator code for user: {UserId}", userId);
        return new()
        {
            IsSuccess = false,
            Message = "Invalid authenticator code"
        };
    }

    var enableResult = await userManager.SetTwoFactorEnabledAsync(user, true);
    if (!enableResult.Succeeded)
    {
        _logger.LogWarning("Failed to enable 2FA for user {UserId}: {Errors}", userId,
            string.Join(", ", enableResult.Errors.Select(e => e.Description)));
        return new()
        {
            IsSuccess = false,
            Message = "Failed to enable 2FA"
        };
    }
    
    _logger.LogInformation("2FA enabled successfully for user: {UserId}", userId);
    return new()
    {
        IsSuccess = true,
        Message = "2FA enabled successfully"
    };
}
0
0
0

Delete project?

Are you sure you want to permanently delete this project? This action cannot be undone.

All devlogs, followers, and associated data will be removed.

Followers

Loading…