Nest.jsでWebSocketの実装時、認証のGuardを貼りたいときなどGuardの中でcookieを取得したい時があります。
要はExecutionContextのどこからcookieを取り出すかですが他にも必要な設定が足りていないとcookie取得できないのでやり方をまとめていきます。
環境
今回動作確認する環境は以下。
バックエンド | Nest.js | |
クライアント | Next.js(App Router) |
バックエンドの作成
Nest.jsのバックエンドを設定します。
main.ts
でクライアントからのリクエストのcorsの許可設定、cookieのやり取りをするためにcredentials: true
を設定します。
corsの設定はGateway
で行うためmain.ts
はデフォルト。
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(8000);
}
bootstrap();
適当なGateway
を作成します。
import {
WebSocketGateway,
SubscribeMessage,
MessageBody,
WebSocketServer,
} from '@nestjs/websockets';
import { CreateWebsocketDto } from './dto/create-websocket.dto';
import { Server } from 'socket.io';
import { Logger } from '@nestjs/common';
@WebSocketGateway()
export class WebsocketGateway {
private readonly logger: Logger = new Logger('Gateway Log');
@WebSocketServer()
private readonly server: Server;
@SubscribeMessage('websocket')
websocket(@MessageBody() data: CreateWebsocketDto) {
this.logger.log(data);
this.server.emit('websocket', data);
}
}
受け取ったデータをログに出してそのままクライアントに渡してみました。
ここに適応するGuardを作成します。下記コマンドでファイル作成。
$ nest g gu auth
Guard
のcanActivate
メソッドの引数はcontext: ExecutionContext
のパラメータを受け取ることができ、現在の実行コンテキストに関する情報を提供します。
https://docs.nestjs.com/fundamentals/execution-context
websocketでcontextからcookieを受け取るには次のようにします。
import {
CanActivate,
ExecutionContext,
Injectable,
Logger,
} from '@nestjs/common';
import { Observable } from 'rxjs';
@Injectable()
export class AuthGuard implements CanActivate {
private readonly logger: Logger = new Logger('AuthGuard Log');
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
const client = context.switchToWs().getClient();
const cookies: string[] = client.handshake.headers.cookie.split('; ');
this.logger.log(cookies);
return true;
}
}
cookieが取得できているか確認したいだけなのでtrueを返しておきます。headersまでの取得はテンプレで覚えてしまってもいいような気がします。
このGuardを先ほどのGatewayに設定します。ついでにcorsの許可やcookieのやり取りに必要な設定も追加します。
import {
WebSocketGateway,
SubscribeMessage,
MessageBody,
WebSocketServer,
} from '@nestjs/websockets';
import { CreateWebsocketDto } from './dto/create-websocket.dto';
import { Server } from 'socket.io';
import { Logger, UseGuards } from '@nestjs/common';
import { AuthGuard } from 'src/auth/auth.guard';
@WebSocketGateway({
cors: {
// corsの許可
origin: 'http://localhost:3000',
// cookieのやり取りに必要な設定
credentials: true,
},
})
// 先ほど作成したGuardを設定
@UseGuards(new AuthGuard())
export class WebsocketGateway {
private readonly logger: Logger = new Logger('Gateway Log');
@WebSocketServer()
private readonly server: Server;
@SubscribeMessage('websocket')
websocket(@MessageBody() data: CreateWebsocketDto) {
this.logger.log(data);
this.server.emit('websocket', data);
}
}
バックエンドはこれで設定完了。
続いてReactでクライアントからデータを送信してみます。
クライアントの作成
socket.io-client
で受信してログに出力、ボタンでの送信を行います。
cookieの送信のためにwithCredentials: true
の設定を行います。
"use client";
import { useEffect } from "react";
import { io } from "socket.io-client";
export default function Home() {
const socket = io("http://localhost:8000", { withCredentials: true });
useEffect(() => {
socket.on("websocket", (data) => {
console.log(data);
});
}, []);
function handleClick() {
socket.emit("websocket", { message: "hello" });
}
return <button onClick={handleClick}>send</button>;
}
動作確認
http://localhost:3000/へアクセスし動作確認してみます。
ボタンを押すと、バックエンドのターミナルにcookieと送信したデータが表示されました。
[Nest] 13151 - 2024/02/10 19:52:45 LOG [AuthGuard Log] Object:
[
"_ga=xxxxxxxxxxxxxxxxxxxxxx"
]
[Nest] 13151 - 2024/02/10 19:52:45 LOG [Gateway Log] Object:
{
"message": "hello"
}
お仕事のお知らせはこちらからお願いいたします。
お問い合わせ