feat: cross-origin-channel package
Issue: Adapt OAuth2 client to work when Web IDE is hos... (#420)
What does this MR do and why?
This MR introduces the cross-origin-channel
package. This package handles bi-directional communication between the Web IDE origin and the GitLab rails application origin. The communication between both origins happens securely via the postMessage
API and the MessageChannel
API. This package contains two classes that use the aforementioned APIs to send messages between the two origins:
DefaultWindowChannel
This class accepts two Window
objects and handles communication between them. Each Window
object represents an origin.
-
localWindow
represents the current execution context. -
remoteWindow
represents the external execution context that we are communicating with. -
remoteWindowOrigin
The URL that represents the origin expected for theremoteWindow
.
For example, let's say that we have a web page with an iframe embedded. If we create a DefaultWindowChannel
object in the web page's execution context, localWindow
would be the page's global window
object and remoteWindow
would be the iframe's contentWindow
property. Conversely, if I create the object in the iframe's execution context, the localWindow
will be the iframe's window
object and remoteWindow
will be the window.parent
property. In real use cases, both execution contexts create their own instances of DefaultWindowChannel
to send and receive messages.
You can invoke the postMessage
method to send messages from the localWindow
to the remoteWindow
. Use waitForMessage
to obtain a promise that is resolved when localWindow
receives a message from remoteWindow
.
DefaultPortChannel
You create a secondary channel using the DefaultWindowChannel
's createPortChannel(portName)
method which returns an instance of DefaultPortChannel
. DefaultPortChannel
wraps a MessagePort
object. Since a MessageChannel
exposes two ports, the first port is available in the window that created the port channel. The 2nd port can be requested by a remote window using the requestPortChannel(portName)
.
Why did I create this package?
Rather than using the postMessage
and MessageChannel
native APIs directly, I created these abstractions to encapsulate a few concerns:
- Security: The
DefaultWindowChannel
class ensures we only send and receive messages from the expected origin. - Type Safety: The type system encodes the messages allowed through the different channels.
- A friendlier API for complex message-based business logic: It's easier to wait for a specific message and to transfer secondary channels between origins.
How to review this MR?
I highly recommend looking at the MR Authentication Provider based on cross-origin-c... (!397) to see how these classes are used in practice:
- Creating
DefaultWindowChannel
objects in the web page !397 (diffs) and in the iframe that contains VSCode !397 (diffs). - Using the
DefaultPortChannel
to share access tokens: !397 (diffs) and !397 (diffs).
After that, read the implementation of the DefaultWindowChannel
and DefaultPortChannel
classes.
Follow-up Merge Request
Authentication Provider based on cross-origin-c... (!397) uses the cross-origin-channel
package to implement an authentication provider that obtains OAuth Access tokens via a PortChannel
. You can read that Merge Request to understand how we use the cross-origin-channel
package in a real use case. I broke this effort into two Merge Requests due to the overall size of the changes.
MR acceptance checklist
Please evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.
Screenshots or screen recordings
This Merge Request doesn't introduce user-facing changes.
How to set up and validate locally
The Web IDE behavior hasn't been changed in this MR.