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.