Merge branch 'master' of github.com:thecodingmachine/workadventure into develop
# Conflicts: # front/src/Connexion/ConnectionManager.ts # pusher/src/Controller/AuthenticateController.ts # pusher/src/Controller/IoSocketController.ts # pusher/src/Services/JWTTokenManager.ts
This commit is contained in:
commit
4e042389c5
@ -62,6 +62,7 @@
|
|||||||
} + (if adminUrl != null then {
|
} + (if adminUrl != null then {
|
||||||
"ADMIN_API_URL": adminUrl,
|
"ADMIN_API_URL": adminUrl,
|
||||||
"ADMIN_API_TOKEN": env.ADMIN_API_TOKEN,
|
"ADMIN_API_TOKEN": env.ADMIN_API_TOKEN,
|
||||||
|
"ADMIN_SOCKETS_TOKEN": env.ADMIN_SOCKETS_TOKEN,
|
||||||
} else {})
|
} else {})
|
||||||
},
|
},
|
||||||
"front": {
|
"front": {
|
||||||
|
@ -269,7 +269,7 @@ class IframeListener {
|
|||||||
|
|
||||||
registerScript(scriptUrl: string): Promise<void> {
|
registerScript(scriptUrl: string): Promise<void> {
|
||||||
return new Promise<void>((resolve, reject) => {
|
return new Promise<void>((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") {
|
if (!process.env.NODE_ENV || process.env.NODE_ENV === "development") {
|
||||||
// Using external iframe mode (
|
// Using external iframe mode (
|
||||||
|
@ -111,7 +111,7 @@ class ConnectionManager {
|
|||||||
|
|
||||||
this._currentRoom = await Room.createRoom(new URL(localUserStore.getLastRoomUrl()));
|
this._currentRoom = await Room.createRoom(new URL(localUserStore.getLastRoomUrl()));
|
||||||
try {
|
try {
|
||||||
await this.checkAuthUserConnexion();
|
await this.checkAuthUserConnexion(this._currentRoom.key);
|
||||||
analyticsClient.loggedWithSso();
|
analyticsClient.loggedWithSso();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
@ -177,6 +177,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
|
//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));
|
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.
|
//todo: add here some kind of warning if authToken has expired.
|
||||||
if (!this.authToken && !this._currentRoom.authenticationMandatory) {
|
if (!this.authToken && !this._currentRoom.authenticationMandatory) {
|
||||||
await this.anonymousLogin();
|
await this.anonymousLogin();
|
||||||
@ -293,7 +296,7 @@ class ConnectionManager {
|
|||||||
return this.connexionType;
|
return this.connexionType;
|
||||||
}
|
}
|
||||||
|
|
||||||
async checkAuthUserConnexion() {
|
async checkAuthUserConnexion(playUri: string) {
|
||||||
//set connected store for menu at false
|
//set connected store for menu at false
|
||||||
userIsConnected.set(false);
|
userIsConnected.set(false);
|
||||||
|
|
||||||
@ -310,10 +313,12 @@ class ConnectionManager {
|
|||||||
throw "No Auth code provided";
|
throw "No Auth code provided";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const { authToken } = await Axios.get(`${PUSHER_URL}/login-callback`, { params: { code, nonce, token } }).then(
|
const { authToken, userUuid, textures, email } = await Axios.get(`${PUSHER_URL}/login-callback`, { params: { code, nonce, token } }).then(
|
||||||
(res) => res.data
|
(res) => res.data
|
||||||
);
|
);
|
||||||
localUserStore.setAuthToken(authToken);
|
localUserStore.setAuthToken(authToken);
|
||||||
|
this.localUser = new LocalUser(userUuid, textures, email);
|
||||||
|
localUserStore.saveUser(this.localUser);
|
||||||
this.authToken = authToken;
|
this.authToken = authToken;
|
||||||
|
|
||||||
//user connected, set connected store for menu at true
|
//user connected, set connected store for menu at true
|
||||||
|
@ -67,6 +67,9 @@ function createChatMessagesStore() {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
addPersonnalMessage(text: string) {
|
addPersonnalMessage(text: string) {
|
||||||
|
//post message iframe listener
|
||||||
|
iframeListener.sendUserInputChat(text);
|
||||||
|
|
||||||
newChatMessageStore.set(text);
|
newChatMessageStore.set(text);
|
||||||
update((list) => {
|
update((list) => {
|
||||||
const lastMessage = list[list.length - 1];
|
const lastMessage = list[list.length - 1];
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { v4 } from "uuid";
|
import { v4 } from "uuid";
|
||||||
import { HttpRequest, HttpResponse, TemplatedApp } from "uWebSockets.js";
|
import { HttpRequest, HttpResponse, TemplatedApp } from "uWebSockets.js";
|
||||||
import { BaseController } from "./BaseController";
|
import { BaseController } from "./BaseController";
|
||||||
import { adminApi } from "../Services/AdminApi";
|
import { adminApi, FetchMemberDataByUuidResponse } from "../Services/AdminApi";
|
||||||
import { AuthTokenData, jwtTokenManager } from "../Services/JWTTokenManager";
|
import { AuthTokenData, jwtTokenManager } from "../Services/JWTTokenManager";
|
||||||
import { parse } from "query-string";
|
import { parse } from "query-string";
|
||||||
import { openIDClient } from "../Services/OpenIDClient";
|
import { openIDClient } from "../Services/OpenIDClient";
|
||||||
@ -56,7 +56,8 @@ export class AuthenticateController extends BaseController {
|
|||||||
res.onAborted(() => {
|
res.onAborted(() => {
|
||||||
console.warn("/message request was aborted");
|
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 {
|
try {
|
||||||
//verify connected by token
|
//verify connected by token
|
||||||
if (token != undefined) {
|
if (token != undefined) {
|
||||||
@ -68,7 +69,7 @@ export class AuthenticateController extends BaseController {
|
|||||||
const resCheckTokenAuth = await openIDClient.checkTokenAuth(authTokenData.accessToken);
|
const resCheckTokenAuth = await openIDClient.checkTokenAuth(authTokenData.accessToken);
|
||||||
res.writeStatus("200");
|
res.writeStatus("200");
|
||||||
this.addCorsHeaders(res);
|
this.addCorsHeaders(res);
|
||||||
return res.end(JSON.stringify({ authToken: token }));
|
return res.end(JSON.stringify({ ...data, authToken: token }));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.info("User was not connected", err);
|
console.info("User was not connected", err);
|
||||||
}
|
}
|
||||||
@ -81,9 +82,14 @@ export class AuthenticateController extends BaseController {
|
|||||||
throw new Error("No email in the response");
|
throw new Error("No email in the response");
|
||||||
}
|
}
|
||||||
const authToken = jwtTokenManager.createAuthToken(email, userInfo.access_token);
|
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");
|
res.writeStatus("200");
|
||||||
this.addCorsHeaders(res);
|
this.addCorsHeaders(res);
|
||||||
return res.end(JSON.stringify({ authToken }));
|
return res.end(JSON.stringify({ ...data, authToken }));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("openIDCallback => ERROR", e);
|
console.error("openIDCallback => ERROR", e);
|
||||||
return this.errorToResponse(e, res);
|
return this.errorToResponse(e, res);
|
||||||
@ -229,4 +235,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<FetchMemberDataByUuidResponse | object> {
|
||||||
|
let data: FetchMemberDataByUuidResponse | object = {};
|
||||||
|
try {
|
||||||
|
data = await adminApi.fetchMemberDataByUuid(email, playUri, IPAddress);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("openIDCallback => fetchMemberDataByUuid", err);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,10 +26,9 @@ import { jwtTokenManager, tokenInvalidException } from "../Services/JWTTokenMana
|
|||||||
import { adminApi, FetchMemberDataByUuidResponse } from "../Services/AdminApi";
|
import { adminApi, FetchMemberDataByUuidResponse } from "../Services/AdminApi";
|
||||||
import { SocketManager, socketManager } from "../Services/SocketManager";
|
import { SocketManager, socketManager } from "../Services/SocketManager";
|
||||||
import { emitInBatch } from "../Services/IoSocketHelpers";
|
import { emitInBatch } from "../Services/IoSocketHelpers";
|
||||||
import { ADMIN_API_TOKEN, ADMIN_API_URL, DISABLE_ANONYMOUS, SOCKET_IDLE_TIMER } from "../Enum/EnvironmentVariable";
|
import { ADMIN_SOCKETS_TOKEN, ADMIN_API_URL, DISABLE_ANONYMOUS, SOCKET_IDLE_TIMER } from "../Enum/EnvironmentVariable";
|
||||||
import { Zone } from "_Model/Zone";
|
import { Zone } from "_Model/Zone";
|
||||||
import { ExAdminSocketInterface } from "_Model/Websocket/ExAdminSocketInterface";
|
import { ExAdminSocketInterface } from "_Model/Websocket/ExAdminSocketInterface";
|
||||||
import { v4 } from "uuid";
|
|
||||||
import { CharacterTexture } from "../Services/AdminApi/CharacterTexture";
|
import { CharacterTexture } from "../Services/AdminApi/CharacterTexture";
|
||||||
|
|
||||||
export class IoSocketController {
|
export class IoSocketController {
|
||||||
@ -48,15 +47,19 @@ export class IoSocketController {
|
|||||||
const websocketProtocol = req.getHeader("sec-websocket-protocol");
|
const websocketProtocol = req.getHeader("sec-websocket-protocol");
|
||||||
const websocketExtensions = req.getHeader("sec-websocket-extensions");
|
const websocketExtensions = req.getHeader("sec-websocket-extensions");
|
||||||
const token = query.token;
|
const token = query.token;
|
||||||
if (token !== ADMIN_API_TOKEN) {
|
let authorizedRoomIds: string[];
|
||||||
console.log("Admin access refused for token: " + token);
|
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");
|
res.writeStatus("401 Unauthorized").end("Incorrect token");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const roomId = query.roomId;
|
const roomId = query.roomId;
|
||||||
if (typeof roomId !== "string") {
|
if (typeof roomId !== "string" || !authorizedRoomIds.includes(roomId)) {
|
||||||
console.error("Received");
|
console.error("Invalid room id");
|
||||||
res.writeStatus("400 Bad Request").end("Missing room id");
|
res.writeStatus("403 Bad Request").end("Invalid room id");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,8 +73,6 @@ export class IoSocketController {
|
|||||||
},
|
},
|
||||||
message: (ws, arrayBuffer, isBinary): void => {
|
message: (ws, arrayBuffer, isBinary): void => {
|
||||||
try {
|
try {
|
||||||
const roomId = ws.roomId as string;
|
|
||||||
|
|
||||||
//TODO refactor message type and data
|
//TODO refactor message type and data
|
||||||
const message: { event: string; message: { type: string; message: unknown; userUuid: string } } =
|
const message: { event: string; message: { type: string; message: unknown; userUuid: string } } =
|
||||||
JSON.parse(new TextDecoder("utf-8").decode(new Uint8Array(arrayBuffer)));
|
JSON.parse(new TextDecoder("utf-8").decode(new Uint8Array(arrayBuffer)));
|
||||||
|
@ -4,6 +4,7 @@ const API_URL = process.env.API_URL || "";
|
|||||||
const ADMIN_API_URL = process.env.ADMIN_API_URL || "";
|
const ADMIN_API_URL = process.env.ADMIN_API_URL || "";
|
||||||
const ADMIN_URL = process.env.ADMIN_URL || "";
|
const ADMIN_URL = process.env.ADMIN_URL || "";
|
||||||
const ADMIN_API_TOKEN = process.env.ADMIN_API_TOKEN || "myapitoken";
|
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 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_URL: string | undefined = process.env.JITSI_URL === "" ? undefined : process.env.JITSI_URL;
|
||||||
const JITSI_ISS = process.env.JITSI_ISS || "";
|
const JITSI_ISS = process.env.JITSI_ISS || "";
|
||||||
|
@ -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 { uuid } from "uuidv4";
|
||||||
import Jwt, { verify } from "jsonwebtoken";
|
import Jwt, { verify } from "jsonwebtoken";
|
||||||
import { TokenInterface } from "../Controller/AuthenticateController";
|
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
|
identifier: string; //will be a email if logged in or an uuid if anonymous
|
||||||
accessToken?: string;
|
accessToken?: string;
|
||||||
}
|
}
|
||||||
|
export interface AdminSocketTokenData {
|
||||||
|
authorizedRoomIds: string[]; //the list of rooms the client is authorized to read from.
|
||||||
|
}
|
||||||
export const tokenInvalidException = "tokenInvalid";
|
export const tokenInvalidException = "tokenInvalid";
|
||||||
|
|
||||||
class JWTTokenManager {
|
class JWTTokenManager {
|
||||||
|
public verifyAdminSocketToken(token: string): AdminSocketTokenData {
|
||||||
|
return Jwt.verify(token, ADMIN_SOCKETS_TOKEN) as AdminSocketTokenData;
|
||||||
|
}
|
||||||
|
|
||||||
public createAuthToken(identifier: string, accessToken?: string) {
|
public createAuthToken(identifier: string, accessToken?: string) {
|
||||||
return Jwt.sign({ identifier, accessToken }, SECRET_KEY, { expiresIn: "30d" });
|
return Jwt.sign({ identifier, accessToken }, SECRET_KEY, { expiresIn: "30d" });
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user