token refresh

This commit is contained in:
Julian Krauser 2024-08-23 14:42:47 +02:00
parent bcfba8b448
commit 55caf69bf0
4 changed files with 102 additions and 8 deletions

View file

@ -3,6 +3,6 @@ export interface CreateRefreshCommand {
} }
export interface DeleteRefreshCommand { export interface DeleteRefreshCommand {
id: number; token: string;
userId: number; userId: number;
} }

View file

@ -2,9 +2,10 @@ import { dataSource } from "../data-source";
import { refresh } from "../entity/refresh"; import { refresh } from "../entity/refresh";
import InternalException from "../exceptions/internalException"; import InternalException from "../exceptions/internalException";
import { JWTHelper } from "../helpers/jwtHelper"; import { JWTHelper } from "../helpers/jwtHelper";
import { StringHelper } from "../helpers/stringHelper";
import UserService from "../service/userService"; import UserService from "../service/userService";
import { JWTRefresh } from "../type/jwtTypes"; import { JWTRefresh } from "../type/jwtTypes";
import { CreateRefreshCommand } from "./refreshCommand"; import { CreateRefreshCommand, DeleteRefreshCommand } from "./refreshCommand";
import ms from "ms"; import ms from "ms";
export default abstract class RefreshCommandHandler { export default abstract class RefreshCommandHandler {
@ -14,10 +15,11 @@ export default abstract class RefreshCommandHandler {
* @returns {Promise<string>} * @returns {Promise<string>}
*/ */
static async create(createRefresh: CreateRefreshCommand): Promise<string> { static async create(createRefresh: CreateRefreshCommand): Promise<string> {
let createRefreshToken: JWTRefresh = { // let createRefreshToken: JWTRefresh = {
userId: createRefresh.userId, // userId: createRefresh.userId,
}; // };
const refreshToken = await JWTHelper.create(createRefreshToken); // const refreshToken = await JWTHelper.create(createRefreshToken);
const refreshToken = StringHelper.random(32);
return await dataSource return await dataSource
.createQueryBuilder() .createQueryBuilder()
@ -26,7 +28,7 @@ export default abstract class RefreshCommandHandler {
.values({ .values({
token: refreshToken, token: refreshToken,
user: await UserService.getById(createRefresh.userId), user: await UserService.getById(createRefresh.userId),
expiry: ms(process.env.REFRESH_EXPIRATION), expiry: new Date(Date.now() + ms(process.env.REFRESH_EXPIRATION)),
}) })
.execute() .execute()
.then((result) => { .then((result) => {
@ -36,4 +38,23 @@ export default abstract class RefreshCommandHandler {
throw new InternalException("Failed saving refresh token"); throw new InternalException("Failed saving refresh token");
}); });
} }
/**
* @description delete refresh by user and token
* @param DeleteRefreshCommand
* @returns {Promise<refresh>}
*/
static async deleteByToken(deleteRefresh: DeleteRefreshCommand): Promise<any> {
return await dataSource
.createQueryBuilder()
.delete()
.from(refresh)
.where("refresh.token = :token", { token: deleteRefresh.token })
.andWhere("refresh.userId = :userId", { userId: deleteRefresh.userId })
.execute()
.then((res) => {})
.catch((err) => {
throw new InternalException("failed refresh removal");
});
}
} }

View file

@ -1,6 +1,6 @@
import { Request, Response } from "express"; import { Request, Response } from "express";
import { JWTHelper } from "../helpers/jwtHelper"; import { JWTHelper } from "../helpers/jwtHelper";
import { JWTToken } from "../type/jwtTypes"; import { JWTData, JWTToken } from "../type/jwtTypes";
import InternalException from "../exceptions/internalException"; import InternalException from "../exceptions/internalException";
import RefreshCommandHandler from "../command/refreshCommandHandler"; import RefreshCommandHandler from "../command/refreshCommandHandler";
import { CreateRefreshCommand } from "../command/refreshCommand"; import { CreateRefreshCommand } from "../command/refreshCommand";
@ -10,6 +10,8 @@ import UnauthorizedRequestException from "../exceptions/unauthorizedRequestExcep
import QRCode from "qrcode"; import QRCode from "qrcode";
import { CreateUserCommand } from "../command/userCommand"; import { CreateUserCommand } from "../command/userCommand";
import UserCommandHandler from "../command/userCommandHandler"; import UserCommandHandler from "../command/userCommandHandler";
import RefreshService from "../service/refreshService";
import BadRequestException from "../exceptions/badRequestException";
/** /**
* @description Check authentication status by token * @description Check authentication status by token
@ -80,6 +82,51 @@ export async function logout(req: Request, res: Response): Promise<any> {}
export async function refresh(req: Request, res: Response): Promise<any> { export async function refresh(req: Request, res: Response): Promise<any> {
let token = req.body.token; let token = req.body.token;
let refresh = req.body.refresh; let refresh = req.body.refresh;
const tokenUser = await JWTHelper.decode(token);
if (typeof tokenUser == "string" || !tokenUser) {
throw new InternalException("process failed");
}
let tokenUserId = (tokenUser as JWTToken).userId;
let { user } = await RefreshService.getByToken(refresh);
if (tokenUserId != user.id) {
throw new UnauthorizedRequestException("user not identified with token and refresh");
}
let { id, username } = await UserService.getById(tokenUserId);
let jwtData: JWTToken = {
userId: id,
username: username,
rights: [],
};
let accessToken: string;
let refreshToken: string;
JWTHelper.create(jwtData)
.then((result) => {
accessToken = result;
})
.catch((err) => {
console.log(err);
throw new InternalException("Failed accessToken creation");
});
let refreshCommand: CreateRefreshCommand = {
userId: id,
};
refreshToken = await RefreshCommandHandler.create(refreshCommand);
await RefreshCommandHandler.deleteByToken(refresh);
res.json({
accessToken,
refreshToken,
});
} }
/** /**

View file

@ -0,0 +1,26 @@
import { dataSource } from "../data-source";
import { refresh } from "../entity/refresh";
import InternalException from "../exceptions/internalException";
export default abstract class RefreshService {
/**
* @description get refresh by token
* @param token string
* @returns {Promise<refresh>}
*/
static async getByToken(token: string): Promise<refresh> {
return await dataSource
.getRepository(refresh)
.createQueryBuilder("refresh")
.leftJoinAndSelect("refresh.user", "user")
.where("refresh.token = :token", { token: token })
.andWhere("refresh.expiry >= :expiry", { expiry: new Date() })
.getOneOrFail()
.then((res) => {
return res;
})
.catch((err) => {
throw new InternalException("refresh not found");
});
}
}