Skip to content
This repository has been archived by the owner on Jun 12, 2024. It is now read-only.

Website API

koto edited this page Dec 16, 2014 · 18 revisions

End-To-End extension provides message encryption, decryption and signing services for third-party web applications through the Website API. By using the API web applications (e.g. webmail systems, instant messaging applications) can interact with End-To-End, giving them a secure, asynchronous way to encrypt/sign and verify arbitrary content. This page serves as a documentation for the API in its current version.

High-level description

End-to-End extension allows the user to encrypt/decrypt/compose and verify messages by clicking on the extension page action button. In order to communicate with the web application in the current browser tab, the extension tries to establish a permanent HTML5 Channel Messaging connection with the Javascript code of the web application. Then API requests and responses are sent between the extension and the web application through the established channel. Responses to the requests are sent asynchronously.

If the channel cannot be established, the extension fallbacks to using standard DOM methods to extract currently selected/active objects and inject information back into the document.

API connection timeline

  1. User clicks the extension page action button
  2. If content script has already been injected, go to 3. Otherwise, content script is injected into the current browser tab document.
  3. If API connection is already established or if DOM fallback is enabled, go to 8. Otherwise, content script detects API support in the web application. If support is not detected, enable DOM fallback and go to 8.
  4. For chosen origins (e.g. Gmail) appropriate stub script is injected, enabling API support.
  5. Content script sends a bootstrap message to the web application, passing a message port.
  6. Web application responds to a bootstrap message. If the response timeouts or indicates no API availability, enable DOM fallback and go to 8.
  7. Content script and web application attach event listeners to a message port message event.
  8. If DOM fallback is enabled, call an appropriate DOM method in a content script. Otherwise, send the API request through the message port to the web application and forward the response back.

Detecting API support

To minimise message noise, End-To-End extension only starts the API bootstrap procedure if the current browser tab document is enabled for the API support. As of now, the whitelist of origins with the API support is hardcoded in the extension. Please contact us to discuss adding Website API support for your web application.

Starting the connection

In order to extract or inject messages into the current browser tab, the extension injects a content script into the current tab document, when the user first clicks on the page action button (for a given document). The content script is responsible for detecting if the Website API endpoint is provided by the web application in the current document and tries to establish a permanent connection with the API endpoint by sending a bootstrap message.

Bootstrap message

Bootstrap message is sent using HTML5 Cross-document messaging from the content script in the current window. The event that is triggered by sending the message will have the following properties:

event.data = {
  api: 'e2e-init'
};
event.origin = window.location.origin; // The message is sent from a content script from the same origin
event.ports = [messagePort]; // The port through which the messages should be sent.

In order to establish the connection, the web application SHOULD send a following message through the provided messagePort:

{
  api: 'e2e-init',
  version: 1
  available: true|false
}

Value true of the available field signifies that the web application is ready to receive further API requests over the messagePort. If the value of available field is false, the extension will end the connection and fallback to the DOM API.

Example

Example code that correctly responds to a bootstrap message can be found below:

/**
 * Handles the bootstrap message coming from the extension.
 * @param {MessageEvent} e bootstrap message sent from the extension.
 */
var bootstrapMessageHandler = function(e) {
  if (e.origin == window.location.origin &&
      e.data.api == 'e2e-init' &&
      e.ports &&
      e.ports.length == 1) {
    e.ports[0].addEventListener('message', messageHandler, false);
    e.ports[0].start();
    e.ports[0].postMessage({
      api: 'e2e-init',
      version: 1,
      available: true
    });
    window.removeEventListener('message', bootstrapMessageHandler);
  }
};

window.addEventListener('message', bootstrapMessageHandler, false);

Request and response format

Further messages (requests and responses) are exchanged by peers over the port. Requests have the following format:

{
  id: string, // Unique ID
  call: string, // API function name to call
  args: Object|null // Call parameters
}

If the request is successful, a peer SHOULD respond by sending the following result object:

{
  requestId: string // ID of the request
  result: *  // Result
}

To notify the peer of an error condition, the following result object SHOULD be sent instead:

{
  requestId: string // ID of the request
  error: string  // Error message
  result: null
}

Example

Exemplary function to handle requests coming from the extension is provided below:

/**
 * Handles incoming E2E requests.
 * @param  {MessageEvent} event Event to handle.
 */
var messageHandler = function(event) {
  var request = event.data;
  var port = event.target;
  var call = event.data.call;
  var args = event.data.args;
  var requestId = event.data.id;
  // ...
  if (error) {
    port.postMessage({
      requestId: requestId,
      result: null,
      error: 'An error occurred.'
    }); 
  } else {
    port.postMessage({
      requestId: requestId,
      result: theResult
    }); 
  }
};

This function should be set up as an event listener for the message event on the MessagePort object during bootstrapping.

API requests

After the API connection has been bootstrapped, API requests can originate from either of the peer types (the extension or the web application). Different API calls are available for each of the peer types.

Requests sent from the extension

start

Notifies that extension is ready to accept requests from the web application.

  • Request parameters: none
  • Response format: response is ignored.

getCurrentMessage

Called to retrieve the current message element to create a reading glass IFRAME for and/or fetch the message contents to decrypt.

  • Request parameters: none

  • Response format:

      {
        selector: string // document.querySelector-compatible selector of the element with the active message.
        body: string // Text contents of the active message, with HTML formatting removed.
      }
    

hasActiveDraft

Called to check if the web application has a currently open draft message to fetch it contents to encrypt or decrypt.

  • Request parameters: none

  • Response format:

      boolean // does the web application have the active draft message
    

###getActiveDraft Called to fetch the currently opened message draft, to start an encrypt/decrypt/sign UI flow.

  • Request parameters: none

  • Response format:

      {
        to: Array<{address: string, name: string}> // Intended recipients of the currently active message draft. E-mails in the address fields.
        cc: Array<{address: string, name: string}> // Recipients to CC
        body: string // Text contents of the active draft, with HTML formatting removed.
        subject: string|undefined // Message subject. Optional
      }
    

setActiveDraft

Called to set the new (usually encrypted or signed) message body and recipients in the active message draft.

  • Request parameters:

      {
        to: Array<{address: string, name: string}> // Intended recipients of the currently active message draft. E-mails in the address fields.
        cc: Array<{address: string, name: string}> // Recipients to CC
        body: string // Text contents of the draft, with HTML formatting removed.
        subject: string|undefined // Message subject. Optional.
      }
    
  • Response format:

      boolean // Was the draft updated successfully
    

Requests sent from the web application

As of now, no requests from web applications are defined. Available requests will be added in the future, provided they do not introduce additional threats described in our Threat model.

Timeouts

In order to provide better user experience, a timeout for responding to events is enforced. Currently the web application needs to respond to the bootstrap request in 1000 ms. Timeout for subsequent API requests is 100 ms.