From f87422187f1ca06011373027a461dfeba3ec2477 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Thu, 14 Oct 2021 17:22:43 +0200 Subject: [PATCH 1/7] HotFix user data connection Create local store for user connected in SSO Signed-off-by: Gregoire Parant --- front/src/Connexion/ConnectionManager.ts | 19 +++++--- .../src/Controller/AuthenticateController.ts | 44 +++++++++++++++++-- 2 files changed, 53 insertions(+), 10 deletions(-) diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index b346f450..b85e045e 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -98,7 +98,7 @@ class ConnectionManager { localUserStore.setCode(code); this._currentRoom = await Room.createRoom(new URL(localUserStore.getLastRoomUrl())); try { - await this.checkAuthUserConnexion(); + await this.checkAuthUserConnexion(this._currentRoom.key); analyticsClient.loggedWithSso(); } catch (err) { console.error(err); @@ -169,7 +169,7 @@ class ConnectionManager { await this.anonymousLogin(); } else { try { - await this.checkAuthUserConnexion(); + await this.checkAuthUserConnexion(this._currentRoom.key); } catch (err) { console.error(err); } @@ -275,7 +275,7 @@ class ConnectionManager { return this.connexionType; } - async checkAuthUserConnexion() { + async checkAuthUserConnexion(playUri: string) { //set connected store for menu at false userIsConnected.set(false); @@ -289,10 +289,17 @@ class ConnectionManager { } const nonce = localUserStore.getNonce(); const token = localUserStore.getAuthToken(); - const { authToken } = await Axios.get(`${PUSHER_URL}/login-callback`, { params: { code, nonce, token } }).then( - (res) => res.data - ); + const { authToken, userUuid, tags, textures, emails } = await Axios.get(`${PUSHER_URL}/login-callback`, { + params: { code, nonce, token, playUri }, + }).then((res) => res.data); localUserStore.setAuthToken(authToken); + const localUser: LocalUser = { + uuid: userUuid, + textures: textures, + email: emails, + }; + this.localUser = new LocalUser(userUuid, textures, emails); + localUserStore.saveUser(this.localUser); this.authToken = authToken; //user connected, set connected store for menu at true diff --git a/pusher/src/Controller/AuthenticateController.ts b/pusher/src/Controller/AuthenticateController.ts index 972cc102..3d01d30c 100644 --- a/pusher/src/Controller/AuthenticateController.ts +++ b/pusher/src/Controller/AuthenticateController.ts @@ -1,7 +1,7 @@ import { v4 } from "uuid"; import { HttpRequest, HttpResponse, TemplatedApp } from "uWebSockets.js"; import { BaseController } from "./BaseController"; -import { adminApi } from "../Services/AdminApi"; +import { adminApi, FetchMemberDataByUuidResponse } from "../Services/AdminApi"; import { AuthTokenData, jwtTokenManager } from "../Services/JWTTokenManager"; import { parse } from "query-string"; import { openIDClient } from "../Services/OpenIDClient"; @@ -55,7 +55,8 @@ export class AuthenticateController extends BaseController { res.onAborted(() => { console.warn("/message request was aborted"); }); - const { code, nonce, token } = parse(req.getQuery()); + const IPAddress = req.getHeader("x-forwarded-for"); + const { code, nonce, token, playUri } = parse(req.getQuery()); try { //verify connected by token if (token != undefined) { @@ -65,9 +66,17 @@ export class AuthenticateController extends BaseController { throw Error("Token cannot to be check on Hydra"); } await openIDClient.checkTokenAuth(authTokenData.hydraAccessToken); + + //Get user data from Admin Back Office + //This is very important to create User Local in LocalStorage in WorkAdventure + const data = await this.getUserByUserIdentifier( + authTokenData.identifier, + playUri as string, + IPAddress + ); res.writeStatus("200"); this.addCorsHeaders(res); - return res.end(JSON.stringify({ authToken: token })); + return res.end(JSON.stringify({ ...data, authToken: token })); } catch (err) { console.info("User was not connected", err); } @@ -80,9 +89,14 @@ export class AuthenticateController extends BaseController { throw new Error("No email in the response"); } const authToken = jwtTokenManager.createAuthToken(email, userInfo.access_token); + + //Get user data from Admin Back Office + //This is very important to create User Local in LocalStorage in WorkAdventure + const data = await this.getUserByUserIdentifier(email, playUri as string, IPAddress); + res.writeStatus("200"); this.addCorsHeaders(res); - return res.end(JSON.stringify({ authToken })); + return res.end(JSON.stringify({ ...data, authToken })); } catch (e) { console.error("openIDCallback => ERROR", e); return this.errorToResponse(e, res); @@ -223,4 +237,26 @@ export class AuthenticateController extends BaseController { } }); } + + /** + * + * @param email + * @param playUri + * @param IPAddress + * @return FetchMemberDataByUuidResponse|object + * @private + */ + private async getUserByUserIdentifier( + email: string, + playUri: string, + IPAddress: string + ): Promise { + let data: FetchMemberDataByUuidResponse | object = {}; + try { + data = await adminApi.fetchMemberDataByUuid(email, playUri, IPAddress); + } catch (err) { + console.error("openIDCallback => fetchMemberDataByUuid", err); + } + return data; + } } From b7692dd3550ad1ec1b5f3726795cadbd2ba5ca4e Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Thu, 14 Oct 2021 17:25:36 +0200 Subject: [PATCH 2/7] Fix local user useless Signed-off-by: Gregoire Parant --- front/src/Connexion/ConnectionManager.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index b85e045e..06c20a5c 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -293,11 +293,6 @@ class ConnectionManager { params: { code, nonce, token, playUri }, }).then((res) => res.data); localUserStore.setAuthToken(authToken); - const localUser: LocalUser = { - uuid: userUuid, - textures: textures, - email: emails, - }; this.localUser = new LocalUser(userUuid, textures, emails); localUserStore.saveUser(this.localUser); this.authToken = authToken; From 497a7c3467aed14577ce1762f9197614095b410e Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Thu, 14 Oct 2021 17:33:53 +0200 Subject: [PATCH 3/7] Fix typo emails => email Signed-off-by: Gregoire Parant --- front/src/Connexion/ConnectionManager.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index 06c20a5c..1995759e 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -289,11 +289,11 @@ class ConnectionManager { } const nonce = localUserStore.getNonce(); const token = localUserStore.getAuthToken(); - const { authToken, userUuid, tags, textures, emails } = await Axios.get(`${PUSHER_URL}/login-callback`, { + const { authToken, userUuid, textures, email } = await Axios.get(`${PUSHER_URL}/login-callback`, { params: { code, nonce, token, playUri }, }).then((res) => res.data); localUserStore.setAuthToken(authToken); - this.localUser = new LocalUser(userUuid, textures, emails); + this.localUser = new LocalUser(userUuid, textures, email); localUserStore.saveUser(this.localUser); this.authToken = authToken; From d809682c08c71f9860b883c70de66275fe8ec6f3 Mon Sep 17 00:00:00 2001 From: Kharhamel Date: Tue, 26 Oct 2021 14:58:34 +0200 Subject: [PATCH 4/7] HOTFIX: now uses a specific secret token for the admin sockets --- deeployer.libsonnet | 1 + pusher/src/Controller/IoSocketController.ts | 5 ++--- pusher/src/Enum/EnvironmentVariable.ts | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/deeployer.libsonnet b/deeployer.libsonnet index d3320bc0..c4b34e38 100644 --- a/deeployer.libsonnet +++ b/deeployer.libsonnet @@ -62,6 +62,7 @@ } + (if adminUrl != null then { "ADMIN_API_URL": adminUrl, "ADMIN_API_TOKEN": env.ADMIN_API_TOKEN, + "ADMIN_SOCKETS_TOKEN": env.ADMIN_SOCKETS_TOKEN, } else {}) }, "front": { diff --git a/pusher/src/Controller/IoSocketController.ts b/pusher/src/Controller/IoSocketController.ts index 0466100c..c2aded67 100644 --- a/pusher/src/Controller/IoSocketController.ts +++ b/pusher/src/Controller/IoSocketController.ts @@ -26,10 +26,9 @@ import { jwtTokenManager, tokenInvalidException } from "../Services/JWTTokenMana import { adminApi, FetchMemberDataByUuidResponse } from "../Services/AdminApi"; import { SocketManager, socketManager } from "../Services/SocketManager"; import { emitInBatch } from "../Services/IoSocketHelpers"; -import { ADMIN_API_TOKEN, ADMIN_API_URL, SOCKET_IDLE_TIMER } from "../Enum/EnvironmentVariable"; +import { ADMIN_SOCKETS_TOKEN, ADMIN_API_URL, SOCKET_IDLE_TIMER } from "../Enum/EnvironmentVariable"; import { Zone } from "_Model/Zone"; import { ExAdminSocketInterface } from "_Model/Websocket/ExAdminSocketInterface"; -import { v4 } from "uuid"; import { CharacterTexture } from "../Services/AdminApi/CharacterTexture"; export class IoSocketController { @@ -48,7 +47,7 @@ export class IoSocketController { const websocketProtocol = req.getHeader("sec-websocket-protocol"); const websocketExtensions = req.getHeader("sec-websocket-extensions"); const token = query.token; - if (token !== ADMIN_API_TOKEN) { + if (token !== ADMIN_SOCKETS_TOKEN) { console.log("Admin access refused for token: " + token); res.writeStatus("401 Unauthorized").end("Incorrect token"); return; diff --git a/pusher/src/Enum/EnvironmentVariable.ts b/pusher/src/Enum/EnvironmentVariable.ts index ab1ce110..ad369a17 100644 --- a/pusher/src/Enum/EnvironmentVariable.ts +++ b/pusher/src/Enum/EnvironmentVariable.ts @@ -4,6 +4,7 @@ const API_URL = process.env.API_URL || ""; const ADMIN_API_URL = process.env.ADMIN_API_URL || ""; const ADMIN_URL = process.env.ADMIN_URL || ""; const ADMIN_API_TOKEN = process.env.ADMIN_API_TOKEN || "myapitoken"; +export const ADMIN_SOCKETS_TOKEN = process.env.ADMIN_SOCKETS_TOKEN || "myapitoken"; const CPU_OVERHEAT_THRESHOLD = Number(process.env.CPU_OVERHEAT_THRESHOLD) || 80; const JITSI_URL: string | undefined = process.env.JITSI_URL === "" ? undefined : process.env.JITSI_URL; const JITSI_ISS = process.env.JITSI_ISS || ""; From f8ae189cee2e0f1d8133fcde39b220da5d1e045e Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Wed, 27 Oct 2021 14:29:39 +0200 Subject: [PATCH 5/7] HotFix handler message in chat store Signed-off-by: Gregoire Parant --- front/src/Api/IframeListener.ts | 2 +- front/src/Stores/ChatStore.ts | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/front/src/Api/IframeListener.ts b/front/src/Api/IframeListener.ts index 5a9aca85..668b2e94 100644 --- a/front/src/Api/IframeListener.ts +++ b/front/src/Api/IframeListener.ts @@ -284,7 +284,7 @@ class IframeListener { registerScript(scriptUrl: string): Promise { return new Promise((resolve, reject) => { - console.log("Loading map related script at ", scriptUrl); + console.info("Loading map related script at ", scriptUrl); if (!process.env.NODE_ENV || process.env.NODE_ENV === "development") { // Using external iframe mode ( diff --git a/front/src/Stores/ChatStore.ts b/front/src/Stores/ChatStore.ts index feb1f3ec..0cda645d 100644 --- a/front/src/Stores/ChatStore.ts +++ b/front/src/Stores/ChatStore.ts @@ -1,6 +1,7 @@ import { writable } from "svelte/store"; import { playersStore } from "./PlayersStore"; import type { PlayerInterface } from "../Phaser/Game/PlayerInterface"; +import { iframeListener } from "../Api/IframeListener"; export const chatVisibilityStore = writable(false); export const chatInputFocusStore = writable(false); @@ -66,6 +67,9 @@ function createChatMessagesStore() { }); }, addPersonnalMessage(text: string) { + //post message iframe listener + iframeListener.sendUserInputChat(text); + newChatMessageStore.set(text); update((list) => { const lastMessage = list[list.length - 1]; From 6c0d8942e54081adf5e25dcfa2858a1611b12da3 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Wed, 27 Oct 2021 16:48:33 +0200 Subject: [PATCH 6/7] HotFix set last room for first connexion Signed-off-by: Gregoire Parant --- front/src/Connexion/ConnectionManager.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index 1995759e..70d29f08 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -164,6 +164,9 @@ class ConnectionManager { //before set token of user we must load room and all information. For example the mandatory authentication could be require on current room this._currentRoom = await Room.createRoom(new URL(roomPath)); + //defined last room url this room path + localUserStore.setLastRoomUrl(this._currentRoom.key); + //todo: add here some kind of warning if authToken has expired. if (!this.authToken && !this._currentRoom.authenticationMandatory) { await this.anonymousLogin(); From 6c78717d9790775fb05575ee3dfc02f56b4c484b Mon Sep 17 00:00:00 2001 From: Kharhamel Date: Thu, 28 Oct 2021 14:53:07 +0200 Subject: [PATCH 7/7] FIX: the admin sockets now uses a short live to check room authorization --- pusher/src/Controller/IoSocketController.ts | 16 +++++++++------- pusher/src/Services/JWTTokenManager.ts | 9 ++++++++- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/pusher/src/Controller/IoSocketController.ts b/pusher/src/Controller/IoSocketController.ts index c2aded67..7cfd9bd7 100644 --- a/pusher/src/Controller/IoSocketController.ts +++ b/pusher/src/Controller/IoSocketController.ts @@ -47,15 +47,19 @@ export class IoSocketController { const websocketProtocol = req.getHeader("sec-websocket-protocol"); const websocketExtensions = req.getHeader("sec-websocket-extensions"); const token = query.token; - if (token !== ADMIN_SOCKETS_TOKEN) { - console.log("Admin access refused for token: " + token); + let authorizedRoomIds: string[]; + try { + const data = jwtTokenManager.verifyAdminSocketToken(token as string); + authorizedRoomIds = data.authorizedRoomIds; + } catch (e) { + console.error("Admin access refused for token: " + token); res.writeStatus("401 Unauthorized").end("Incorrect token"); return; } const roomId = query.roomId; - if (typeof roomId !== "string") { - console.error("Received"); - res.writeStatus("400 Bad Request").end("Missing room id"); + if (typeof roomId !== "string" || !authorizedRoomIds.includes(roomId)) { + console.error("Invalid room id"); + res.writeStatus("403 Bad Request").end("Invalid room id"); return; } @@ -69,8 +73,6 @@ export class IoSocketController { }, message: (ws, arrayBuffer, isBinary): void => { try { - const roomId = ws.roomId as string; - //TODO refactor message type and data const message: { event: string; message: { type: string; message: unknown; userUuid: string } } = JSON.parse(new TextDecoder("utf-8").decode(new Uint8Array(arrayBuffer))); diff --git a/pusher/src/Services/JWTTokenManager.ts b/pusher/src/Services/JWTTokenManager.ts index 24393084..fe418475 100644 --- a/pusher/src/Services/JWTTokenManager.ts +++ b/pusher/src/Services/JWTTokenManager.ts @@ -1,4 +1,4 @@ -import { ADMIN_API_URL, ALLOW_ARTILLERY, SECRET_KEY } from "../Enum/EnvironmentVariable"; +import { ADMIN_API_URL, ADMIN_SOCKETS_TOKEN, ALLOW_ARTILLERY, SECRET_KEY } from "../Enum/EnvironmentVariable"; import { uuid } from "uuidv4"; import Jwt, { verify } from "jsonwebtoken"; import { TokenInterface } from "../Controller/AuthenticateController"; @@ -8,9 +8,16 @@ export interface AuthTokenData { identifier: string; //will be a email if logged in or an uuid if anonymous hydraAccessToken?: string; } +export interface AdminSocketTokenData { + authorizedRoomIds: string[]; //the list of rooms the client is authorized to read from. +} export const tokenInvalidException = "tokenInvalid"; class JWTTokenManager { + public verifyAdminSocketToken(token: string): AdminSocketTokenData { + return Jwt.verify(token, ADMIN_SOCKETS_TOKEN) as AdminSocketTokenData; + } + public createAuthToken(identifier: string, hydraAccessToken?: string) { return Jwt.sign({ identifier, hydraAccessToken }, SECRET_KEY, { expiresIn: "30d" }); }