Squashed commit of the following:
commit41748a4036
Merge:3b1d4d63
4991a70b
Author: grégoire parant <g.parant@thecodingmachine.com> Date: Mon Aug 2 21:38:37 2021 +0200 Merge pull request #1327 from thecodingmachine/hotFixErrorCardBack Fix error generated commit4991a70bba
Author: Gregoire Parant <g.parant@thecodingmachine.com> Date: Mon Aug 2 21:34:03 2021 +0200 Fix error generated Don't generate error if file is Invalid commit3b1d4d630c
Merge:f52b4598
02e5860e
Author: grégoire parant <g.parant@thecodingmachine.com> Date: Mon Aug 2 21:03:18 2021 +0200 Merge pull request #1326 from thecodingmachine/HotFixCreateMapFeature Hot fix create map feature commit02e5860e43
Author: Gregoire Parant <g.parant@thecodingmachine.com> Date: Mon Aug 2 20:59:13 2021 +0200 HotFix redirect on production domain of WorkAdventure - Update domain `ADMIN_URL` by `workadventu.re` commitf52b459872
Merge:3d657b4a
3ab069d6
Author: grégoire parant <g.parant@thecodingmachine.com> Date: Mon Aug 2 11:23:16 2021 +0200 Merge pull request #1324 from thecodingmachine/develop Release v1.4.11 commit3ab069d650
Merge:2b748138
9d4ffe54
Author: Kharhamel <Kharhamel@users.noreply.github.com> Date: Fri Jul 30 15:51:07 2021 +0200 Merge pull request #1323 from thecodingmachine/openIDPoc FIX: bomp the node version of pusher commit9d4ffe542c
Author: kharhamel <oognic@gmail.com> Date: Fri Jul 30 15:50:30 2021 +0200 FIX: bomp the node version of pusher commit2b7481383f
Merge:74975ac9
9c803a69
Author: Kharhamel <Kharhamel@users.noreply.github.com> Date: Fri Jul 30 15:48:56 2021 +0200 Merge pull request #1251 from thecodingmachine/openIDPoc POC for the openID connect commit9c803a69ff
Author: kharhamel <oognic@gmail.com> Date: Tue Jul 27 16:37:01 2021 +0200 FEATURE: users can now login via an openID client commit74975ac9d8
Merge:315fe7ca
ebdcf880
Author: Kharhamel <Kharhamel@users.noreply.github.com> Date: Fri Jul 30 14:54:33 2021 +0200 Merge pull request #1322 from thecodingmachine/improveCapacityWarning FEATURE: improved the room capacity warning visuals commitebdcf8804d
Author: kharhamel <oognic@gmail.com> Date: Fri Jul 30 14:08:27 2021 +0200 added admin link to the warning container commit41ac51f291
Author: kharhamel <oognic@gmail.com> Date: Thu Jul 29 18:02:36 2021 +0200 FEATURE: improved the room capacity warning visuals commit315fe7ca82
Author: David Négrier <d.negrier@thecodingmachine.com> Date: Thu Jul 29 17:49:51 2021 +0200 Adding a "font-family" property for text objects. (#1311) - Tiled displays your system fonts. - Computers have different sets of fonts. Therefore, browsers never rely on system fonts - Which means if you select a font in Tiled, it is quite unlikely it will render properly in WorkAdventure To circumvent this problem, in your text object in Tiled, you can now add an additional property: `font-family`. The `font-family` property can contain any "web-font" that can be loaded by your browser. This allows us to use the "Press Start 2P" 8px font in text objects, which renders way better than the default "Sans serif" font of your browser. commit7ffe564e8e
Author: GRL78 <80678534+GRL78@users.noreply.github.com> Date: Thu Jul 29 17:42:16 2021 +0200 Graphic upgrade of the global message console (#1287) * Graphic upgrade of the global message console Fix: error if LoginScene doesn't exist * Rework graphic of global message console * Rework graphic of global message console * Remove console.log commit2a1af2a131
Author: grégoire parant <g.parant@thecodingmachine.com> Date: Thu Jul 29 16:42:31 2021 +0200 PWA service workers (#1319) * PWA services worker - [x] Register service worker of PWA to install WorkAdventure application on desktop and mobile - [x] Create webpage specifique for PWA - [ ] Add register service to save and redirect on a card - [ ] Add possibilities to install PWA for one World (with register token if existing) * Finish PWA strategy to load last map visited * Fix feedback @Kharhamel * Fix feedback @Kharhamel
This commit is contained in:
parent
6ac25d344b
commit
c177f0a1b3
54 changed files with 1469 additions and 1901 deletions
|
@ -49,6 +49,7 @@
|
|||
"grpc": "^1.24.4",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"mkdirp": "^1.0.4",
|
||||
"openid-client": "^4.7.4",
|
||||
"prom-client": "^12.0.0",
|
||||
"query-string": "^6.13.3",
|
||||
"uWebSockets.js": "uNetworking/uWebSockets.js#v18.5.0",
|
||||
|
|
|
@ -4,6 +4,7 @@ import { BaseController } from "./BaseController";
|
|||
import { adminApi } from "../Services/AdminApi";
|
||||
import { jwtTokenManager } from "../Services/JWTTokenManager";
|
||||
import { parse } from "query-string";
|
||||
import { openIDClient } from "../Services/OpenIDClient";
|
||||
|
||||
export interface TokenInterface {
|
||||
userUuid: string;
|
||||
|
@ -12,11 +13,58 @@ export interface TokenInterface {
|
|||
export class AuthenticateController extends BaseController {
|
||||
constructor(private App: TemplatedApp) {
|
||||
super();
|
||||
this.openIDLogin();
|
||||
this.openIDCallback();
|
||||
this.register();
|
||||
this.verify();
|
||||
this.anonymLogin();
|
||||
}
|
||||
|
||||
openIDLogin() {
|
||||
//eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
this.App.get("/login-screen", async (res: HttpResponse, req: HttpRequest) => {
|
||||
res.onAborted(() => {
|
||||
console.warn("/message request was aborted");
|
||||
});
|
||||
|
||||
const { nonce, state } = parse(req.getQuery());
|
||||
if (!state || !nonce) {
|
||||
res.writeStatus("400 Unauthorized").end("missing state and nonce URL parameters");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const loginUri = await openIDClient.authorizationUrl(state as string, nonce as string);
|
||||
res.writeStatus("302");
|
||||
res.writeHeader("Location", loginUri);
|
||||
return res.end();
|
||||
} catch (e) {
|
||||
return this.errorToResponse(e, res);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
openIDCallback() {
|
||||
//eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
this.App.get("/login-callback", async (res: HttpResponse, req: HttpRequest) => {
|
||||
res.onAborted(() => {
|
||||
console.warn("/message request was aborted");
|
||||
});
|
||||
const { code, nonce } = parse(req.getQuery());
|
||||
try {
|
||||
const userInfo = await openIDClient.getUserInfo(code as string, nonce as string);
|
||||
const email = userInfo.email || userInfo.sub;
|
||||
if (!email) {
|
||||
throw new Error("No email in the response");
|
||||
}
|
||||
const authToken = jwtTokenManager.createAuthToken(email);
|
||||
res.writeStatus("200");
|
||||
this.addCorsHeaders(res);
|
||||
return res.end(JSON.stringify({ authToken }));
|
||||
} catch (e) {
|
||||
return this.errorToResponse(e, res);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//Try to login with an admin token
|
||||
private register() {
|
||||
this.App.options("/register", (res: HttpResponse, req: HttpRequest) => {
|
||||
|
@ -39,11 +87,12 @@ export class AuthenticateController extends BaseController {
|
|||
if (typeof organizationMemberToken != "string") throw new Error("No organization token");
|
||||
const data = await adminApi.fetchMemberDataByToken(organizationMemberToken);
|
||||
const userUuid = data.userUuid;
|
||||
const email = data.email;
|
||||
const roomUrl = data.roomUrl;
|
||||
const mapUrlStart = data.mapUrlStart;
|
||||
const textures = data.textures;
|
||||
|
||||
const authToken = jwtTokenManager.createJWTToken(userUuid);
|
||||
const authToken = jwtTokenManager.createAuthToken(email || userUuid);
|
||||
res.writeStatus("200 OK");
|
||||
this.addCorsHeaders(res);
|
||||
res.end(
|
||||
|
@ -63,45 +112,6 @@ 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 () => {
|
||||
const query = parse(req.getQuery());
|
||||
|
||||
res.onAborted(() => {
|
||||
console.warn("verify request was aborted");
|
||||
});
|
||||
|
||||
try {
|
||||
await jwtTokenManager.getUserUuidFromToken(query.token as string);
|
||||
} catch (e) {
|
||||
res.writeStatus("400 Bad Request");
|
||||
this.addCorsHeaders(res);
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
success: false,
|
||||
message: "Invalid JWT token",
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
res.writeStatus("200 OK");
|
||||
this.addCorsHeaders(res);
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
success: true,
|
||||
})
|
||||
);
|
||||
})();
|
||||
});
|
||||
}
|
||||
|
||||
//permit to login on application. Return token to connect on Websocket IO.
|
||||
private anonymLogin() {
|
||||
this.App.options("/anonymLogin", (res: HttpResponse, req: HttpRequest) => {
|
||||
|
@ -115,7 +125,7 @@ export class AuthenticateController extends BaseController {
|
|||
});
|
||||
|
||||
const userUuid = v4();
|
||||
const authToken = jwtTokenManager.createJWTToken(userUuid);
|
||||
const authToken = jwtTokenManager.createAuthToken(userUuid);
|
||||
res.writeStatus("200 OK");
|
||||
this.addCorsHeaders(res);
|
||||
res.end(
|
||||
|
|
|
@ -22,7 +22,7 @@ import {
|
|||
import { UserMovesMessage } from "../Messages/generated/messages_pb";
|
||||
import { TemplatedApp } from "uWebSockets.js";
|
||||
import { parse } from "query-string";
|
||||
import { jwtTokenManager } from "../Services/JWTTokenManager";
|
||||
import { jwtTokenManager, tokenInvalidException } from "../Services/JWTTokenManager";
|
||||
import { adminApi, FetchMemberDataByUuidResponse } from "../Services/AdminApi";
|
||||
import { SocketManager, socketManager } from "../Services/SocketManager";
|
||||
import { emitInBatch } from "../Services/IoSocketHelpers";
|
||||
|
@ -173,31 +173,34 @@ export class IoSocketController {
|
|||
characterLayers = [characterLayers];
|
||||
}
|
||||
|
||||
const userUuid = await jwtTokenManager.getUserUuidFromToken(token, IPAddress, roomId);
|
||||
const tokenData =
|
||||
token && typeof token === "string" ? jwtTokenManager.decodeJWTToken(token) : null;
|
||||
const userIdentifier = tokenData ? tokenData.identifier : "";
|
||||
|
||||
let memberTags: string[] = [];
|
||||
let memberVisitCardUrl: string | null = null;
|
||||
let memberMessages: unknown;
|
||||
let memberTextures: CharacterTexture[] = [];
|
||||
const room = await socketManager.getOrCreateRoom(roomId);
|
||||
let userData: FetchMemberDataByUuidResponse = {
|
||||
userUuid: userIdentifier,
|
||||
tags: [],
|
||||
visitCardUrl: null,
|
||||
textures: [],
|
||||
messages: [],
|
||||
anonymous: true,
|
||||
};
|
||||
if (ADMIN_API_URL) {
|
||||
try {
|
||||
let userData: FetchMemberDataByUuidResponse = {
|
||||
uuid: v4(),
|
||||
tags: [],
|
||||
visitCardUrl: null,
|
||||
textures: [],
|
||||
messages: [],
|
||||
anonymous: true,
|
||||
};
|
||||
try {
|
||||
userData = await adminApi.fetchMemberDataByUuid(userUuid, roomId);
|
||||
userData = await adminApi.fetchMemberDataByUuid(userIdentifier, roomId, IPAddress);
|
||||
} catch (err) {
|
||||
if (err?.response?.status == 404) {
|
||||
// If we get an HTTP 404, the token is invalid. Let's perform an anonymous login!
|
||||
|
||||
console.warn(
|
||||
'Cannot find user with uuid "' +
|
||||
userUuid +
|
||||
'Cannot find user with email "' +
|
||||
(userIdentifier || "anonymous") +
|
||||
'". Performing an anonymous login instead.'
|
||||
);
|
||||
} else if (err?.response?.status == 403) {
|
||||
|
@ -235,7 +238,12 @@ export class IoSocketController {
|
|||
throw new Error("Use the login URL to connect");
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("access not granted for user " + userUuid + " and room " + roomId);
|
||||
console.log(
|
||||
"access not granted for user " +
|
||||
(userIdentifier || "anonymous") +
|
||||
" and room " +
|
||||
roomId
|
||||
);
|
||||
console.error(e);
|
||||
throw new Error("User cannot access this world");
|
||||
}
|
||||
|
@ -257,7 +265,7 @@ export class IoSocketController {
|
|||
// Data passed here is accessible on the "websocket" socket object.
|
||||
url,
|
||||
token,
|
||||
userUuid,
|
||||
userUuid: userData.userUuid,
|
||||
IPAddress,
|
||||
roomId,
|
||||
name,
|
||||
|
@ -287,15 +295,10 @@ export class IoSocketController {
|
|||
context
|
||||
);
|
||||
} catch (e) {
|
||||
/*if (e instanceof Error) {
|
||||
console.log(e.message);
|
||||
res.writeStatus("401 Unauthorized").end(e.message);
|
||||
} else {
|
||||
res.writeStatus("500 Internal Server Error").end('An error occurred');
|
||||
}*/
|
||||
return res.upgrade(
|
||||
res.upgrade(
|
||||
{
|
||||
rejected: true,
|
||||
reason: e.reason || null,
|
||||
message: e.message ? e.message : "500 Internal Server Error",
|
||||
},
|
||||
websocketKey,
|
||||
|
@ -310,12 +313,14 @@ export class IoSocketController {
|
|||
open: (ws) => {
|
||||
if (ws.rejected === true) {
|
||||
//FIX ME to use status code
|
||||
if (ws.message === "World is full") {
|
||||
if (ws.reason === tokenInvalidException) {
|
||||
socketManager.emitTokenExpiredMessage(ws);
|
||||
} else if (ws.message === "World is full") {
|
||||
socketManager.emitWorldFullMessage(ws);
|
||||
} else {
|
||||
socketManager.emitConnexionErrorMessage(ws, ws.message as string);
|
||||
}
|
||||
ws.close();
|
||||
setTimeout(() => ws.close(), 0);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
const SECRET_KEY = process.env.SECRET_KEY || "THECODINGMACHINE_SECRET_KEY";
|
||||
const MINIMUM_DISTANCE = process.env.MINIMUM_DISTANCE ? Number(process.env.MINIMUM_DISTANCE) : 64;
|
||||
const GROUP_RADIUS = process.env.GROUP_RADIUS ? Number(process.env.GROUP_RADIUS) : 48;
|
||||
const ALLOW_ARTILLERY = process.env.ALLOW_ARTILLERY ? process.env.ALLOW_ARTILLERY == "true" : false;
|
||||
const API_URL = process.env.API_URL || "";
|
||||
const ADMIN_API_URL = process.env.ADMIN_API_URL || "";
|
||||
const ADMIN_API_TOKEN = process.env.ADMIN_API_TOKEN || "myapitoken";
|
||||
const MAX_USERS_PER_ROOM = parseInt(process.env.MAX_USERS_PER_ROOM || "") || 600;
|
||||
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 || "";
|
||||
|
@ -13,14 +10,16 @@ const SECRET_JITSI_KEY = process.env.SECRET_JITSI_KEY || "";
|
|||
const PUSHER_HTTP_PORT = parseInt(process.env.PUSHER_HTTP_PORT || "8080") || 8080;
|
||||
export const SOCKET_IDLE_TIMER = parseInt(process.env.SOCKET_IDLE_TIMER as string) || 30; // maximum time (in second) without activity before a socket is closed
|
||||
|
||||
export const FRONT_URL = process.env.FRONT_URL || "http://localhost";
|
||||
export const OPID_CLIENT_ID = process.env.OPID_CLIENT_ID || "";
|
||||
export const OPID_CLIENT_SECRET = process.env.OPID_CLIENT_SECRET || "";
|
||||
export const OPID_CLIENT_ISSUER = process.env.OPID_CLIENT_ISSUER || "";
|
||||
|
||||
export {
|
||||
SECRET_KEY,
|
||||
MINIMUM_DISTANCE,
|
||||
API_URL,
|
||||
ADMIN_API_URL,
|
||||
ADMIN_API_TOKEN,
|
||||
MAX_USERS_PER_ROOM,
|
||||
GROUP_RADIUS,
|
||||
ALLOW_ARTILLERY,
|
||||
CPU_OVERHEAT_THRESHOLD,
|
||||
JITSI_URL,
|
||||
|
|
|
@ -7,6 +7,7 @@ import { RoomRedirect } from "./AdminApi/RoomRedirect";
|
|||
|
||||
export interface AdminApiData {
|
||||
roomUrl: string;
|
||||
email: string | null;
|
||||
mapUrlStart: string;
|
||||
tags: string[];
|
||||
policy_type: number;
|
||||
|
@ -21,7 +22,7 @@ export interface AdminBannedData {
|
|||
}
|
||||
|
||||
export interface FetchMemberDataByUuidResponse {
|
||||
uuid: string;
|
||||
userUuid: string;
|
||||
tags: string[];
|
||||
visitCardUrl: string | null;
|
||||
textures: CharacterTexture[];
|
||||
|
@ -46,12 +47,16 @@ class AdminApi {
|
|||
return res.data;
|
||||
}
|
||||
|
||||
async fetchMemberDataByUuid(uuid: string, roomId: string): Promise<FetchMemberDataByUuidResponse> {
|
||||
async fetchMemberDataByUuid(
|
||||
userIdentifier: string | null,
|
||||
roomId: string,
|
||||
ipAddress: string
|
||||
): Promise<FetchMemberDataByUuidResponse> {
|
||||
if (!ADMIN_API_URL) {
|
||||
return Promise.reject(new Error("No admin backoffice set!"));
|
||||
}
|
||||
const res = await Axios.get(ADMIN_API_URL + "/api/room/access", {
|
||||
params: { uuid, roomId },
|
||||
params: { userIdentifier, roomId, ipAddress },
|
||||
headers: { Authorization: `${ADMIN_API_TOKEN}` },
|
||||
});
|
||||
return res.data;
|
||||
|
|
|
@ -1,100 +1,25 @@
|
|||
import { ADMIN_API_URL, ALLOW_ARTILLERY, SECRET_KEY } from "../Enum/EnvironmentVariable";
|
||||
import { uuid } from "uuidv4";
|
||||
import Jwt from "jsonwebtoken";
|
||||
import Jwt, { verify } from "jsonwebtoken";
|
||||
import { TokenInterface } from "../Controller/AuthenticateController";
|
||||
import { adminApi, AdminBannedData } from "../Services/AdminApi";
|
||||
|
||||
export interface AuthTokenData {
|
||||
identifier: string; //will be a email if logged in or an uuid if anonymous
|
||||
}
|
||||
export const tokenInvalidException = "tokenInvalid";
|
||||
|
||||
class JWTTokenManager {
|
||||
public createJWTToken(userUuid: string) {
|
||||
return Jwt.sign({ userUuid: userUuid }, SECRET_KEY, { expiresIn: "200d" }); //todo: add a mechanic to refresh or recreate token
|
||||
public createAuthToken(identifier: string) {
|
||||
return Jwt.sign({ identifier }, SECRET_KEY, { expiresIn: "3d" });
|
||||
}
|
||||
|
||||
public async getUserUuidFromToken(token: unknown, ipAddress?: string, roomUrl?: string): Promise<string> {
|
||||
if (!token) {
|
||||
throw new Error("An authentication error happened, a user tried to connect without a token.");
|
||||
public decodeJWTToken(token: string): AuthTokenData {
|
||||
try {
|
||||
return Jwt.verify(token, SECRET_KEY, { ignoreExpiration: false }) as AuthTokenData;
|
||||
} catch (e) {
|
||||
throw { reason: tokenInvalidException, message: e.message };
|
||||
}
|
||||
if (typeof token !== "string") {
|
||||
throw new Error("Token is expected to be a string");
|
||||
}
|
||||
|
||||
if (token === "test") {
|
||||
if (ALLOW_ARTILLERY) {
|
||||
return uuid();
|
||||
} else {
|
||||
throw new Error(
|
||||
"In order to perform a load-testing test on this environment, you must set the ALLOW_ARTILLERY environment variable to 'true'"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
Jwt.verify(token, SECRET_KEY, {}, (err, tokenDecoded) => {
|
||||
const tokenInterface = tokenDecoded as TokenInterface;
|
||||
if (err) {
|
||||
console.error("An authentication error happened, invalid JsonWebToken.", err);
|
||||
reject(new Error("An authentication error happened, invalid JsonWebToken. " + err.message));
|
||||
return;
|
||||
}
|
||||
if (tokenDecoded === undefined) {
|
||||
console.error("Empty token found.");
|
||||
reject(new Error("Empty token found."));
|
||||
return;
|
||||
}
|
||||
|
||||
//verify token
|
||||
if (!this.isValidToken(tokenInterface)) {
|
||||
reject(new Error("Authentication error, invalid token structure."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (ADMIN_API_URL) {
|
||||
//verify user in admin
|
||||
let promise = new Promise((resolve) => resolve());
|
||||
if (ipAddress && roomUrl) {
|
||||
promise = this.verifyBanUser(tokenInterface.userUuid, ipAddress, roomUrl);
|
||||
}
|
||||
promise
|
||||
.then(() => {
|
||||
adminApi
|
||||
.fetchCheckUserByToken(tokenInterface.userUuid)
|
||||
.then(() => {
|
||||
resolve(tokenInterface.userUuid);
|
||||
})
|
||||
.catch((err) => {
|
||||
//anonymous user
|
||||
if (err.response && err.response.status && err.response.status === 404) {
|
||||
resolve(tokenInterface.userUuid);
|
||||
return;
|
||||
}
|
||||
reject(err);
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
reject(err);
|
||||
});
|
||||
} else {
|
||||
resolve(tokenInterface.userUuid);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private verifyBanUser(userUuid: string, ipAddress: string, roomUrl: string): Promise<AdminBannedData> {
|
||||
return adminApi
|
||||
.verifyBanUser(userUuid, ipAddress, roomUrl)
|
||||
.then((data: AdminBannedData) => {
|
||||
if (data && data.is_banned) {
|
||||
throw new Error("User was banned");
|
||||
}
|
||||
return data;
|
||||
})
|
||||
.catch((err) => {
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
|
||||
private isValidToken(token: object): token is TokenInterface {
|
||||
return !(typeof (token as TokenInterface).userUuid !== "string");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
43
pusher/src/Services/OpenIDClient.ts
Normal file
43
pusher/src/Services/OpenIDClient.ts
Normal file
|
@ -0,0 +1,43 @@
|
|||
import { Issuer, Client } from "openid-client";
|
||||
import { OPID_CLIENT_ID, OPID_CLIENT_SECRET, OPID_CLIENT_ISSUER, FRONT_URL } from "../Enum/EnvironmentVariable";
|
||||
|
||||
const opidRedirectUri = FRONT_URL + "/jwt";
|
||||
|
||||
class OpenIDClient {
|
||||
private issuerPromise: Promise<Client> | null = null;
|
||||
|
||||
private initClient(): Promise<Client> {
|
||||
if (!this.issuerPromise) {
|
||||
this.issuerPromise = Issuer.discover(OPID_CLIENT_ISSUER).then((issuer) => {
|
||||
return new issuer.Client({
|
||||
client_id: OPID_CLIENT_ID,
|
||||
client_secret: OPID_CLIENT_SECRET,
|
||||
redirect_uris: [opidRedirectUri],
|
||||
response_types: ["code"],
|
||||
});
|
||||
});
|
||||
}
|
||||
return this.issuerPromise;
|
||||
}
|
||||
|
||||
public authorizationUrl(state: string, nonce: string) {
|
||||
return this.initClient().then((client) => {
|
||||
return client.authorizationUrl({
|
||||
scope: "openid email",
|
||||
prompt: "login",
|
||||
state: state,
|
||||
nonce: nonce,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public getUserInfo(code: string, nonce: string): Promise<{ email: string; sub: string }> {
|
||||
return this.initClient().then((client) => {
|
||||
return client.callback(opidRedirectUri, { code }, { nonce }).then((tokenSet) => {
|
||||
return client.userinfo(tokenSet);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const openIDClient = new OpenIDClient();
|
|
@ -28,6 +28,7 @@ import {
|
|||
UserLeftRoomMessage,
|
||||
AdminMessage,
|
||||
BanMessage,
|
||||
TokenExpiredMessage,
|
||||
RefreshRoomMessage,
|
||||
EmotePromptMessage,
|
||||
VariableMessage,
|
||||
|
@ -117,7 +118,7 @@ export class SocketManager implements ZoneEventListener {
|
|||
console.warn("Admin connection lost to back server");
|
||||
// Let's close the front connection if the back connection is closed. This way, we can retry connecting from the start.
|
||||
if (!client.disconnecting) {
|
||||
this.closeWebsocketConnection(client, 1011, "Connection lost to back server");
|
||||
this.closeWebsocketConnection(client, 1011, "Admin Connection lost to back server");
|
||||
}
|
||||
console.log("A user left");
|
||||
})
|
||||
|
@ -140,24 +141,6 @@ export class SocketManager implements ZoneEventListener {
|
|||
}
|
||||
}
|
||||
|
||||
getAdminSocketDataFor(roomId: string): AdminSocketData {
|
||||
throw new Error("Not reimplemented yet");
|
||||
/*const data:AdminSocketData = {
|
||||
rooms: {},
|
||||
users: {},
|
||||
}
|
||||
const room = this.Worlds.get(roomId);
|
||||
if (room === undefined) {
|
||||
return data;
|
||||
}
|
||||
const users = room.getUsers();
|
||||
data.rooms[roomId] = users.size;
|
||||
users.forEach(user => {
|
||||
data.users[user.uuid] = true
|
||||
})
|
||||
return data;*/
|
||||
}
|
||||
|
||||
async handleJoinRoom(client: ExSocketInterface): Promise<void> {
|
||||
const viewport = client.viewport;
|
||||
try {
|
||||
|
@ -598,7 +581,19 @@ export class SocketManager implements ZoneEventListener {
|
|||
const serverToClientMessage = new ServerToClientMessage();
|
||||
serverToClientMessage.setWorldfullmessage(errorMessage);
|
||||
|
||||
client.send(serverToClientMessage.serializeBinary().buffer, true);
|
||||
if (!client.disconnecting) {
|
||||
client.send(serverToClientMessage.serializeBinary().buffer, true);
|
||||
}
|
||||
}
|
||||
public emitTokenExpiredMessage(client: WebSocket) {
|
||||
const errorMessage = new TokenExpiredMessage();
|
||||
|
||||
const serverToClientMessage = new ServerToClientMessage();
|
||||
serverToClientMessage.setTokenexpiredmessage(errorMessage);
|
||||
|
||||
if (!client.disconnecting) {
|
||||
client.send(serverToClientMessage.serializeBinary().buffer, true);
|
||||
}
|
||||
}
|
||||
|
||||
public emitConnexionErrorMessage(client: WebSocket, message: string) {
|
||||
|
|
1820
pusher/yarn.lock
1820
pusher/yarn.lock
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue