-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[PM-5976] Safari Browser SSO Initialization Race Condition Attempted …
…Fix 3 (#7800) * [PM-5976] Safari Browser SSO Initialization Race Condition Attempted Fix 3 * [PM-5976] Safari Browser SSO Initialization Race Condition Attempted Fix 3 * [PM-5976] Removing usage of pinging system and keeping reworked top-level registration of window message listener events * [PM-5976] Pulling the implementation of the static content script delcaration for the content-message-handler file to the top of the list of content_scripts * [PM-5976] Pulling the implementation of the static content script delcaration for the content-message-handler file to the top of the list of content_scripts * [PM-5976] Removing the useCapture value within the window message event listener
- Loading branch information
1 parent
c8009ba
commit 57ccfd8
Showing
11 changed files
with
168 additions
and
159 deletions.
There are no files selected for viewing
28 changes: 23 additions & 5 deletions
28
apps/browser/src/autofill/content/abstractions/content-message-handler.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,24 @@ | ||
interface ContentMessageHandler { | ||
init(): void; | ||
destroy(): void; | ||
} | ||
type ContentMessageWindowData = { | ||
command: string; | ||
lastpass?: boolean; | ||
code?: string; | ||
state?: string; | ||
data?: string; | ||
remember?: boolean; | ||
}; | ||
type ContentMessageWindowEventParams = { | ||
data: ContentMessageWindowData; | ||
referrer: string; | ||
}; | ||
|
||
export { ContentMessageHandler }; | ||
type ContentMessageWindowEventHandlers = { | ||
[key: string]: ({ data, referrer }: ContentMessageWindowEventParams) => void; | ||
authResult: ({ data, referrer }: ContentMessageWindowEventParams) => void; | ||
webAuthnResult: ({ data, referrer }: ContentMessageWindowEventParams) => void; | ||
}; | ||
|
||
export { | ||
ContentMessageWindowData, | ||
ContentMessageWindowEventParams, | ||
ContentMessageWindowEventHandlers, | ||
}; |
12 changes: 0 additions & 12 deletions
12
apps/browser/src/autofill/content/bootstrap-content-message-handler.ts
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
166 changes: 99 additions & 67 deletions
166
apps/browser/src/autofill/content/content-message-handler.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,78 +1,110 @@ | ||
import { ContentMessageHandler as ContentMessageHandlerInterface } from "./abstractions/content-message-handler"; | ||
import { | ||
ContentMessageWindowData, | ||
ContentMessageWindowEventHandlers, | ||
} from "./abstractions/content-message-handler"; | ||
|
||
class ContentMessageHandler implements ContentMessageHandlerInterface { | ||
private forwardCommands = [ | ||
"bgUnlockPopoutOpened", | ||
"addToLockedVaultPendingNotifications", | ||
"unlockCompleted", | ||
"addedCipher", | ||
]; | ||
/** | ||
* IMPORTANT: Safari seems to have a bug where it doesn't properly handle | ||
* window message events from content scripts when the listener these events | ||
* is registered within a class. This is why these listeners are registered | ||
* at the top level of this file. | ||
*/ | ||
window.addEventListener("message", handleWindowMessageEvent, false); | ||
chrome.runtime.onMessage.addListener(handleExtensionMessage); | ||
setupExtensionDisconnectAction(() => { | ||
window.removeEventListener("message", handleWindowMessageEvent); | ||
chrome.runtime.onMessage.removeListener(handleExtensionMessage); | ||
}); | ||
|
||
/** | ||
* Initialize the content message handler. Sets up | ||
* a window message listener and a chrome runtime | ||
* message listener. | ||
*/ | ||
init() { | ||
window.addEventListener("message", this.handleWindowMessage, false); | ||
chrome.runtime.onMessage.addListener(this.handleExtensionMessage); | ||
} | ||
|
||
/** | ||
* Handle a message from the window. This implementation | ||
* specifically handles the authResult and webAuthnResult | ||
* commands. This facilitates single sign-on. | ||
* | ||
* @param event - The message event. | ||
*/ | ||
private handleWindowMessage = (event: MessageEvent) => { | ||
const { source, data } = event; | ||
/** | ||
* Handlers for window messages from the content script. | ||
*/ | ||
const windowMessageHandlers: ContentMessageWindowEventHandlers = { | ||
authResult: ({ data, referrer }: { data: any; referrer: string }) => | ||
handleAuthResultMessage(data, referrer), | ||
webAuthnResult: ({ data, referrer }: { data: any; referrer: string }) => | ||
handleWebAuthnResultMessage(data, referrer), | ||
}; | ||
|
||
if (source !== window || !data?.command) { | ||
return; | ||
} | ||
/** | ||
* Handles the auth result message from the window. | ||
* | ||
* @param data - Data from the window message | ||
* @param referrer - The referrer of the window | ||
*/ | ||
async function handleAuthResultMessage(data: ContentMessageWindowData, referrer: string) { | ||
const { command, lastpass, code, state } = data; | ||
await chrome.runtime.sendMessage({ command, code, state, lastpass, referrer }); | ||
} | ||
|
||
const { command } = data; | ||
const referrer = source.location.hostname; | ||
/** | ||
* Handles the webauthn result message from the window. | ||
* | ||
* @param data - Data from the window message | ||
* @param referrer - The referrer of the window | ||
*/ | ||
async function handleWebAuthnResultMessage(data: ContentMessageWindowData, referrer: string) { | ||
const { command, remember } = data; | ||
await chrome.runtime.sendMessage({ command, data: data.data, remember, referrer }); | ||
} | ||
|
||
if (command === "checkIfReadyForAuthResult") { | ||
window.postMessage({ command: "readyToReceiveAuthResult" }, "*"); | ||
} | ||
/** | ||
* Handles the window message event. | ||
* | ||
* @param event - The window message event | ||
*/ | ||
function handleWindowMessageEvent(event: MessageEvent) { | ||
const { source, data } = event; | ||
if (source !== window || !data?.command) { | ||
return; | ||
} | ||
|
||
if (command === "authResult") { | ||
const { lastpass, code, state } = data; | ||
chrome.runtime.sendMessage({ command, code, state, lastpass, referrer }); | ||
} | ||
const referrer = source.location.hostname; | ||
const handler = windowMessageHandlers[data.command]; | ||
if (handler) { | ||
handler({ data, referrer }); | ||
} | ||
} | ||
|
||
if (command === "webAuthnResult") { | ||
const { remember } = data; | ||
chrome.runtime.sendMessage({ command, data: data.data, remember, referrer }); | ||
} | ||
}; | ||
/** | ||
* Commands to forward from this script to the extension background. | ||
*/ | ||
const forwardCommands = new Set([ | ||
"bgUnlockPopoutOpened", | ||
"addToLockedVaultPendingNotifications", | ||
"unlockCompleted", | ||
"addedCipher", | ||
]); | ||
|
||
/** | ||
* Handle a message from the extension. This | ||
* implementation forwards the message to the | ||
* extension background so that it can be received | ||
* in other contexts of the background script. | ||
* | ||
* @param message - The message from the extension. | ||
*/ | ||
private handleExtensionMessage = (message: any) => { | ||
if (this.forwardCommands.includes(message.command)) { | ||
chrome.runtime.sendMessage(message); | ||
} | ||
}; | ||
/** | ||
* Handles messages from the extension. Currently, this is | ||
* used to forward messages from the background context to | ||
* other scripts within the extension. | ||
* | ||
* @param message - The message from the extension | ||
*/ | ||
async function handleExtensionMessage(message: any) { | ||
if (forwardCommands.has(message.command)) { | ||
await chrome.runtime.sendMessage(message); | ||
} | ||
} | ||
|
||
/** | ||
* Destroy the content message handler. Removes | ||
* the window message listener and the chrome | ||
* runtime message listener. | ||
*/ | ||
destroy = () => { | ||
window.removeEventListener("message", this.handleWindowMessage); | ||
chrome.runtime.onMessage.removeListener(this.handleExtensionMessage); | ||
/** | ||
* Duplicate implementation of the same named method within `apps/browser/src/autofill/utils/index.ts`. | ||
* This is done due to some strange observed compilation behavior present when importing the method from | ||
* the utils file. | ||
* | ||
* TODO: Investigate why webpack tree shaking is not removing other methods when importing from the utils file. | ||
* Possible cause can be seen below: | ||
* @see https://stackoverflow.com/questions/71679366/webpack5-does-not-seem-to-tree-shake-unused-exports | ||
* | ||
* @param callback - Callback function to run when the extension disconnects | ||
*/ | ||
function setupExtensionDisconnectAction(callback: (port: chrome.runtime.Port) => void) { | ||
const port = chrome.runtime.connect({ name: "autofill-injected-script-port" }); | ||
const onDisconnectCallback = (disconnectedPort: chrome.runtime.Port) => { | ||
callback(disconnectedPort); | ||
port.onDisconnect.removeListener(onDisconnectCallback); | ||
}; | ||
port.onDisconnect.addListener(onDisconnectCallback); | ||
} | ||
|
||
export default ContentMessageHandler; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,7 @@ | ||
import { AutofillInit } from "./content/abstractions/autofill-init"; | ||
import ContentMessageHandler from "./content/content-message-handler"; | ||
|
||
declare global { | ||
interface Window { | ||
bitwardenAutofillInit?: AutofillInit; | ||
bitwardenContentMessageHandler?: ContentMessageHandler; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.