mirror of
https://github.com/HeyPuter/puter.git
synced 2026-07-08 08:12:15 +00:00
chore: drop linear/issue ticket references from source comments (#3159)
54 references across 18 files (PUT-1010, PUT-1014, PUT-1019, PUT-1021, PUT-1022, PUT-1023, PUT-1024 + sub-tags AUTH-2/4/5, SDK-1, PJS-1/2, GUI-1/2, ROLLOUT-1) removed from inline comments, doc comments, test describe blocks, and SQL migration headers. The substantive explanations stay; only the ticket pointers go. No behavior change. Full backend test suite: 2172 passed / 16 skipped. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -15,7 +15,7 @@
|
||||
-- You should have received a copy of the GNU Affero General Public License
|
||||
-- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
-- AUTH-5 (PUT-1019) — mirrors SQLite migration 0053. Adds the
|
||||
-- Mirrors SQLite migration 0053. Adds the
|
||||
-- `access_token_uid` reverse-lookup column on `sessions` so raw-uuid
|
||||
-- revoke can find the matching session row when only the v2 token_uid
|
||||
-- (no JWT) is presented.
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
-- You should have received a copy of the GNU Affero General Public License
|
||||
-- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
-- AUTH-2 (PUT-1014) — composite-key lookups + audit columns. Mirrors
|
||||
-- Composite-key lookups + audit columns. Mirrors
|
||||
-- SQLite migration 0052. MySQL has no partial unique indexes, so the
|
||||
-- "at most one active row per (user_id, app_uid)" / "one active row
|
||||
-- per legacy_token_uid" semantics are encoded via VIRTUAL generated
|
||||
|
||||
@@ -15,14 +15,14 @@
|
||||
-- You should have received a copy of the GNU Affero General Public License
|
||||
-- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
-- AUTH-2 (PUT-1014) — composite-key lookups + audit columns.
|
||||
-- Composite-key lookups + audit columns.
|
||||
-- - `app_uid` : binds `kind='app'` rows to their app authorization
|
||||
-- target. (user_id, app_uid) is the idempotency key.
|
||||
-- - `legacy_token_uid` : keys lazy-backfilled rows to the v1 token_uid that
|
||||
-- originally minted them.
|
||||
-- - `created_via` : audit sentinel (e.g. 'legacy_backfill').
|
||||
-- - `auth_id` : stable per-user identity that survives re-login
|
||||
-- (PUT-1010); lets manage-sessions group by identity.
|
||||
-- - `auth_id` : stable per-user identity that survives re-login;
|
||||
-- lets manage-sessions group by identity.
|
||||
|
||||
ALTER TABLE `sessions` ADD COLUMN `app_uid` TEXT;
|
||||
ALTER TABLE `sessions` ADD COLUMN `legacy_token_uid` TEXT;
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
-- You should have received a copy of the GNU Affero General Public License
|
||||
-- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
-- AUTH-5 (PUT-1019) — let `kind='access_token'` rows be reverse-looked-up
|
||||
-- Let `kind='access_token'` rows be reverse-looked-up
|
||||
-- from the `token_uid` claim that lives only in `access_token_permissions`.
|
||||
-- Required so `POST /auth/revoke-access-token` with a raw token_uid input
|
||||
-- (no JWT) can find and soft-revoke the session row, matching the JWT
|
||||
|
||||
@@ -3408,10 +3408,10 @@ describe('AuthController.handleRevokeSession additional branches', () => {
|
||||
});
|
||||
|
||||
it('refuses to revoke the caller’s OWN current session row (400)', async () => {
|
||||
// PUT-1019 invariant: a self-revoke leaves the client in an
|
||||
// ambiguous identity state because the response can't write
|
||||
// fresh auth state. /logout is the only path that should end
|
||||
// the session you're currently authenticated under.
|
||||
// Invariant: a self-revoke leaves the client in an ambiguous
|
||||
// identity state because the response can't write fresh auth
|
||||
// state. /logout is the only path that should end the session
|
||||
// you're currently authenticated under.
|
||||
const { user, actor } = await makeUserAndActor();
|
||||
const sessionRes = await server.services.auth.createSessionToken(
|
||||
user,
|
||||
@@ -3464,7 +3464,7 @@ describe('AuthController.handleRevokeSession additional branches', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// ── handleMigrateToken (PUT-1021 SDK-1) ─────────────────────────────
|
||||
// ── handleMigrateToken ──────────────────────────────────────────────
|
||||
|
||||
describe('AuthController.handleMigrateToken', () => {
|
||||
const TEST_ORIGIN = 'https://migrate.test.local';
|
||||
@@ -3572,10 +3572,10 @@ describe('AuthController.handleMigrateToken', () => {
|
||||
});
|
||||
|
||||
it('returns 409 reauth_required for v1 web/session tokens', async () => {
|
||||
// Web tokens never migrate silently — they always go through the
|
||||
// interactive reauth flow (PUT-1023). The body code is what
|
||||
// puter.js / GUI key on; the 409 status is what tells SDK code
|
||||
// "this isn't a generic auth failure, route through reauth".
|
||||
// Web tokens never migrate silently — they always go through
|
||||
// the interactive reauth flow. The body code is what puter.js /
|
||||
// GUI key on; the 409 status is what tells SDK code "this
|
||||
// isn't a generic auth failure, route through reauth".
|
||||
const { user } = await makeUserAndActor();
|
||||
const v1 = mintV1Token({
|
||||
type: 'session',
|
||||
|
||||
@@ -44,7 +44,7 @@ interface StubAuth {
|
||||
seenTokens: string[];
|
||||
/** Legacy setter — accepts the old Actor|null|'throw' shape. */
|
||||
setNext: (next: Actor | null | 'throw') => void;
|
||||
/** AUTH-4: set the full AuthResult to be returned by `authenticate()`. */
|
||||
/** Set the full AuthResult to be returned by `authenticate()`. */
|
||||
setNextResult: (next: AuthResultLike | 'throw') => void;
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ const makeStubAuth = (defaultActor: Actor | null = null): StubAuth => {
|
||||
? { actor: defaultActor }
|
||||
: { invalid: true };
|
||||
const service = {
|
||||
// AUTH-4 entry point used by the probe.
|
||||
// Entry point used by the probe.
|
||||
authenticate: async (token: string) => {
|
||||
seenTokens.push(token);
|
||||
if (nextResult === 'throw') throw new Error('verify failed');
|
||||
@@ -543,9 +543,9 @@ describe('createAuthProbe — actor attachment + failure tracking', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// ── AUTH-4: reauth signal + KV counters ─────────────────────────────
|
||||
// ── Reauth signal + KV counters ─────────────────────────────────────
|
||||
|
||||
describe('createAuthProbe — AUTH-4 reauth signal', () => {
|
||||
describe('createAuthProbe — reauth signal', () => {
|
||||
/** Capture KV increments without a real store. */
|
||||
const makeKvStub = () => {
|
||||
const calls: Array<{
|
||||
|
||||
@@ -137,7 +137,7 @@ describe('requireAuthGate', () => {
|
||||
expectHttpError(got, 403, 'forbidden');
|
||||
});
|
||||
|
||||
// ── AUTH-4 reauth signal ────────────────────────────────────────
|
||||
// ── Reauth signal ───────────────────────────────────────────────
|
||||
|
||||
it('returns 401 reauth_required for a legacy v1 token', () => {
|
||||
const got = runGate(requireAuthGate(), {
|
||||
|
||||
@@ -167,9 +167,9 @@ describe('AuthService (integration)', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// ── AUTH-4: rich `authenticate()` result shape ───────────────────
|
||||
// ── Rich `authenticate()` result shape ──────────────────────────
|
||||
|
||||
describe('authenticate (AUTH-4 reauth signal)', () => {
|
||||
describe('authenticate (reauth signal)', () => {
|
||||
it('returns { actor } for a healthy v2 session token', async () => {
|
||||
const user = await makeUser();
|
||||
const { token } = await authService.createSessionToken(user, {});
|
||||
@@ -1025,13 +1025,13 @@ describe('AuthService (integration)', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// ── AUTH-5 (PUT-1019) revoke coverage ────────────────────────────
|
||||
// ── Revoke coverage ─────────────────────────────────────────────
|
||||
|
||||
describe('revokeAccessToken raw-uuid session-row coverage', () => {
|
||||
// The JWT-input branch has always flipped the session row's
|
||||
// revoked_at. AUTH-5 closes the raw-uuid gap: the new
|
||||
// `sessions.access_token_uid` column lets revoke find the row
|
||||
// for v2-minted tokens even when no JWT was presented.
|
||||
// revoked_at. The raw-uuid gap is closed by the
|
||||
// `sessions.access_token_uid` column, which lets revoke find
|
||||
// the row for v2-minted tokens even when no JWT was presented.
|
||||
|
||||
it('soft-revokes the v2 session row when revoked by raw token_uid', async () => {
|
||||
const user = await makeUser();
|
||||
@@ -1168,7 +1168,7 @@ describe('AuthService (integration)', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// ── SDK-1 (PUT-1021) migrate-token ────────────────────────────────
|
||||
// ── migrate-token ───────────────────────────────────────────────
|
||||
|
||||
describe('migrateLegacyToken', () => {
|
||||
// Hand-mint v1 tokens using the same compression dict the
|
||||
|
||||
@@ -514,10 +514,10 @@ export class AuthService extends PuterService {
|
||||
return this.#migrateAccessToken(decoded as AccessTokenPayload);
|
||||
}
|
||||
if (decoded.type === 'app-under-user') {
|
||||
// Per ROLLOUT-1, app-token migration is the kind that
|
||||
// ultimately retires — flag-gated independently from the
|
||||
// top-level `allow_v1_tokens` so access-token migration
|
||||
// can stay on indefinitely.
|
||||
// App-token migration is the kind that ultimately retires
|
||||
// — flag-gated independently from the top-level
|
||||
// `allow_v1_tokens` so access-token migration can stay on
|
||||
// indefinitely.
|
||||
const allowAppMigration =
|
||||
(this.config as { allow_v1_app_migration?: boolean })
|
||||
.allow_v1_app_migration !== false;
|
||||
@@ -1372,7 +1372,7 @@ export class AuthService extends PuterService {
|
||||
}
|
||||
}
|
||||
|
||||
// Permissions rows still DELETE — the AUTH-5 "no DELETE on revoke"
|
||||
// Permissions rows still DELETE — the "no DELETE on revoke"
|
||||
// rule scoped to the `sessions` table (where the audit trail of
|
||||
// when a session existed/was revoked is load-bearing for forensic
|
||||
// queries and the cascade graph). `access_token_permissions`
|
||||
|
||||
@@ -135,9 +135,9 @@ describe('SessionStore', () => {
|
||||
});
|
||||
|
||||
it('returns null when expires_at is in the past', async () => {
|
||||
// PUT-1014 moved expires_at enforcement into getByUuid so the
|
||||
// row is the single source of truth — no AUTH-4 re-mint pass
|
||||
// needed (we run long-lived JWTs in v2).
|
||||
// expires_at enforcement lives in getByUuid so the row is
|
||||
// the single source of truth — no re-mint pass needed (we
|
||||
// run long-lived JWTs in v2).
|
||||
const user = await makeUser();
|
||||
const past = Math.floor(Date.now() / 1000) - 60;
|
||||
const session = await target.create(user.id, { expires_at: past });
|
||||
@@ -272,7 +272,7 @@ describe('SessionStore', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// ── PUT-1014 composite-key lookups ──────────────────────────────
|
||||
// ── Composite-key lookups ──────────────────────────────────────
|
||||
|
||||
describe('getOrCreateApp', () => {
|
||||
it('creates a kind="app" row on first call with the right shape', async () => {
|
||||
|
||||
@@ -481,7 +481,7 @@ interface IConfigOptional {
|
||||
/**
|
||||
* Legacy HMAC secret for v1 JWTs. New tokens are always signed with
|
||||
* `jwt_secret_v2`; this value is verify-only and accepted as long as
|
||||
* `allow_v1_tokens` is true (flipped off in ROLLOUT-1 to retire v1).
|
||||
* `allow_v1_tokens` is true (flipped off to retire v1).
|
||||
*/
|
||||
jwt_secret: string;
|
||||
/** HMAC secret used to sign and verify v2 auth JWTs (`kid: 'v2'`). */
|
||||
@@ -493,10 +493,10 @@ interface IConfigOptional {
|
||||
allow_v1_tokens: boolean;
|
||||
/**
|
||||
* When false, `POST /auth/migrate-token` returns 410 Gone for v1
|
||||
* `app-under-user` tokens. Per ROLLOUT-1, app-token migration is
|
||||
* retired ahead of access-token migration — keeping these on
|
||||
* separate flags lets ops kill apps first and keep API-key
|
||||
* migration on indefinitely. Default true.
|
||||
* `app-under-user` tokens. App-token migration is retired ahead
|
||||
* of access-token migration — keeping these on separate flags
|
||||
* lets ops kill apps first and keep API-key migration on
|
||||
* indefinitely. Default true.
|
||||
*/
|
||||
allow_v1_app_migration: boolean;
|
||||
/**
|
||||
|
||||
@@ -125,11 +125,11 @@ async function UIDesktop (options) {
|
||||
console.error('GUI Socket Error:', error);
|
||||
});
|
||||
|
||||
// PUT-1023 (GUI-1) — pick up reauth_required signals delivered via the
|
||||
// socket.io handshake. SocketService emits `Error { data: { code:
|
||||
// Pick up reauth_required signals delivered via the socket.io
|
||||
// handshake. SocketService emits `Error { data: { code:
|
||||
// 'reauth_required', reason, auth_id } }` for legacy/revoked/expired
|
||||
// tokens. Without this branch the disconnect would look like a silent
|
||||
// network failure to the user.
|
||||
// tokens. Without this branch the disconnect would look like a
|
||||
// silent network failure to the user.
|
||||
window.socket.on('connect_error', (err) => {
|
||||
const signal = err?.data;
|
||||
if ( signal?.code === 'reauth_required' ) {
|
||||
|
||||
@@ -454,10 +454,10 @@ async function UIWindowLogin (options) {
|
||||
}
|
||||
|
||||
// Prepare data for the request
|
||||
// PUT-1023 (GUI-1) — when this login was triggered by a 401
|
||||
// reauth_required, forward `auth_id` (and the originating
|
||||
// reason) so the backend can re-attach the user/session to the
|
||||
// same stable identity (consumed by GUI-2).
|
||||
// When this login was triggered by a 401 reauth_required,
|
||||
// forward `auth_id` (and the originating reason) so the
|
||||
// backend can re-attach the user/session to the same
|
||||
// stable identity.
|
||||
const reauth_payload = options.auth_id
|
||||
? { auth_id: options.auth_id, reauth_reason: options.reauth_reason }
|
||||
: {};
|
||||
|
||||
@@ -55,10 +55,10 @@ if ( window.logged_in_users === null )
|
||||
window.logged_in_users = [];
|
||||
}
|
||||
|
||||
// this sessions's user — PUT-1023 (GUI-1) prefers the v2 storage key. The
|
||||
// legacy key is still consulted so that users with a stale v1 token get
|
||||
// picked up here (and immediately routed through the reauth modal by the
|
||||
// first 401 from the backend).
|
||||
// this sessions's user — prefer the v2 storage key. The legacy key is
|
||||
// still consulted so that users with a stale v1 token get picked up here
|
||||
// (and immediately routed through the reauth modal by the first 401
|
||||
// from the backend).
|
||||
window.auth_token =
|
||||
localStorage.getItem('auth_token_v2')
|
||||
|| localStorage.getItem('auth_token');
|
||||
|
||||
+22
-23
@@ -31,9 +31,9 @@ import UIWindowLogin from './UI/UIWindowLogin.js';
|
||||
import UIWindowProgress from './UI/UIWindowProgress.js';
|
||||
import UIWindowSaveAccount from './UI/UIWindowSaveAccount.js';
|
||||
|
||||
// PUT-1023 (GUI-1) — localStorage keys for the GUI session token. v2 is the
|
||||
// new key written after the v1→v2 cutover. Reads prefer v2; v1 is only kept
|
||||
// to drive the reauth-modal path when a stale legacy session is found.
|
||||
// localStorage keys for the GUI session token. v2 is the new key written
|
||||
// after the v1→v2 cutover. Reads prefer v2; v1 is only kept to drive the
|
||||
// reauth-modal path when a stale legacy session is found.
|
||||
window.AUTH_TOKEN_KEY_V1 = 'auth_token';
|
||||
window.AUTH_TOKEN_KEY_V2 = 'auth_token_v2';
|
||||
|
||||
@@ -51,16 +51,16 @@ window.is_auth = () => {
|
||||
};
|
||||
|
||||
/**
|
||||
* PUT-1023 (GUI-1) — central handler for `401 { code: 'reauth_required' }`.
|
||||
* Central handler for `401 { code: 'reauth_required' }`.
|
||||
*
|
||||
* The backend `authProbe` middleware (AUTH-4) emits this 401 shape whenever
|
||||
* a token is legacy/revoked/expired. The GUI must NOT silently logout: that
|
||||
* loses window/URL state and surprises the user. Instead we:
|
||||
* The backend `authProbe` middleware emits this 401 shape whenever a
|
||||
* token is legacy/revoked/expired. The GUI must NOT silently logout:
|
||||
* that loses window/URL state and surprises the user. Instead we:
|
||||
* 1. Snapshot enough state to land back where we were after sign-in.
|
||||
* 2. Clear both the v1 and v2 token keys.
|
||||
* 3. Show a soft modal explaining what happened.
|
||||
* 4. Re-open UIWindowLogin, forwarding `auth_id` so temp users (and
|
||||
* pending GUI-2 work) can re-attach to the same identity.
|
||||
* 4. Re-open UIWindowLogin, forwarding `auth_id` so temp users can
|
||||
* re-attach to the same identity.
|
||||
*
|
||||
* Idempotent: parallel 401s while the modal is already open are dropped.
|
||||
*/
|
||||
@@ -84,8 +84,7 @@ window.handleReauthRequired = async (signal = {}) => {
|
||||
} catch ( e ) { /* ignore */ }
|
||||
window.auth_token = null;
|
||||
|
||||
// Soft modal. UIAlert is the lightest-weight existing surface; the
|
||||
// copy is the one specified by GUI-1's spec.
|
||||
// Soft modal. UIAlert is the lightest-weight existing surface.
|
||||
try {
|
||||
await UIAlert({
|
||||
message: i18n('reauth_required_message') || 'Your session was updated for security — please sign in again.',
|
||||
@@ -102,7 +101,7 @@ window.handleReauthRequired = async (signal = {}) => {
|
||||
// fall through to UIWindowLogin directly.
|
||||
}
|
||||
|
||||
// Open the login window, forwarding auth_id (consumed in GUI-2).
|
||||
// Open the login window, forwarding auth_id.
|
||||
try {
|
||||
const login = await UIWindowLogin({
|
||||
reload_on_success: true,
|
||||
@@ -580,8 +579,8 @@ window.refresh_user_data = async (auth_token) => {
|
||||
|
||||
window.update_auth_data = async (auth_token, user, api_origin) => {
|
||||
window.auth_token = auth_token;
|
||||
// PUT-1023 (GUI-1) — write the v2 key going forward and clear any
|
||||
// lingering v1 key so a single localStorage source-of-truth is used.
|
||||
// Write the v2 key going forward and clear any lingering v1 key so
|
||||
// a single localStorage source-of-truth is used.
|
||||
localStorage.setItem(window.AUTH_TOKEN_KEY_V2, auth_token);
|
||||
localStorage.removeItem(window.AUTH_TOKEN_KEY_V1);
|
||||
|
||||
@@ -741,8 +740,8 @@ window.sendWindowWillCloseMsg = function (iframe_element) {
|
||||
window.logout = () => {
|
||||
// clear cache
|
||||
puter._cache.flushall();
|
||||
// PUT-1023 (GUI-1) — clear both old and new token keys. Cookie clear
|
||||
// is handled by the backend logout endpoint.
|
||||
// Clear both old and new token keys. Cookie clear is handled by
|
||||
// the backend logout endpoint.
|
||||
try {
|
||||
localStorage.removeItem(window.AUTH_TOKEN_KEY_V2);
|
||||
localStorage.removeItem(window.AUTH_TOKEN_KEY_V1);
|
||||
@@ -751,10 +750,10 @@ window.logout = () => {
|
||||
// document.dispatchEvent(new Event("logout", { bubbles: true}));
|
||||
};
|
||||
|
||||
// PUT-1023 (GUI-1) — global jQuery ajax error interceptor. Catches any
|
||||
// `401 { code: 'reauth_required' }` that wasn't already handled by a more
|
||||
// specific `statusCode[401]` callback on the call site, so we don't have
|
||||
// to audit every legacy `$.ajax` call when AUTH-4 rolls out.
|
||||
// Global jQuery ajax error interceptor. Catches any `401 { code:
|
||||
// 'reauth_required' }` that wasn't already handled by a more specific
|
||||
// `statusCode[401]` callback on the call site, so we don't have to
|
||||
// audit every legacy `$.ajax` call when the reauth signal rolls out.
|
||||
if ( typeof $ !== 'undefined' && $.fn ) {
|
||||
$(document).ajaxError(function (event, jqxhr, settings, thrownError) {
|
||||
if ( jqxhr?.status !== 401 ) return;
|
||||
@@ -769,9 +768,9 @@ if ( typeof $ !== 'undefined' && $.fn ) {
|
||||
});
|
||||
}
|
||||
|
||||
// PUT-1023 (GUI-1) — multi-tab propagation. If another tab signs in (sets
|
||||
// the v2 token) or signs out (clears it), reflect that here without forcing
|
||||
// a full page reload when we can avoid it.
|
||||
// Multi-tab propagation. If another tab signs in (sets the v2 token) or
|
||||
// signs out (clears it), reflect that here without forcing a full page
|
||||
// reload when we can avoid it.
|
||||
if ( typeof window !== 'undefined' && window.addEventListener ) {
|
||||
window.addEventListener('storage', (event) => {
|
||||
if ( event.key !== window.AUTH_TOKEN_KEY_V2 ) return;
|
||||
|
||||
@@ -392,7 +392,7 @@ window.initgui = async function (options) {
|
||||
if ( r.ok ) {
|
||||
const { token } = await r.json();
|
||||
window.auth_token = token;
|
||||
// PUT-1023 (GUI-1) — write the v2 key; drop legacy v1 key.
|
||||
// Write the v2 key; drop legacy v1 key.
|
||||
localStorage.setItem(window.AUTH_TOKEN_KEY_V2 || 'auth_token_v2', token);
|
||||
try { localStorage.removeItem(window.AUTH_TOKEN_KEY_V1 || 'auth_token'); } catch ( e ) { /* ignore */ }
|
||||
if ( typeof puter !== 'undefined' ) puter.setAuthToken(token, window.api_origin);
|
||||
|
||||
+15
-15
@@ -92,11 +92,11 @@ class Lock {
|
||||
// (using defaultGUIOrigin breaks locally-hosted apps)
|
||||
const PROD_ORIGIN = 'https://puter.com';
|
||||
|
||||
// localStorage keys for the auth token. v1 is the legacy key. v2 is the new
|
||||
// key written after the token rotation in the v1→v2 cutover (PUT-1024).
|
||||
// Reads prefer v2; v1 is only consulted to drive the silent-migration path
|
||||
// (SDK access_token / app kinds) or — for kind='web' — to fail over to the
|
||||
// interactive reauth flow.
|
||||
// localStorage keys for the auth token. v1 is the legacy key. v2 is
|
||||
// the new key written after the token rotation in the v1→v2 cutover.
|
||||
// Reads prefer v2; v1 is only consulted to drive the silent-migration
|
||||
// path (access_token / app kinds) or — for kind='web' — to fail over
|
||||
// to the interactive reauth flow.
|
||||
const STORAGE_KEY_V1 = 'puter.auth.token';
|
||||
const STORAGE_KEY_V2 = 'puter.auth.token.v2';
|
||||
|
||||
@@ -152,7 +152,7 @@ const puterInit = (function () {
|
||||
// Event handling properties
|
||||
eventHandlers = {};
|
||||
|
||||
// Reauth coordinator state (PUT-1022 PJS-1). When the backend signals
|
||||
// Reauth coordinator state. When the backend signals
|
||||
// `401 { code: 'reauth_required' }`, in-flight requests await this
|
||||
// promise; the first caller drives the interactive flow, everyone
|
||||
// else replays after it resolves.
|
||||
@@ -476,11 +476,11 @@ const puterInit = (function () {
|
||||
// URL-param tokens may still be v1 (host apps that
|
||||
// haven't rebuilt yet). Set immediately so submodules
|
||||
// can run, then attempt silent migration in the
|
||||
// background — PJS-2.
|
||||
// background.
|
||||
this.setAuthToken(bootstrapAuthToken);
|
||||
needsSilentMigration = true;
|
||||
} else {
|
||||
// Prefer the v2 storage key (PJS-2). Fall back to v1
|
||||
// Prefer the v2 storage key. Fall back to v1
|
||||
// and queue a silent migrate-token call.
|
||||
const v2 = this.normalizeAuthTokenCandidate(
|
||||
localStorage.getItem(STORAGE_KEY_V2),
|
||||
@@ -527,7 +527,7 @@ const puterInit = (function () {
|
||||
// initialize submodules
|
||||
this.initSubmodules();
|
||||
try {
|
||||
// Prefer the v2 storage key (PJS-2). Fall back to v1 and
|
||||
// Prefer the v2 storage key. Fall back to v1 and
|
||||
// run a silent migration in the background.
|
||||
const v2 = this.normalizeAuthTokenCandidate(
|
||||
localStorage.getItem(STORAGE_KEY_V2),
|
||||
@@ -774,8 +774,8 @@ const puterInit = (function () {
|
||||
};
|
||||
|
||||
/**
|
||||
* PUT-1022 (PJS-1) — reauth coordinator. Called by the network layer
|
||||
* (lib/utils.js) when the backend returns
|
||||
* Reauth coordinator. Called by the network layer (lib/utils.js)
|
||||
* when the backend returns
|
||||
* `401 { code: 'reauth_required', reason, auth_id }`.
|
||||
*
|
||||
* Behavior is environment-specific:
|
||||
@@ -784,7 +784,7 @@ const puterInit = (function () {
|
||||
* that resolves when the user signs in (so callers can replay)
|
||||
* or rejects if reauth fails / is canceled.
|
||||
* - `gui`: no-op — the GUI environment renders its own modal
|
||||
* (PUT-1023 GUI-1) and host code is responsible for the flow.
|
||||
* and host code is responsible for the flow.
|
||||
* - workers / nodejs: there's no UI surface to drive, so reject
|
||||
* with a structured error and let worker code react.
|
||||
*
|
||||
@@ -902,7 +902,7 @@ const puterInit = (function () {
|
||||
|
||||
/**
|
||||
* Register a listener for SDK events. Used by host apps to react
|
||||
* to `puter.auth.reauth_required` (PUT-1022 PJS-1).
|
||||
* to `puter.auth.reauth_required`.
|
||||
*/
|
||||
on = function (eventName, handler) {
|
||||
if ( ! this.eventHandlers[eventName] ) this.eventHandlers[eventName] = [];
|
||||
@@ -918,8 +918,8 @@ const puterInit = (function () {
|
||||
};
|
||||
|
||||
/**
|
||||
* PUT-1024 (PJS-2) — best-effort silent v1→v2 token migration via
|
||||
* the backend `/auth/migrate-token` endpoint (SDK-1). Used at boot
|
||||
* Best-effort silent v1→v2 token migration via the backend
|
||||
* `/auth/migrate-token` endpoint. Used at boot
|
||||
* when only a legacy `puter.auth.token` is present; on success the
|
||||
* v2 token is set via setAuthToken (which clears the v1 key).
|
||||
*
|
||||
|
||||
@@ -100,8 +100,8 @@ function initXhr (endpoint, APIOrigin, authToken, method = 'post', contentType =
|
||||
xhr.setRequestHeader('Content-Type', contentType);
|
||||
xhr.responseType = responseType ?? '';
|
||||
|
||||
// Capture enough request shape to replay this XHR after a reauth_required
|
||||
// (PUT-1022 PJS-1). The body is captured below by intercepting send().
|
||||
// Capture enough request shape to replay this XHR after a
|
||||
// reauth_required. The body is captured below by intercepting send().
|
||||
xhr._puterReq = {
|
||||
endpoint,
|
||||
APIOrigin,
|
||||
@@ -176,12 +176,12 @@ async function handle_resp (success_cb, error_cb, resolve_func, reject_func, res
|
||||
const resp = await parseResponse(response);
|
||||
// error - unauthorized
|
||||
if ( response.status === 401 ) {
|
||||
// PUT-1022 (PJS-1) — v2 reauth signal. The backend `authProbe`
|
||||
// middleware returns `401 { code: 'reauth_required', reason, auth_id }`
|
||||
// for legacy v1 tokens, revoked sessions, and expired sessions
|
||||
// beyond the silent re-mint window. Drive the env-specific reauth
|
||||
// flow on the Puter class, then replay the original request with
|
||||
// the new token.
|
||||
// v2 reauth signal. The backend `authProbe` middleware returns
|
||||
// `401 { code: 'reauth_required', reason, auth_id }` for legacy
|
||||
// v1 tokens, revoked sessions, and expired sessions beyond the
|
||||
// silent re-mint window. Drive the env-specific reauth flow on
|
||||
// the Puter class, then replay the original request with the
|
||||
// new token.
|
||||
if ( resp?.code === 'reauth_required' ) {
|
||||
try {
|
||||
await puter.triggerReauth({
|
||||
@@ -595,8 +595,8 @@ async function driverCall_ (
|
||||
|
||||
// HTTP Error - unauthorized
|
||||
if ( response.target.status === 401 || resp?.code === 'token_auth_failed' ) {
|
||||
// PUT-1022 (PJS-1) — v2 reauth signal. Replay the driver
|
||||
// call by re-entering driverCall_ rather than using the
|
||||
// v2 reauth signal. Replay the driver call by re-entering
|
||||
// driverCall_ rather than using the
|
||||
// generic replayXhrAfterReauth helper: the generic helper
|
||||
// wires the retried XHR through setupXhrEventHandlers, which
|
||||
// resolves with the parsed response and skips driverCall_'s
|
||||
|
||||
Reference in New Issue
Block a user