From 6832fe49901ce28b3413000337a142cc9dae445c Mon Sep 17 00:00:00 2001 From: Lurkars Date: Thu, 21 Oct 2021 16:28:57 +0200 Subject: [PATCH 01/19] use OIDC without admin api, option to disable anonymous login --- .env.template | 1 + docker-compose.single-domain.yaml | 1 + docker-compose.yaml | 1 + front/src/Connexion/ConnectionManager.ts | 34 ++++++++++++++----- .../src/Controller/AuthenticateController.ts | 26 ++++++++------ pusher/src/Controller/IoSocketController.ts | 7 +++- pusher/src/Controller/MapController.ts | 9 +++-- pusher/src/Enum/EnvironmentVariable.ts | 1 + .../src/Services/AdminApi/MapDetailsData.ts | 1 + 9 files changed, 59 insertions(+), 22 deletions(-) diff --git a/.env.template b/.env.template index 715ebeec..f511b398 100644 --- a/.env.template +++ b/.env.template @@ -22,6 +22,7 @@ MAX_USERNAME_LENGTH=8 OPID_CLIENT_ID= OPID_CLIENT_SECRET= OPID_CLIENT_ISSUER= +DISABLE_ANONYMOUS= # If you want to have a contact page in your menu, you MUST set CONTACT_URL to the URL of the page that you want CONTACT_URL= \ No newline at end of file diff --git a/docker-compose.single-domain.yaml b/docker-compose.single-domain.yaml index 4e85d702..2e2cab7d 100644 --- a/docker-compose.single-domain.yaml +++ b/docker-compose.single-domain.yaml @@ -70,6 +70,7 @@ services: OPID_CLIENT_ID: $OPID_CLIENT_ID OPID_CLIENT_SECRET: $OPID_CLIENT_SECRET OPID_CLIENT_ISSUER: $OPID_CLIENT_ISSUER + DISABLE_ANONYMOUS: $DISABLE_ANONYMOUS volumes: - ./pusher:/usr/src/app labels: diff --git a/docker-compose.yaml b/docker-compose.yaml index 4b3904dd..65dcc61f 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -70,6 +70,7 @@ services: OPID_CLIENT_ID: $OPID_CLIENT_ID OPID_CLIENT_SECRET: $OPID_CLIENT_SECRET OPID_CLIENT_ISSUER: $OPID_CLIENT_ISSUER + DISABLE_ANONYMOUS: $DISABLE_ANONYMOUS volumes: - ./pusher:/usr/src/app labels: diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index b346f450..c91e4590 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -42,11 +42,22 @@ class ConnectionManager { localUserStore.setAuthToken(null); //TODO fix me to redirect this URL by pusher - if (!this._currentRoom || !this._currentRoom.iframeAuthentication) { + if (!this._currentRoom) { loginSceneVisibleIframeStore.set(false); return null; } - const redirectUrl = new URL(`${this._currentRoom.iframeAuthentication}`); + + // also allow OIDC login without admin API by using pusher + let redirectUrl: URL; + if (this._currentRoom.iframeAuthentication) { + redirectUrl = new URL(`${this._currentRoom.iframeAuthentication}`); + } else { + // need origin if PUSHER_URL is relative (in Single-Domain-Deployment) + redirectUrl = new URL( + `${PUSHER_URL}/login-screen`, + !PUSHER_URL.startsWith("http:") || !PUSHER_URL.startsWith("https:") ? window.location.origin : undefined + ); + } redirectUrl.searchParams.append("state", state); redirectUrl.searchParams.append("nonce", nonce); redirectUrl.searchParams.append("playUri", this._currentRoom.key); @@ -204,13 +215,18 @@ class ConnectionManager { } public async anonymousLogin(isBenchmark: boolean = false): Promise { - const data = await Axios.post(`${PUSHER_URL}/anonymLogin`).then((res) => res.data); - this.localUser = new LocalUser(data.userUuid, []); - this.authToken = data.authToken; - if (!isBenchmark) { - // In benchmark, we don't have a local storage. - localUserStore.saveUser(this.localUser); - localUserStore.setAuthToken(this.authToken); + try { + const data = await Axios.post(`${PUSHER_URL}/anonymLogin`).then((res) => res.data); + this.localUser = new LocalUser(data.userUuid, []); + this.authToken = data.authToken; + if (!isBenchmark) { + // In benchmark, we don't have a local storage. + localUserStore.saveUser(this.localUser); + localUserStore.setAuthToken(this.authToken); + } + } catch (error) { + // anonymous login failed (through 403 DISABLE_ANONYMOUS) + this.loadOpenIDScreen(); } } diff --git a/pusher/src/Controller/AuthenticateController.ts b/pusher/src/Controller/AuthenticateController.ts index 972cc102..2dafe065 100644 --- a/pusher/src/Controller/AuthenticateController.ts +++ b/pusher/src/Controller/AuthenticateController.ts @@ -5,6 +5,7 @@ import { adminApi } from "../Services/AdminApi"; import { AuthTokenData, jwtTokenManager } from "../Services/JWTTokenManager"; import { parse } from "query-string"; import { openIDClient } from "../Services/OpenIDClient"; +import { DISABLE_ANONYMOUS } from "../Enum/EnvironmentVariable"; export interface TokenInterface { userUuid: string; @@ -175,16 +176,21 @@ export class AuthenticateController extends BaseController { console.warn("Login request was aborted"); }); - const userUuid = v4(); - const authToken = jwtTokenManager.createAuthToken(userUuid); - res.writeStatus("200 OK"); - this.addCorsHeaders(res); - res.end( - JSON.stringify({ - authToken, - userUuid, - }) - ); + if (DISABLE_ANONYMOUS) { + res.writeStatus("403 FORBIDDEN"); + res.end(); + } else { + const userUuid = v4(); + const authToken = jwtTokenManager.createAuthToken(userUuid); + res.writeStatus("200 OK"); + this.addCorsHeaders(res); + res.end( + JSON.stringify({ + authToken, + userUuid, + }) + ); + } }); } diff --git a/pusher/src/Controller/IoSocketController.ts b/pusher/src/Controller/IoSocketController.ts index 0466100c..500f4142 100644 --- a/pusher/src/Controller/IoSocketController.ts +++ b/pusher/src/Controller/IoSocketController.ts @@ -26,7 +26,7 @@ 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_API_TOKEN, ADMIN_API_URL, SOCKET_IDLE_TIMER, DISABLE_ANONYMOUS } from "../Enum/EnvironmentVariable"; import { Zone } from "_Model/Zone"; import { ExAdminSocketInterface } from "_Model/Websocket/ExAdminSocketInterface"; import { v4 } from "uuid"; @@ -175,6 +175,11 @@ export class IoSocketController { const tokenData = token && typeof token === "string" ? jwtTokenManager.verifyJWTToken(token) : null; + + if (DISABLE_ANONYMOUS && !tokenData) { + throw new Error("Expecting token"); + } + const userIdentifier = tokenData ? tokenData.identifier : ""; let memberTags: string[] = []; diff --git a/pusher/src/Controller/MapController.ts b/pusher/src/Controller/MapController.ts index f775b50c..437bac6a 100644 --- a/pusher/src/Controller/MapController.ts +++ b/pusher/src/Controller/MapController.ts @@ -2,9 +2,9 @@ import { HttpRequest, HttpResponse, TemplatedApp } from "uWebSockets.js"; import { BaseController } from "./BaseController"; import { parse } from "query-string"; import { adminApi } from "../Services/AdminApi"; -import { ADMIN_API_URL } from "../Enum/EnvironmentVariable"; +import { ADMIN_API_URL, DISABLE_ANONYMOUS } from "../Enum/EnvironmentVariable"; import { GameRoomPolicyTypes } from "../Model/PusherRoom"; -import { MapDetailsData } from "../Services/AdminApi/MapDetailsData"; +import { isMapDetailsData, MapDetailsData } from "../Services/AdminApi/MapDetailsData"; import { socketManager } from "../Services/SocketManager"; import { AuthTokenData, jwtTokenManager } from "../Services/JWTTokenManager"; import { v4 } from "uuid"; @@ -64,6 +64,7 @@ export class MapController extends BaseController { tags: [], textures: [], contactPage: undefined, + authenticationMandatory: DISABLE_ANONYMOUS, } as MapDetailsData) ); @@ -87,6 +88,10 @@ export class MapController extends BaseController { } const mapDetails = await adminApi.fetchMapDetails(query.playUri as string, userId); + if (isMapDetailsData(mapDetails) && DISABLE_ANONYMOUS) { + (mapDetails as MapDetailsData).authenticationMandatory = true; + } + res.writeStatus("200 OK"); this.addCorsHeaders(res); res.end(JSON.stringify(mapDetails)); diff --git a/pusher/src/Enum/EnvironmentVariable.ts b/pusher/src/Enum/EnvironmentVariable.ts index ab1ce110..be81871d 100644 --- a/pusher/src/Enum/EnvironmentVariable.ts +++ b/pusher/src/Enum/EnvironmentVariable.ts @@ -15,6 +15,7 @@ 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 const DISABLE_ANONYMOUS = process.env.DISABLE_ANONYMOUS ? process.env.DISABLE_ANONYMOUS == "true" : false; export { SECRET_KEY, diff --git a/pusher/src/Services/AdminApi/MapDetailsData.ts b/pusher/src/Services/AdminApi/MapDetailsData.ts index 278b81bb..7a1f57ff 100644 --- a/pusher/src/Services/AdminApi/MapDetailsData.ts +++ b/pusher/src/Services/AdminApi/MapDetailsData.ts @@ -16,6 +16,7 @@ export const isMapDetailsData = new tg.IsInterface() tags: tg.isArray(tg.isString), textures: tg.isArray(isCharacterTexture), contactPage: tg.isUnion(tg.isString, tg.isUndefined), + authenticationMandatory: tg.isUnion(tg.isBoolean, tg.isUndefined), }) .get(); From 78e0d1bead402348f36a3341ceea68b3f8e462e3 Mon Sep 17 00:00:00 2001 From: Lurkars Date: Thu, 21 Oct 2021 16:53:24 +0200 Subject: [PATCH 02/19] fix linter, cleanup --- front/src/Connexion/ConnectionManager.ts | 19 +++++++------------ pusher/src/Controller/MapController.ts | 2 +- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index c91e4590..aae8440a 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -215,18 +215,13 @@ class ConnectionManager { } public async anonymousLogin(isBenchmark: boolean = false): Promise { - try { - const data = await Axios.post(`${PUSHER_URL}/anonymLogin`).then((res) => res.data); - this.localUser = new LocalUser(data.userUuid, []); - this.authToken = data.authToken; - if (!isBenchmark) { - // In benchmark, we don't have a local storage. - localUserStore.saveUser(this.localUser); - localUserStore.setAuthToken(this.authToken); - } - } catch (error) { - // anonymous login failed (through 403 DISABLE_ANONYMOUS) - this.loadOpenIDScreen(); + const data = await Axios.post(`${PUSHER_URL}/anonymLogin`).then((res) => res.data); + this.localUser = new LocalUser(data.userUuid, []); + this.authToken = data.authToken; + if (!isBenchmark) { + // In benchmark, we don't have a local storage. + localUserStore.saveUser(this.localUser); + localUserStore.setAuthToken(this.authToken); } } diff --git a/pusher/src/Controller/MapController.ts b/pusher/src/Controller/MapController.ts index 437bac6a..18748d9e 100644 --- a/pusher/src/Controller/MapController.ts +++ b/pusher/src/Controller/MapController.ts @@ -89,7 +89,7 @@ export class MapController extends BaseController { const mapDetails = await adminApi.fetchMapDetails(query.playUri as string, userId); if (isMapDetailsData(mapDetails) && DISABLE_ANONYMOUS) { - (mapDetails as MapDetailsData).authenticationMandatory = true; + mapDetails.authenticationMandatory = true; } res.writeStatus("200 OK"); From 4c028bfcb3f64748b310645eadadb6163b7b00e8 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Mon, 8 Nov 2021 19:27:01 +0100 Subject: [PATCH 03/19] OpenId from Admin connect - Create admin environment for redirect uri of openID - Add log out redirect when user click on log out button Signed-off-by: Gregoire Parant --- pusher/src/Enum/EnvironmentVariable.ts | 1 + pusher/src/Services/AdminApi.ts | 4 ++++ pusher/src/Services/OpenIDClient.ts | 33 +++++++++++++------------- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/pusher/src/Enum/EnvironmentVariable.ts b/pusher/src/Enum/EnvironmentVariable.ts index ab1ce110..22c4db4f 100644 --- a/pusher/src/Enum/EnvironmentVariable.ts +++ b/pusher/src/Enum/EnvironmentVariable.ts @@ -15,6 +15,7 @@ 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 const OPID_CLIENT_REDIREC_URL = process.env.OPID_CLIENT_REDIREC_URL || FRONT_URL + "/jwt"; export { SECRET_KEY, diff --git a/pusher/src/Services/AdminApi.ts b/pusher/src/Services/AdminApi.ts index e53d00ae..d002ff8b 100644 --- a/pusher/src/Services/AdminApi.ts +++ b/pusher/src/Services/AdminApi.ts @@ -150,6 +150,10 @@ class AdminApi { return ADMIN_URL + `/profile?token=${accessToken}`; } + + async logoutOauth(token: string) { + await Axios.get(ADMIN_API_URL + `/oauth/logout?token=${token}`); + } } export const adminApi = new AdminApi(); diff --git a/pusher/src/Services/OpenIDClient.ts b/pusher/src/Services/OpenIDClient.ts index c9137ad5..1a475224 100644 --- a/pusher/src/Services/OpenIDClient.ts +++ b/pusher/src/Services/OpenIDClient.ts @@ -1,7 +1,10 @@ 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"; +import { + OPID_CLIENT_ID, + OPID_CLIENT_SECRET, + OPID_CLIENT_ISSUER, + OPID_CLIENT_REDIREC_URL, +} from "../Enum/EnvironmentVariable"; class OpenIDClient { private issuerPromise: Promise | null = null; @@ -12,7 +15,7 @@ class OpenIDClient { return new issuer.Client({ client_id: OPID_CLIENT_ID, client_secret: OPID_CLIENT_SECRET, - redirect_uris: [opidRedirectUri], + redirect_uris: [OPID_CLIENT_REDIREC_URL], response_types: ["code"], }); }); @@ -20,30 +23,26 @@ class OpenIDClient { return this.issuerPromise; } - public authorizationUrl(state: string, nonce: string, playUri?: string, redirect?: string) { + public authorizationUrl(playUri?: string, redirect?: string) { return this.initClient().then((client) => { return client.authorizationUrl({ scope: "openid email", prompt: "login", - state: state, - nonce: nonce, playUri: playUri, redirect: redirect, }); }); } - public getUserInfo(code: string, nonce: string): Promise<{ email: string; sub: string; access_token: string }> { + public getUserInfo(accessToken: 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).then((res) => { - return { - ...res, - email: res.email as string, - sub: res.sub, - access_token: tokenSet.access_token as string, - }; - }); + return client.userinfo(accessToken).then((res) => { + return { + ...res, + email: res.email as string, + sub: res.sub, + access_token: accessToken as string, + }; }); }); } From 89baafba2fb69f8252d49958f1a9c455a5251047 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Tue, 9 Nov 2021 00:08:01 +0100 Subject: [PATCH 04/19] Rollback openid connect to use code and nonce Signed-off-by: Gregoire Parant --- front/src/Connexion/LocalUserStore.ts | 4 ++++ pusher/src/Services/OpenIDClient.ts | 22 +++++++++++++--------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/front/src/Connexion/LocalUserStore.ts b/front/src/Connexion/LocalUserStore.ts index 6c20aadb..a113291d 100644 --- a/front/src/Connexion/LocalUserStore.ts +++ b/front/src/Connexion/LocalUserStore.ts @@ -165,6 +165,10 @@ class LocalUserStore { verifyState(value: string): boolean { const oldValue = localStorage.getItem(state); + if (!oldValue) { + localStorage.setItem(state, value); + return true; + } return oldValue === value; } getState(): string | null { diff --git a/pusher/src/Services/OpenIDClient.ts b/pusher/src/Services/OpenIDClient.ts index 1a475224..bc0dd6c9 100644 --- a/pusher/src/Services/OpenIDClient.ts +++ b/pusher/src/Services/OpenIDClient.ts @@ -23,26 +23,30 @@ class OpenIDClient { return this.issuerPromise; } - public authorizationUrl(playUri?: string, redirect?: string) { + public authorizationUrl(state: string, nonce: string, playUri?: string, redirect?: string) { return this.initClient().then((client) => { return client.authorizationUrl({ scope: "openid email", prompt: "login", + state: state, + nonce: nonce, playUri: playUri, redirect: redirect, }); }); } - public getUserInfo(accessToken: string): Promise<{ email: string; sub: string; access_token: string }> { + public getUserInfo(code: string, nonce: string): Promise<{ email: string; sub: string; access_token: string }> { return this.initClient().then((client) => { - return client.userinfo(accessToken).then((res) => { - return { - ...res, - email: res.email as string, - sub: res.sub, - access_token: accessToken as string, - }; + return client.callback(OPID_CLIENT_REDIREC_URL, { code }, { nonce }).then((tokenSet) => { + return client.userinfo(tokenSet).then((res) => { + return { + ...res, + email: res.email as string, + sub: res.sub, + access_token: tokenSet.access_token as string, + }; + }); }); }); } From 7406b620939af5d508144722437ef1965cc26bd4 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Tue, 9 Nov 2021 00:38:32 +0100 Subject: [PATCH 05/19] Add jwt token if is defined un URL Signed-off-by: Gregoire Parant --- front/src/Connexion/ConnectionManager.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index b346f450..032a60f1 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -87,6 +87,8 @@ class ConnectionManager { urlManager.pushRoomIdToUrl(this._currentRoom); } else if (connexionType === GameConnexionTypes.jwt) { const urlParams = new URLSearchParams(window.location.search); + + //TODO if jwt is defined, state and nonce can to be deleted const code = urlParams.get("code"); const state = urlParams.get("state"); if (!state || !localUserStore.verifyState(state)) { @@ -96,6 +98,12 @@ class ConnectionManager { throw "No Auth code provided"; } localUserStore.setCode(code); + + const jwt = urlParams.get("jwt"); + if (jwt) { + this.authToken = jwt; + localUserStore.setAuthToken(jwt); + } this._currentRoom = await Room.createRoom(new URL(localUserStore.getLastRoomUrl())); try { await this.checkAuthUserConnexion(); From 1db22d82af53cbfe241319a956e64435443cef54 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Wed, 10 Nov 2021 18:26:50 +0100 Subject: [PATCH 06/19] Set state in local storage when openid provider redirect on jwt with token value Signed-off-by: Gregoire Parant --- front/src/Connexion/ConnectionManager.ts | 16 ++++++++++------ front/src/Connexion/LocalUserStore.ts | 3 +++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index 032a60f1..cb600d6d 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -91,6 +91,16 @@ class ConnectionManager { //TODO if jwt is defined, state and nonce can to be deleted const code = urlParams.get("code"); const state = urlParams.get("state"); + const jwt = urlParams.get("jwt"); + + if (jwt) { + this.authToken = jwt; + localUserStore.setAuthToken(jwt); + //if we use jwt we can update new state from openid provider + if (state) { + localUserStore.setState(state); + } + } if (!state || !localUserStore.verifyState(state)) { throw "Could not validate state!"; } @@ -98,12 +108,6 @@ class ConnectionManager { throw "No Auth code provided"; } localUserStore.setCode(code); - - const jwt = urlParams.get("jwt"); - if (jwt) { - this.authToken = jwt; - localUserStore.setAuthToken(jwt); - } this._currentRoom = await Room.createRoom(new URL(localUserStore.getLastRoomUrl())); try { await this.checkAuthUserConnexion(); diff --git a/front/src/Connexion/LocalUserStore.ts b/front/src/Connexion/LocalUserStore.ts index a113291d..7607fb2d 100644 --- a/front/src/Connexion/LocalUserStore.ts +++ b/front/src/Connexion/LocalUserStore.ts @@ -171,6 +171,9 @@ class LocalUserStore { } return oldValue === value; } + setState(value: string) { + localStorage.setItem(state, value); + } getState(): string | null { return localStorage.getItem(state); } From d3f120f2bb0b38db31e05e5050b6ade628e1d091 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Fri, 12 Nov 2021 12:13:44 +0100 Subject: [PATCH 07/19] Update to have token when user is connected - Update menu design Signed-off-by: Gregoire Parant --- .../src/Components/Menu/ContactSubMenu.svelte | 52 +++++++++++- .../Components/Menu/CreateMapSubMenu.svelte | 51 ----------- front/src/Components/Menu/GuestSubMenu.svelte | 75 ++++++++++++++++ front/src/Components/Menu/Menu.svelte | 14 +-- .../src/Components/Menu/ProfileSubMenu.svelte | 85 ++++++++++++------- front/src/Connexion/ConnectionManager.ts | 56 ++++++------ front/src/Stores/MenuStore.ts | 10 +-- 7 files changed, 220 insertions(+), 123 deletions(-) delete mode 100644 front/src/Components/Menu/CreateMapSubMenu.svelte create mode 100644 front/src/Components/Menu/GuestSubMenu.svelte diff --git a/front/src/Components/Menu/ContactSubMenu.svelte b/front/src/Components/Menu/ContactSubMenu.svelte index 61ecc56e..3cf1f5fb 100644 --- a/front/src/Components/Menu/ContactSubMenu.svelte +++ b/front/src/Components/Menu/ContactSubMenu.svelte @@ -1,10 +1,60 @@ - +
+
+
+

Getting started

+

+ WorkAdventure allows you to create an online space to communicate spontaneously with others. + And it all starts with creating your own space. Choose from a large selection of prefabricated maps by our team. +

+ +
+ +
+

Create your map

+

You can also create your own custom map by following the step of the documentation.

+ +
+ + +
+
diff --git a/front/src/Components/Menu/GuestSubMenu.svelte b/front/src/Components/Menu/GuestSubMenu.svelte new file mode 100644 index 00000000..13a7981a --- /dev/null +++ b/front/src/Components/Menu/GuestSubMenu.svelte @@ -0,0 +1,75 @@ + + +
+
+ +
+

Share the link of the room !

+ + +
+
+
+ + \ No newline at end of file diff --git a/front/src/Components/Menu/Menu.svelte b/front/src/Components/Menu/Menu.svelte index 4eecb370..83715304 100644 --- a/front/src/Components/Menu/Menu.svelte +++ b/front/src/Components/Menu/Menu.svelte @@ -2,11 +2,11 @@ import {fly} from "svelte/transition"; import SettingsSubMenu from "./SettingsSubMenu.svelte"; import ProfileSubMenu from "./ProfileSubMenu.svelte"; - import CreateMapSubMenu from "./CreateMapSubMenu.svelte"; import AboutRoomSubMenu from "./AboutRoomSubMenu.svelte"; import GlobalMessageSubMenu from "./GlobalMessagesSubMenu.svelte"; import ContactSubMenu from "./ContactSubMenu.svelte"; import CustomSubMenu from "./CustomSubMenu.svelte" + import GuestSubMenu from "./GuestSubMenu.svelte"; import { checkSubMenuToShow, customMenuIframe, @@ -19,21 +19,21 @@ import type {Unsubscriber} from "svelte/store"; import {sendMenuClickedEvent} from "../../Api/iframe/Ui/MenuItem"; - let activeSubMenu: string = SubMenusInterface.settings; - let activeComponent: typeof SettingsSubMenu | typeof CustomSubMenu = SettingsSubMenu; + let activeSubMenu: string = SubMenusInterface.profile; + let activeComponent: typeof ProfileSubMenu | typeof CustomSubMenu = ProfileSubMenu; let props: { url: string, allowApi: boolean }; let unsubscriberSubMenuStore: Unsubscriber; onMount(() => { unsubscriberSubMenuStore = subMenusStore.subscribe(() => { if(!get(subMenusStore).includes(activeSubMenu)) { - switchMenu(SubMenusInterface.settings); + switchMenu(SubMenusInterface.profile); } }) checkSubMenuToShow(); - switchMenu(SubMenusInterface.settings); + switchMenu(SubMenusInterface.profile); }) onDestroy(() => { @@ -52,8 +52,8 @@ case SubMenusInterface.profile: activeComponent = ProfileSubMenu; break; - case SubMenusInterface.createMap: - activeComponent = CreateMapSubMenu; + case SubMenusInterface.invite: + activeComponent = GuestSubMenu; break; case SubMenusInterface.aboutRoom: activeComponent = AboutRoomSubMenu; diff --git a/front/src/Components/Menu/ProfileSubMenu.svelte b/front/src/Components/Menu/ProfileSubMenu.svelte index 83ec329c..e543096e 100644 --- a/front/src/Components/Menu/ProfileSubMenu.svelte +++ b/front/src/Components/Menu/ProfileSubMenu.svelte @@ -55,54 +55,73 @@
- {#if $userIsConnected} + + +
+ {#if $userIsConnected} +
+ {#if PROFILE_URL != undefined} + + {/if} +
+
+ +
+ {:else} +
+ Sign in +
+ {/if} +
+ + +
+
+ +
+
+ Profile validated by domain: ${domain} +
+
+ Your email: ${email} +
+
+ + ` + ); + } +} diff --git a/pusher/src/Enum/EnvironmentVariable.ts b/pusher/src/Enum/EnvironmentVariable.ts index 43bfc7bf..52382266 100644 --- a/pusher/src/Enum/EnvironmentVariable.ts +++ b/pusher/src/Enum/EnvironmentVariable.ts @@ -16,6 +16,7 @@ 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 const OPID_CLIENT_REDIREC_URL = process.env.OPID_CLIENT_REDIREC_URL || FRONT_URL + "/jwt"; +export const OPID_PROFILE_SCREEN_PROVIDER = process.env.OPID_PROFILE_SCREEN_PROVIDER || ADMIN_URL + "/profile"; export const DISABLE_ANONYMOUS = process.env.DISABLE_ANONYMOUS || false; export { diff --git a/pusher/src/Services/AdminApi.ts b/pusher/src/Services/AdminApi.ts index d002ff8b..6e1848eb 100644 --- a/pusher/src/Services/AdminApi.ts +++ b/pusher/src/Services/AdminApi.ts @@ -1,4 +1,4 @@ -import { ADMIN_API_TOKEN, ADMIN_API_URL, ADMIN_URL } from "../Enum/EnvironmentVariable"; +import { ADMIN_API_TOKEN, ADMIN_API_URL, ADMIN_URL, OPID_PROFILE_SCREEN_PROVIDER } from "../Enum/EnvironmentVariable"; import Axios from "axios"; import { GameRoomPolicyTypes } from "_Model/PusherRoom"; import { CharacterTexture } from "./AdminApi/CharacterTexture"; @@ -142,13 +142,15 @@ class AdminApi { }); } - /*TODO add constant to use profile companny*/ + /** + * + * @param accessToken + */ getProfileUrl(accessToken: string): string { - if (!ADMIN_URL) { + if (!OPID_PROFILE_SCREEN_PROVIDER) { throw new Error("No admin backoffice set!"); } - - return ADMIN_URL + `/profile?token=${accessToken}`; + return `${OPID_PROFILE_SCREEN_PROVIDER}?accessToken=${accessToken}`; } async logoutOauth(token: string) { From 16c08d86f29e144bf995b72b136d48fc5f41c5ba Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Mon, 15 Nov 2021 12:30:25 +0100 Subject: [PATCH 16/19] Update hydraAccessToken to accessToken --- pusher/src/Controller/AuthenticateController.ts | 15 ++++++++------- pusher/src/Services/JWTTokenManager.ts | 6 +++--- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/pusher/src/Controller/AuthenticateController.ts b/pusher/src/Controller/AuthenticateController.ts index 2dafe065..7b1f50bd 100644 --- a/pusher/src/Controller/AuthenticateController.ts +++ b/pusher/src/Controller/AuthenticateController.ts @@ -62,10 +62,11 @@ export class AuthenticateController extends BaseController { if (token != undefined) { try { const authTokenData: AuthTokenData = jwtTokenManager.verifyJWTToken(token as string, false); - if (authTokenData.hydraAccessToken == undefined) { + if (authTokenData.accessToken == undefined) { throw Error("Token cannot to be check on Hydra"); } - await openIDClient.checkTokenAuth(authTokenData.hydraAccessToken); + const resCheckTokenAuth = await openIDClient.checkTokenAuth(authTokenData.accessToken); + console.log("resCheckTokenAuth", resCheckTokenAuth); res.writeStatus("200"); this.addCorsHeaders(res); return res.end(JSON.stringify({ authToken: token })); @@ -100,10 +101,10 @@ export class AuthenticateController extends BaseController { try { const authTokenData: AuthTokenData = jwtTokenManager.verifyJWTToken(token as string, false); - if (authTokenData.hydraAccessToken == undefined) { + if (authTokenData.accessToken == undefined) { throw Error("Token cannot to be logout on Hydra"); } - await openIDClient.logoutUser(authTokenData.hydraAccessToken); + await openIDClient.logoutUser(authTokenData.accessToken); } catch (error) { console.error("openIDCallback => logout-callback", error); } finally { @@ -208,14 +209,14 @@ export class AuthenticateController extends BaseController { if (token != undefined) { try { const authTokenData: AuthTokenData = jwtTokenManager.verifyJWTToken(token as string, false); - if (authTokenData.hydraAccessToken == undefined) { + if (authTokenData.accessToken == undefined) { throw Error("Token cannot to be check on Hydra"); } - await openIDClient.checkTokenAuth(authTokenData.hydraAccessToken); + await openIDClient.checkTokenAuth(authTokenData.accessToken); //get login profile res.writeStatus("302"); - res.writeHeader("Location", adminApi.getProfileUrl(authTokenData.hydraAccessToken)); + res.writeHeader("Location", adminApi.getProfileUrl(authTokenData.accessToken)); this.addCorsHeaders(res); // eslint-disable-next-line no-unsafe-finally return res.end(); diff --git a/pusher/src/Services/JWTTokenManager.ts b/pusher/src/Services/JWTTokenManager.ts index 24393084..2f482dbf 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; + accessToken?: string; } export const tokenInvalidException = "tokenInvalid"; class JWTTokenManager { - public createAuthToken(identifier: string, hydraAccessToken?: string) { - return Jwt.sign({ identifier, hydraAccessToken }, SECRET_KEY, { expiresIn: "30d" }); + public createAuthToken(identifier: string, accessToken?: string) { + return Jwt.sign({ identifier, accessToken }, SECRET_KEY, { expiresIn: "30d" }); } public verifyJWTToken(token: string, ignoreExpiration: boolean = false): AuthTokenData { From 61b8d584af01432deca9b4d014c5b97f3e086e2e Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Mon, 15 Nov 2021 14:34:23 +0100 Subject: [PATCH 17/19] delete useless userIdentify --- pusher/src/Controller/AuthenticateController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pusher/src/Controller/AuthenticateController.ts b/pusher/src/Controller/AuthenticateController.ts index 7b1f50bd..0cef24bb 100644 --- a/pusher/src/Controller/AuthenticateController.ts +++ b/pusher/src/Controller/AuthenticateController.ts @@ -203,7 +203,7 @@ export class AuthenticateController extends BaseController { res.onAborted(() => { console.warn("/message request was aborted"); }); - const { userIdentify, token } = parse(req.getQuery()); + const { token } = parse(req.getQuery()); try { //verify connected by token if (token != undefined) { From bbc2ac255002133b00607b0cb8d6e73bc0c6dfc9 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Mon, 15 Nov 2021 15:25:49 +0100 Subject: [PATCH 18/19] Update Connection manager to clean url history of navigator --- front/src/Connexion/ConnectionManager.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index 1f844bf2..793831bf 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -212,6 +212,8 @@ class ConnectionManager { analyticsClient.identifyUser(this.localUser.uuid, this.localUser.email); } + //clean history with new URL + window.history.pushState({}, document.title, window.location.pathname); this.serviceWorker = new _ServiceWorker(); return Promise.resolve(this._currentRoom); } From 210a789aa460c63d759dc3d85e3eb5a49def0346 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Mon, 15 Nov 2021 15:58:08 +0100 Subject: [PATCH 19/19] Fix feedback @moufmouf --- .env.template | 2 +- docker-compose.single-domain.yaml | 2 +- docker-compose.yaml | 2 +- front/src/Connexion/ConnectionManager.ts | 3 + .../src/Controller/AuthenticateController.ts | 1 - .../src/Controller/OpenIdProfileController.ts | 64 +++++++++---------- pusher/src/Enum/EnvironmentVariable.ts | 2 +- pusher/src/Services/OpenIDClient.ts | 6 +- 8 files changed, 42 insertions(+), 40 deletions(-) diff --git a/.env.template b/.env.template index 5328fe08..0bd7bf6d 100644 --- a/.env.template +++ b/.env.template @@ -22,7 +22,7 @@ MAX_USERNAME_LENGTH=8 OPID_CLIENT_ID= OPID_CLIENT_SECRET= OPID_CLIENT_ISSUER= -OPID_CLIENT_REDIREC_URL= +OPID_CLIENT_REDIRECT_URL= OPID_LOGIN_SCREEN_PROVIDER=http://pusher.workadventure.localhost/login-screen OPID_PROFILE_SCREEN_PROVIDER= DISABLE_ANONYMOUS= diff --git a/docker-compose.single-domain.yaml b/docker-compose.single-domain.yaml index e241c108..cd38a0f9 100644 --- a/docker-compose.single-domain.yaml +++ b/docker-compose.single-domain.yaml @@ -71,7 +71,7 @@ services: OPID_CLIENT_ID: $OPID_CLIENT_ID OPID_CLIENT_SECRET: $OPID_CLIENT_SECRET OPID_CLIENT_ISSUER: $OPID_CLIENT_ISSUER - OPID_CLIENT_REDIREC_URL: $OPID_CLIENT_REDIREC_URL + OPID_CLIENT_REDIRECT_URL: $OPID_CLIENT_REDIRECT_URL OPID_PROFILE_SCREEN_PROVIDER: $OPID_PROFILE_SCREEN_PROVIDER DISABLE_ANONYMOUS: $DISABLE_ANONYMOUS volumes: diff --git a/docker-compose.yaml b/docker-compose.yaml index 03395f22..0e22fa91 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -73,7 +73,7 @@ services: OPID_CLIENT_ID: $OPID_CLIENT_ID OPID_CLIENT_SECRET: $OPID_CLIENT_SECRET OPID_CLIENT_ISSUER: $OPID_CLIENT_ISSUER - OPID_CLIENT_REDIREC_URL: $OPID_CLIENT_REDIREC_URL + OPID_CLIENT_REDIRECT_URL: $OPID_CLIENT_REDIRECT_URL OPID_PROFILE_SCREEN_PROVIDER: $OPID_PROFILE_SCREEN_PROVIDER DISABLE_ANONYMOUS: $DISABLE_ANONYMOUS volumes: diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index 793831bf..9316e37f 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -183,8 +183,11 @@ class ConnectionManager { } else { try { await this.checkAuthUserConnexion(); + analyticsClient.loggedWithSso(); } catch (err) { console.error(err); + this.loadOpenIDScreen(); + return Promise.reject(new Error("You will be redirect on login page")); } } this.localUser = localUserStore.getLocalUser() as LocalUser; //if authToken exist in localStorage then localUser cannot be null diff --git a/pusher/src/Controller/AuthenticateController.ts b/pusher/src/Controller/AuthenticateController.ts index 0cef24bb..70e333a8 100644 --- a/pusher/src/Controller/AuthenticateController.ts +++ b/pusher/src/Controller/AuthenticateController.ts @@ -66,7 +66,6 @@ export class AuthenticateController extends BaseController { throw Error("Token cannot to be check on Hydra"); } const resCheckTokenAuth = await openIDClient.checkTokenAuth(authTokenData.accessToken); - console.log("resCheckTokenAuth", resCheckTokenAuth); res.writeStatus("200"); this.addCorsHeaders(res); return res.end(JSON.stringify({ authToken: token })); diff --git a/pusher/src/Controller/OpenIdProfileController.ts b/pusher/src/Controller/OpenIdProfileController.ts index 372b603b..f33e7a22 100644 --- a/pusher/src/Controller/OpenIdProfileController.ts +++ b/pusher/src/Controller/OpenIdProfileController.ts @@ -44,37 +44,37 @@ export class OpenIdProfileController extends BaseController { } buildHtml(domain: string, email: string, pictureUrl?: string) { - return ( - "" + - ` -
- -
- -
-
- -
-
- Profile validated by domain: ${domain} -
-
- Your email: ${email} -
-
- - ` - ); + return ` + + + + + + +
+
+ +
+
+ Profile validated by domain: ${domain} +
+
+ Your email: ${email} +
+
+ + + `; } } diff --git a/pusher/src/Enum/EnvironmentVariable.ts b/pusher/src/Enum/EnvironmentVariable.ts index 52382266..23d2c23f 100644 --- a/pusher/src/Enum/EnvironmentVariable.ts +++ b/pusher/src/Enum/EnvironmentVariable.ts @@ -15,7 +15,7 @@ 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 const OPID_CLIENT_REDIREC_URL = process.env.OPID_CLIENT_REDIREC_URL || FRONT_URL + "/jwt"; +export const OPID_CLIENT_REDIRECT_URL = process.env.OPID_CLIENT_REDIRECT_URL || FRONT_URL + "/jwt"; export const OPID_PROFILE_SCREEN_PROVIDER = process.env.OPID_PROFILE_SCREEN_PROVIDER || ADMIN_URL + "/profile"; export const DISABLE_ANONYMOUS = process.env.DISABLE_ANONYMOUS || false; diff --git a/pusher/src/Services/OpenIDClient.ts b/pusher/src/Services/OpenIDClient.ts index bc0dd6c9..13bf6f76 100644 --- a/pusher/src/Services/OpenIDClient.ts +++ b/pusher/src/Services/OpenIDClient.ts @@ -3,7 +3,7 @@ import { OPID_CLIENT_ID, OPID_CLIENT_SECRET, OPID_CLIENT_ISSUER, - OPID_CLIENT_REDIREC_URL, + OPID_CLIENT_REDIRECT_URL, } from "../Enum/EnvironmentVariable"; class OpenIDClient { @@ -15,7 +15,7 @@ class OpenIDClient { return new issuer.Client({ client_id: OPID_CLIENT_ID, client_secret: OPID_CLIENT_SECRET, - redirect_uris: [OPID_CLIENT_REDIREC_URL], + redirect_uris: [OPID_CLIENT_REDIRECT_URL], response_types: ["code"], }); }); @@ -38,7 +38,7 @@ class OpenIDClient { public getUserInfo(code: string, nonce: string): Promise<{ email: string; sub: string; access_token: string }> { return this.initClient().then((client) => { - return client.callback(OPID_CLIENT_REDIREC_URL, { code }, { nonce }).then((tokenSet) => { + return client.callback(OPID_CLIENT_REDIRECT_URL, { code }, { nonce }).then((tokenSet) => { return client.userinfo(tokenSet).then((res) => { return { ...res,