From 4aab06611e09d0bd2357bb081e07d2f1549aafd0 Mon Sep 17 00:00:00 2001 From: Daniel Salazar Date: Wed, 4 Mar 2026 17:54:58 -0800 Subject: [PATCH] feat: redirect away cookie (#2609) --- .../routers/hosting/puterSiteMiddleware.js | 36 +++++++++++++++++++ .../hosting/puterSiteMiddleware.test.js | 4 ++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/backend/src/routers/hosting/puterSiteMiddleware.js b/src/backend/src/routers/hosting/puterSiteMiddleware.js index 994163efe..d10960095 100644 --- a/src/backend/src/routers/hosting/puterSiteMiddleware.js +++ b/src/backend/src/routers/hosting/puterSiteMiddleware.js @@ -318,6 +318,28 @@ function getPrivateAccessRejectionReason (error) { return error?.code || error?.message || 'unknown'; } +function stripBootstrapAuthTokenFromOriginalUrl (originalUrl) { + if ( typeof originalUrl !== 'string' || !originalUrl ) return null; + + try { + const placeholderOrigin = 'https://placeholder.puter.local'; + const parsedUrl = new URL(originalUrl, placeholderOrigin); + const hadToken = + parsedUrl.searchParams.has('puter.auth.token') + || parsedUrl.searchParams.has('auth_token'); + if ( ! hadToken ) return null; + + parsedUrl.searchParams.delete('puter.auth.token'); + parsedUrl.searchParams.delete('auth_token'); + + const search = parsedUrl.searchParams.toString(); + const cleanPath = parsedUrl.pathname || '/'; + return search ? `${cleanPath}?${search}` : cleanPath; + } catch { + return null; + } +} + function getTokenFromAuthorizationHeader (req) { const authorizationHeader = req.headers?.authorization; if ( typeof authorizationHeader !== 'string' ) return null; @@ -905,6 +927,20 @@ async function evaluatePrivateAppAccess ({ req, res, services, app, requestPath requestHostname: req.hostname, }), ); + + const sanitizedUrl = stripBootstrapAuthTokenFromOriginalUrl(req.originalUrl); + if ( sanitizedUrl ) { + logPrivateAccessEvent('private_access.allowed_cookie_redirect', { + appUid: app.uid, + userUid: identity.userUid ?? null, + requestHost: req.hostname, + requestPath, + source: identity.source, + redirectUrl: sanitizedUrl, + }); + res.redirect(sanitizedUrl); + return false; + } } logPrivateAccessEvent('private_access.allowed', { diff --git a/src/backend/src/routers/hosting/puterSiteMiddleware.test.js b/src/backend/src/routers/hosting/puterSiteMiddleware.test.js index d391d8b4d..0df8ce981 100644 --- a/src/backend/src/routers/hosting/puterSiteMiddleware.test.js +++ b/src/backend/src/routers/hosting/puterSiteMiddleware.test.js @@ -1056,11 +1056,12 @@ describe('PuterSiteMiddleware', () => { is_custom_domain: false, baseUrl: '', path: '/asset.js', - originalUrl: '/asset.js?puter.auth.token=bootstrap-token', + originalUrl: '/asset.js?puter.auth.token=bootstrap-token&foo=bar', cookies: {}, headers: {}, query: { 'puter.auth.token': 'bootstrap-token', + foo: 'bar', }, ctx: mockContextInstance, }; @@ -1097,6 +1098,7 @@ describe('PuterSiteMiddleware', () => { 'private-token', { sameSite: 'none' }, ); + expect(mockRes.redirect).toHaveBeenCalledWith('/asset.js?foo=bar'); expect(mockNext).not.toHaveBeenCalled(); });