merge fix/refresh-token

This commit is contained in:
vchikalkin 2024-01-16 12:32:42 +03:00
parent 946d977db8
commit 69ff7a8ff7
6 changed files with 39 additions and 18 deletions

3
.env
View File

@ -20,4 +20,5 @@ LDAP_ATTRIBUTE=
API_SECRET=
API_TOKEN_TTL=
API_CACHE_TTL=
COOKIE_TOKEN_NAME=token
COOKIE_TOKEN_NAME=token
COOKIE_TOKEN_MAX_AGE=

View File

@ -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);

View File

@ -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();
}
}

View File

@ -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,
};

View File

@ -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(),

View File

@ -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);