Authentifier et protéger les connexions Socket
Appliquez des gardes et vérifiez les tokens lors de la négociation et des événements de messages pour sécuriser l’accès en temps réel.
Authentifier et protéger les connexions Socket est une leçon NestJS Enterprise Backend APIs gratuite sur CoddyKit. Ceci est la leçon 2 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage NestJS Enterprise Backend APIs, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours NestJS Enterprise Backend APIs comprend 4 leçons au total.
Certaines parties de cette leçon n'ont pas encore été traduites et s'affichent en anglais.
Why Sockets Need Their Own Auth Story
HTTP routes in NestJS are protected by middleware and guards that read the Authorization header on every request. WebSockets are different: the client opens one long-lived connection during the handshake, then exchanges many messages over it.
- You authenticate once at connection time, not per message.
- The socket stays open for minutes or hours, so a token that expires mid-session is a real concern.
- Standard HTTP guards do not automatically run on WebSocket events.
This lesson shows how to verify a token during the handshake, attach the user to the socket, and guard individual message events for secure realtime access.
Where the Token Lives in a Handshake
A browser WebSocket cannot set custom headers, so clients pass the token in one of three places during the Socket.IO handshake:
handshake.auth.token— the modern, preferred slot (set via the clientauthoption).handshake.headers.authorization— works when a native header is available.handshake.query.token— a fallback, but tokens land in server/proxy logs, so avoid it.
A small helper centralizes extraction so every guard and lifecycle hook reads the token the same way.
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;
}Verifying on Connection with handleConnection
The cleanest place to authenticate is the gateway's handleConnection lifecycle hook. It runs the moment a client connects. If the token is missing or invalid, call client.disconnect() so the socket never participates in any room or event.
On success, attach the decoded user to client.data — a per-socket bag that survives for the connection's lifetime and is readable in every later event handler.
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);
}
}
}Modeling the Authenticated User
Storing client.data.user as an untyped object invites typos later. Define a small interface and a typed helper so every handler reads user.id and user.role with full IntelliSense and compile-time safety.
This is the same JWT payload pattern you use for HTTP routes, keeping authorization logic consistent across both transports.
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}`);Guarding Individual Message Events
Connection-time auth proves who the user is, but some events also need authorization checks — for example, only admins can broadcast a system message. NestJS guards work on WebSocket events too; you just read the socket from the execution context.
Inside a guard, switch the context to ws, grab the client, and inspect client.data.user that handleConnection populated.
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;
}
}Role-Based Guards with Metadata
To restrict an event to certain roles, combine a custom decorator (storing required roles as metadata) with a guard that reads it via Reflector. This mirrors the HTTP @Roles() pattern, so your team learns one mental model.
The guard rejects the event with a WsException when the connected user lacks the required role — the message handler never runs.
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;
}
}Applying Guards to Subscribe Handlers
Attach guards to message handlers with @UseGuards(), exactly like controller routes. Stack the base auth guard with the roles guard, and decorate the handler with the required roles.
Guards run in declaration order, so put the cheaper authentication check first and the role check second.
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 };
}
}Why Guards Alone Miss the Handshake
A subtle gotcha: by default a WebSocket guard runs on message events, not on the initial connection. If you rely only on @UseGuards and skip handleConnection, an unauthenticated client can still open a socket and sit idle in your server.
- Use
handleConnectionto reject unauthenticated sockets at the door. - Use event guards for fine-grained, per-action authorization.
The two layers complement each other: one controls entry, the other controls actions.
Surfacing Errors Cleanly to Clients
When a guard throws WsException, NestJS emits an exception event to that client instead of crashing the connection. Add a WsExceptionFilter to shape the payload so the frontend gets a predictable error object it can show to the user.
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(),
});
}
}Handling Token Expiry on a Live Socket
A connection authenticated an hour ago may now hold an expired token. Two common strategies:
- Re-verify per sensitive event — store the raw token on
client.data.tokenand callverifyAsyncagain inside the guard for high-value actions. - Periodic revalidation — a server interval checks each socket's token
expand disconnects expired ones.
This small pure function shows the expiry check at the heart of either approach.
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 openWiring It Together in the Module
Guards that inject services like Reflector or JwtService must be resolvable by Nest's DI. Because the gateway and its guards live in the same module, register JwtModule and provide the gateway; the guards are instantiated by Nest when referenced in @UseGuards.
You can also register the auth guard globally for sockets with APP_GUARD if every event should be authenticated by default.
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 {}Quick Check: Connection vs Event Auth
You want to (1) block unauthenticated clients from ever opening a socket and (2) allow only admin users to emit a broadcast event. Which combination correctly achieves both?
Recap: Securing Realtime Connections
You now have a complete, layered approach to securing NestJS WebSocket gateways:
- Extract the token consistently from
handshake.auth, headers, or query. - Authenticate at the door in
handleConnection, disconnecting invalid clients and attaching the user toclient.data. - Authorize per event with guards that switch to the
wscontext, including role checks viaReflectorand a@WsRolesdecorator. - Report errors through a
WsExceptionfilter, and handle expiry with re-verification or periodic checks.
Connection auth controls entry; event guards control actions — together they keep your realtime system secure.
Apprends TypeScript avec un tuteur IA — gratuit
Écris et exécute du vrai code dans ton navigateur, obtiens de l'aide instantanée d'un tuteur IA disponible 24h/24, et reprends là où tu t'es arrêté sur le web ou dans l'app.
- Cours
- 20
- Leçons
- 76
Questions Fréquemment Posées
La leçon « Authentifier et protéger les connexions Socket » est-elle gratuite ?
Oui — le texte complet de « Authentifier et protéger les connexions Socket » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours NestJS Enterprise Backend APIs, passe à CoddyKit PRO. Le cours NestJS Enterprise Backend APIs comprend 4 leçons au total.
Qu'est-ce que j'apprendrai dans « Authentifier et protéger les connexions Socket » ?
Appliquez des gardes et vérifiez les tokens lors de la négociation et des événements de messages pour sécuriser l’accès en temps réel. Tu pratiques NestJS Enterprise Backend APIs avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.
Dois-je avoir de l'expérience pour commencer NestJS Enterprise Backend APIs ?
Aucune expérience préalable n'est requise. NestJS Enterprise Backend APIs sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 2 sur 4.
Combien de temps prend la leçon « Authentifier et protéger les connexions Socket » ?
La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.
Peux-tu écrire et exécuter du code dans cette leçon NestJS Enterprise Backend APIs ?
Oui. Chaque leçon NestJS Enterprise Backend APIs inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.
Toutes les leçons de ce cours
- Passerelles WebSocket avec l’adaptateur Socket.IO
- Authentifier et protéger les connexions Socket
- Événements envoyés par le serveur pour une diffusion unidirectionnelle
- Mettre à l’échelle le temps réel avec un adaptateur Redis Pub/Sub