Skip to main content
Version: Next

GetAgent

The getAgent() function is the recommended way for web applications to connect to an FDC3 Desktop Agent. The function is exported by the @finos/fdc3 NPM module and returns a Desktop Agent implementation:

import { getAgent, DesktopAgent, AgentError } from "@finos/fdc3";

try {
const desktopAgent: DesktopAgent = await getAgent();
//do FDC3 things here
} catch (e: AgentError) {
// Failed to connect
}

The getAgent() function allows web applications to retrieve an FDC3 Desktop Agent API interface to work with, whether they are running in an environment that supports a Desktop Agent Preload (a container-injected API implementation) or a Desktop Agent Proxy (a Browser-based Desktop Agent running in another window or frame). The behavior of getAgent() is defined by the FDC3 Web Connection Protocol (WCP) and communication with a Desktop Agent Proxy in a web-browser is defined by the Desktop Agent Communication Protocol (DACP). Hence, it allows applications to be written that will work in either scenario without modification or the inclusion of vendor-specific libraries.

To handle situations where no Desktop Agent is found, a failover function may be supplied by an app allowing it to start or otherwise connect to a Desktop Agent (e.g. by loading a proprietary adaptor that returns a DesktopAgent implementation or by creating a window or iframe of its own that will provide a Desktop Agent Proxy).

The definition of the getAgent() function is as follows:

type GetAgentType = ( 
params?: GetAgentParams,
) => Promise<DesktopAgent>;

A small number of arguments are accepted that can affect the behavior of getAgent and to provide a fallback in case a Desktop Agent is not found, allowing the application to start its own agent or use another mechanism (such as a proprietary adaptor) to connect to one.

/** 
* @typedef {Object} GetAgentParams Type representing parameters passed to the
* getAgent function.
*
* @property {number} timeoutMs Number of milliseconds to allow for an FDC3
* implementation to be found before calling the failover function or
* rejecting (default 1000). Note that the timeout is cancelled as soon as a
* Desktop Agent is detected. There may be additional set-up steps to perform
* which will happen outside the timeout.
*
* @property {string} identityUrl The app's current URL is normally sent to
* a web-based desktop agent to help establish its identity. This property
* may be used to override the URL sent (to handle situations where an app's
* URL is not sufficiently stable to use for identity purposes, e.g. due to
* client-side route changes when navigating within the app). The URL set MUST
* match the origin of the application (scheme, hostname, and port) or it will
* be ignored. If not specified, the app's current URL will be used.
*
* @property {boolean} channelSelector Flag indicating that the application
* needs access to a channel selector UI (i.e. because it supports User Channels
* and does not implement its own UI for selecting channels). Defaults to
* `true`. MAY be ignored by Desktop Agent Preload (container) implementations.
*
* @property {boolean} intentResolver Flag indicating that the application
* needs access to an intent resolver UI (i.e. because it supports raising one
* or more intents and and does not implement its own UI for selecting target
* apps). Defaults to `true`. MAY be ignored by Desktop Agent Preload (container)
* implementations.
*
* @property {boolean} dontSetWindowFdc3 For backwards compatibility, `getAgent`
* will set a reference to the Desktop Agent implementation at `window.fdc3`
* if one does not already exist, and will fire the fdc3Ready event. Defaults to
* `false`. Setting this flag to `true` will inhibit that behavior, leaving
* `window.fdc3` unset.
*
* @property {function} failover An optional function that provides a
* means of connecting to or starting a Desktop Agent, which will be called
* if no Desktop Agent is detected. Must return either a Desktop Agent
* implementation directly (e.g. by using a proprietary adaptor) or a
* WindowProxy (e.g a reference to another window returned by `window.open`
* or an iframe's `contentWindow`) for a window or frame in which it has loaded
* a Desktop Agent or suitable proxy to one that works with FDC3 Web Connection
* and Desktop Agent Communication Protocols.
*/
type GetAgentParams = {
timeoutMs?: number,
identityUrl?: string,
channelSelector?: boolean,
intentResolver?: boolean,
dontSetWindowFdc3?: boolean,
failover?: (args: GetAgentParams) => Promise<WindowProxy | DesktopAgent>
};
note

As web applications can navigate to or be navigated by users to different URLs and become different applications, validation of an app's identity is necessary. The web application's current URL is passed to web browser-based Desktop Agents to allow them to establish the app's identity - usually connecting it with an App Directory record already known to the Desktop Agent. For more details on identity validation see the identity validation section of the Web Connection Protocol (WCP).

Finally, if there is still no Desktop Agent available, or an issue prevents connection to it, the getAgent() function will reject its promise with a message from the AgentError enumeration.

Injected iframes for adaptors and user interfaces

The getAgent() function may try to create hidden iframes within an application window in order to load either an adaptor to a Desktop Agent, or Intent Resolver and Channel Selector user interfaces when needed. The iframes are used in order to sandbox the relevant software, and are communicated with securely via the HTML Standard's Channel Messaging API (MDN, HTML Living Standard).

danger

The Content-Security-Policy directives frame-src, child-src and default-src can prevent iframes injected into an application from loading content. Where these are used, please ensure that they allow the loading of content from the domains of Desktop Agents and UI implementations that you wish to work with (including fdc3.finos.org where the reference Intent Resolver and Channel Selector UIs may be loaded from).

Failover function

Desktop Agent retrieval can time out, for instance if the DA doesn't exist or is unresponsive. The default timeout of 750 milliseconds can be overridden by setting the timeoutMs parameter. An application may also provide a failover function which will be called if an interface cannot be retrieved or times out.

Example: Decreasing the timeout and providing a failover function

    const desktopAgent = await getAgent({
timeoutMs: 250,
failover: async (params: GetAgentParams) => {
// return WindowProxy | DesktopAgent
}
});

The failover function allows an application to provide a backup mechanism for connecting to a DA. It is called only when establishment through normal procedures fails or times out.

note

If you wish to completely override FDC3's standard discovery mechanisms, then do not use a failover function. Instead, simply skip the getAgent() call and provide your own DesktopAgent object.

Failover functions MUST be asynchronous and MUST resolve to one of the following types:

  1. DesktopAgent The application may choose to directly import or load code that provides a DesktopAgent implementation. getAgent() will then resolve to the provided DesktopAgent.
  2. WindowProxy (MDN) The application may open a window or create a hidden iframe which may then provide access to a compliant browser-resident DA. Ensure that the iframe has loaded (listen for the load event) or for separate windows allow a suitable timeout for the window load, then resolve to the WindowProxy object for the window or iframe. The getAgent() call will then use the supplied WindowProxy to establish a connection.

Persisted Connection Data

The getAgent() function uses SessionStorage (MDN) to persist information on an instance of an app and how it connected to a Desktop Agent in order to ensure a consistent connection type and instanceId when reconnecting after navigation or refresh events.

danger

Apps do not need to and SHOULD NOT interact with data persisted in SessionStorage by getAgent directly, rather it is set and used by the getAgent() implementation.

SessionStorage is used as for persistence as it is scoped to a single browser window and the current origin, ensuring apps in different windows or on different origins access different storage. However, as multiple iframes from the same-domain embedded within a window can share a SessionStorage instance, and multiple applications can be hosted on the same origin and be navigated to and from within a window, additional scoping is applied.

To differentiate storage for multiple iframes the name of the browsing context (window.name) is used to generate the key used in SessionStorage by concatenating the string "FDC3-Desktop-Agent-Details-" with window.name. The window.name remains stable during same-origin navigation and is persisted by the browser in its session history and is appropriate for scoping to a particular browsing context (window or iframe). Desktop Agents should assign a unique name to each window and iframe when they are created to facilitate such scoping. If no name is assigned, then the getAgent will assign a UUID as a unique name for that browsing context.

The data persisted is structured as an object conforming to the SessionStorageFormat type below, with the identityUrl of the app used as the key (if no identityUrl is provided the actualUrl is used instead), with the value conforming to the DesktopAgentDetails type. Hence, the data might be retrieved as follows:

const sessionData: SessionStorageFormat = sessionStorage.get("FDC3-Desktop-Agent-Details-myWindowName");
const agentDetails: DesktopAgentDetails = sessionStorage["myApIdentityUrl"];

Type Definitions

/** Type representing the format of data stored by `getAgent`
* in Session Storage. The `identityUrl` of each app is used
* as the key. */
type SessionStorageFormat = {
/** */
[key: string]: DesktopAgentDetails
}

/** Type representing data on the Desktop Agent that an app
* connected to that is persisted by the getAgent function
* to be used when re-connecting (after a navigation or
* refresh event) and to ensure a consistent instanceId.
*/
type DesktopAgentDetails = {
/** The type of Desktop Agent connected to. Used to
* prevent an inadvertent switch to a different agent.*/
agentType: WebDesktopAgentType,

/** The URL that was previously sent to the Desktop Agent
* to establish the app's identity.*/
identityUrl?: string,

/** The current URL at the time of the last connection to
* a Desktop Agent.*/
actualUrl?: string,

/** Optional URL field that should be used to store any
* URL that was used to connect to a Desktop Agent. URLs
* may have been provided by a parent window that has since
* gone away and persisting may allow re-connection in such
* cases. */
agentUrl?: string,

/** The appId that was identified for the application by the
* Desktop Agent.*/
appId: string,

/** The instanceId that was issued to the app by the Desktop
* Agent. */
instanceId: string,

/** The instanceUuid that was issued to the app. This should be
* passed when connecting to the Desktop Agent to help
* identify that this app has connected before and which
* instance it is, enabling the Desktop Agent to reissue
* the same instanceId. The instanceUuid should never be shared
* with other applications and is not available through the
* FDC3 API, allowing it to be used as a shared secret with
* the Desktop Agent that issued the associated instanceId.*/
instanceUuid: string
}

/** Enumeration of values used to describe types of web-based
* Desktop Agent. Each 'type' refers to the means by which
* a connection to the agent is made and/or an interface to it
* received. */
enum WebDesktopAgentType {
/** Denotes Desktop Agents that inject the FDC3 interface
* at `window.fdc3`. */
Preload = "PRELOAD",

/** Denotes Desktop Agents that run (or provide an interface)
* within a parent window or frame, a reference to which
* will be found at `window.opener`, `window.parent`,
* `window.parent.opener` etc. */
ProxyParent = "PROXY_PARENT",

/** Denotes Desktop Agents that are connected to by loading a URL
* into a hidden iframe whose URL was returned by a parent window
* or frame. */
ProxyUrl = "PROXY_URL",

/** Denotes a Desktop Agent that was returned by a failover
* function that was passed by the application. */
Failover = "FAILOVER"
}

const DESKTOP_AGENT_SESSION_STORAGE_KEY_PREFIX = 'fdc3-desktop-agent-details';

const DEFAULT_TIMEOUT_MS = 1000;