🔖
NestJSでリクエストのCookieを利用する方法
NestJSの学習をしていて、あるエラーに出会いました。
デバッグをして調べたら、どうやらリクエストのCookieを取得しようとしているところでCookieが上手く取得できていなかったことが原因のようです。
学習コンテンツ
GitHub
コード
main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';
import * as cookieParser from 'cookie-parser';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe({ whitelist: true }));
app.enableCors({
credentials: true,
origin: ['http://localhost:3000'],
});
await app.listen(3005);
}
bootstrap();
jwt.strategy.ts
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { PrismaService } from '../../prisma/prisma.service';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
constructor(
private readonly config: ConfigService,
private readonly prisma: PrismaService,
) {
super({
jwtFromRequest: ExtractJwt.fromExtractors([
(req) => {
let jwt = null;
if (req && req.cookies) {
jwt = req.cookies['access_token'];
}
return jwt;
},
]),
ignoreExpiration: false,
secretOrKey: config.get('JWT_SECRET'),
});
}
async validate(payload: { sub: number; email: string }) {
const user = await this.prisma.user.findUnique({
where: {
id: payload.sub,
},
});
delete user.hashedPassword;
return user;
}
}
UserController.ts
import { Controller, Get, Req, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { User } from '@prisma/client';
import { Request } from 'express';
import { UserService } from './user.service';
@UseGuards(AuthGuard('jwt'))
@Controller('user')
export class UserController {
constructor(private readonly userService: UserService) {}
@Get()
getLoginUser(@Req() req: Request): Omit<User, 'hashedPassword'> {
return req.user;
}
}
}
事象
今回は、Postmanで http://localhost:3005/user を実行したときに、想定動作にならず、下記のレスポンスとなりました。
{
"statusCode": 401,
"message": "Unauthorized"
}
想定動作
ログインしているユーザ情報がレスポンスされる
{
"id": 1,
"createdAt": "2023-01-13T09:52:30.509Z",
"updatedAt": "2023-01-14T04:58:35.679Z",
"email": "xxx@gmail.com",
"nickName": "とらのこ"
}
原因
デバッグをして調査したところ、jwt.strategy.ts
のreq.cookies
がNullで存在しないことが判明しました。
jwt.strategy.ts (一部抜粋)
super({
jwtFromRequest: ExtractJwt.fromExtractors([
(req) => {
let jwt = null;
if (req && req.cookies) {
jwt = req.cookies['access_token']; ⇦ req.cookiesがない!!
}
return jwt;
},
]),
ignoreExpiration: false,
secretOrKey: config.get('JWT_SECRET'),
});
そこで、NestJSでリクエストのCookieを取得できる方法を公式ドキュメントで確認することにしました。
NestJSでリクエストのCookieを利用する方法
まずは下記のとおり、必要なパッケージをインストールする。
$ npm i cookie-parser
$ npm i -D @types/cookie-parser
その後、ルートモジュールで下記のように定義する。
main.ts
import * as cookieParser from 'cookie-parser';
// somewhere in your initialization file
app.use(cookieParser());
これだけ・・??
パッケージのインストールはした覚えあるし、実際packege.json
を見てもちゃんとインストールされていました。
まさかと思い、main.ts
を見に行ったら、なんとapp.use(cookieParser());
の記載が抜けていました🥹 パッケージのインポートまでしているのに・・
非常に初歩的なミスでしたが、main.ts
にapp.use(cookieParser());
を追加したら、想定動作になりました!
修正後のコード
jwt.strategy.ts
とUserController.ts
は修正前と同一のため割愛します。
main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';
import * as cookieParser from 'cookie-parser';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.use(cookieParser()); ⇦ 追加!!
app.useGlobalPipes(new ValidationPipe({ whitelist: true }));
app.enableCors({
credentials: true,
origin: ['http://localhost:3000'],
});
await app.listen(3005);
}
bootstrap();
以上です。
Discussion