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
@@ -95,6 +95,11 @@ dashboard POST /api/vest/session holds it in memory for the firm's Vest cl
|
||||
- The hook only reads the Vest app's own `/v3/auth/refresh` and `/register`
|
||||
answers. It renews nothing and sends nothing; the session stays the app's,
|
||||
so the bot never logs the tab out.
|
||||
- It takes the login's token only. The app renews each trading account's own
|
||||
token through the same `/v3/auth/refresh` (with that account's refresh token
|
||||
in the body), and one of those sees a single account. Before 0.17.2 the hook
|
||||
relayed those too, and the dashboard dropped every other account; the
|
||||
dashboard now refuses such a token and falls back to the login's.
|
||||
- The token lives ~15 minutes and the app renews it by itself while the tab is
|
||||
open. The tab is marked not auto-discardable, since a discarded tab stops
|
||||
renewing.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "AutoFirmer Capture",
|
||||
"version": "0.17.1",
|
||||
"version": "0.17.2",
|
||||
|
||||
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxdbOMRR+58a9dHlbIKtlALtS3E4eltBGqfTnUc0lzJuWj5ZA4+5YFSdUnxv5N1ogxwpJUGkUV3MnKJuZnTekCmsNwjCy41a20Xtc7jQLHrIBZkesB6cB+d0l9Vo8M6EC3ZMD8Iv6MdAalU3GZh1Hn9vm24NjJ+xUT1qNIVHEcNUIb9evYobR/XHv+JAp6bOFnaLM+3wpyYKe4efMhsz0F86u9thRNhQnraTqUWfnKNpsHPK2zivaYHrziEEWX7Y3EsMHkf2FRhkUe7IDoxeJM49DBToTo9sAJUkcgLeGCGZLo7H3mcxyn/zERYR+rD3k5xdeBtjoZHXFx2+FanLb9wIDAQAB",
|
||||
"update_url": "https://ext.juicerroom.com/updates.xml",
|
||||
|
||||
+22
-7
@@ -14,6 +14,12 @@
|
||||
* It sends nothing anywhere, changes no request and never renews anything
|
||||
* itself: the session stays the app's, refreshed by the app with its own
|
||||
* device key, so the tab is never logged out by the bot.
|
||||
*
|
||||
* The app also renews each trading account's own token through the same
|
||||
* /v3/auth/refresh, sending that account's refresh token in the body (the
|
||||
* login's rides in a cookie) and getting it back renewed. Those tokens see
|
||||
* one account only, so they are skipped: relaying one would leave the
|
||||
* dashboard with a single account.
|
||||
*/
|
||||
|
||||
(() => {
|
||||
@@ -22,11 +28,17 @@
|
||||
|
||||
const AUTH = /^https:\/\/api-gateway\.hz\.vestmarkets\.com\/v3\/auth\/(refresh|register)(\?|$)/;
|
||||
|
||||
function take(url, status, text) {
|
||||
if (status < 200 || status >= 300 || !AUTH.test(url)) return;
|
||||
/** A request body that carries a refresh token: an account's renewal. */
|
||||
function accountRenewal(reqBody) {
|
||||
return typeof reqBody === 'string' && /"refreshToken"\s*:\s*"[^"]/.test(reqBody);
|
||||
}
|
||||
|
||||
function take(url, status, text, accountRequest) {
|
||||
if (accountRequest || status < 200 || status >= 300 || !AUTH.test(url)) return;
|
||||
let body;
|
||||
try { body = JSON.parse(text); } catch { return; }
|
||||
if (!body || typeof body.accessToken !== 'string' || !body.accessToken) return;
|
||||
if (typeof body.refreshToken === 'string' && body.refreshToken) return; // an account's token, renewed
|
||||
const expiresAt = Number(body.accessExpiresAtMs);
|
||||
if (!Number.isFinite(expiresAt)) return;
|
||||
window.__afVestSession = {
|
||||
@@ -44,12 +56,14 @@
|
||||
try { this.__afVestUrl = String(url); } catch { /* ignore */ }
|
||||
return open.apply(this, arguments);
|
||||
};
|
||||
XMLHttpRequest.prototype.send = function () {
|
||||
XMLHttpRequest.prototype.send = function (reqBody) {
|
||||
if (this.__afVestUrl && AUTH.test(this.__afVestUrl)) {
|
||||
let acct = false;
|
||||
try { acct = accountRenewal(reqBody); } catch { /* ignore */ }
|
||||
this.addEventListener('loadend', () => {
|
||||
try {
|
||||
if (this.responseType === '' || this.responseType === 'text') take(this.__afVestUrl, this.status, this.responseText);
|
||||
else if (this.responseType === 'json') take(this.__afVestUrl, this.status, JSON.stringify(this.response));
|
||||
if (this.responseType === '' || this.responseType === 'text') take(this.__afVestUrl, this.status, this.responseText, acct);
|
||||
else if (this.responseType === 'json') take(this.__afVestUrl, this.status, JSON.stringify(this.response), acct);
|
||||
} catch { /* never break the app */ }
|
||||
});
|
||||
}
|
||||
@@ -57,11 +71,12 @@
|
||||
};
|
||||
|
||||
const fetch0 = window.fetch;
|
||||
window.fetch = async function (input) {
|
||||
window.fetch = async function (input, init) {
|
||||
const res = await fetch0.apply(this, arguments);
|
||||
try {
|
||||
const url = typeof input === 'string' ? input : input instanceof URL ? input.href : input && input.url;
|
||||
if (url && AUTH.test(url)) res.clone().text().then((t) => take(url, res.status, t)).catch(() => {});
|
||||
const acct = accountRenewal(init && init.body);
|
||||
if (url && AUTH.test(url)) res.clone().text().then((t) => take(url, res.status, t, acct)).catch(() => {});
|
||||
} catch { /* never break the app */ }
|
||||
return res;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user