diff --git a/src/puter-js/src/modules/Auth.js b/src/puter-js/src/modules/Auth.js index 26cad9d71..2ccd451e5 100644 --- a/src/puter-js/src/modules/Auth.js +++ b/src/puter-js/src/modules/Auth.js @@ -56,6 +56,9 @@ class Auth { let settled = false; // Interval id for polling whether the user closed the popup. let checkClosed = null; + // The auth popup window we opened. Pinned as the expected + // `event.source` when validating the token message. + let popupWindow = null; const cleanup = () => { if ( checkClosed ) { @@ -66,6 +69,20 @@ class Auth { }; function messageHandler (e) { + // Only accept the token from the Puter GUI origin AND from the + // popup window we opened. Origin alone is insufficient (any + // frame on the GUI domain could post), so also pin + // event.source. Mirrors the validated handler in index.js. + // msg_id binds the message to this attempt. + if ( e.origin !== puter.defaultGUIOrigin ) { + return; + } + if ( popupWindow && e.source !== popupWindow ) { + return; + } + if ( e.data?.msg !== 'puter.token' ) { + return; + } if ( e.data?.msg_id != msg_id ) { return; } @@ -101,6 +118,8 @@ class Auth { reject({ error: 'popup_blocked', msg: 'The sign-in popup was blocked by the browser.' }); return; } + // Record the popup so messageHandler can pin event.source. + popupWindow = popup; checkClosed = setInterval(() => { if ( ! popup.closed ) { return; diff --git a/src/puter-js/src/modules/PuterDialog.js b/src/puter-js/src/modules/PuterDialog.js index 9482719c6..1236366cd 100644 --- a/src/puter-js/src/modules/PuterDialog.js +++ b/src/puter-js/src/modules/PuterDialog.js @@ -440,6 +440,16 @@ class PuterDialog extends (globalThis.HTMLElement || Object) { // It will fall b // Event listener for the 'message' event this.messageListener = async (event) => { + // Only accept the token from the Puter GUI origin AND from the + // popup we opened. Origin alone is insufficient (any frame on the + // GUI domain could post), so also pin event.source. Mirrors the + // validated handler in index.js. + if ( event.origin !== puter.defaultGUIOrigin ) { + return; + } + if ( this.authPopup && event.source !== this.authPopup ) { + return; + } if ( event.data.msg === 'puter.token' ) { this.close(); // Set the authToken property @@ -519,6 +529,8 @@ class PuterDialog extends (globalThis.HTMLElement || Object) { // It will fall b // safe from being popup-blocked because it happens inside a click. this.shadowRoot.querySelector('#launch-auth-popup')?.addEventListener('click', () => { const popup = openAuthPopup(this.#popupURL()); + // Pinned as the expected event.source in messageListener. + this.authPopup = popup; // Launcher mode: hand the popup back to the caller and close the // consent dialog — its only job was to provide the user gesture. @@ -544,6 +556,8 @@ class PuterDialog extends (globalThis.HTMLElement || Object) { // It will fall b open () { if ( hasUserActivation() ) { const popup = openAuthPopup(this.#popupURL()); + // Pinned as the expected event.source in messageListener. + this.authPopup = popup; if ( this.options.popupURL && typeof this.options.onLaunch === 'function' ) { this.options.onLaunch(popup); }