Skip to content

feat: cross-origin-channel package

Enrique Alcántara requested to merge ealcantara/420-cross-origin-channel into main

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.

  1. localWindow represents the current execution context.
  2. remoteWindow represents the external execution context that we are communicating with.
  3. remoteWindowOrigin The URL that represents the origin expected for the remoteWindow.

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.

image.png

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:

  1. Security: The DefaultWindowChannel class ensures we only send and receive messages from the expected origin.
  2. Type Safety: The type system encodes the messages allowed through the different channels.
  3. 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:

  1. Creating DefaultWindowChannel objects in the web page !397 (diffs) and in the iframe that contains VSCode !397 (diffs).
  2. 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.

Edited by Enrique Alcántara

Merge request reports

Loading