Full Trust European Hosting

BLOG about Full Trust Hosting and Its Technology - Dedicated to European Windows Hosting Customer

AngularJS Hosting Europe - HostForLIFE :: Authentication and Login (Angular)

clock January 29, 2026 08:16 by author Peter

In this lecture, we will use PrimeNg Free Template sakai-ng and Angular 20 to create a complete Backoffice Admin Panel.

This system will allow your administrators to:

  • Manage products
  • Update settings
  • Handle security & authorization

Interact with the backend APIs you've previously created

Additionally, we will develop a clear, scalable Angular architecture and use JWT authentication.

1. Angular environment setup We will Use Free Version of PrimeNG (sakai-ng)

Clone Project
git clone https://github.com/primefaces/sakai-ng

Create New environment files
Path: "/src/environments/environment.ts" and "/src/environments/environment.prod.ts"

export const environment = {
  production: false,
  // Set your API base URL here, e.g. 'https://api.example.com'
    baseUrl: 'https://localhost:7104/api'
};

2. Folder structure

3. Services + Models
Create Auth Service
Path : "/src/app/services/api/auth.service.ts"

auth.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
import { environment } from '../../../environments/environment';

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

export interface RegisterRequest {
  email: string;
  password: string;
  firstName?: string;
  lastName?: string;
}

export interface RefreshRequest {
  token: string;
  refreshToken: string;
}

export interface AuthResponse {
  token: string;
  refreshToken: string;
}

@Injectable({
  providedIn: 'root'
})
export class AuthApiService {
    private baseUrl = environment.baseUrl + '/auth';



  constructor(private http: HttpClient) {}

  login(request: LoginRequest): Observable<AuthResponse> {
    return this.http.post<AuthResponse>(`${this.baseUrl}/login`, request).pipe(
      tap((response) => {
        if (response.token && response.refreshToken) {
          this.saveTokens(response.token, response.refreshToken);
        }
      })
    );
  }

  register(request: RegisterRequest): Observable<AuthResponse> {
    return this.http.post<AuthResponse>(`${this.baseUrl}/register`, request).pipe(
      tap((response) => {
        if (response.token && response.refreshToken) {
          this.saveTokens(response.token, response.refreshToken);
        }
      })
    );
  }

  refresh(request: RefreshRequest): Observable<AuthResponse> {
    return this.http.post<AuthResponse>(`${this.baseUrl}/refresh`, request).pipe(
      tap((response) => {
        if (response.token && response.refreshToken) {
          this.saveTokens(response.token, response.refreshToken);
        }
      })
    );
  }

  private saveTokens(token: string, refreshToken: string): void {
    localStorage.setItem('auth_token', token);
    localStorage.setItem('refresh_token', refreshToken);
  }

  getToken(): string | null {
    return localStorage.getItem('auth_token');
  }

  getRefreshToken(): string | null {
    return localStorage.getItem('refresh_token');
  }

  logout(): void {
    localStorage.removeItem('auth_token');
    localStorage.removeItem('refresh_token');
  }

  isAuthenticated(): boolean {
    return !!this.getToken();
  }
}


4. Authentication + Interceptor
Create Interceptor
Path: "/src/app/core/interceptors/auth.interceptor.ts"
import { HttpInterceptorFn } from '@angular/common/http';
import { inject } from '@angular/core';
import { AuthApiService } from '../../app/services/api/auth.service';
import { Router } from '@angular/router';
import { catchError } from 'rxjs/operators';
import { throwError } from 'rxjs';

export const authInterceptor: HttpInterceptorFn = (req, next) => {
    const authService = inject(AuthApiService);
    const router = inject(Router);
    const token = authService.getToken();

    // Skip interceptor logic for auth endpoints (login, register, refresh)
    if (req.url.includes('/auth/login') || req.url.includes('/auth/register') || req.url.includes('/auth/refresh')) {
        return next(req).pipe(
            catchError((error) => {
                return throwError(() => error);
            })
        );
    }

    // If no token and trying to access protected routes, redirect to login
    if (!token) {
        router.navigate(['/auth/login']);
        return throwError(() => new Error('No authentication token found'));
    }

    // Clone request and add Authorization header with Bearer token
    req = req.clone({
        setHeaders: {
            Authorization: `Bearer ${token}`
        }
    });

    return next(req).pipe(
        catchError((error) => {
            // If 401 Unauthorized, clear localStorage and redirect to login
            if (error.status === 401) {
                authService.logout();
                router.navigate(['/auth/login']);
                return throwError(() => new Error('Session expired. Please login again.'));
            }
            return throwError(() => error);
        })
    );
};


5. Move Login Component Features

Path: "/src/app/features/auth/login/login.html"
"/src/app/features/auth/login/login.scss" will be Blank file
"/src/app/features/auth/login/login.ts"
"/src/app/features/auth/auth.routes.ts"


login.html
<p-toast />
<app-floating-configurator />
<div class="bg-surface-50 dark:bg-surface-950 flex items-center justify-center min-h-screen min-w-screen overflow-hidden">
    <div class="flex flex-col items-center justify-center">
        <div style="border-radius: 56px; padding: 0.3rem; background: linear-gradient(180deg, var(--primary-color) 10%, rgba(33, 150, 243, 0) 30%)">
            <div class="w-full bg-surface-0 dark:bg-surface-900 py-20 px-8 sm:px-20" style="border-radius: 53px">
                <div class="text-center mb-8">
                    <svg viewBox="0 0 54 40" fill="none" xmlns="http://www.w3.org/2000/svg" class="mb-8 w-16 shrink-0 mx-auto">
                        <path
                            fill-rule="evenodd"
                            clip-rule="evenodd"
                            d="M17.1637 19.2467C17.1566 19.4033 17.1529 19.561 17.1529 19.7194C17.1529 25.3503 21.7203 29.915 27.3546 29.915C32.9887 29.915 37.5561 25.3503 37.5561 19.7194C37.5561 19.5572 37.5524 19.3959 37.5449 19.2355C38.5617 19.0801 39.5759 18.9013 40.5867 18.6994L40.6926 18.6782C40.7191 19.0218 40.7326 19.369 40.7326 19.7194C40.7326 27.1036 34.743 33.0896 27.3546 33.0896C19.966 33.0896 13.9765 27.1036 13.9765 19.7194C13.9765 19.374 13.9896 19.0316 14.0154 18.6927L14.0486 18.6994C15.0837 18.9062 16.1223 19.0886 17.1637 19.2467ZM33.3284 11.4538C31.6493 10.2396 29.5855 9.52381 27.3546 9.52381C25.1195 9.52381 23.0524 10.2421 21.3717 11.4603C20.0078 11.3232 18.6475 11.1387 17.2933 10.907C19.7453 8.11308 23.3438 6.34921 27.3546 6.34921C31.36 6.34921 34.9543 8.10844 37.4061 10.896C36.0521 11.1292 34.692 11.3152 33.3284 11.4538ZM43.826 18.0518C43.881 18.6003 43.9091 19.1566 43.9091 19.7194C43.9091 28.8568 36.4973 36.2642 27.3546 36.2642C18.2117 36.2642 10.8 28.8568 10.8 19.7194C10.8 19.1615 10.8276 18.61 10.8816 18.0663L7.75383 17.4411C7.66775 18.1886 7.62354 18.9488 7.62354 19.7194C7.62354 30.6102 16.4574 39.4388 27.3546 39.4388C38.2517 39.4388 47.0855 30.6102 47.0855 19.7194C47.0855 18.9439 47.0407 18.1789 46.9536 17.4267L43.826 18.0518ZM44.2613 9.54743L40.9084 10.2176C37.9134 5.95821 32.9593 3.1746 27.3546 3.1746C21.7442 3.1746 16.7856 5.96385 13.7915 10.2305L10.4399 9.56057C13.892 3.83178 20.1756 0 27.3546 0C34.5281 0 40.8075 3.82591 44.2613 9.54743Z"
                            fill="var(--primary-color)"
                        />
                        <mask id="mask0_1413_1551" style="mask-type: alpha" maskUnits="userSpaceOnUse" x="0" y="8" width="54" height="11">
                            <path d="M27 18.3652C10.5114 19.1944 0 8.88892 0 8.88892C0 8.88892 16.5176 14.5866 27 14.5866C37.4824 14.5866 54 8.88892 54 8.88892C54 8.88892 43.4886 17.5361 27 18.3652Z" fill="var(--primary-color)" />
                        </mask>
                        <g mask="url(#mask0_1413_1551)">
                            <path
                                d="M-4.673e-05 8.88887L3.73084 -1.91434L-8.00806 17.0473L-4.673e-05 8.88887ZM27 18.3652L26.4253 6.95109L27 18.3652ZM54 8.88887L61.2673 17.7127L50.2691 -1.91434L54 8.88887ZM-4.673e-05 8.88887C-8.00806 17.0473 -8.00469 17.0505 -8.00132 17.0538C-8.00018 17.055 -7.99675 17.0583 -7.9944 17.0607C-7.98963 17.0653 -7.98474 17.0701 -7.97966 17.075C-7.96949 17.0849 -7.95863 17.0955 -7.94707 17.1066C-7.92401 17.129 -7.89809 17.1539 -7.86944 17.1812C-7.8122 17.236 -7.74377 17.3005 -7.66436 17.3743C-7.50567 17.5218 -7.30269 17.7063 -7.05645 17.9221C-6.56467 18.3532 -5.89662 18.9125 -5.06089 19.5534C-3.39603 20.83 -1.02575 22.4605 1.98012 24.0457C7.97874 27.2091 16.7723 30.3226 27.5746 29.7793L26.4253 6.95109C20.7391 7.23699 16.0326 5.61231 12.6534 3.83024C10.9703 2.94267 9.68222 2.04866 8.86091 1.41888C8.45356 1.10653 8.17155 0.867278 8.0241 0.738027C7.95072 0.673671 7.91178 0.637576 7.90841 0.634492C7.90682 0.63298 7.91419 0.639805 7.93071 0.65557C7.93897 0.663455 7.94952 0.673589 7.96235 0.686039C7.96883 0.692262 7.97582 0.699075 7.98338 0.706471C7.98719 0.710167 7.99113 0.714014 7.99526 0.718014C7.99729 0.720008 8.00047 0.723119 8.00148 0.724116C8.00466 0.727265 8.00796 0.730446 -4.673e-05 8.88887ZM27.5746 29.7793C37.6904 29.2706 45.9416 26.3684 51.6602 23.6054C54.5296 22.2191 56.8064 20.8465 58.4186 19.7784C59.2265 19.2431 59.873 18.7805 60.3494 18.4257C60.5878 18.2482 60.7841 18.0971 60.9374 17.977C61.014 17.9169 61.0799 17.8645 61.1349 17.8203C61.1624 17.7981 61.1872 17.7781 61.2093 17.7602C61.2203 17.7512 61.2307 17.7427 61.2403 17.7348C61.2452 17.7308 61.2499 17.727 61.2544 17.7233C61.2566 17.7215 61.2598 17.7188 61.261 17.7179C61.2642 17.7153 61.2673 17.7127 54 8.88887C46.7326 0.0650536 46.7357 0.0625219 46.7387 0.0600241C46.7397 0.0592345 46.7427 0.0567658 46.7446 0.0551857C46.7485 0.0520238 46.7521 0.0489887 46.7557 0.0460799C46.7628 0.0402623 46.7694 0.0349487 46.7753 0.0301318C46.7871 0.0204986 46.7966 0.0128495 46.8037 0.00712562C46.818 -0.00431848 46.8228 -0.00808311 46.8184 -0.00463784C46.8096 0.00228345 46.764 0.0378652 46.6828 0.0983779C46.5199 0.219675 46.2165 0.439161 45.7812 0.727519C44.9072 1.30663 43.5257 2.14765 41.7061 3.02677C38.0469 4.79468 32.7981 6.63058 26.4253 6.95109L27.5746 29.7793ZM54 8.88887C50.2691 -1.91433 50.27 -1.91467 50.271 -1.91498C50.2712 -1.91506 50.272 -1.91535 50.2724 -1.9155C50.2733 -1.91581 50.274 -1.91602 50.2743 -1.91616C50.2752 -1.91643 50.275 -1.91636 50.2738 -1.91595C50.2714 -1.91515 50.2652 -1.91302 50.2552 -1.9096C50.2351 -1.90276 50.1999 -1.89078 50.1503 -1.874C50.0509 -1.84043 49.8938 -1.78773 49.6844 -1.71863C49.2652 -1.58031 48.6387 -1.377 47.8481 -1.13035C46.2609 -0.635237 44.0427 0.0249875 41.5325 0.6823C36.215 2.07471 30.6736 3.15796 27 3.15796V26.0151C33.8087 26.0151 41.7672 24.2495 47.3292 22.7931C50.2586 22.026 52.825 21.2618 54.6625 20.6886C55.5842 20.4011 56.33 20.1593 56.8551 19.986C57.1178 19.8993 57.3258 19.8296 57.4735 19.7797C57.5474 19.7548 57.6062 19.7348 57.6493 19.72C57.6709 19.7127 57.6885 19.7066 57.7021 19.7019C57.7089 19.6996 57.7147 19.6976 57.7195 19.696C57.7219 19.6952 57.7241 19.6944 57.726 19.6938C57.7269 19.6934 57.7281 19.693 57.7286 19.6929C57.7298 19.6924 57.7309 19.692 54 8.88887ZM27 3.15796C23.3263 3.15796 17.7849 2.07471 12.4674 0.6823C9.95717 0.0249875 7.73904 -0.635237 6.15184 -1.13035C5.36118 -1.377 4.73467 -1.58031 4.3155 -1.71863C4.10609 -1.78773 3.94899 -1.84043 3.84961 -1.874C3.79994 -1.89078 3.76474 -1.90276 3.74471 -1.9096C3.73469 -1.91302 3.72848 -1.91515 3.72613 -1.91595C3.72496 -1.91636 3.72476 -1.91643 3.72554 -1.91616C3.72593 -1.91602 3.72657 -1.91581 3.72745 -1.9155C3.72789 -1.91535 3.72874 -1.91506 3.72896 -1.91498C3.72987 -1.91467 3.73084 -1.91433 -4.673e-05 8.88887C-3.73093 19.692 -3.72983 19.6924 -3.72868 19.6929C-3.72821 19.693 -3.72698 19.6934 -3.72603 19.6938C-3.72415 19.6944 -3.72201 19.6952 -3.71961 19.696C-3.71482 19.6976 -3.70901 19.6996 -3.7022 19.7019C-3.68858 19.7066 -3.67095 19.7127 -3.6494 19.72C-3.60629 19.7348 -3.54745 19.7548 -3.47359 19.7797C-3.32589 19.8296 -3.11788 19.8993 -2.85516 19.986C-2.33008 20.1593 -1.58425 20.4011 -0.662589 20.6886C1.17485 21.2618 3.74125 22.026 6.67073 22.7931C12.2327 24.2495 20.1913 26.0151 27 26.0151V3.15796Z"
                                fill="var(--primary-color)"
                            />
                        </g>
                    </svg>
                    <div class="text-surface-900 dark:text-surface-0 text-3xl font-medium mb-4">Welcome to PrimeLand!</div>
                    <span class="text-muted-color font-medium">Sign in to continue</span>
                </div>

                <div>
                    <label for="email1" class="block text-surface-900 dark:text-surface-0 text-xl font-medium mb-2">Email</label>
                    <input pInputText id="email1" type="text" placeholder="Email address" class="w-full md:w-120 mb-8" [(ngModel)]="email" />

                    <label for="password1" class="block text-surface-900 dark:text-surface-0 font-medium text-xl mb-2">Password</label>
                    <p-password id="password1" [(ngModel)]="password" placeholder="Password" [toggleMask]="true" styleClass="mb-4" [fluid]="true" [feedback]="false"></p-password>

                    <div class="flex items-center justify-between mt-2 mb-8 gap-8">
                        <div class="flex items-center">
                            <p-checkbox [(ngModel)]="checked" id="rememberme1" binary class="mr-2"></p-checkbox>
                            <label for="rememberme1">Remember me</label>
                        </div>
                        <span class="font-medium no-underline ml-2 text-right cursor-pointer text-primary">Forgot password?</span>
                    </div>
                    <p-button label="Sign In" styleClass="w-full" (onClick)="handleLogin()" [loading]="isLoading"></p-button>
                </div>
            </div>
        </div>
    </div>
</div>

login.ts
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { Router, RouterModule } from '@angular/router';
import { ButtonModule } from 'primeng/button';
import { CheckboxModule } from 'primeng/checkbox';
import { InputTextModule } from 'primeng/inputtext';
import { PasswordModule } from 'primeng/password';
import { RippleModule } from 'primeng/ripple';
import { AppFloatingConfigurator } from '../../../layout/component/app.floatingconfigurator';
import { AuthApiService } from '../../../services/api/auth.service';
import { MessageService } from 'primeng/api';
import { ToastModule } from 'primeng/toast';
import { CommonModule } from '@angular/common';

@Component({
    selector: 'app-login',
    standalone: true,
    imports: [
        CommonModule,
        ButtonModule,
        CheckboxModule,
        InputTextModule,
        PasswordModule,
        FormsModule,
        RouterModule,
        RippleModule,
        AppFloatingConfigurator,
        ToastModule
    ],
    providers: [MessageService],
    templateUrl: './login.html',
    styleUrl: './login.scss'
})
export class Login {
    email: string = '';
    password: string = '';
    checked: boolean = false;
    isLoading: boolean = false;

    constructor(
        private authApi: AuthApiService,
        private messageService: MessageService,
        private router: Router
    ) {}

    handleLogin() {
        if (!this.email || !this.password) {
            this.messageService.add({
                severity: 'warn',
                summary: 'Validation Error',
                detail: 'Email and password are required.',
                life: 3000
            });
            return;
        }

        this.isLoading = true;
        this.authApi.login({ email: this.email, password: this.password }).subscribe({
            next: (response: any) => {
                this.isLoading = false;
                this.messageService.add({
                    severity: 'success',
                    summary: 'Success',
                    detail: 'Login successful!',
                    life: 2000
                });
                // Redirect to dashboard after short delay
                setTimeout(() => {
                    this.router.navigate(['/dashboard']);
                }, 500);
            },
            error: (err: any) => {
                this.isLoading = false;
                const errorMsg = err.error?.errors?.[0] || 'Login failed. Please try again.';
                this.messageService.add({
                    severity: 'error',
                    summary: 'Login Failed',
                    detail: errorMsg,
                    life: 3000
                });
            }
        });
    }
}


auth.routes.ts
import { Routes } from '@angular/router';
import { Login } from './login/login';

export default [
    { path: '', redirectTo: 'login', pathMatch: 'full' },
    { path: 'login', component: Login }
] as Routes;

6. Update App Routes

Path : "src/app/app.routes.ts"
import { Routes } from '@angular/router';
import { AppLayout } from './app/layout/component/app.layout';
import { Dashboard } from './app/pages/dashboard/dashboard';
import { Documentation } from './app/pages/documentation/documentation';
import { Landing } from './app/pages/landing/landing';
import { Notfound } from './app/pages/notfound/notfound';

export const appRoutes: Routes = [
    {
        path: '',
        redirectTo: () => {
            const token = localStorage.getItem('auth_token');
            return token ? '/dashboard' : '/auth/login';
        },
        pathMatch: 'full'
    },
    {
        path: 'dashboard',
        component: AppLayout,
        children: [
            { path: '', component: Dashboard }
        ]
    },
    {
        path: 'uikit',
        component: AppLayout,
        children: [
            { path: '', loadChildren: () => import('./app/pages/uikit/uikit.routes') }
        ]
    },
    {
        path: 'pages',
        component: AppLayout,
        children: [
            { path: '', loadChildren: () => import('./app/pages/pages.routes') }
        ]
    },
    {
        path: 'documentation',
        component: AppLayout,
        children: [
            { path: '', component: Documentation }
        ]
    },
    { path: 'landing', component: Landing },
    { path: 'notfound', component: Notfound },
    { path: 'auth', loadChildren: () => import('./app/features/auth/auth.routes') },
    { path: '**', component: Notfound }
];

7. Update App Config
Path: "src/app/app.config.ts"
import { provideHttpClient, withFetch, withInterceptors } from '@angular/common/http';
import { ApplicationConfig } from '@angular/core';
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
import { provideRouter, withEnabledBlockingInitialNavigation, withInMemoryScrolling } from '@angular/router';
import Aura from '@primeuix/themes/aura';
import { providePrimeNG } from 'primeng/config';
import { appRoutes } from './app.routes';
import { authInterceptor } from '@/core/interceptors/auth.interceptor';

export const appConfig: ApplicationConfig = {
    providers: [
        provideRouter(appRoutes, withInMemoryScrolling({ anchorScrolling: 'enabled', scrollPositionRestoration: 'enabled' }), withEnabledBlockingInitialNavigation()),
        provideHttpClient(withFetch(), withInterceptors([authInterceptor])),
        provideAnimationsAsync(),
        providePrimeNG({ theme: { preset: Aura, options: { darkModeSelector: '.app-dark' } } })
    ]
};

8. Lets Test
Install npm packages

npm i

After Installation Lets run both Application backend and angular

ng s -o


Click on Login

Login Sucessfully, redirected to Dashboard and toke in local storage



Node.js Hosting - HostForLIFE.eu :: After Updating Node.js to The Most Recent LTS, How Can Developers Identify Breaking Changes?

clock January 22, 2026 09:49 by author Peter

It is crucial to update Node.js to the most recent Long-Term Support (LTS) version for long-term maintenance, security, and performance. Nevertheless, many developers encounter unforeseen problems following an upgrade, such as builds failing, tests failing, or changes in production behavior.


To put it simply, these problems typically result from breaking changes. A breaking change occurs when a feature that was functional in the previous version of Node.js is no longer functional in the current version. Using straightforward language and real-world examples, this article demonstrates how developers can safely and early identify breaking changes when updating Node.js.

Recognize What Node.js "Breaking Change" Means
There are several ways that a breaking change can manifest:

  • APIs altered or eliminated
  • More stringent error handling or validation
  • Incompatibilities between dependencies
  • Modifications to the default behavior

For instance:
Code works in Node.js 16 → Fails in Node.js 20

Understanding that not all breaks are syntax errors helps developers debug faster.

Read Node.js LTS Release Notes First

Before upgrading, developers always review official Node.js release notes. These notes list deprecated features, removed APIs, and behavior changes.

What to look for:

  • Removed or deprecated APIs
  • Changes in default flags
  • Updated OpenSSL or V8 versions

Example workflow:
Read release notes → Identify risky changes → Prepare fixes

This step prevents surprises later.

Run the Application with the New Node.js Version Locally

The fastest way to detect breaking changes is running the app locally with the new LTS version.

Example:
node -v

node index.js
Common early signals include:

  • Startup crashes
  • Deprecation warnings
  • Runtime errors

Local testing catches obvious issues quickly.

Enable Strict Error and Warning Logs
Newer Node.js versions often log warnings more strictly.

Example:
node --trace-warnings index.js

This highlights deprecated APIs and unsafe patterns that may break in future releases.

Run Automated Test Suites

Automated tests are one of the most reliable ways to detect breaking changes.

Best practice:
Upgrade Node.js → Run all tests → Fix failures

Example:
npm test

If tests fail after the upgrade, the failures usually point directly to incompatible behavior.

Compare Behavior Using Multiple Node Versions

Developers often compare behavior between the old and new Node.js versions.

Example using version managers:
nvm use 18
npm test

nvm use 20
npm test


Differences in output or failures highlight breaking changes clearly.

Check Dependency Compatibility

Many breaking changes come from dependencies that do not yet support the new Node.js version.

Steps:

  • Check dependency release notes
  • Look for Node.js engine requirements
  • Upgrade incompatible packages


Example check:
npm outdated

Outdated dependencies are a common source of upgrade pain.

Watch for Native Module and Build Failures

Packages with native bindings often break during Node.js upgrades.

Common symptoms:

  • Build errors
  • Runtime crashes
  • Installation failures

Example:
npm install

If native modules fail, upgrading or rebuilding them usually resolves the issue.

Use Linting and Static Analysis Tools

Static analysis tools detect risky patterns that newer Node.js versions reject.

Example idea:
Lint warnings → Potential runtime break
Running linters after upgrade helps catch subtle issues early.

Monitor Runtime Behavior in Staging

Some breaking changes only appear under real traffic.

Best practice:
Deploy to staging → Monitor logs and metrics → Validate behavior

Watch for:

  • Memory usage changes
  • Performance regressions
  • Unexpected exceptions


Staging environments reduce production risk.
Enable Feature Flags for Risky Changes

For large applications, developers often guard risky changes behind feature flags.

Example concept:
New Node.js behavior → Feature flag → Gradual rollout

This allows fast rollback if issues appear.

Use Canary Releases in Production

Canary deployments expose a small portion of traffic to the new Node.js version.

Example approach:
5% traffic → New Node.js → Monitor → Expand rollout
This limits impact if breaking changes are missed.

Monitor Errors After Upgrade

After deployment, close monitoring is critical.

Key signals:

  • Error rate increases
  • New exception types
  • Slower response times

Example:
Upgrade complete → Metrics spike → Investigate immediately

Early detection prevents major outages.

Keep Rollback Options Ready

Even with careful preparation, some issues appear late.

Best practice:
Upgrade → Issue detected → Roll back Node.js version

Rollback readiness is a core part of safe upgrades.

Common Mistakes to Avoid

Avoid these upgrade mistakes:

  • Skipping tests
  • Ignoring warnings
  • Upgrading Node.js and dependencies together without validation
  • Deploying directly to production
  • Incremental and controlled upgrades work best.

Summary
After updating Node.js to the most recent LTS, developers examine release notes, run apps locally, enable strict warnings, run automated tests, verify dependency compatibility, and validate behavior in staging environments to find breaking changes. Risk is further decreased by employing canary releases, closely monitoring production, and comparing behavior across Node versions. Teams can reliably embrace new Node.js LTS versions without interfering with production systems thanks to a controlled upgrading process.



AngularJS Hosting Europe - HostForLIFE :: What's Replacing JWT and Why Is It Failing?

clock January 8, 2026 06:41 by author Peter

JSON Web Tokens (JWT) have been the standard for authentication in contemporary web applications for about ten years. It's likely that you have used JWT-based authentication several times if you have worked with Angular, React, or any SPA supported by REST APIs. Because it was straightforward, stateless, scalable, and simple to incorporate, it gained popularity.

However, an intriguing development has occurred in recent years. Security teams, big businesses, and senior engineers have begun to doubt JWT. Not merely minor adjustments or enhancements, but the core model itself. JWT is no longer regarded as the "best practice" approach that it formerly was. JWT is currently a source of complexity, hidden faults, and security issues in many real-world systems.

This article explains:

  • Why JWT became popular
  • Where JWT fundamentally breaks down
  • Real production issues teams face today
  • Why “just do it correctly” is not enough
  • What is replacing JWT in modern architectures
  • How Angular applications should adapt
  • Practical migration strategies

This is not a beginner-level overview. This article is written for senior developers who have built and maintained authentication systems in production.

Why JWT Became Popular in the First Place?

To understand why JWT is failing, we first need to understand why it succeeded.
The Problem JWT Originally Solved

Before JWT, authentication often relied on:

  • Server-side sessions
  • Sticky sessions
  • Shared session stores (Redis, DB)
  • Complex load balancer configurations

These approaches had real downsides:

  • Scaling sessions horizontally was painful
  • Stateful servers complicated deployments
  • Mobile and SPA apps struggled with cookies
  • JWT solved these problems elegantly.

Key Advantages of JWT
Stateless Authentication

The server does not store session data. All information is in the token.

Scales Well

Any server can validate a JWT using a shared secret or public key.

Works Across Platforms
Mobile apps, SPAs, APIs, and microservices all understand JWT.

Performance Friendly
No database or cache lookup per request.

Easy to Implement
Many libraries, tutorials, and examples.

For Angular SPAs talking to REST APIs, JWT felt perfect.

The Core Design of JWT (And Its Hidden Assumptions)
A JWT is basically:

  • A signed JSON object
  • Stored on the client
  • Sent with every request
  • Trusted by the server if the signature is valid

A typical JWT contains:

  • User ID
  • Roles or permissions
  • Expiry timestamp
  • Issuer and audience

The Hidden Assumption

JWT assumes that:
“If the token is valid and not expired, the user is authorized.”

This assumption works only in simple systems.

Modern applications are not simple.

Where JWT Starts to Fail in Real Systems?


JWT does not fail because it is “bad”.
It fails because real-world requirements evolved.

Let us look at the most common failure points.

Problem 1: Token Revocation Is Broken by Design

JWTs are immutable. Once issued, they are valid until they expire.

Why This Is a Serious Problem?

In real applications, you must be able to:

  • Log out users instantly
  • Revoke access after password change
  • Disable compromised accounts
  • Enforce policy changes

With JWT:

  1. You cannot revoke a token
  2. You can only wait for it to expire

Common “Solutions” That Don’t Really Work
Short-Lived Tokens

  • Improves security
  • Breaks UX
  • Adds refresh-token complexity

Token Blacklists

  • Reintroduces server-side state
  • Requires DB or cache lookup
  • Breaks the main benefit of JWT

At this point, JWT is no longer stateless.

Problem 2: JWT Makes Logout a Lie
In many Angular apps:

  • Clicking “Logout” just deletes the token from local storage
  • The server still considers the token valid

If the token is stolen:

  • Logout does nothing
  • Attacker keeps access until expiry

From a security perspective, this is unacceptable.

Problem 3: Token Theft Is Too Easy
JWTs are commonly stored in:

  • LocalStorage
  • SessionStorage
  • Memory

Why This Is Dangerous?
XSS attacks can steal tokens

Browser extensions can access tokens

Tokens can be leaked via logs or error reports

Once stolen:

  • The attacker has full access
  • There is no server-side kill switch

Cookies with HttpOnly flags solve some of this, but many JWT setups do not use them correctly.

Problem 4: Overloaded Tokens Become Security Liabilities

JWTs often include:

  • Roles
  • Permissions
  • Feature flags
  • Tenant data

This leads to:

  • Large token sizes
  • Stale authorization data
  • Authorization logic duplicated across services

If a user’s role changes:

  • Existing tokens still contain old roles
  • Access is incorrectly granted

This is a data consistency problem, not just a security issue.

Problem 5: Microservices + JWT = Hidden Complexity
JWT is often promoted as “microservice-friendly”.

In practice:

  1. Every service must understand token structure
  2. Authorization logic spreads everywhere
  3. Policy changes require coordinated updates

This leads to:

  • Tight coupling
  • Inconsistent access control
  • Security drift over time

Problem 6: Refresh Tokens Are a Minefield
To fix short-lived JWTs, teams introduce refresh tokens.

Now you have:

  • Access tokens
  • Refresh tokens
  • Rotation logic
  • Storage rules
  • Expiry policies

Mistakes here are common and dangerous:

  • Refresh token reuse
  • Long-lived refresh tokens
  • Poor rotation strategies

The system becomes harder to reason about than classic sessions.

Problem 7: Compliance and Auditing Are Hard
Modern regulations require:

  • Session tracking
  • Forced logout
  • Access audits
  • User-level controls

JWT makes this difficult because:

  • Sessions do not exist on the server
  • There is no single source of truth

Auditors do not like “trust the token”.

Why “Using JWT Correctly” Is Not Enough?
A common response is:
“JWT is fine, people just implement it incorrectly.”

This is partially true, but incomplete.

Even with:

  • Short-lived tokens
  • Secure cookies
  • Refresh token rotation
  • Token introspection

You end up rebuilding:

  • Sessions
  • Revocation
  • State
  • Centralized control

At that point, JWT is no longer the core solution.

So What Is Replacing JWT?
JWT is not disappearing entirely, but its role is changing.
Modern systems are moving toward controlled, state-aware authentication models.
Let us explore what is replacing JWT.

Replacement 1: Session-Based Authentication (Yes, It’s Back)

Sessions never truly went away.

What changed is:

  • Infrastructure
  • Scalability
  • Implementation quality
  • Why Sessions Are Making a Comeback

Modern session systems:

  • Use Redis or distributed caches
  • Scale horizontally
  • Support instant revocation
  • Enable strong security controls

How It Works Today?

  • Client stores a secure, HttpOnly session cookie
  • Server stores session state
  • Logout destroys the session
  • Revocation is immediate

This model:

  • Is easier to reason about
  • Is more secure by default
  • Fits compliance requirements

For Angular apps, this works very well with modern CORS and cookie policies.

Replacement 2: OAuth 2.1 with Token Introspection
OAuth 2.1 moves away from blindly trusting JWTs.
Token Introspection

Instead of validating JWT locally:

  • Services call the authorization server
  • Token validity is checked centrally

This allows:

  • Immediate revocation
  • Central policy enforcement
  • Better auditing

JWT may still exist, but it is not self-authorizing anymore.

Replacement 3: Backend-for-Frontend (BFF) Pattern

The BFF pattern is becoming the default for SPAs.

How BFF Changes Everything

  • Angular talks only to its backend
  • Backend manages authentication
  • No tokens stored in the browser
  • Cookies handle session state

Benefits:

  • No JWT in frontend
  • Reduced attack surface
  • Cleaner architecture

This is one of the strongest arguments against frontend JWT storage.

Replacement 4: Short-Lived Tokens + Server State
In some systems:

  • JWTs exist but live for seconds or minutes
  • Server tracks session state
  • Tokens are just transport credentials


JWT becomes an implementation detail, not a security boundary.

Replacement 5: Passkeys and WebAuthn (Future Direction)
Passwords themselves are being replaced.

Passkeys and WebAuthn:

  • Remove password theft
  • Reduce phishing risks
  • Integrate well with session-based auth

JWT plays a minimal role in these systems.

What This Means for Angular Applications?

Angular developers must rethink authentication.

What Angular Should NOT Do Anymore

  • Store JWT in localStorage
  • Decode tokens for authorization logic
  • Trust client-side role checks
  • Handle refresh logic in components

Recommended Angular Architecture Today

  • Use BFF pattern
  • Rely on HttpOnly cookies
  • Let backend manage auth state
  • Angular focuses on UI and UX
  • Authorization checks happen server-side

Angular becomes simpler, not more complex.

Example: Angular with Session-Based Auth

Angular makes requests like this:

this.http.get('/api/user/profile', {
withCredentials: true
});


No tokens.
No headers.
No decoding.
No expiry handling.

The backend:

  • Validates session
  • Enforces access
  • Returns data

This is boring, and that is a good thing.

Migrating Away from JWT Safely

You do not need to rewrite everything overnight.

Step-by-Step Migration Strategy

  • Introduce BFF layer
  • Move auth logic to backend
  • Switch to HttpOnly cookies
  • Reduce token lifespan
  • Gradually remove frontend JWT usage

Most teams do this incrementally.

When JWT Still Makes Sense?

JWT is not dead.

It still works well for:

  • Machine-to-machine communication
  • Internal service tokens
  • Short-lived infrastructure credentials

The key is:
Do not use JWT as a user session mechanism.


Common Misconceptions

  1. “Sessions do not scale” – they do now.
  2. “JWT is more secure” – often false.
  3. “Cookies are legacy” – incorrect.
  4. “Stateless is always better” – context matters.

Final Thoughts
JWT solved a real problem at a specific time.
But software architecture evolves.

Today’s applications need:

  • Revocation
  • Control
  • Auditing
  • Security by default

JWT, as commonly used for user authentication, fails these requirements.

What replaces it is not one thing, but a shift in mindset:

  1. From stateless trust
  2. To controlled, observable sessions

Angular developers who adapt early will build systems that are:

  • Safer
  • Simpler
  • Easier to maintain

JWT is not evil.
It is just no longer the right default.



About HostForLIFE.eu

HostForLIFE.eu is European Windows Hosting Provider which focuses on Windows Platform only. We deliver on-demand hosting solutions including Shared hosting, Reseller Hosting, Cloud Hosting, Dedicated Servers, and IT as a Service for companies of all sizes.

We have offered the latest Windows 2016 Hosting, ASP.NET Core 2.2.1 Hosting, ASP.NET MVC 6 Hosting and SQL 2017 Hosting.


Tag cloud

Sign in