Implementing Basic Authentication for an Angular/ NodeJS-App

This tutorial demonstrates how to build a secure web application by integrating a Node.js HTTP server with Basic Authentication in TypeScript and an Angular frontend. The Node.js server protects routes with username/password authentication, while the Angular app consumes these endpoints, handling authentication seamlessly. Perfect for developers learning full-stack development, this guide covers setting up the server, creating an Angular client, and securing communication. Leveraging TypeScript’s type safety and Angular’s power, you’ll build a robust app ready for real-world scenarios.

Build a Simple Node.js Server with Basic Authentication in TypeScript

Want to secure a Node.js server with basic authentication? In this tutorial, we’ll create a lightweight HTTP server using TypeScript, no frameworks needed! It features a public route and a secure route protected by username/password, perfect for learning Node.js and TypeScript basics.

Prerequisites

  • Node.js (LTS, e.g., 20.x): Install from nodejs.org and verify:
node -v
npm -v
  • Text editor: VS Code or IntelliJ.
  • Terminal: For commands.

Step 1: Set Up the Project

Create a project folder and initialize it:

mkdir nodejs-http-auth
cd nodejs-http-auth
npm init -y

Install TypeScript and Node.js types:

npm install typescript @types/node --save-dev

Initialize TypeScript configuration:

npx tsc --init

Edit tsconfig.json:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "outDir": "./dist",
    "strict": true,
    "esModuleInterop": true
  }
}

Step 2: Write the Server Code

Create src/server.ts:

import http, { IncomingMessage, ServerResponse } from 'http';

const authenticate = (req: IncomingMessage): boolean => {
  const authHeader: string | undefined = req.headers.authorization;
  if (!authHeader) return false;
  const base64Credentials: string = authHeader.split(' ')[1];
  const credentials: string = Buffer.from(base64Credentials, 'base64').toString('ascii');
  const [username, password]: string[] = credentials.split(':');
  return username === 'admin' && password === 'password123';
};

const server = http.createServer((req: IncomingMessage, res: ServerResponse) => {
  if (req.url === '/secure') {
    if (authenticate(req)) {
      res.writeHead(200, { 'Content-Type': 'text/plain' });
      res.end('Welcome to the secure area! Hello World\n');
    } else {
      res.writeHead(401, {
        'Content-Type': 'text/plain',
        'WWW-Authenticate': 'Basic realm="Secure Area"'
      });
      res.end('Authentication required\n');
    }
  } else {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('Public page: Hello World\n');
  }
});

const PORT: number = 3000;
server.listen(PORT, () => {
  console.log(`Server running at http://localhost:${PORT}/`);
});

What’s Happening?

  • Imports: Use Node.js’s http module with TypeScript types.
  • Authentication: Checks Authorization header for Basic <Base64(username:password)>. Hardcodes admin:password123 (replace with a database in production).
  • Routes: /secure requires auth; / is public.
  • Response: Sends “Hello World” or prompts for credentials.

Step 3: Run the Server

Compile TypeScript:

npx tsc

Run the server:

node dist/server.js

See: Server running at http://localhost:3000/.

Step 4: Test the Server

  • Browser:
    • Visit http://localhost:3000/ → “Public page: Hello World”.
    • Visit http://localhost:3000/secure → Enter admin/password123 → “Welcome to the secure area! Hello World”. Wrong credentials show “Authentication required”.
curl http://localhost:3000  # Public: "Public page: Hello World"
curl -u admin:password123 http://localhost:3000/secure  # Secure: "Welcome to the secure area! Hello World"
curl http://localhost:3000/secure  # No auth: "Authentication required"

Security Notes

  • Hardcoded Credentials: Use a database (e.g., PostgreSQL) or environment variables in production.
  • HTTPS: Basic Auth over HTTP is insecure. Use HTTPS for real apps.
  • Limitations: The http module is minimal; for complex apps, consider Express.js.

Why This Matters

This simple server showcases TypeScript’s type safety and Node.js’s HTTP capabilities, ideal for learning or prototyping. It’s a stepping stone to building secure APIs, like those in German tech roles (e.g., CHECK24).

Want to dive deeper? Try adding a database or Dockerizing the app. Share your thoughts in the comments!

Build an Angular Client with Basic Authentication

This tutorial shows how to create an Angular app that connects to a Node.js HTTP server with Basic Authentication (running at http://localhost:3000). You’ll build a navigation bar, routing, an auth guard, authentication service, and components for login, dashboard (all users), and admin-only pages, using TypeScript for type safety.

Prerequisites

  • Node.js server: From previous tutorial at http://localhost:3000 (credentials: admin:password123).
  • Node.js (LTS, e.g., 20.x) and Angular CLI: Install with:npm install -g @angular/cli node -v ng version
  • Text editor: VS Code or IntelliJ.
  • Terminal: For commands.

Step 1: Set Up the Angular Project

Create a new Angular project:

ng new angular-auth-app --style=css --routing
cd angular-auth-app

Step 2: Create Components

Generate components for Login, Dashboard, and Admin pages:

ng generate component login-page
ng generate component dashboard
ng generate component admin-page

Step 3: Set Up Authentication Service

Create an authentication service to handle login and Basic Auth:

ng generate service auth

Edit src/app/auth.service.ts:

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable({ providedIn: 'root' })
export class AuthService {
  private apiUrl = 'http://localhost:3000';
  private token: string | null = null;
  private username: string | null = null;

  constructor(private http: HttpClient) {}

  login(username: string, password: string): Observable<any> {
    const headers = new HttpHeaders({
      Authorization: 'Basic ' + btoa(`${username}:${password}`)
    });
    return this.http.get(`${this.apiUrl}/secure`, { headers });
  }

  setCredentials(username: string, password: string): void {
    this.token = btoa(`${username}:${password}`);
    this.username = username;
    localStorage.setItem('token', this.token); // Persist for demo
    localStorage.setItem('username', username);
  }

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

  getUsername(): string | null {
    return localStorage.getItem('username') || this.username;
  }

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

  isAdmin(): boolean {
    return this.getUsername() === 'admin';
  }

  logout(): void {
    this.token = null;
    this.username = null;
    localStorage.removeItem('token');
    localStorage.removeItem('username');
  }
}

Note: Uses localStorage for persistence across refreshes (simplifies demo, but see security notes).

Step 4: Create Auth Guard

Generate an auth guard to protect routes:

ng generate guard auth --implements=CanActivate

Edit src/app/auth.guard.ts:

import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';
import { AuthService } from './auth.service';

@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate {
  constructor(private authService: AuthService, private router: Router) {}

  canActivate(): boolean {
    if (this.authService.isLoggedIn()) {
      return true;
    }
    this.router.navigate(['/login']);
    return false;
  }
}

Generate an admin guard:

ng generate guard admin --implements=CanActivate

Edit src/app/admin.guard.ts:

import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';
import { AuthService } from './auth.service';

@Injectable({ providedIn: 'root' })
export class AdminGuard implements CanActivate {
  constructor(private authService: AuthService, private router: Router) {}

  canActivate(): boolean {
    if (this.authService.isLoggedIn() && this.authService.isAdmin()) {
      return true;
    }
    this.router.navigate(['/dashboard']);
    return false;
  }
}

Step 5: Set Up Routing

Edit src/app/app-routing.module.ts:

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { LoginPageComponent } from './login-page/login-page.component';
import { DashboardComponent } from './dashboard/dashboard.component';
import { AdminPageComponent } from './admin-page/admin-page.component';
import { AuthGuard } from './auth.guard';
import { AdminGuard } from './admin.guard';

const routes: Routes = [
  { path: 'login', component: LoginPageComponent },
  { path: 'dashboard', component: DashboardComponent, canActivate: [AuthGuard] },
  { path: 'admin', component: AdminPageComponent, canActivate: [AuthGuard, AdminGuard] },
  { path: '', redirectTo: '/login', pathMatch: 'full' },
  { path: '**', redirectTo: '/login' }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule {}

Step 6: Create Navigation Bar

Generate a navigation component:

ng generate component navigation-bar

Edit src/app/navigation-bar/navigation-bar.component.html:

<nav class="navbar navbar-expand-lg navbar-light bg-light">
  <a class="navbar-brand" href="#">Angular Auth App</a>
  <div class="collapse navbar-collapse">
    <ul class="navbar-nav mr-auto">
      <li class="nav-item" *ngIf="authService.isLoggedIn()">
        <a class="nav-link" routerLink="/dashboard">Dashboard</a>
      </li>
      <li class="nav-item" *ngIf="authService.isLoggedIn() && authService.isAdmin()">
        <a class="nav-link" routerLink="/admin">Admin</a>
      </li>
    </ul>
    <button class="btn btn-outline-danger" *ngIf="authService.isLoggedIn()" (click)="logout()">Logout</button>
    <a class="btn btn-outline-primary" *ngIf="!authService.isLoggedIn()" routerLink="/login">Login</a>
  </div>
</nav>

Edit src/app/navigation-bar/navigation-bar.component.ts:

import { Component } from '@angular/core';
import { AuthService } from '../auth.service';
import { Router } from '@angular/router';

@Component({
  selector: 'app-navigation-bar',
  templateUrl: './navigation-bar.component.html',
  styleUrls: ['./navigation-bar.component.css']
})
export class NavigationBarComponent {
  constructor(public authService: AuthService, private router: Router) {}

  logout(): void {
    this.authService.logout();
    this.router.navigate(['/login']);
  }
}

Add to src/app/app.component.html:

<app-navigation-bar></app-navigation-bar>
<router-outlet></router-outlet>

Step 7: Create Login Page

Edit src/app/login-page/login-page.component.html:

<div class="container mt-5">
  <h2>Login</h2>
  <form (ngSubmit)="onSubmit()" #loginForm="ngForm">
    <div class="form-group">
      <label for="username">Username</label>
      <input type="text" class="form-control" id="username" [(ngModel)]="username" name="username" required>
    </div>
    <div class="form-group">
      <label for="password">Password</label>
      <input type="password" class="form-control" id="password" [(ngModel)]="password" name="password" required>
    </div>
    <button type="submit" class="btn btn-primary" [disabled]="!loginForm.valid">Login</button>
    <p *ngIf="error" class="text-danger mt-2">{{ error }}</p>
  </form>
</div>

Edit src/app/login-page/login-page.component.ts:

import { Component } from '@angular/core';
import { AuthService } from '../auth.service';
import { Router } from '@angular/router';

@Component({
  selector: 'app-login-page',
  templateUrl: './login-page.component.html',
  styleUrls: ['./login-page.component.css']
})
export class LoginPageComponent {
  username: string = '';
  password: string = '';
  error: string = '';

  constructor(private authService: AuthService, private router: Router) {}

  onSubmit(): void {
    this.authService.login(this.username, this.password).subscribe({
      next: () => {
        this.authService.setCredentials(this.username, this.password);
        this.router.navigate(['/dashboard']);
      },
      error: () => {
        this.error = 'Invalid username or password';
      }
    });
  }
}

Step 8: Create Dashboard and Admin Pages

Edit src/app/dashboard/dashboard.component.html:

<div class="container mt-5">
  <h2>Dashboard</h2>
  <p>Welcome to the dashboard, accessible to all authenticated users!</p>
</div>

Edit src/app/admin-page/admin-page.component.html:

<div class="container mt-5">
  <h2>Admin Page</h2>
  <p>This page is only for the admin user.</p>
</div>

Step 9: Update App Module

Edit src/app/app.module.ts to include forms and HTTP:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
import { FormsModule } from '@angular/forms';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { NavigationBarComponent } from './navigation-bar/navigation-bar.component';
import { LoginPageComponent } from './login-page/login-page.component';
import { DashboardComponent } from './dashboard/dashboard.component';
import { AdminPageComponent } from './admin-page/admin-page.component';

@NgModule({
  declarations: [
    AppComponent,
    NavigationBarComponent,
    LoginPageComponent,
    DashboardComponent,
    AdminPageComponent
  ],
  imports: [
    BrowserModule,
    HttpClientModule,
    FormsModule,
    AppRoutingModule
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}

Step 10: Run the Application

Ensure the Node.js server is running:

cd nodejs-http-auth
npm run start

Start the Angular app:

ng serve

Visit http://localhost:4200:

  • Login: Use admin:password123 or any valid credentials.
  • Dashboard: Accessible after login.
  • Admin: Only for admin user.
  • Logout: Returns to login page.

Security Notes

  • LocalStorage: Used for demo persistence; prefer in-memory storage or HTTP-only cookies in production to prevent XSS (see token security).
  • HTTPS: Use HTTPS for secure communication.
  • Hardcoded Credentials: Replace with a database (e.g., PostgreSQL).
  • CORS: If Angular (localhost:4200) fails to connect to Node.js (localhost:3000), add CORS to the server (future tutorial).

Why This Matters

This full-stack app showcases Angular’s routing, guards, and services with Node.js authentication, ideal for German tech roles (e.g., CHECK24). It’s a great starting point for secure web apps.

Try adding a database or Dockerizing the app! Share your feedback in the comments.

Leave a Reply