mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 12:10:46 +00:00
bugfix: user receives unauthorized on reconnection (#2601)
## Description: When rejoining, the client did not refresh the play token, so the jwt could be expired, causing the server to reject it. Also improved the error log when token fails ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: evan
This commit is contained in:
@@ -57,7 +57,6 @@ export interface LobbyConfig {
|
||||
playerName: string;
|
||||
clientID: ClientID;
|
||||
gameID: GameID;
|
||||
token: string;
|
||||
turnstileToken: string | null;
|
||||
// GameStartInfo only exists when playing a singleplayer game.
|
||||
gameStartInfo?: GameStartInfo;
|
||||
|
||||
+1
-2
@@ -8,7 +8,7 @@ import { GameType } from "../core/game/Game";
|
||||
import { UserSettings } from "../core/game/UserSettings";
|
||||
import "./AccountModal";
|
||||
import { getUserMe } from "./Api";
|
||||
import { getPlayToken, userAuth } from "./Auth";
|
||||
import { userAuth } from "./Auth";
|
||||
import { joinLobby } from "./ClientGameRunner";
|
||||
import { fetchCosmetics } from "./Cosmetics";
|
||||
import "./DarkModeButton";
|
||||
@@ -495,7 +495,6 @@ class Client {
|
||||
},
|
||||
turnstileToken: await this.getTurnstileToken(lobby),
|
||||
playerName: this.usernameInput?.getCurrentUsername() ?? "",
|
||||
token: await getPlayToken(),
|
||||
clientID: lobby.clientID,
|
||||
gameStartInfo: lobby.gameStartInfo ?? lobby.gameRecord?.info,
|
||||
gameRecord: lobby.gameRecord,
|
||||
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
Winner,
|
||||
} from "../core/Schemas";
|
||||
import { replacer } from "../core/Util";
|
||||
import { getPlayToken } from "./Auth";
|
||||
import { LobbyConfig } from "./ClientGameRunner";
|
||||
import { LocalServer } from "./LocalServer";
|
||||
|
||||
@@ -388,25 +389,25 @@ export class Transport {
|
||||
}
|
||||
}
|
||||
|
||||
joinGame() {
|
||||
async joinGame() {
|
||||
this.sendMsg({
|
||||
type: "join",
|
||||
gameID: this.lobbyConfig.gameID,
|
||||
clientID: this.lobbyConfig.clientID,
|
||||
token: this.lobbyConfig.token,
|
||||
username: this.lobbyConfig.playerName,
|
||||
cosmetics: this.lobbyConfig.cosmetics,
|
||||
turnstileToken: this.lobbyConfig.turnstileToken,
|
||||
token: await getPlayToken(),
|
||||
} satisfies ClientJoinMessage);
|
||||
}
|
||||
|
||||
rejoinGame(lastTurn: number) {
|
||||
async rejoinGame(lastTurn: number) {
|
||||
this.sendMsg({
|
||||
type: "rejoin",
|
||||
gameID: this.lobbyConfig.gameID,
|
||||
clientID: this.lobbyConfig.clientID,
|
||||
lastTurn: lastTurn,
|
||||
token: this.lobbyConfig.token,
|
||||
token: await getPlayToken(),
|
||||
} satisfies ClientRejoinMessage);
|
||||
}
|
||||
|
||||
|
||||
@@ -337,8 +337,10 @@ export async function startWorker() {
|
||||
|
||||
// Verify token signature
|
||||
const result = await verifyClientToken(clientMsg.token, config);
|
||||
if (result === false) {
|
||||
log.warn("Unauthorized: Invalid token");
|
||||
if (result.type === "error") {
|
||||
log.warn(`Invalid token: ${result.message}`, {
|
||||
clientID: clientMsg.clientID,
|
||||
});
|
||||
ws.close(1002, "Unauthorized");
|
||||
return;
|
||||
}
|
||||
|
||||
+16
-7
@@ -11,17 +11,18 @@ import { PersistentIdSchema } from "../core/Schemas";
|
||||
|
||||
type TokenVerificationResult =
|
||||
| {
|
||||
type: "success";
|
||||
persistentId: string;
|
||||
claims: TokenPayload | null;
|
||||
}
|
||||
| false;
|
||||
| { type: "error"; message: string };
|
||||
|
||||
export async function verifyClientToken(
|
||||
token: string,
|
||||
config: ServerConfig,
|
||||
): Promise<TokenVerificationResult> {
|
||||
if (PersistentIdSchema.safeParse(token).success) {
|
||||
return { persistentId: token, claims: null };
|
||||
return { type: "success", persistentId: token, claims: null };
|
||||
}
|
||||
try {
|
||||
const issuer = config.jwtIssuer();
|
||||
@@ -34,15 +35,23 @@ export async function verifyClientToken(
|
||||
});
|
||||
const result = TokenPayloadSchema.safeParse(payload);
|
||||
if (!result.success) {
|
||||
const error = z.prettifyError(result.error);
|
||||
console.warn("Error parsing token payload", error);
|
||||
return false;
|
||||
return {
|
||||
type: "error",
|
||||
message: z.prettifyError(result.error),
|
||||
};
|
||||
}
|
||||
const claims = result.data;
|
||||
const persistentId = claims.sub;
|
||||
return { persistentId, claims };
|
||||
return { type: "success", persistentId, claims };
|
||||
} catch (e) {
|
||||
return false;
|
||||
const message =
|
||||
e instanceof Error
|
||||
? e.message
|
||||
: typeof e === "string"
|
||||
? e
|
||||
: "An unknown error occurred";
|
||||
|
||||
return { type: "error", message };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user