I was creating authentication mechanism for my service. And at some moment I had problem with cookies. More you can find here , so I solved this.
The problem was that I was trying to send cookie through 2 requests. My Next.js
front-end sends request to its internal API, and only then, internal API sends this request to back-end.
The solution of this problem was very easy, what I had to do - is to set cookie on back-end and return it in headers. Here is how flow looks, like.
This is how it looks like, endpoint in Next.js
front-end. Except of data in response, it receives header, where cookie is set (response from back-end) and send it in header of response, that will be send on front-end, where cookie will be set:
import { NextApiRequest, NextApiResponse } from "next";
import { AxiosError } from "axios";
import { api } from "../../../api";
export default async (
req: NextApiRequest,
res: NextApiResponse
) => {
try {
const { data, headers } = await api.post('/user/sign-in', req.body)
if (headers["set-cookie"]) {
res.setHeader("Set-Cookie", headers["set-cookie"]);
}
return res.json(data)
} catch (error) {
return res
.status((error as AxiosError).response?.status as number)
.json((error as AxiosError).response?.data);
}
}
And endpoint on back-end:
import { Response as Res } from 'express';
import * as dayjs from 'dayjs';
...
async signIn(@Body() signInUserDto: SignInUserDto, @Response() res: Res) {
const { _at, _rt } = await this.userService.signIn(signInUserDto);
res.cookie('_rt', _rt, {
httpOnly: true,
expires: dayjs().add(7, 'days').toDate()
});
return res.send(_at);
}
And here is the problem, because of this Response
class of express
I keep getting this warning:
Error: This is caused by either a bug in Node.js or incorrect usage of Node.js internals.
Please open an issue with this stack trace at https://github.com/nodejs/node/issues
at new NodeError (node:internal/errors:371:5)
at assert (node:internal/assert:14:11)
at ServerResponse.detachSocket (node:_http_server:249:3)
at resOnFinish (node:_http_server:819:7)
at ServerResponse.emit (node:events:390:28)
at onFinish (node:_http_outgoing:830:10)
at callback (node:internal/streams/writable:552:21)
at afterWrite (node:internal/streams/writable:497:5)
at afterWriteTick (node:internal/streams/writable:484:10)
at processTicksAndRejections (node:internal/process/task_queues:82:21)
It is definitely because of how this signIn
function looks like, because I was trying to return just like this - return this.userService.signIn(signInUserDto)
- and it worked, but I can't cookie in this case.
So, my question is - what is this error? Can I just ignore it? If not, then how can I fix it?
Thanks in advance!
Finally, I was able to fix this error, first of all, as I said, my goes through 2 API's, from back-end to front-end API, and only then, this front-end API sends this request to actual front-end.
So, what I did, is just returned 2 tokens - refresh and access - as body.
@ApiOperation({ summary: 'Resource for sign in user.' })
@ApiResponse({ status: 200, type: TokensDto })
@Post('/sign-in')
async signIn(@Body() signInUserDto: SignInUserDto) {
return this.userService.signIn(signInUserDto);
}
Then, on front-end, I installed cookie
and @types/cookie
and in this front-end endpoint, in headers, I just serialized this refresh token from body payload, and removed from it.
import { NextApiRequest, NextApiResponse } from "next";
import { AxiosError } from "axios";
import { api } from "../../../api";
import { serialize } from 'cookie';
export default async (
req: NextApiRequest,
res: NextApiResponse
) => {
try {
const { data } = await api.post('/user/sign-in', req.body)
res.setHeader('Set-Cookie', serialize(
'_rt',
data._rt,
{ path: '/', httpOnly: true })
);
delete data._rt
return res.json(data)
} catch (error) {
return res
.status((error as AxiosError).response?.status as number)
.json((error as AxiosError).response?.data);
}
}
And it works perfectly fine, I don't have this Node.js
error any more because of response with Express
response class, and I'm able to set cookie.
I have improved this code in even better way by using fastify
and in the whole pipeline cookie is set in header. First of all, on back-end install @fastify/cookie
and @nestjs/platform-fastify
. Then, add this in file, where you start you Nest.js
app:
import {
FastifyAdapter,
NestFastifyApplication
} from '@nestjs/platform-fastify';
import { fastifyCookie } from '@fastify/cookie';
async function bootstrap() {
const PORT = process.env.PORT || 3002;
const app = await NestFactory.create<NestFastifyApplication>(
AppModule,
new FastifyAdapter()
);
await app.register(fastifyCookie, {
secret: 'my-secret'
});
This will allow you to use FastifyReply
from fastify
, this will eliminate this Node.js
error as response class:
import { FastifyReply } from 'fastify';
@ApiTags('User')
@Controller('user')
export class UserController {
constructor(private userService: UserService) {}
@Post('/sign-in')
async signIn(
@Body() signInUserDto: SignInUserDto,
@Res({ passthrough: true }) res: FastifyReply
) {
const { _at, _rt } = await this.userService.signIn(signInUserDto);
res.setCookie('_rt', _rt);
return res.send(_at);
}
...
And the last step, on front-end endpoint, using cookie
, parse this cookie and send it to front.
const { data, headers } = await api.post('/user/sign-in', req.body)
if (headers["set-cookie"]) {
const refreshToken = headers["set-cookie"][0].split('=')[1];
res.setHeader('Set-Cookie', serialize(
'_rt', refreshToken, { path: '/', httpOnly: true })
);
}
return res.json(data)
And this is the best way, that I've found, because it allows you to send cookie in header though all pipeline, not in body and then delete it, and this solution eliminates this strange Node.js
error.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.