BoardKitGuides
Custom Auth Guard
Step-by-step guide to implementing BoardAuthGuard
This guide walks through implementing a custom BoardAuthGuard for different authentication strategies.
The Interface
interface AuthenticatedUser {
userId: string;
displayName: string;
}
abstract class BoardAuthGuard {
abstract validateConnection(token: string): Promise<AuthenticatedUser | null>;
abstract validateRequest(token: string): Promise<AuthenticatedUser | null>;
}validateConnection— Called when a WebSocket client sends ajoinmessage. Thetokencomes from theJoinMessage.tokenfield.validateRequest— Called for HTTP requests. Thetokenis extracted from theAuthorization: Bearer <token>header.
Return null to reject the request, or return an AuthenticatedUser object to allow it.
Example: JWT Authentication
import { BoardAuthGuard, type AuthenticatedUser } from '@hfu.digital/boardkit-nestjs';
import { verify } from 'jsonwebtoken';
interface JwtPayload {
sub: string;
name: string;
exp: number;
}
export class JwtAuthGuard extends BoardAuthGuard {
constructor(private readonly secret: string) {
super();
}
async validateConnection(token: string): Promise<AuthenticatedUser | null> {
return this.validate(token);
}
async validateRequest(token: string): Promise<AuthenticatedUser | null> {
return this.validate(token);
}
private validate(token: string): AuthenticatedUser | null {
try {
const payload = verify(token, this.secret) as JwtPayload;
return {
userId: payload.sub,
displayName: payload.name,
};
} catch {
return null;
}
}
}Example: API Key Authentication
export class ApiKeyAuthGuard extends BoardAuthGuard {
constructor(private readonly apiKeys: Map<string, { userId: string; displayName: string }>) {
super();
}
async validateConnection(token: string): Promise<AuthenticatedUser | null> {
return this.apiKeys.get(token) ?? null;
}
async validateRequest(token: string): Promise<AuthenticatedUser | null> {
return this.apiKeys.get(token) ?? null;
}
}Example: OAuth / External Auth Service
export class OAuthAuthGuard extends BoardAuthGuard {
constructor(private readonly authServiceUrl: string) {
super();
}
async validateConnection(token: string): Promise<AuthenticatedUser | null> {
return this.validate(token);
}
async validateRequest(token: string): Promise<AuthenticatedUser | null> {
return this.validate(token);
}
private async validate(token: string): Promise<AuthenticatedUser | null> {
const res = await fetch(`${this.authServiceUrl}/verify`, {
headers: { Authorization: `Bearer ${token}` },
});
if (!res.ok) return null;
const user = await res.json();
return {
userId: user.id,
displayName: user.name,
};
}
}Registration
BoardModule.register({
// ...
authGuard: new JwtAuthGuard(process.env.JWT_SECRET!),
})Frontend Integration
The authToken passed to BoardKitProvider is sent:
- As the
Authorization: Bearer <token>header on all HTTP requests - As the
tokenfield in WebSocketjoinmessages
<BoardKitProvider config={{ apiUrl: '/api', wsUrl: 'ws://localhost:3000/board', authToken: jwtToken }}>
<App />
</BoardKitProvider>Testing
Use MockAuthGuard to skip authentication in tests:
import { MockAuthGuard } from '@hfu.digital/boardkit-nestjs';
BoardModule.register({
// ...
authGuard: new MockAuthGuard(),
// Accepts any token, returns { userId: 'test-user', displayName: 'Test User' }
})