diff --git a/.env.template b/.env.template
index 715ebeec..5328fe08 100644
--- a/.env.template
+++ b/.env.template
@@ -22,6 +22,10 @@ MAX_USERNAME_LENGTH=8
OPID_CLIENT_ID=
OPID_CLIENT_SECRET=
OPID_CLIENT_ISSUER=
+OPID_CLIENT_REDIREC_URL=
+OPID_LOGIN_SCREEN_PROVIDER=http://pusher.workadventure.localhost/login-screen
+OPID_PROFILE_SCREEN_PROVIDER=
+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..e241c108 100644
--- a/docker-compose.single-domain.yaml
+++ b/docker-compose.single-domain.yaml
@@ -40,6 +40,7 @@ services:
TURN_USER: ""
TURN_PASSWORD: ""
START_ROOM_URL: "$START_ROOM_URL"
+ DISABLE_ANONYMOUS: "$DISABLE_ANONYMOUS"
command: yarn run start
volumes:
- ./front:/usr/src/app
@@ -70,6 +71,9 @@ 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_PROFILE_SCREEN_PROVIDER: $OPID_PROFILE_SCREEN_PROVIDER
+ DISABLE_ANONYMOUS: $DISABLE_ANONYMOUS
volumes:
- ./pusher:/usr/src/app
labels:
diff --git a/docker-compose.yaml b/docker-compose.yaml
index f68ed6a0..03395f22 100644
--- a/docker-compose.yaml
+++ b/docker-compose.yaml
@@ -43,6 +43,8 @@ services:
START_ROOM_URL: "$START_ROOM_URL"
MAX_PER_GROUP: "$MAX_PER_GROUP"
MAX_USERNAME_LENGTH: "$MAX_USERNAME_LENGTH"
+ DISABLE_ANONYMOUS: "$DISABLE_ANONYMOUS"
+ OPID_LOGIN_SCREEN_PROVIDER: "$OPID_LOGIN_SCREEN_PROVIDER"
command: yarn run start
volumes:
- ./front:/usr/src/app
@@ -71,6 +73,9 @@ 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_PROFILE_SCREEN_PROVIDER: $OPID_PROFILE_SCREEN_PROVIDER
+ 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 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);
}
diff --git a/front/src/Connexion/Room.ts b/front/src/Connexion/Room.ts
index 860223c6..535d2f8d 100644
--- a/front/src/Connexion/Room.ts
+++ b/front/src/Connexion/Room.ts
@@ -1,5 +1,5 @@
import Axios from "axios";
-import { CONTACT_URL, PUSHER_URL } from "../Enum/EnvironmentVariable";
+import { CONTACT_URL, PUSHER_URL, DISABLE_ANONYMOUS, OPID_LOGIN_SCREEN_PROVIDER } from "../Enum/EnvironmentVariable";
import type { CharacterTexture } from "./LocalUser";
import { localUserStore } from "./LocalUserStore";
@@ -14,8 +14,8 @@ export interface RoomRedirect {
export class Room {
public readonly id: string;
public readonly isPublic: boolean;
- private _authenticationMandatory: boolean = false;
- private _iframeAuthentication?: string;
+ private _authenticationMandatory: boolean = DISABLE_ANONYMOUS as boolean;
+ private _iframeAuthentication?: string = OPID_LOGIN_SCREEN_PROVIDER;
private _mapUrl: string | undefined;
private _textures: CharacterTexture[] | undefined;
private instance: string | undefined;
@@ -106,8 +106,8 @@ export class Room {
this._mapUrl = data.mapUrl;
this._textures = data.textures;
this._group = data.group;
- this._authenticationMandatory = data.authenticationMandatory || false;
- this._iframeAuthentication = data.iframeAuthentication;
+ this._authenticationMandatory = data.authenticationMandatory || (DISABLE_ANONYMOUS as boolean);
+ this._iframeAuthentication = data.iframeAuthentication || OPID_LOGIN_SCREEN_PROVIDER;
this._contactPage = data.contactPage || CONTACT_URL;
return new MapDetail(data.mapUrl, data.textures);
}
diff --git a/front/src/Enum/EnvironmentVariable.ts b/front/src/Enum/EnvironmentVariable.ts
index cf76a87d..644b7a77 100644
--- a/front/src/Enum/EnvironmentVariable.ts
+++ b/front/src/Enum/EnvironmentVariable.ts
@@ -23,6 +23,8 @@ export const CONTACT_URL = process.env.CONTACT_URL || undefined;
export const PROFILE_URL = process.env.PROFILE_URL || undefined;
export const POSTHOG_API_KEY: string = (process.env.POSTHOG_API_KEY as string) || "";
export const POSTHOG_URL = process.env.POSTHOG_URL || undefined;
+export const DISABLE_ANONYMOUS = process.env.DISABLE_ANONYMOUS || false;
+export const OPID_LOGIN_SCREEN_PROVIDER = process.env.OPID_LOGIN_SCREEN_PROVIDER;
export const isMobile = (): boolean => window.innerWidth <= 800 || window.innerHeight <= 600;
diff --git a/front/webpack.config.ts b/front/webpack.config.ts
index 003db1fc..9d18d51f 100644
--- a/front/webpack.config.ts
+++ b/front/webpack.config.ts
@@ -7,7 +7,6 @@ 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 { POSTHOG_API_KEY, PROFILE_URL } from "./src/Enum/EnvironmentVariable";
const mode = process.env.NODE_ENV ?? "development";
const buildNpmTypingsForApi = !!process.env.BUILD_TYPINGS;
@@ -208,6 +207,8 @@ module.exports = {
POSTHOG_API_KEY: null,
POSTHOG_URL: null,
NODE_ENV: mode,
+ DISABLE_ANONYMOUS: false,
+ OPID_LOGIN_SCREEN_PROVIDER: null,
}),
],
} as Configuration & WebpackDevServer.Configuration;
diff --git a/pusher/src/App.ts b/pusher/src/App.ts
index 81aed045..327d493c 100644
--- a/pusher/src/App.ts
+++ b/pusher/src/App.ts
@@ -6,6 +6,7 @@ import { PrometheusController } from "./Controller/PrometheusController";
import { DebugController } from "./Controller/DebugController";
import { App as uwsApp } from "./Server/sifrr.server";
import { AdminController } from "./Controller/AdminController";
+import { OpenIdProfileController } from "./Controller/OpenIdProfileController";
class App {
public app: uwsApp;
@@ -15,6 +16,7 @@ class App {
public prometheusController: PrometheusController;
private debugController: DebugController;
private adminController: AdminController;
+ private openIdProfileController: OpenIdProfileController;
constructor() {
this.app = new uwsApp();
@@ -26,6 +28,7 @@ class App {
this.prometheusController = new PrometheusController(this.app);
this.debugController = new DebugController(this.app);
this.adminController = new AdminController(this.app);
+ this.openIdProfileController = new OpenIdProfileController(this.app);
}
}
diff --git a/pusher/src/Controller/AuthenticateController.ts b/pusher/src/Controller/AuthenticateController.ts
index 972cc102..0cef24bb 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;
@@ -61,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 }));
@@ -99,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 {
@@ -175,16 +177,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,
+ })
+ );
+ }
});
}
@@ -196,20 +203,20 @@ 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) {
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/Controller/IoSocketController.ts b/pusher/src/Controller/IoSocketController.ts
index 0466100c..f73e44fd 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, DISABLE_ANONYMOUS, SOCKET_IDLE_TIMER } 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..18748d9e 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.authenticationMandatory = true;
+ }
+
res.writeStatus("200 OK");
this.addCorsHeaders(res);
res.end(JSON.stringify(mapDetails));
diff --git a/pusher/src/Controller/OpenIdProfileController.ts b/pusher/src/Controller/OpenIdProfileController.ts
new file mode 100644
index 00000000..372b603b
--- /dev/null
+++ b/pusher/src/Controller/OpenIdProfileController.ts
@@ -0,0 +1,80 @@
+import { BaseController } from "./BaseController";
+import { HttpRequest, HttpResponse, TemplatedApp } from "uWebSockets.js";
+import { parse } from "query-string";
+import { openIDClient } from "../Services/OpenIDClient";
+import { AuthTokenData, jwtTokenManager } from "../Services/JWTTokenManager";
+import { adminApi } from "../Services/AdminApi";
+import { OPID_CLIENT_ISSUER } from "../Enum/EnvironmentVariable";
+import { IntrospectionResponse } from "openid-client";
+
+export class OpenIdProfileController extends BaseController {
+ constructor(private App: TemplatedApp) {
+ super();
+ this.profileOpenId();
+ }
+
+ profileOpenId() {
+ //eslint-disable-next-line @typescript-eslint/no-misused-promises
+ this.App.get("/profile", async (res: HttpResponse, req: HttpRequest) => {
+ res.onAborted(() => {
+ console.warn("/message request was aborted");
+ });
+
+ const { accessToken } = parse(req.getQuery());
+ if (!accessToken) {
+ throw Error("Access token expected cannot to be check on Hydra");
+ }
+ try {
+ const resCheckTokenAuth = await openIDClient.checkTokenAuth(accessToken as string);
+ if (!resCheckTokenAuth.email) {
+ throw "Email was not found";
+ }
+ res.end(
+ this.buildHtml(
+ OPID_CLIENT_ISSUER,
+ resCheckTokenAuth.email as string,
+ resCheckTokenAuth.picture as string | undefined
+ )
+ );
+ } catch (error) {
+ console.error("profileCallback => ERROR", error);
+ this.errorToResponse(error, res);
+ }
+ });
+ }
+
+ buildHtml(domain: string, email: string, pictureUrl?: string) {
+ return (
+ "" +
+ `
+