From a34800103616cfa414ceb343f7e84663084fc9aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Thu, 15 Oct 2020 16:48:42 +0200 Subject: [PATCH] Adding a new endpoint to verify the JWT token server-side before connecting --- back/src/Controller/AuthenticateController.ts | 41 +++++++++++++++++-- front/src/Connexion/ConnectionManager.ts | 26 +++++++++--- 2 files changed, 59 insertions(+), 8 deletions(-) diff --git a/back/src/Controller/AuthenticateController.ts b/back/src/Controller/AuthenticateController.ts index 4d950815..c298c684 100644 --- a/back/src/Controller/AuthenticateController.ts +++ b/back/src/Controller/AuthenticateController.ts @@ -3,6 +3,7 @@ import {HttpRequest, HttpResponse, TemplatedApp} from "uWebSockets.js"; import {BaseController} from "./BaseController"; import {adminApi} from "../Services/AdminApi"; import {jwtTokenManager} from "../Services/JWTTokenManager"; +import {parse} from "query-string"; export interface TokenInterface { userUuid: string @@ -13,11 +14,12 @@ export class AuthenticateController extends BaseController { constructor(private App : TemplatedApp) { super(); this.register(); + this.verify(); this.anonymLogin(); } //Try to login with an admin token - register(){ + private register(){ this.App.options("/register", (res: HttpResponse, req: HttpRequest) => { this.addCorsHeaders(res); @@ -36,7 +38,7 @@ export class AuthenticateController extends BaseController { //todo: what to do if the organizationMemberToken is already used? const organizationMemberToken:string|null = param.organizationMemberToken; - + try { if (typeof organizationMemberToken != 'string') throw new Error('No organization token'); const data = await adminApi.fetchMemberDataByToken(organizationMemberToken); @@ -68,8 +70,41 @@ export class AuthenticateController extends BaseController { } + private verify(){ + this.App.options("/verify", (res: HttpResponse, req: HttpRequest) => { + this.addCorsHeaders(res); + + res.end(); + }); + + this.App.get("/verify", (res: HttpResponse, req: HttpRequest) => { + (async () => { + this.addCorsHeaders(res); + + const query = parse(req.getQuery()); + + res.onAborted(() => { + console.warn('verify request was aborted'); + }) + + try { + await jwtTokenManager.createJWTToken(query.token as string); + } catch (e) { + res.writeStatus("400 Bad Request").end(JSON.stringify({ + "success": false, + "message": "Invalid JWT token" + })); + } + res.writeStatus("200 OK").end(JSON.stringify({ + "success": true + })); + })(); + }); + + } + //permit to login on application. Return token to connect on Websocket IO. - anonymLogin(){ + private anonymLogin(){ this.App.options("/anonymLogin", (res: HttpResponse, req: HttpRequest) => { this.addCorsHeaders(res); diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index 34731647..26432df5 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -35,11 +35,16 @@ class ConnectionManager { const localUser = localUserStore.getLocalUser(); if (localUser && localUser.jwtToken && localUser.uuid) { - this.localUser = localUser + this.localUser = localUser; + try { + await this.verifyToken(localUser.jwtToken); + } catch(e) { + // If the token is invalid, let's generate an anonymous one. + console.error('JWT token invalid. Did it expire? Login anonymously instead.'); + await this.anonymousLogin(); + } } else { - const data = await Axios.post(`${API_URL}/anonymLogin`).then(res => res.data); - this.localUser = new LocalUser(data.userUuid, data.authToken); - localUserStore.saveUser(this.localUser); + await this.anonymousLogin(); } let roomId: string if (connexionType === GameConnexionTypes.empty) { @@ -54,7 +59,8 @@ class ConnectionManager { const localUser = localUserStore.getLocalUser(); if (localUser) { - this.localUser = localUser + this.localUser = localUser; + await this.verifyToken(localUser.jwtToken); const room = new Room(window.location.pathname + window.location.hash); return Promise.resolve(room); } else { @@ -66,6 +72,16 @@ class ConnectionManager { return Promise.reject('Invalid URL'); } + private async verifyToken(token: string): Promise { + await Axios.get(`${API_URL}/verify`, {params: {token}}); + } + + private async anonymousLogin(): Promise { + const data = await Axios.post(`${API_URL}/anonymLogin`).then(res => res.data); + this.localUser = new LocalUser(data.userUuid, data.authToken); + localUserStore.saveUser(this.localUser); + } + public initBenchmark(): void { this.localUser = new LocalUser('', 'test'); }