diff --git a/front/dist/resources/html/gameMenu.html b/front/dist/resources/html/gameMenu.html
new file mode 100644
index 00000000..e69de29b
diff --git a/front/src/Components/Menu/ProfileSubMenu.svelte b/front/src/Components/Menu/ProfileSubMenu.svelte
index 2a2b7af0..39214b4f 100644
--- a/front/src/Components/Menu/ProfileSubMenu.svelte
+++ b/front/src/Components/Menu/ProfileSubMenu.svelte
@@ -1,15 +1,17 @@
+ {#if $userIsConnected}
+
+ {#if PROFILE_URL != undefined}
+
+ {/if}
+
+
+ {:else}
+
+ {/if}
-
-
@@ -74,6 +95,12 @@
align-items: center;
margin-bottom: 20px;
+ iframe{
+ width: 100%;
+ height: 50vh;
+ border: none;
+ }
+
button {
height: 50px;
width: 250px;
diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts
index 9e245d3a..2010689c 100644
--- a/front/src/Connexion/ConnectionManager.ts
+++ b/front/src/Connexion/ConnectionManager.ts
@@ -7,6 +7,8 @@ import { localUserStore } from "./LocalUserStore";
import { CharacterTexture, LocalUser } from "./LocalUser";
import { Room } from "./Room";
import { _ServiceWorker } from "../Network/ServiceWorker";
+import { loginSceneVisibleIframeStore } from "../Stores/LoginSceneStore";
+import { userIsConnected } from "../Stores/MenuStore";
class ConnectionManager {
private localUser!: LocalUser;
@@ -15,6 +17,7 @@ class ConnectionManager {
private reconnectingTimeout: NodeJS.Timeout | null = null;
private _unloading: boolean = false;
private authToken: string | null = null;
+ private _currentRoom: Room | null = null;
private serviceWorker?: _ServiceWorker;
@@ -30,28 +33,39 @@ class ConnectionManager {
}
/**
- * @return Promise
+ * TODO fix me to be move in game manager
*/
- public loadOpenIDScreen(): Promise {
+ public loadOpenIDScreen() {
const state = localUserStore.generateState();
const nonce = localUserStore.generateNonce();
localUserStore.setAuthToken(null);
- //TODO refactor this and don't realise previous call
- return Axios.get(`http://${PUSHER_URL}/login-screen?state=${state}&nonce=${nonce}`)
- .then(() => {
- window.location.assign(`http://${PUSHER_URL}/login-screen?state=${state}&nonce=${nonce}`);
- })
- .catch((err) => {
- console.error(err, "We don't have URL to regenerate authentication user");
- //TODO show modal login
- window.location.reload();
- });
+ //TODO fix me to redirect this URL by pusher
+ if (!this._currentRoom || !this._currentRoom.iframeAuthentication) {
+ loginSceneVisibleIframeStore.set(false);
+ return null;
+ }
+ const redirectUrl = `${this._currentRoom.iframeAuthentication}?state=${state}&nonce=${nonce}`;
+ window.location.assign(redirectUrl);
+ return redirectUrl;
}
- public logout() {
+ /**
+ * Logout
+ */
+ public async logout() {
+ //user logout, set connected store for menu at false
+ userIsConnected.set(false);
+
+ //Logout user in pusher and hydra
+ const token = localUserStore.getAuthToken();
+ const { authToken } = await Axios.get(`${PUSHER_URL}/logout-callback`, { params: { token } }).then(
+ (res) => res.data
+ );
localUserStore.setAuthToken(null);
- window.location.reload();
+
+ //Go on login page can permit to clear token and start authentication process
+ window.location.assign("/login");
}
/**
@@ -60,8 +74,13 @@ class ConnectionManager {
public async initGameConnexion(): Promise {
const connexionType = urlManager.getGameConnexionType();
this.connexionType = connexionType;
- let room: Room | null = null;
- if (connexionType === GameConnexionTypes.jwt) {
+ this._currentRoom = null;
+ if (connexionType === GameConnexionTypes.login) {
+ //TODO clear all cash and redirect on login scene (iframe)
+ localUserStore.setAuthToken(null);
+ this._currentRoom = await Room.createRoom(new URL(localUserStore.getLastRoomUrl()));
+ urlManager.pushRoomIdToUrl(this._currentRoom);
+ } else if (connexionType === GameConnexionTypes.jwt) {
const urlParams = new URLSearchParams(window.location.search);
const code = urlParams.get("code");
const state = urlParams.get("state");
@@ -71,14 +90,15 @@ class ConnectionManager {
if (!code) {
throw "No Auth code provided";
}
- const nonce = localUserStore.getNonce();
- const { authToken } = await Axios.get(`${PUSHER_URL}/login-callback`, { params: { code, nonce } }).then(
- (res) => res.data
- );
- localUserStore.setAuthToken(authToken);
- this.authToken = authToken;
- room = await Room.createRoom(new URL(localUserStore.getLastRoomUrl()));
- urlManager.pushRoomIdToUrl(room);
+ localUserStore.setCode(code);
+ try {
+ await this.checkAuthUserConnexion();
+ } catch (err) {
+ console.error(err);
+ this.loadOpenIDScreen();
+ }
+ this._currentRoom = await Room.createRoom(new URL(localUserStore.getLastRoomUrl()));
+ urlManager.pushRoomIdToUrl(this._currentRoom);
} else if (connexionType === GameConnexionTypes.register) {
//@deprecated
const organizationMemberToken = urlManager.getOrganizationToken();
@@ -92,7 +112,7 @@ class ConnectionManager {
const roomUrl = data.roomUrl;
- room = await Room.createRoom(
+ this._currentRoom = await Room.createRoom(
new URL(
window.location.protocol +
"//" +
@@ -102,7 +122,7 @@ class ConnectionManager {
window.location.hash
)
);
- urlManager.pushRoomIdToUrl(room);
+ urlManager.pushRoomIdToUrl(this._currentRoom);
} else if (
connexionType === GameConnexionTypes.organization ||
connexionType === GameConnexionTypes.anonymous ||
@@ -112,12 +132,18 @@ class ConnectionManager {
//todo: add here some kind of warning if authToken has expired.
if (!this.authToken) {
await this.anonymousLogin();
+ } else {
+ try {
+ await this.checkAuthUserConnexion();
+ } catch (err) {
+ console.error(err);
+ }
}
this.localUser = localUserStore.getLocalUser() as LocalUser; //if authToken exist in localStorage then localUser cannot be null
let roomPath: string;
if (connexionType === GameConnexionTypes.empty) {
- roomPath = window.location.protocol + "//" + window.location.host + START_ROOM_URL;
+ roomPath = localUserStore.getLastRoomUrl();
//get last room path from cache api
try {
const lastRoomUrl = await localUserStore.getLastRoomUrlCacheApi();
@@ -138,13 +164,13 @@ class ConnectionManager {
}
//get detail map for anonymous login and set texture in local storage
- room = await Room.createRoom(new URL(roomPath));
- if (room.textures != undefined && room.textures.length > 0) {
+ this._currentRoom = await Room.createRoom(new URL(roomPath));
+ if (this._currentRoom.textures != undefined && this._currentRoom.textures.length > 0) {
//check if texture was changed
if (this.localUser.textures.length === 0) {
- this.localUser.textures = room.textures;
+ this.localUser.textures = this._currentRoom.textures;
} else {
- room.textures.forEach((newTexture) => {
+ this._currentRoom.textures.forEach((newTexture) => {
const alreadyExistTexture = this.localUser.textures.find((c) => newTexture.id === c.id);
if (this.localUser.textures.findIndex((c) => newTexture.id === c.id) !== -1) {
return;
@@ -155,12 +181,12 @@ class ConnectionManager {
localUserStore.saveUser(this.localUser);
}
}
- if (room == undefined) {
+ if (this._currentRoom == undefined) {
return Promise.reject(new Error("Invalid URL"));
}
this.serviceWorker = new _ServiceWorker();
- return Promise.resolve(room);
+ return Promise.resolve(this._currentRoom);
}
public async anonymousLogin(isBenchmark: boolean = false): Promise {
@@ -215,9 +241,6 @@ class ConnectionManager {
});
connection.onConnect((connect: OnConnectInterface) => {
- //save last room url connected
- localUserStore.setLastRoomUrl(roomUrl);
-
resolve(connect);
});
}).catch((err) => {
@@ -237,6 +260,34 @@ class ConnectionManager {
get getConnexionType() {
return this.connexionType;
}
+
+ async checkAuthUserConnexion() {
+ //set connected store for menu at false
+ userIsConnected.set(false);
+
+ const state = localUserStore.getState();
+ const code = localUserStore.getCode();
+ if (!state || !localUserStore.verifyState(state)) {
+ throw "Could not validate state!";
+ }
+ if (!code) {
+ throw "No Auth code provided";
+ }
+ 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
+ );
+ localUserStore.setAuthToken(authToken);
+ this.authToken = authToken;
+
+ //user connected, set connected store for menu at true
+ userIsConnected.set(true);
+ }
+
+ get currentRoom() {
+ return this._currentRoom;
+ }
}
export const connectionManager = new ConnectionManager();
diff --git a/front/src/Connexion/LocalUserStore.ts b/front/src/Connexion/LocalUserStore.ts
index 05c9613d..6c20aadb 100644
--- a/front/src/Connexion/LocalUserStore.ts
+++ b/front/src/Connexion/LocalUserStore.ts
@@ -1,5 +1,6 @@
import { areCharacterLayersValid, isUserNameValid, LocalUser } from "./LocalUser";
import { v4 as uuidv4 } from "uuid";
+import { START_ROOM_URL } from "../Enum/EnvironmentVariable";
const playerNameKey = "playerName";
const selectedPlayerKey = "selectedPlayer";
@@ -17,6 +18,7 @@ const authToken = "authToken";
const state = "state";
const nonce = "nonce";
const notification = "notificationPermission";
+const code = "code";
const cameraSetup = "cameraSetup";
const cacheAPIIndex = "workavdenture-cache";
@@ -126,7 +128,9 @@ class LocalUserStore {
});
}
getLastRoomUrl(): string {
- return localStorage.getItem(lastRoomUrl) ?? "";
+ return (
+ localStorage.getItem(lastRoomUrl) ?? window.location.protocol + "//" + window.location.host + START_ROOM_URL
+ );
}
getLastRoomUrlCacheApi(): Promise {
return caches.open(cacheAPIIndex).then((cache) => {
@@ -161,19 +165,24 @@ class LocalUserStore {
verifyState(value: string): boolean {
const oldValue = localStorage.getItem(state);
- localStorage.removeItem(state);
return oldValue === value;
}
+ getState(): string | null {
+ return localStorage.getItem(state);
+ }
generateNonce(): string {
const newNonce = uuidv4();
localStorage.setItem(nonce, newNonce);
return newNonce;
}
-
getNonce(): string | null {
- const oldValue = localStorage.getItem(nonce);
- localStorage.removeItem(nonce);
- return oldValue;
+ return localStorage.getItem(nonce);
+ }
+ setCode(value: string): void {
+ localStorage.setItem(code, value);
+ }
+ getCode(): string | null {
+ return localStorage.getItem(code);
}
setCameraSetup(cameraId: string) {
diff --git a/front/src/Connexion/Room.ts b/front/src/Connexion/Room.ts
index be9b5294..0e624d78 100644
--- a/front/src/Connexion/Room.ts
+++ b/front/src/Connexion/Room.ts
@@ -14,6 +14,8 @@ export interface RoomRedirect {
export class Room {
public readonly id: string;
public readonly isPublic: boolean;
+ private _authenticationMandatory: boolean = false;
+ private _iframeAuthentication?: string;
private _mapUrl: string | undefined;
private _textures: CharacterTexture[] | undefined;
private instance: string | undefined;
@@ -101,6 +103,8 @@ export class Room {
console.log("Map ", this.id, " resolves to URL ", data.mapUrl);
this._mapUrl = data.mapUrl;
this._textures = data.textures;
+ this._authenticationMandatory = data.authenticationMandatory || false;
+ this._iframeAuthentication = data.iframeAuthentication;
return new MapDetail(data.mapUrl, data.textures);
}
@@ -186,4 +190,12 @@ export class Room {
}
return this._mapUrl;
}
+
+ get authenticationMandatory(): boolean {
+ return this._authenticationMandatory;
+ }
+
+ get iframeAuthentication(): string | undefined {
+ return this._iframeAuthentication;
+ }
}
diff --git a/front/src/Connexion/RoomConnection.ts b/front/src/Connexion/RoomConnection.ts
index 248253a5..08672be1 100644
--- a/front/src/Connexion/RoomConnection.ts
+++ b/front/src/Connexion/RoomConnection.ts
@@ -78,6 +78,11 @@ export class RoomConnection implements RoomConnection {
*
* @param token A JWT token containing the email of the user
* @param roomUrl The URL of the room in the form "https://example.com/_/[instance]/[map_url]" or "https://example.com/@/[org]/[event]/[map]"
+ * @param name
+ * @param characterLayers
+ * @param position
+ * @param viewport
+ * @param companion
*/
public constructor(
token: string | null,
@@ -218,7 +223,7 @@ export class RoomConnection implements RoomConnection {
worldFullMessageStream.onMessage();
this.closed = true;
} else if (message.hasTokenexpiredmessage()) {
- connectionManager.loadOpenIDScreen();
+ connectionManager.logout();
this.closed = true; //technically, this isn't needed since loadOpenIDScreen() will do window.location.assign() but I prefer to leave it for consistency
} else if (message.hasWorldconnexionmessage()) {
worldFullMessageStream.onMessage(message.getWorldconnexionmessage()?.getMessage());
diff --git a/front/src/Enum/EnvironmentVariable.ts b/front/src/Enum/EnvironmentVariable.ts
index 907162d4..60678822 100644
--- a/front/src/Enum/EnvironmentVariable.ts
+++ b/front/src/Enum/EnvironmentVariable.ts
@@ -19,6 +19,7 @@ export const MAX_PER_GROUP = parseInt(process.env.MAX_PER_GROUP || "4");
export const DISPLAY_TERMS_OF_USE = process.env.DISPLAY_TERMS_OF_USE == "true";
export const NODE_ENV = process.env.NODE_ENV || "development";
export const CONTACT_URL = process.env.CONTACT_URL || undefined;
+export const PROFILE_URL = process.env.PROFILE_URL || undefined;
export const isMobile = (): boolean => window.innerWidth <= 800 || window.innerHeight <= 600;
diff --git a/front/src/Phaser/Game/GameManager.ts b/front/src/Phaser/Game/GameManager.ts
index 068e1734..1bf18d8d 100644
--- a/front/src/Phaser/Game/GameManager.ts
+++ b/front/src/Phaser/Game/GameManager.ts
@@ -35,7 +35,9 @@ export class GameManager {
this.startRoom = await connectionManager.initGameConnexion();
this.loadMap(this.startRoom);
- if (!this.playerName) {
+ //If player name was not set show login scene with player name
+ //If Room si not public and Auth was not set, show login scene to authenticate user (OpenID - SSO - Anonymous)
+ if (!this.playerName || (this.startRoom.authenticationMandatory && !localUserStore.getAuthToken())) {
return LoginSceneName;
} else if (!this.characterLayers || !this.characterLayers.length) {
return SelectCharacterSceneName;
@@ -143,6 +145,10 @@ export class GameManager {
if (this.currentGameSceneName === null) throw "No current scene id set!";
return this.scenePlugin.get(this.currentGameSceneName) as GameScene;
}
+
+ public get currentStartedRoom() {
+ return this.startRoom;
+ }
}
export const gameManager = new GameManager();
diff --git a/front/src/Phaser/Login/LoginScene.ts b/front/src/Phaser/Login/LoginScene.ts
index 0cca9ccd..7421934d 100644
--- a/front/src/Phaser/Login/LoginScene.ts
+++ b/front/src/Phaser/Login/LoginScene.ts
@@ -1,7 +1,9 @@
-import { gameManager } from "../Game/GameManager";
import { SelectCharacterSceneName } from "./SelectCharacterScene";
import { ResizableScene } from "./ResizableScene";
-import { loginSceneVisibleStore } from "../../Stores/LoginSceneStore";
+import { loginSceneVisibleIframeStore, loginSceneVisibleStore } from "../../Stores/LoginSceneStore";
+import { localUserStore } from "../../Connexion/LocalUserStore";
+import { connectionManager } from "../../Connexion/ConnectionManager";
+import { gameManager } from "../Game/GameManager";
export const LoginSceneName = "LoginScene";
@@ -18,6 +20,16 @@ export class LoginScene extends ResizableScene {
preload() {}
create() {
+ loginSceneVisibleIframeStore.set(false);
+ //If authentication is mandatory, push authentication iframe
+ if (
+ localUserStore.getAuthToken() == undefined &&
+ gameManager.currentStartedRoom &&
+ gameManager.currentStartedRoom?.authenticationMandatory
+ ) {
+ connectionManager.loadOpenIDScreen();
+ loginSceneVisibleIframeStore.set(true);
+ }
loginSceneVisibleStore.set(true);
}
diff --git a/front/src/Stores/LoginSceneStore.ts b/front/src/Stores/LoginSceneStore.ts
index 6e2ea18b..4bd5b10a 100644
--- a/front/src/Stores/LoginSceneStore.ts
+++ b/front/src/Stores/LoginSceneStore.ts
@@ -1,3 +1,4 @@
import { writable } from "svelte/store";
export const loginSceneVisibleStore = writable(false);
+export const loginSceneVisibleIframeStore = writable(false);
diff --git a/front/src/Stores/MenuStore.ts b/front/src/Stores/MenuStore.ts
index 024c742d..035ba174 100644
--- a/front/src/Stores/MenuStore.ts
+++ b/front/src/Stores/MenuStore.ts
@@ -6,6 +6,8 @@ import { CONTACT_URL } from "../Enum/EnvironmentVariable";
export const menuIconVisiblilityStore = writable(false);
export const menuVisiblilityStore = writable(false);
export const menuInputFocusStore = writable(false);
+export const loginUrlStore = writable(false);
+export const userIsConnected = writable(false);
let warningContainerTimeout: Timeout | null = null;
function createWarningContainerStore() {
diff --git a/front/src/Stores/PlayersStore.ts b/front/src/Stores/PlayersStore.ts
index 86ab136f..e6f5b1af 100644
--- a/front/src/Stores/PlayersStore.ts
+++ b/front/src/Stores/PlayersStore.ts
@@ -2,6 +2,7 @@ import { writable } from "svelte/store";
import type { PlayerInterface } from "../Phaser/Game/PlayerInterface";
import type { RoomConnection } from "../Connexion/RoomConnection";
import { getRandomColor } from "../WebRtc/ColorGenerator";
+import { localUserStore } from "../Connexion/LocalUserStore";
let idCount = 0;
diff --git a/front/src/Url/UrlManager.ts b/front/src/Url/UrlManager.ts
index 6d4949ab..f1e15db1 100644
--- a/front/src/Url/UrlManager.ts
+++ b/front/src/Url/UrlManager.ts
@@ -1,4 +1,5 @@
import type { Room } from "../Connexion/Room";
+import { localUserStore } from "../Connexion/LocalUserStore";
export enum GameConnexionTypes {
anonymous = 1,
@@ -7,13 +8,16 @@ export enum GameConnexionTypes {
empty,
unknown,
jwt,
+ login,
}
//this class is responsible with analysing and editing the game's url
class UrlManager {
public getGameConnexionType(): GameConnexionTypes {
const url = window.location.pathname.toString();
- if (url === "/jwt") {
+ if (url === "/login") {
+ return GameConnexionTypes.login;
+ } else if (url === "/jwt") {
return GameConnexionTypes.jwt;
} else if (url.includes("_/")) {
return GameConnexionTypes.anonymous;
@@ -35,6 +39,8 @@ class UrlManager {
public pushRoomIdToUrl(room: Room): void {
if (window.location.pathname === room.id) return;
+ //Set last room visited! (connected or nor, must to be saved in localstorage and cache API)
+ localUserStore.setLastRoomUrl(room.key);
const hash = window.location.hash;
const search = room.search.toString();
history.pushState({}, "WorkAdventure", room.id + (search ? "?" + search : "") + hash);
diff --git a/front/webpack.config.ts b/front/webpack.config.ts
index 9d9a4a7b..48db1afa 100644
--- a/front/webpack.config.ts
+++ b/front/webpack.config.ts
@@ -7,6 +7,7 @@ import MiniCssExtractPlugin from "mini-css-extract-plugin";
import sveltePreprocess from "svelte-preprocess";
import ForkTsCheckerWebpackPlugin from "fork-ts-checker-webpack-plugin";
import NodePolyfillPlugin from "node-polyfill-webpack-plugin";
+import { PROFILE_URL } from "./src/Enum/EnvironmentVariable";
const mode = process.env.NODE_ENV ?? "development";
const buildNpmTypingsForApi = !!process.env.BUILD_TYPINGS;
@@ -191,6 +192,7 @@ module.exports = {
UPLOADER_URL: null,
ADMIN_URL: undefined,
CONTACT_URL: null,
+ PROFILE_URL: null,
DEBUG_MODE: null,
STUN_SERVER: null,
TURN_SERVER: null,
diff --git a/pusher/src/Controller/AuthenticateController.ts b/pusher/src/Controller/AuthenticateController.ts
index ffb6c048..000ac0ca 100644
--- a/pusher/src/Controller/AuthenticateController.ts
+++ b/pusher/src/Controller/AuthenticateController.ts
@@ -2,7 +2,7 @@ import { v4 } from "uuid";
import { HttpRequest, HttpResponse, TemplatedApp } from "uWebSockets.js";
import { BaseController } from "./BaseController";
import { adminApi } from "../Services/AdminApi";
-import { jwtTokenManager } from "../Services/JWTTokenManager";
+import { AuthTokenData, jwtTokenManager } from "../Services/JWTTokenManager";
import { parse } from "query-string";
import { openIDClient } from "../Services/OpenIDClient";
@@ -17,6 +17,7 @@ export class AuthenticateController extends BaseController {
this.openIDCallback();
this.register();
this.anonymLogin();
+ this.profileCallback();
}
openIDLogin() {
@@ -48,14 +49,31 @@ export class AuthenticateController extends BaseController {
res.onAborted(() => {
console.warn("/message request was aborted");
});
- const { code, nonce } = parse(req.getQuery());
+ const { code, nonce, token } = parse(req.getQuery());
try {
+ //verify connected by token
+ if (token != undefined) {
+ try {
+ const authTokenData: AuthTokenData = jwtTokenManager.verifyJWTToken(token as string, false);
+ if (authTokenData.hydraAccessToken == undefined) {
+ throw Error("Token cannot to be check on Hydra");
+ }
+ await openIDClient.checkTokenAuth(authTokenData.hydraAccessToken);
+ res.writeStatus("200");
+ this.addCorsHeaders(res);
+ return res.end(JSON.stringify({ authToken: token }));
+ } catch (err) {
+ console.info("User was not connected", err);
+ }
+ }
+
+ //user have not token created, check data on hydra and create token
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);
+ const authToken = jwtTokenManager.createAuthToken(email, userInfo.access_token);
res.writeStatus("200");
this.addCorsHeaders(res);
return res.end(JSON.stringify({ authToken }));
@@ -63,6 +81,30 @@ export class AuthenticateController extends BaseController {
return this.errorToResponse(e, res);
}
});
+
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
+ this.App.get("/logout-callback", async (res: HttpResponse, req: HttpRequest) => {
+ res.onAborted(() => {
+ console.warn("/message request was aborted");
+ });
+
+ const { token } = parse(req.getQuery());
+
+ try {
+ const authTokenData: AuthTokenData = jwtTokenManager.verifyJWTToken(token as string, false);
+ if (authTokenData.hydraAccessToken == undefined) {
+ throw Error("Token cannot to be logout on Hydra");
+ }
+ await openIDClient.logoutUser(authTokenData.hydraAccessToken);
+ } catch (error) {
+ console.error("openIDCallback => logout-callback", error);
+ } finally {
+ res.writeStatus("200");
+ this.addCorsHeaders(res);
+ // eslint-disable-next-line no-unsafe-finally
+ return res.end();
+ }
+ });
}
//Try to login with an admin token
@@ -136,4 +178,39 @@ export class AuthenticateController extends BaseController {
);
});
}
+
+ profileCallback() {
+ //eslint-disable-next-line @typescript-eslint/no-misused-promises
+ // @ts-ignore
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
+ this.App.get("/profile-callback", async (res: HttpResponse, req: HttpRequest) => {
+ res.onAborted(() => {
+ console.warn("/message request was aborted");
+ });
+ const { userIdentify, token } = parse(req.getQuery());
+ try {
+ //verify connected by token
+ if (token != undefined) {
+ try {
+ const authTokenData: AuthTokenData = jwtTokenManager.verifyJWTToken(token as string, false);
+ if (authTokenData.hydraAccessToken == undefined) {
+ throw Error("Token cannot to be check on Hydra");
+ }
+ await openIDClient.checkTokenAuth(authTokenData.hydraAccessToken);
+
+ //get login profile
+ res.writeStatus("302");
+ res.writeHeader("Location", adminApi.getProfileUrl(authTokenData.hydraAccessToken));
+ this.addCorsHeaders(res);
+ // eslint-disable-next-line no-unsafe-finally
+ return res.end();
+ } catch (error) {
+ return this.errorToResponse(error, res);
+ }
+ }
+ } catch (error) {
+ this.errorToResponse(error, res);
+ }
+ });
+ }
}
diff --git a/pusher/src/Enum/EnvironmentVariable.ts b/pusher/src/Enum/EnvironmentVariable.ts
index ea8f22d8..4a078d90 100644
--- a/pusher/src/Enum/EnvironmentVariable.ts
+++ b/pusher/src/Enum/EnvironmentVariable.ts
@@ -2,6 +2,7 @@ const SECRET_KEY = process.env.SECRET_KEY || "THECODINGMACHINE_SECRET_KEY";
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_URL = process.env.ADMIN_URL || "";
const ADMIN_API_TOKEN = process.env.ADMIN_API_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;
@@ -19,6 +20,7 @@ export {
SECRET_KEY,
API_URL,
ADMIN_API_URL,
+ ADMIN_URL,
ADMIN_API_TOKEN,
ALLOW_ARTILLERY,
CPU_OVERHEAT_THRESHOLD,
diff --git a/pusher/src/Services/AdminApi.ts b/pusher/src/Services/AdminApi.ts
index f33480ca..e53d00ae 100644
--- a/pusher/src/Services/AdminApi.ts
+++ b/pusher/src/Services/AdminApi.ts
@@ -1,4 +1,4 @@
-import { ADMIN_API_TOKEN, ADMIN_API_URL } from "../Enum/EnvironmentVariable";
+import { ADMIN_API_TOKEN, ADMIN_API_URL, ADMIN_URL } from "../Enum/EnvironmentVariable";
import Axios from "axios";
import { GameRoomPolicyTypes } from "_Model/PusherRoom";
import { CharacterTexture } from "./AdminApi/CharacterTexture";
@@ -141,6 +141,15 @@ class AdminApi {
return data.data;
});
}
+
+ /*TODO add constant to use profile companny*/
+ getProfileUrl(accessToken: string): string {
+ if (!ADMIN_URL) {
+ throw new Error("No admin backoffice set!");
+ }
+
+ return ADMIN_URL + `/profile?token=${accessToken}`;
+ }
}
export const adminApi = new AdminApi();
diff --git a/pusher/src/Services/JWTTokenManager.ts b/pusher/src/Services/JWTTokenManager.ts
index 4711ccfd..24393084 100644
--- a/pusher/src/Services/JWTTokenManager.ts
+++ b/pusher/src/Services/JWTTokenManager.ts
@@ -6,13 +6,13 @@ import { adminApi, AdminBannedData } from "../Services/AdminApi";
export interface AuthTokenData {
identifier: string; //will be a email if logged in or an uuid if anonymous
+ hydraAccessToken?: string;
}
export const tokenInvalidException = "tokenInvalid";
class JWTTokenManager {
- public createAuthToken(identifier: string) {
- //TODO fix me 200d when ory authentication will be available
- return Jwt.sign({ identifier }, SECRET_KEY, { expiresIn: "200d" });
+ public createAuthToken(identifier: string, hydraAccessToken?: string) {
+ return Jwt.sign({ identifier, hydraAccessToken }, SECRET_KEY, { expiresIn: "30d" });
}
public verifyJWTToken(token: string, ignoreExpiration: boolean = false): AuthTokenData {
diff --git a/pusher/src/Services/OpenIDClient.ts b/pusher/src/Services/OpenIDClient.ts
index 4fda8b5e..b6506a5e 100644
--- a/pusher/src/Services/OpenIDClient.ts
+++ b/pusher/src/Services/OpenIDClient.ts
@@ -1,4 +1,4 @@
-import { Issuer, Client } from "openid-client";
+import { Issuer, Client, IntrospectionResponse } from "openid-client";
import { OPID_CLIENT_ID, OPID_CLIENT_SECRET, OPID_CLIENT_ISSUER, FRONT_URL } from "../Enum/EnvironmentVariable";
const opidRedirectUri = FRONT_URL + "/jwt";
@@ -31,13 +31,32 @@ class OpenIDClient {
});
}
- public getUserInfo(code: string, nonce: string): Promise<{ email: string; sub: string }> {
+ public getUserInfo(code: string, nonce: string): Promise<{ email: string; sub: string; access_token: string }> {
return this.initClient().then((client) => {
return client.callback(opidRedirectUri, { code }, { nonce }).then((tokenSet) => {
- return client.userinfo(tokenSet);
+ return client.userinfo(tokenSet).then((res) => {
+ return {
+ ...res,
+ email: res.email as string,
+ sub: res.sub,
+ access_token: tokenSet.access_token as string,
+ };
+ });
});
});
}
+
+ public logoutUser(token: string): Promise {
+ return this.initClient().then((client) => {
+ return client.revoke(token);
+ });
+ }
+
+ public checkTokenAuth(token: string): Promise {
+ return this.initClient().then((client) => {
+ return client.userinfo(token);
+ });
+ }
}
export const openIDClient = new OpenIDClient();