From 6403e386ab3a2ec5a994ad8f50fecdc73a8791c9 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Fri, 18 Dec 2020 19:07:31 +0100 Subject: [PATCH] Now with smooth access token refreshing ref #6 --- package.json | 3 +- src/authchecker.ts | 61 +++++++++++++++++++++---------- src/jwtcreator.ts | 6 +-- src/models/actions/RefreshAuth.ts | 2 +- 4 files changed, 47 insertions(+), 25 deletions(-) diff --git a/package.json b/package.json index c8a2473..b37638d 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "class-validator": "^0.12.2", "class-validator-jsonschema": "^2.0.3", "consola": "^2.15.0", + "cookie": "^0.4.1", "cookie-parser": "^1.4.5", "cors": "^2.8.5", "csvtojson": "^2.0.10", @@ -81,4 +82,4 @@ "docs/*" ] } -} \ No newline at end of file +} diff --git a/src/authchecker.ts b/src/authchecker.ts index 344e0c1..5243598 100644 --- a/src/authchecker.ts +++ b/src/authchecker.ts @@ -1,10 +1,13 @@ +import cookie from "cookie"; import * as jwt from "jsonwebtoken"; import { Action } from "routing-controllers"; import { getConnectionManager } from 'typeorm'; import { config } from './config'; import { IllegalJWTError, NoPermissionError, UserNonexistantOrRefreshtokenInvalidError } from './errors/AuthError'; +import { JwtCreator, JwtUser } from './JwtCreator'; import { User } from './models/entities/User'; + const authchecker = async (action: Action, permissions: string[] | string) => { let required_permissions = undefined; if (typeof permissions === "string") { @@ -13,34 +16,52 @@ const authchecker = async (action: Action, permissions: string[] | string) => { required_permissions = permissions } - const provided_token = action.request.headers["authorization"].replace("Bearer ", ""); + let provided_token = "" + action.request.headers["authorization"]; + try { + provided_token = provided_token.replace("Bearer ", ""); + } + catch { } let jwtPayload = undefined try { jwtPayload = jwt.verify(provided_token, config.jwt_secret); + jwtPayload = jwtPayload["userdetails"]; } catch (error) { - throw new IllegalJWTError() + jwtPayload = await refresh(action); } - const user = await getConnectionManager().get().getRepository(User).findOne({ id: jwtPayload["userdetails"]["id"], refreshTokenCount: jwtPayload["userdetails"]["refreshTokenCount"] }, { relations: ['permissions'] }) + const user = await getConnectionManager().get().getRepository(User).findOne({ id: jwtPayload["id"], refreshTokenCount: jwtPayload["refreshTokenCount"] }, { relations: ['permissions'] }) if (!user) { throw new UserNonexistantOrRefreshtokenInvalidError() } - if (!jwtPayload.permissions) { throw new NoPermissionError(); } + if (!jwtPayload["permissions"]) { throw new NoPermissionError(); } action.response.local = {} - action.response.local.jwtPayload = jwtPayload.permissions - required_permissions.forEach(r => { - const permission_key = r.split(":")[0] - const actual_accesslevel_for_permission = jwtPayload.permissions[permission_key] - const permission_access_level = r.split(":")[1] - if (actual_accesslevel_for_permission.includes(permission_access_level)) { - return true; - } else { - throw new NoPermissionError() - } - }); - try { - jwt.verify(provided_token, config.jwt_secret); - return true - } catch (error) { - return false + action.response.local.jwtPayload = jwtPayload; + for (let required_permission of required_permissions) { + if (!(jwtPayload["permissions"].includes(required_permission))) { return false; } } + return true; +} + +const refresh = async (action: Action) => { + let refresh_token = undefined; + try { + cookie.parse(action.request.headers["cookie"])["lfk_backend__refresh_token"]; + } + catch { + throw new IllegalJWTError(); + } + + let jwtPayload = undefined; + try { + jwtPayload = jwt.verify(refresh_token, config.jwt_secret); + } catch (error) { + throw new IllegalJWTError(); + } + + const user = await getConnectionManager().get().getRepository(User).findOne({ id: jwtPayload["id"], refreshTokenCount: jwtPayload["refreshTokenCount"] }, { relations: ['permissions'] }) + if (!user) { throw new UserNonexistantOrRefreshtokenInvalidError() } + + let newAccess = JwtCreator.createAccess(user); + action.response.header("authorization", "Bearer " + newAccess); + + return await new JwtUser(user); } export default authchecker \ No newline at end of file diff --git a/src/jwtcreator.ts b/src/jwtcreator.ts index d32d231..cb3269d 100644 --- a/src/jwtcreator.ts +++ b/src/jwtcreator.ts @@ -7,8 +7,8 @@ export class JwtCreator { public static createRefresh(user: User, expiry_timestamp?: number) { if (!expiry_timestamp) { expiry_timestamp = Math.floor(Date.now() / 1000) + 10 * 36000; } return jsonwebtoken.sign({ - refreshtokencount: user.refreshTokenCount, - userid: user.id, + refreshTokenCount: user.refreshTokenCount, + id: user.id, exp: expiry_timestamp }, config.jwt_secret) } @@ -22,7 +22,7 @@ export class JwtCreator { } } -class JwtUser { +export class JwtUser { @IsInt() id: number; diff --git a/src/models/actions/RefreshAuth.ts b/src/models/actions/RefreshAuth.ts index 757dea3..1435436 100644 --- a/src/models/actions/RefreshAuth.ts +++ b/src/models/actions/RefreshAuth.ts @@ -23,7 +23,7 @@ export class RefreshAuth { } catch (error) { throw new IllegalJWTError() } - const found_user = await getConnectionManager().get().getRepository(User).findOne({ id: decoded["userid"] }, { relations: ['groups', 'permissions'] }); + const found_user = await getConnectionManager().get().getRepository(User).findOne({ id: decoded["id"] }, { relations: ['groups', 'permissions'] }); if (!found_user) { throw new UserNotFoundError() }