From c30839b12bd0b97041017348e6c10dfca211ad17 Mon Sep 17 00:00:00 2001 From: Scott Anderson Date: Fri, 23 May 2025 19:54:26 -0400 Subject: [PATCH] Better login handling (#855) ## Description: - Display "Checking login..." while waiting for the API server to respond to our profile lookup request. - Set the client to logged out if any of the API calls fail. - Fix a bug causing the client to send a JWT even if the API server was down. Closes #824 ![image](https://github.com/user-attachments/assets/d6413489-b6af-43b5-be8a-676142697495) ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced - [x] I understand that submitting code with bugs that could have been caught through manual testing blocks releases and new features for all contributors Co-authored-by: Scott Anderson <662325+scottanderson@users.noreply.github.com> --- resources/lang/en.json | 1 + src/client/ClientGameRunner.ts | 4 ++-- src/client/LocalServer.ts | 4 ++-- src/client/Main.ts | 30 ++++++++++++++++++++---------- src/client/jwt.ts | 14 ++++++++++---- 5 files changed, 35 insertions(+), 18 deletions(-) diff --git a/resources/lang/en.json b/resources/lang/en.json index 4add1cf6f..dd04c9548 100644 --- a/resources/lang/en.json +++ b/resources/lang/en.json @@ -9,6 +9,7 @@ "title": "OpenFront (ALPHA)", "join_discord": "Join the Discord!", "login_discord": "Login with Discord", + "checking_login": "Checking login...", "logged_in": "Logged in!", "log_out": "Log out", "create_lobby": "Create Lobby", diff --git a/src/client/ClientGameRunner.ts b/src/client/ClientGameRunner.ts index b9530fbc1..3ffb77e36 100644 --- a/src/client/ClientGameRunner.ts +++ b/src/client/ClientGameRunner.ts @@ -26,7 +26,7 @@ import { UserSettings } from "../core/game/UserSettings"; import { WorkerClient } from "../core/worker/WorkerClient"; import { InputHandler, MouseMoveEvent, MouseUpEvent } from "./InputHandler"; import { endGame, startGame, startTime } from "./LocalPersistantStats"; -import { getPersistentIDFromCookie } from "./Main"; +import { getPersistentID } from "./Main"; import { SendAttackIntentEvent, SendBoatAttackIntentEvent, @@ -193,7 +193,7 @@ export class ClientGameRunner { const players: PlayerRecord[] = [ { playerID: this.myPlayer.id(), - persistentID: getPersistentIDFromCookie(), + persistentID: getPersistentID(), username: this.lobby.playerName, clientID: this.lobby.clientID, stats: update.allPlayersStats[this.lobby.clientID], diff --git a/src/client/LocalServer.ts b/src/client/LocalServer.ts index 0467f1e26..12b8956bf 100644 --- a/src/client/LocalServer.ts +++ b/src/client/LocalServer.ts @@ -13,7 +13,7 @@ import { } from "../core/Schemas"; import { createGameRecord, decompressGameRecord } from "../core/Util"; import { LobbyConfig } from "./ClientGameRunner"; -import { getPersistentIDFromCookie } from "./Main"; +import { getPersistentID } from "./Main"; export class LocalServer { // All turns from the game record on replay. @@ -177,7 +177,7 @@ export class LocalServer { const players: PlayerRecord[] = [ { playerID: this.lobbyConfig.clientID, // hack? - persistentID: getPersistentIDFromCookie(), + persistentID: getPersistentID(), username: this.lobbyConfig.playerName, clientID: this.lobbyConfig.clientID, stats: this.allPlayersStats[this.lobbyConfig.clientID], diff --git a/src/client/Main.ts b/src/client/Main.ts index 3e896afbd..22bd98b65 100644 --- a/src/client/Main.ts +++ b/src/client/Main.ts @@ -158,17 +158,16 @@ class Client { hlpModal.open(); }); - const claims = isLoggedIn(); - if (claims === false) { + if (isLoggedIn() === false) { // Not logged in loginDiscordButton.disable = false; loginDiscordButton.translationKey = "main.login_discord"; loginDiscordButton.addEventListener("click", discordLogin); logoutDiscordButton.hidden = true; } else { - // JWT appears to be valid, assume we are logged in + // JWT appears to be valid loginDiscordButton.disable = true; - loginDiscordButton.translationKey = "main.logged_in"; + loginDiscordButton.translationKey = "main.checking_login"; logoutDiscordButton.hidden = false; logoutDiscordButton.addEventListener("click", () => { // Log out @@ -190,6 +189,8 @@ class Client { return; } // TODO: Update the page for logged in user + loginDiscordButton.translationKey = "main.logged_in"; + const { user, player } = userMeResponse; }); } @@ -289,7 +290,7 @@ class Client { ? "" : this.flagInput.getCurrentFlag(), playerName: this.usernameInput?.getCurrentUsername() ?? "", - token: localStorage.getItem("token") ?? getPersistentIDFromCookie(), + token: getPlayToken(), clientID: lobby.clientID, gameStartInfo: lobby.gameStartInfo ?? lobby.gameRecord?.info, gameRecord: lobby.gameRecord, @@ -368,12 +369,21 @@ function setFavicon(): void { } // WARNING: DO NOT EXPOSE THIS ID -export function getPersistentIDFromCookie(): string { - const claims = isLoggedIn(); - if (claims !== false && claims.sub) { - return claims.sub; - } +function getPlayToken(): string { + const result = isLoggedIn(); + if (result !== false) return result.token; + return getPersistentIDFromCookie(); +} +// WARNING: DO NOT EXPOSE THIS ID +export function getPersistentID(): string { + const result = isLoggedIn(); + if (result !== false) return result.claims.sub; + return getPersistentIDFromCookie(); +} + +// WARNING: DO NOT EXPOSE THIS ID +function getPersistentIDFromCookie(): string { const COOKIE_NAME = "player_persistent_id"; // Try to get existing cookie diff --git a/src/client/jwt.ts b/src/client/jwt.ts index b9e194b6a..99c337f5a 100644 --- a/src/client/jwt.ts +++ b/src/client/jwt.ts @@ -65,14 +65,17 @@ export async function logOut(allSessions: boolean = false) { return true; } -let __isLoggedIn: TokenPayload | false | undefined = undefined; -export function isLoggedIn(): TokenPayload | false { +export type IsLoggedInResponse = + | { token: string; claims: TokenPayload } + | false; +let __isLoggedIn: IsLoggedInResponse | undefined = undefined; +export function isLoggedIn(): IsLoggedInResponse { if (__isLoggedIn === undefined) { __isLoggedIn = _isLoggedIn(); } return __isLoggedIn; } -export function _isLoggedIn(): TokenPayload | false { +function _isLoggedIn(): IsLoggedInResponse { try { const token = getToken(); if (!token) { @@ -144,7 +147,8 @@ export function _isLoggedIn(): TokenPayload | false { return false; } - return result.data; + const claims = result.data; + return { token, claims }; } catch (e) { console.log(e); return false; @@ -177,6 +181,7 @@ export async function postRefresh(): Promise { localStorage.setItem("token", result.data.token); return true; } catch (e) { + __isLoggedIn = false; return false; } } @@ -205,6 +210,7 @@ export async function getUserMe(): Promise { } return result.data; } catch (e) { + __isLoggedIn = false; return false; } }