Enterprise-backend-API's met NestJS · Les

Socketverbindingen authenticeren en beveiligen

Pas guards en tokenverificatie toe op de handshake en message-events voor veilige realtime toegang.

Les 2 van 413 stappen

Socketverbindingen authenticeren en beveiligen is een gratis Enterprise-backend-API's met NestJS-les op CoddyKit. Dit is les 2 van 4. Je kunt de volledige les hieronder gratis lezen en daarna in de browser praktisch oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is. Deze les maakt deel uit van het leertraject Enterprise-backend-API's met NestJS. Je voortgang wordt gesynchroniseerd op het web en in de CoddyKit-app. De cursus Enterprise-backend-API's met NestJS bevat in totaal 4 lessen.

Waarom sockets hun eigen authenticatie nodig hebben

HTTP-routes in NestJS worden beveiligd door middleware en guards die bij elk verzoek de Authorization-header lezen. WebSockets werken anders: de client opent tijdens de handshake één langdurige verbinding en wisselt daar vervolgens veel berichten over uit.

  • Je authenticeert één keer bij het maken van de verbinding, niet per bericht.
  • De socket blijft minuten of uren open, dus een token dat tijdens de sessie verloopt, is een reëel aandachtspunt.
  • Standaard HTTP-guards worden niet automatisch uitgevoerd voor WebSocket-gebeurtenissen.

Deze les laat zien hoe je tijdens de handshake een token controleert, de gebruiker aan de socket koppelt en afzonderlijke berichtgebeurtenissen beveiligt voor veilige realtime-toegang.

Waar het token zich in een handshake bevindt

Een browser-WebSocket kan geen aangepaste headers instellen. Daarom geven clients het token tijdens de Socket.IO-handshake op een van drie plaatsen door:

  • handshake.auth.token — de moderne en aanbevolen plek (ingesteld via de clientoptie auth).
  • handshake.headers.authorization — werkt wanneer er een native header beschikbaar is.
  • handshake.query.token — een terugvaloptie, maar tokens komen dan in server- en proxylogboeken terecht, dus vermijd deze optie.

Een kleine helper centraliseert het uitlezen, zodat elke guard en levenscyclus-hook het token op dezelfde manier leest.

import { Socket } from 'socket.io';

export function extractToken(client: Socket): string | null {
  const auth = client.handshake.auth?.token;
  if (typeof auth === 'string') return auth;

  const header = client.handshake.headers?.authorization;
  if (typeof header === 'string' && header.startsWith('Bearer ')) {
    return header.slice(7);
  }

  return null;
}

Verifiëren bij het verbinden met handleConnection

De duidelijkste plek om te authenticeren is de levenscyclus-hook handleConnection van de gateway. Deze wordt uitgevoerd zodra een client verbinding maakt. Als het token ontbreekt of ongeldig is, roep je client.disconnect() aan, zodat de socket nooit aan een room of gebeurtenis deelneemt.

Sla bij succes de gedecodeerde gebruiker op in client.data — een gegevensobject per socket dat gedurende de levensduur van de verbinding behouden blijft en in elke volgende gebeurtenishandler kan worden gelezen.

import { OnGatewayConnection, WebSocketGateway } from '@nestjs/websockets';
import { JwtService } from '@nestjs/jwt';
import { Socket } from 'socket.io';
import { extractToken } from './extract-token';

@WebSocketGateway({ cors: true })
export class ChatGateway implements OnGatewayConnection {
  constructor(private readonly jwt: JwtService) {}

  async handleConnection(client: Socket) {
    try {
      const token = extractToken(client);
      if (!token) throw new Error('No token');
      const payload = await this.jwt.verifyAsync(token);
      client.data.user = { id: payload.sub, role: payload.role };
    } catch {
      client.disconnect(true);
    }
  }
}

De geauthenticeerde gebruiker modelleren

Als je client.data.user opslaat als een object zonder type, maak je typefouten later waarschijnlijker. Definieer een kleine interface en een getypeerde helper, zodat elke handler user.id en user.role kan lezen met volledige IntelliSense en controle tijdens het compileren.

Dit is hetzelfde JWT-payloadpatroon dat je voor HTTP-routes gebruikt, waardoor de autorisatielogica voor beide transporten consistent blijft.

export interface SocketUser {
  id: string;
  role: 'admin' | 'member' | 'guest';
}

export interface JwtPayload {
  sub: string;
  role: SocketUser['role'];
  exp: number;
}

export function toSocketUser(payload: JwtPayload): SocketUser {
  return { id: payload.sub, role: payload.role };
}

// Demo: a decoded token becomes a typed user
const payload: JwtPayload = { sub: 'u_42', role: 'member', exp: 1893456000 };
const user = toSocketUser(payload);
console.log(`${user.id} connected as ${user.role}`);

Afzonderlijke berichtgebeurtenissen beveiligen

Authenticatie bij het maken van de verbinding bewijst wie de gebruiker is, maar voor sommige gebeurtenissen zijn ook autorisatiecontroles nodig — alleen beheerders mogen bijvoorbeeld een systeembericht uitzenden. NestJS-guards werken ook voor WebSocket-gebeurtenissen; je leest de socket gewoon uit de uitvoeringscontext.

Schakel in een guard de context over naar ws, haal de client op en controleer client.data.user, dat door handleConnection is gevuld.

import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { WsException } from '@nestjs/websockets';
import { Socket } from 'socket.io';

@Injectable()
export class WsAuthGuard implements CanActivate {
  canActivate(context: ExecutionContext): boolean {
    const client = context.switchToWs().getClient<Socket>();
    const user = client.data.user;
    if (!user) {
      throw new WsException('Unauthorized');
    }
    return true;
  }
}

Op rollen gebaseerde guards met metadata

Als je een gebeurtenis wilt beperken tot bepaalde rollen, combineer je een aangepaste decorator (die vereiste rollen als metadata opslaat) met een guard die deze via Reflector leest. Dit komt overeen met het HTTP-patroon @Roles(), zodat je team één denkmodel leert.

De guard weigert de gebeurtenis met een WsException wanneer de verbonden gebruiker niet de vereiste rol heeft — de bericht-handler wordt nooit uitgevoerd.

import { SetMetadata } from '@nestjs/common';
export const WsRoles = (...roles: string[]) => SetMetadata('ws_roles', roles);

import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { WsException } from '@nestjs/websockets';
import { Socket } from 'socket.io';

@Injectable()
export class WsRolesGuard implements CanActivate {
  constructor(private reflector: Reflector) {}

  canActivate(context: ExecutionContext): boolean {
    const required = this.reflector.get<string[]>('ws_roles', context.getHandler());
    if (!required?.length) return true;
    const user = context.switchToWs().getClient<Socket>().data.user;
    if (!user || !required.includes(user.role)) {
      throw new WsException('Forbidden');
    }
    return true;
  }
}

Guards toepassen op subscribe-handlers

Koppel guards aan bericht-handlers met @UseGuards(), precies zoals bij controllerroutes. Stapel de algemene authenticatieguard met de rollenguard en voorzie de handler van de vereiste rollen.

Guards worden uitgevoerd in de volgorde waarin ze zijn gedeclareerd. Plaats daarom eerst de goedkopere authenticatiecontrole en daarna de rolcontrole.

import { SubscribeMessage, WebSocketGateway, MessageBody } from '@nestjs/websockets';
import { UseGuards } from '@nestjs/common';
import { WsAuthGuard } from './ws-auth.guard';
import { WsRolesGuard } from './ws-roles.guard';
import { WsRoles } from './ws-roles.decorator';

@WebSocketGateway()
export class AdminGateway {
  @UseGuards(WsAuthGuard, WsRolesGuard)
  @WsRoles('admin')
  @SubscribeMessage('broadcast')
  handleBroadcast(@MessageBody() text: string) {
    return { event: 'broadcast', data: text };
  }
}

Waarom guards alleen de handshake missen

Een subtiele valkuil: standaard wordt een WebSocket-guard uitgevoerd bij berichtgebeurtenissen, niet bij de eerste verbinding. Als je alleen op @UseGuards vertrouwt en handleConnection overslaat, kan een niet-geauthenticeerde client nog steeds een socket openen en inactief op je server blijven.

  • Gebruik handleConnection om niet-geauthenticeerde sockets bij de deur te weigeren.
  • Gebruik gebeurtenisguards voor gedetailleerde autorisatie per actie.

De twee lagen vullen elkaar aan: de ene bepaalt wie binnenkomt, de andere bepaalt welke acties zijn toegestaan.

Fouten netjes aan clients doorgeven

Wanneer een guard WsException gooit, stuurt NestJS een exception-gebeurtenis naar die client in plaats van de verbinding te laten crashen. Voeg een WsExceptionFilter toe om de payload vorm te geven, zodat de frontend een voorspelbaar foutobject ontvangt dat aan de gebruiker kan worden getoond.

import { ArgumentsHost, Catch } from '@nestjs/common';
import { BaseWsExceptionFilter, WsException } from '@nestjs/websockets';
import { Socket } from 'socket.io';

@Catch(WsException)
export class WsErrorFilter extends BaseWsExceptionFilter {
  catch(exception: WsException, host: ArgumentsHost) {
    const client = host.switchToWs().getClient<Socket>();
    client.emit('error', {
      message: exception.getError(),
      timestamp: new Date().toISOString(),
    });
  }
}

Tokenverval op een actieve socket afhandelen

Een verbinding die een uur geleden is geauthenticeerd, kan nu een verlopen token bevatten. Twee gebruikelijke strategieën:

  • Opnieuw verifiëren per gevoelige gebeurtenis — sla het onbewerkte token op in client.data.token en roep verifyAsync opnieuw aan in de guard voor acties met hoge waarde.
  • Periodiek opnieuw valideren — een serverinterval controleert de exp van het token van elke socket en verbreekt verlopen sockets.

Deze kleine pure functie toont de vervalcontrole die de kern vormt van beide aanpakken.

interface TokenInfo {
  exp: number; // unix seconds
}

function isExpired(token: TokenInfo, nowSeconds: number): boolean {
  return token.exp <= nowSeconds;
}

const now = 1_700_000_000;
console.log(isExpired({ exp: 1_699_999_000 }, now)); // true  -> disconnect
console.log(isExpired({ exp: 1_700_500_000 }, now)); // false -> keep open

Alles samenbrengen in de module

Guards die services zoals Reflector of JwtService injecteren, moeten oplosbaar zijn via Nest's DI. Omdat de gateway en de guards in dezelfde module staan, registreer je JwtModule en lever je de gateway aan; de guards worden door Nest geïnstantieerd wanneer ze in @UseGuards worden gebruikt.

Je kunt de authenticatieguard voor sockets ook globaal registreren met APP_GUARD als elke gebeurtenis standaard geauthenticeerd moet worden.

import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { APP_GUARD } from '@nestjs/core';
import { ChatGateway } from './chat.gateway';
import { WsAuthGuard } from './ws-auth.guard';

@Module({
  imports: [
    JwtModule.register({ secret: process.env.JWT_SECRET }),
  ],
  providers: [
    ChatGateway,
    { provide: APP_GUARD, useClass: WsAuthGuard },
  ],
})
export class RealtimeModule {}

Korte controle: authenticatie bij verbinding versus gebeurtenis

Je wilt (1) voorkomen dat niet-geauthenticeerde clients ooit een socket openen en (2) alleen gebruikers met de rol admin toestaan een broadcast-gebeurtenis uit te zenden. Welke combinatie bereikt beide doelen correct?

Samenvatting: realtimeverbindingen beveiligen

Je beschikt nu over een complete aanpak in lagen om NestJS WebSocket-gateways te beveiligen:

  • Lees het token consequent uit handshake.auth, headers of de query.
  • Authenticeer bij de deur in handleConnection, verbreek de verbinding met ongeldige clients en koppel de gebruiker aan client.data.
  • Autoriseer per gebeurtenis met guards die overschakelen naar de ws-context, inclusief rolcontroles via Reflector en een @WsRoles-decorator.
  • Rapporteer fouten via een WsException-filter en handel verval af met herverificatie of periodieke controles.

Authenticatie bij de verbinding bepaalt wie binnenkomt; gebeurtenisguards bepalen welke acties zijn toegestaan — samen houden ze je realtime-systeem veilig.

Gratis beginnen

Leer TypeScript met een AI-tutor — gratis

Schrijf echte code en voer die uit in je browser, krijg direct hulp van een AI-tutor die 24/7 beschikbaar is en ga verder waar je gebleven bent op het web of in de app.

Cursussen
20
Lessen
76

Veelgestelde vragen

Is de les “Socketverbindingen authenticeren en beveiligen” gratis?

Ja — de volledige tekst van “Socketverbindingen authenticeren en beveiligen” kun je hier gratis op het web lezen. Als je interactief wilt oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is, en de rest van de cursus Enterprise-backend-API's met NestJS wilt ontgrendelen, kun je upgraden naar CoddyKit PRO. De cursus Enterprise-backend-API's met NestJS bevat in totaal 4 lessen.

Wat leer ik in “Socketverbindingen authenticeren en beveiligen”?

Pas guards en tokenverificatie toe op de handshake en message-events voor veilige realtime toegang. Je oefent met Enterprise-backend-API's met NestJS door code rechtstreeks in de browser uit te voeren. Een AI-begeleider die 24/7 beschikbaar is beantwoordt je vragen terwijl je de les doorwerkt.

Heb ik ervaring nodig om met Enterprise-backend-API's met NestJS te beginnen?

Ervaring vooraf is niet nodig. Enterprise-backend-API's met NestJS op CoddyKit is opgebouwd voor beginners tot gevorderden, zodat je hier of bij het begin kunt starten en in je eigen tempo kunt leren. Dit is les 2 van 4.

Hoe lang duurt de les “Socketverbindingen authenticeren en beveiligen”?

De meeste lessen van CoddyKit duren ongeveer 5–10 minuten. Elke les is kort en interactief, zodat je gestaag vooruitgaat en op het web en in de app precies verdergaat waar je was gebleven.

Kan ik code schrijven en uitvoeren in deze les over Enterprise-backend-API's met NestJS?

Ja. Elke les over Enterprise-backend-API's met NestJS bevat een ingebouwde code-editor, zodat je rechtstreeks in je browser echte code kunt schrijven en uitvoeren en direct feedback van AI krijgt — lokale installatie is niet nodig.

Alle lessen in deze cursus

  1. WebSocket-gateways met de Socket.IO-adapter
  2. Socketverbindingen authenticeren en beveiligen
  3. Server-Sent Events voor eenrichtingspush
  4. Realtime schalen met een Redis Pub/Sub-adapter
← Terug naar Enterprise-backend-API's met NestJS