Vest: relay the login's token, never one account's
The Vest app renews each trading account's own token through the same POST /v3/auth/refresh as the login's (with that account's refresh token in the body). The extension relayed those too, and the dashboard kept whichever token lived longest — so while someone traded in an account on Vest, Me synced with a token that sees that account alone and dropped the other 24 from the list. Extension 0.17.2 skips account renewals. The dashboard also refuses a token that sees one account and no primary (or names that account in its claims), keeps the last account list, and falls back to the login's token it replaced. An account gone from Vest's list no longer leaves a stale position holding the lane. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
95cc3bd23f
commit
a0e7c5b7c2
+36
-1
@@ -32,12 +32,30 @@ export interface VestSessionInfo {
|
||||
receivedAt: number;
|
||||
}
|
||||
|
||||
const g = globalThis as typeof globalThis & { __vestSessions?: Map<string, VestSession> };
|
||||
const g = globalThis as typeof globalThis & {
|
||||
__vestSessions?: Map<string, VestSession>;
|
||||
__vestPrevious?: Map<string, VestSession>;
|
||||
__vestRefused?: Map<string, number>;
|
||||
};
|
||||
|
||||
function store(): Map<string, VestSession> {
|
||||
return (g.__vestSessions ??= new Map());
|
||||
}
|
||||
|
||||
/** The session each wallet's current one replaced — what a refused token
|
||||
* falls back to. */
|
||||
function previous(): Map<string, VestSession> {
|
||||
return (g.__vestPrevious ??= new Map());
|
||||
}
|
||||
|
||||
/** Tokens the client found to be one account's, not the login's (by their
|
||||
* tail — enough to tell them apart, never the whole token), with when. */
|
||||
function refused(): Map<string, number> {
|
||||
return (g.__vestRefused ??= new Map());
|
||||
}
|
||||
|
||||
const REFUSED_KEEP = 50;
|
||||
|
||||
/** Shortest life a relayed token must have left to be taken. */
|
||||
const MIN_REMAINING_MS = 30_000;
|
||||
/** Longest a relayed token may claim to live — a Vest access token is
|
||||
@@ -54,8 +72,10 @@ export function putVestSession(input: { accessToken: unknown; expiresAt: unknown
|
||||
if (!/^0x[0-9a-f]{40}$/.test(addr)) return 'primaryAddr is not a wallet address';
|
||||
if (!Number.isFinite(expiresAt) || expiresAt - now < MIN_REMAINING_MS) return 'token already expired';
|
||||
if (expiresAt - now > MAX_REMAINING_MS) return 'expiresAt is implausibly far away';
|
||||
if (refused().has(token.slice(-32))) return "that is one Vest account's token, not the login's — reload the Vest tab, or update the extension";
|
||||
const prev = store().get(addr);
|
||||
if (prev && prev.expiresAt > expiresAt && prev.accessToken !== token) return null; // keep the fresher one
|
||||
if (prev && prev.accessToken !== token) previous().set(addr, prev);
|
||||
store().set(addr, {
|
||||
accessToken: token,
|
||||
expiresAt,
|
||||
@@ -73,6 +93,21 @@ export function vestSessionFor(primaryAddr: string, now = Date.now(), minRemaini
|
||||
return s && s.expiresAt - now >= minRemainingMs ? s : null;
|
||||
}
|
||||
|
||||
/** The client found the wallet's token to be one account's own (see
|
||||
* accountScopedToken): drop it, and take it no more if it's relayed again —
|
||||
* the login's own token, when the Vest app next renews it, takes its place. */
|
||||
export function refuseVestSession(primaryAddr: string, token: string, now = Date.now()): void {
|
||||
const key = primaryAddr.trim().toLowerCase();
|
||||
refused().set(token.slice(-32), now);
|
||||
while (refused().size > REFUSED_KEEP) refused().delete(refused().keys().next().value as string);
|
||||
if (store().get(key)?.accessToken !== token) return;
|
||||
store().delete(key);
|
||||
// Back to the token it replaced, if that one is still good.
|
||||
const before = previous().get(key);
|
||||
previous().delete(key);
|
||||
if (before && !refused().has(before.accessToken.slice(-32)) && before.expiresAt - now >= MIN_REMAINING_MS) store().set(key, before);
|
||||
}
|
||||
|
||||
/** Drop a wallet's session — the gateway refused it. */
|
||||
export function dropVestSession(primaryAddr: string, token: string): void {
|
||||
const key = primaryAddr.trim().toLowerCase();
|
||||
|
||||
Reference in New Issue
Block a user