API security

OpenFin API security enables desktop owners and application providers to determine the API calls that are available for an OpenFin application. Applications must declare in their manifest that they use security-sensitive APIs (secured APIs) for functionality that is outside the security sandbox, such as launching an external application or using fullscreen mode. While these features can be beneficial, OpenFin understands that desktop owners may need to restrict certain APIs from running on a desktop computer, particularly from an unknown or untrusted application. API security enables this by giving the Desktop Owner tools to prevent application developers from implementing features that may be deemed sensitive to an organization.

📘

Note

"API" in this article can refer to individual functions or methods (such as System.openUrlWithBrowser()) or to entire Web APIs (such as geolocation).

Historically, desktop owners have been able to block access to such APIs on a per-app basis, if a particular app is not trusted or should not have elevated permissions. However, this arrangement is opt-out: desktop owners must explicitly block particular apps from using particular API functions; all others are allowed.

Starting in v20 of the OpenFin runtime, the enhanced security model prevents apps from accessing secured API functions unless the apps are explicitly granted permission to use them.

Enhanced security model

In order to leverage secured APIs, application providers must declare the use of specific API functions in their application manifest file. This assists desktop owners to recognize API intent up-front. If an API function is not permitted by the organization or needs to be enabled for ease of application functionality, the desktop owner can define settings to block or allow use of the API function.

For versions of OpenFin prior to v20, if a desktop owner does not block an application’s use of a secured API, it is permitted. Starting in v20, if the desktop owner (or their delegate) does not explicitly allow a secured API for an application, it is blocked by default.
See API security for desktop owners for details on how desktop owners can control access to secured APIs.

Versions of OpenFin between v20 and v24 automatically allow secured APIs if the runtime fails to connect to the RVM or if the RVM doesn't exist. Starting in v24, secured APIs are blocked if the RVM cannot be reached (due to messaging failures) or if the RVM doesn't exist (the runtime was launched from the CLI).

Development exception

To simplify application development, if the application is running from localhost, API security is the same as in v19 and earlier: Only the app manifest declaration is needed to access the API; no desktop owner setting or other permission is required.

For developers, this exception removes the need to take an extra step to allow APIs to be used. Be sure to test your application on a non-local server, and with both enabling and disabling the secured APIs it uses, to ensure that it behaves correctly under those circumstances.

Desktop owners can this exception for developers’ desktops by defining a specific registry key.
See API security for desktop owners for details.
If the key is not defined or is set to a non-zero value, the exception for localhost is allowed.

Secured APIs

In OpenFin, the following APIs are secured. Therefore, in order for an application to use them, they must be declared in an application’s manifest, and allowed by the desktop owner or their delegate.

APIType
System.downloadAssetOpenFin
System.launchExternalProcessOpenFin
System.readRegistryValueOpenFin
System.registerCustomProtocolOpenFin (RVM 12+, Runtime v34+)
System.unregisterCustomProtocolOpenFin (RVM 12+, Runtime v34+)
System.checkCustomProtocolStateOpenFin (RVM 12+, Runtime v34+)
System.terminateExternalProcessOpenFin
System.getAllExternalWindowsOpenFin (deprecated in Runtime v20; removed in v22)
System.openUrlWithBrowserOpenFin
ExternalWindow.wrapOpenFin (deprecated in Runtime v20; removed in v22)
Application.getFileDownloadLocationOpenFin (Runtime v32+)
Application.setFileDownloadLocationOpenFin
audioWeb
videoWeb
geolocationWeb
notificationsWeb
midiSysexWeb
pointerLockWeb
fullscreenWeb
openExternalWeb (from Electron)
clipboard-readWeb
clipboard-sanitized-write (starting in OpenFin version 23)Web

Declare APIs in an application manifest file

An application must declare the secured APIs that it uses in its manifest file. If it attempts to use secured APIs that it hasn’t declared, they are blocked.
For complete details on defining an application manifest, refer to Manifest settings.

The properties used to declare use of secured APIs depend on the version of the OpenFin Runtime being used. Runtime 37 and later version support domain-based permissions, where you can declare default permissions and exceptions based on domain match patterns.

In Runtime v36 and earlier versions, you declare permissions for the application, and also for viewing contexts (windows and views).

In either case, regardless of Runtime version, you must list secured APIs within a top-level permissions object, categorized by the namespace they belong to (which may be webAPIs).
For most APIs, the name is a key with a Boolean value, except Web APIs, where listing the API name in an array indicates that it is used.

For System.openUrlWithBrowser, the key is an object with two members:

  • enabled, a Boolean for whether the method is used

  • protocols, an array of custom protocols

For methods related to custom protocols, the permission can be either a plain Boolean or an object similar to the one used for System.openUrlWithBrowser; the protocols member in this case declares custom protocol schemes that are used with each method. The methods related to custom procotols are the following:

Domain-based permissions

In v37 or later, in addition to the top-level permissions object, you can define you can define default permissions for nested content in the domainSettings.default.api.permissions object.
If this object exists with no contents, then all secured APIs are blocked by default.
If this object is not defined, then context-based permissions are used, as described in the next section.
You can define exceptions to the default permissions based on domain match patterns, as described in Domain-based permissions.

Example application manifest excerpt, with domain-based permissions

{
  "platform": {
...
    "permissions": {
    //These APIs are all used by the application
      "System": {
        "launchExternalProcess": true,
        "openUrlWithBrowser": true,
        "registerCustomProtocol": true,
        "checkCustomProtocolState": true,
        "readRegistryValue": true,
        "terminateExternalProcess": true
      },
      "webAPIs": [
        "notifications",
        "audio",
        "video"
      ]
    },
    "domainSettings": {
      "default": {
      //By default, these APIs are blocked
        "permissions": {
          "System": {
            "launchExternalProcess": false,
            "openUrlWithBrowser": false,
            "registerCustomProtocol": false,
            "checkCustomProtocolState": false,
            "readRegistryValue": false,
            "terminateExternalProcess": false
          },
          "webAPIs": [
            "notifications",
            "audio",
            "video"
          ]
        }
      },
      "rules": [
        {
          "match": ["*://example.com/*"],
          "options": {
            "api": {
              "permissions": {
              //Domains that match the pattern can use these APIs
                "System": {
                  "launchExternalProcess": {
                    "enabled": true,
                    "assets": {
                      "enabled": true
                    },
                    "downloads": {
                      "enabled": true
                    },
                    "executables": {
                      "enabled": true
                    }
                  },
                  "openUrlWithBrowser": {
                    "enabled": true,
                    "protocols": [
                      "slack",
                      "msteams"
                    ]
                  },
                  "registerCustomProtocol" : true,
                  "unregisterCustomProtocol": {
                    "enable": true,
                    "protocols": [
                      "PROTOCOL1",
                      "PROTOCOL2"
                    ]
                  },
                  "checkCustomProtocolState": {
                    "enable": true,
                    "protocols": [
                      "PROTOCOL1",
                      "PROTOCOL2"
                    ]
                  },
                  "terminateExternalProcess": true
                },
                "webAPIs": [
                  "notifications",
                  "audio",
                  "video"
                ]
              }
            }
          }
        }
      ]
    }
  }
...
}

Context-based permissions

In v36 and earlier, the location of the permissions object varies, depending on the way the application uses OpenFin APIs.

  • For applications that use the Platform API, you can set permissions inside the following objects:

    • platform: The permissions object declares APIs for the platform only (and not for windows or views). If you specify a providerUrl, then this URL is able to execute the enabled permissions.

    • defaultWindowOptions: The permissions object declares APIs for every window launched.

    • viewDefaultOptions: The permissions object declares APIs for every view launched. Be sure to consider whether you load third-party content into views, which might also use APIs.

  • startup_app: Use this object for applications that do not use the Platform API.

Example platform application manifest excerpt with context-based permissions

{
  "platform": {
...
    "permissions": {
      "System": {
        "launchExternalProcess": true,
        "openUrlWithBrowser": true,
        "registerCustomProtocol" : true,
        "unregisterCustomProtocol": true,
        "checkCustomProtocolState": true,
        "readRegistryValue": false,
        "terminateExternalProcess": true
      },
      "webAPIs": [
        "notifications",
        "audio",
        "video"
      ]
    },
    "defaultWindowOptions": {
      "permissions": {
        "System": {
          "launchExternalProcess": {
            "enabled": true,
            "assets": {
              "enabled": true
            },
            "downloads": {
              "enabled": true
            },
            "executables": {
              "enabled": true
            }
          },
          "readRegistryValue": false,
          "terminateExternalProcess": true
        },
        "webAPIs": [
          "notifications",
          "audio",
          "video"
        ]
      },
      "defaultViewOptions": {
        "permissions": {
          "System": {
            "launchExternalProcess": {
              "enabled": true,
              "assets": {
                "enabled": true
              },
              "downloads": {
                "enabled": true
              },
              "executables": {
                "enabled": true
              }
            },
            "readRegistryValue": false,
            "terminateExternalProcess": true
          },
          "webAPIs": [
            "notifications",
            "audio",
            "video"
          ]
        }
      }
    }
  }
}

Example application manifest excerpt with context-based permissions

{
  "startup_app": {
...
    "permissions": {
      "System": {
        "launchExternalProcess": true,
          "openUrlWithBrowser": true,
          "registerCustomProtocol" : true,
          "unregisterCustomProtocol": true,
          "getCustomProtocolState": true,
          "terminateExternalProcess": true
        },
        "webAPIs": [
          "notifications",
          "audio",
          "video"
        ]
    }
  },
...
}

Query secure API permissions

You might want your app to take preemptive action when certain capabilities are not available. For example, a Launch External App button could be disabled when that capability is not permitted.

Starting in v21, the System.queryPermissionForCurrentContext() method returns the permission state of a specific secured API. By using this method, an app can alter the UI to disable or hide features that cannot be performed due to the permission status.

Example usage

The System.queryPermissionForCurrentContext() method has one parameter, the name of the secure API to query.

Here is what it looks like to use it:

System.queryPermissionForCurrentContext('System.launchExternalProcess').then(result => {
  if (result.granted) {
    renderLaunchExternalProcessButton();
  } 
});

The shape of the return value is QueryPermissionResult which is as follows:

interface QueryPermissionResult {
  permission: string,
  state: 'granted' | 'denied' | 'unavailable',
  granted: boolean, 
  rawValue?: unknown
}