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>
This commit is contained in:
Scott Anderson
2025-05-23 19:54:26 -04:00
committed by GitHub
parent 773c9566fc
commit c30839b12b
5 changed files with 35 additions and 18 deletions
+1
View File
@@ -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",
+2 -2
View File
@@ -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],
+2 -2
View File
@@ -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],
+20 -10
View File
@@ -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
+10 -4
View File
@@ -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<boolean> {
localStorage.setItem("token", result.data.token);
return true;
} catch (e) {
__isLoggedIn = false;
return false;
}
}
@@ -205,6 +210,7 @@ export async function getUserMe(): Promise<UserMeResponse | false> {
}
return result.data;
} catch (e) {
__isLoggedIn = false;
return false;
}
}