From 69ff7a8ff7c1176c562a8fa5f180ad62bbbfa071 Mon Sep 17 00:00:00 2001 From: vchikalkin Date: Tue, 16 Jan 2024 12:32:42 +0300 Subject: [PATCH] merge fix/refresh-token --- .env | 3 +- apps/api/src/account/account.service.ts | 2 +- apps/api/src/app.controller.ts | 43 +++++++++++++++++-------- apps/api/src/config/cookie.ts | 2 +- apps/api/src/config/schema/env.ts | 1 + apps/api/src/schemas/account.schema.ts | 6 ++-- 6 files changed, 39 insertions(+), 18 deletions(-) diff --git a/.env b/.env index 98b13d2..c1a02c4 100644 --- a/.env +++ b/.env @@ -20,4 +20,5 @@ LDAP_ATTRIBUTE= API_SECRET= API_TOKEN_TTL= API_CACHE_TTL= -COOKIE_TOKEN_NAME=token \ No newline at end of file +COOKIE_TOKEN_NAME=token +COOKIE_TOKEN_MAX_AGE= \ No newline at end of file diff --git a/apps/api/src/account/account.service.ts b/apps/api/src/account/account.service.ts index 39ec7b5..efdb9d4 100644 --- a/apps/api/src/account/account.service.ts +++ b/apps/api/src/account/account.service.ts @@ -71,7 +71,7 @@ export class AccountService { const payload: TokenPayload = { username: login, - ...omit(account.toJSON(), ['password']), + ...omit(account.toJSON(), ['password', '_id', '__v']), }; return this.jwtService.sign(payload); diff --git a/apps/api/src/app.controller.ts b/apps/api/src/app.controller.ts index cc7bd9a..dd4263e 100644 --- a/apps/api/src/app.controller.ts +++ b/apps/api/src/app.controller.ts @@ -3,29 +3,46 @@ import { env } from './config/env'; import { Controller, Get, HttpStatus, Req, Res } from '@nestjs/common'; import { ApiExcludeController } from '@nestjs/swagger'; import { FastifyReply, FastifyRequest } from 'fastify'; +import { cookieOptions } from 'src/config/cookie'; @Controller() @ApiExcludeController() export class AppController { constructor(private readonly appService: AppService) {} + @Get('auth') public async auth(@Req() req: FastifyRequest, @Res() reply: FastifyReply) { + const token = req.cookies[env.COOKIE_TOKEN_NAME] || req.headers?.authorization?.split(' ')[1]; + if (!token) return reply.status(HttpStatus.UNAUTHORIZED).send(); + try { - const token = req.cookies[env.COOKIE_TOKEN_NAME] || req.headers.authorization.split(' ')[1]; + return this.handleDefaultCheck(req, reply, token); + } catch (error) { + const _err = error as Error; + const isTokenExpired = _err.name?.toLocaleLowerCase().includes('expired'); + const refreshToken = req.headers['refresh-token'] === '1'; - this.appService.checkToken(token); + if (isTokenExpired && refreshToken) return this.handleExpiredToken(req, reply, token); - reply.header('Authorization', `Bearer ${token}`); - - return reply.send(); - } catch { - // if (error.name === 'TokenExpiredError') { - // const newToken = this.appService.refreshToken(token); - - // return reply.setCookie(env.COOKIE_TOKEN_NAME, newToken, cookieOptions).send(); - // } - - return reply.status(HttpStatus.UNAUTHORIZED).send(); + return this.handleError(req, reply); } } + + private handleDefaultCheck(req: FastifyRequest, reply: FastifyReply, token: string) { + this.appService.checkToken(token); + reply.header('Authorization', `Bearer ${token}`); + + return reply.send(); + } + + private handleExpiredToken(req: FastifyRequest, reply: FastifyReply, token: string) { + const newToken = this.appService.refreshToken(token); + reply.header('Authorization', `Bearer ${newToken}`); + + return reply.setCookie(env.COOKIE_TOKEN_NAME, newToken, cookieOptions).send(); + } + + private handleError(req: FastifyRequest, reply: FastifyReply) { + return reply.status(HttpStatus.UNAUTHORIZED).send(); + } } diff --git a/apps/api/src/config/cookie.ts b/apps/api/src/config/cookie.ts index d9799cb..a12effb 100644 --- a/apps/api/src/config/cookie.ts +++ b/apps/api/src/config/cookie.ts @@ -3,7 +3,7 @@ import { env } from 'src/config/env'; export const cookieOptions: CookieSerializeOptions = { httpOnly: true, - maxAge: env.API_TOKEN_TTL, + maxAge: env.COOKIE_TOKEN_MAX_AGE, path: '/', secure: true, }; diff --git a/apps/api/src/config/schema/env.ts b/apps/api/src/config/schema/env.ts index 107f866..9ce3ca8 100644 --- a/apps/api/src/config/schema/env.ts +++ b/apps/api/src/config/schema/env.ts @@ -5,6 +5,7 @@ const envSchema = z.object({ API_PORT: z.number().optional().default(3001), API_SECRET: z.string(), API_TOKEN_TTL: z.string().transform((val) => Number.parseInt(val, 10)), + COOKIE_TOKEN_MAX_AGE: z.string().transform((val) => Number.parseInt(val, 10)), COOKIE_TOKEN_NAME: z.string().default('token'), LDAP_ATTRIBUTE: z.string(), LDAP_BASE: z.string(), diff --git a/apps/api/src/schemas/account.schema.ts b/apps/api/src/schemas/account.schema.ts index 2f57073..5f922fe 100644 --- a/apps/api/src/schemas/account.schema.ts +++ b/apps/api/src/schemas/account.schema.ts @@ -11,12 +11,14 @@ export class Account { @ApiResponseProperty() @ApiProperty() @Prop({ index: { unique: true }, required: true }) - username: string; + public username: string; @ApiResponseProperty() @ApiProperty() @Prop({ required: true }) - password: string; + public password: string; + + readonly [key: string]: unknown; } export const AccountSchema = SchemaFactory.createForClass(Account);