The JavaScript adapter is an experimental feature. Function signatures and behavior may change in future releases.
Most times, Finsemble launches your HTML apps as part of your smart desktop. But there may be some apps that cannot be launched by Finsemble. We call apps launched outside of Finsemble "freestanding" apps.
Examples of freestanding apps include:
Freestanding apps can participate in FDC3 interop using Finsemble's JavaScript adapter.
You can find the JavaScript adapter and a sample app in the @finsemble/finsemble-core npm module:
node_modules/@finsemble/finsemble-core/dist/finsemble-javascript-adapter.js
node_modules/@finsemble/finsemble-core/dist/javascript-adapter-example-app.html
Run
npm install "@finsemble/finsemble-core"
to download the package (see steps below for hosting options if you are not using npm modules).
The JavaScript adapter must be loaded into your freestanding app with one of the following methods:
<script>
tag (hosted where you are hosting your smart desktop):<script charset="UTF-8" src="http://localhost:3375/build/finsemble/finsemble-javascript-adapter.js"></script>"
This is the simplest approach when your freestanding apps will be interacting with a Finsemble desktop that you maintain.)
<script>
tag hosted using "unpkg":<script charset="UTF-8" src="https://unpkg.com/@finsemble/finsemble-core@6.1.2/dist/finsemble-javascript-adapter.js" />
This is a good solution when your freestanding app may interact with Finsemble desktops run by other firms.
<script>
tag hosted on your own website:<script charset="UTF-8" src="https://yoursite.com/finsemble-javascript-adapter.js" />
You must serve <script> as UTF-8 (by setting charset as in these examples or by configuring your HTTP server to serve .js files encoded with UTF-8). Not setting UTF-8 properly will result in the error
SyntaxError: Invalid or unexpected token
. (That error can also be caused when your server is missing the file. Check for "404 Not Found" errors in the debugging console for your app.)
Some containers, such as webviews, offer the ability to preload (inject) JavaScript.
This example demonstrates how to preload Finsemble's JavaScript adapter using DotNetBrowser (a popular webview for .NET):
// Load the app in a Browser object
IEngine engine = EngineFactory.Create();
IBrowser browser = engine.CreateBrowser();
browser.Navigation.LoadUrl("https://yourserver.com/yourapp.html");
// Inject the JavaScript adapter
IFrame mainFrame = browser.MainFrame;
mainFrame.ExecuteJavaScript<string>("<script charset='utf-8' src='http://localhost:3375/build/finsemble/finsemble-javascript-adapter.js'></script>");
mainFrame.ExecuteJavaScript<string>("FSBLJSAdapter.startApp()");
// Display in a Browserview
BrowserView view = new BrowserView();
view.InitializeFrom(browser);
The JavaScript adapter is a UMD module. It is not available as an
import
because it exposes global variables rather than module exports. See "Building With TypeScript" for how to get ambient types for these global variables.
By loading the JavaScript adapter as a script or preload, two global objects are added to your app: FSBLJSAdapter
and fdc3
.
Your code must explicitly start the JavaScript adapter by calling FSBLJSAdapter.startApp()
, either in your app or by preloading/injecting the call. This function takes optional parameters:
appName
(string) - This is used to look up your app in Finsemble's AppD configuration (e.g. appd.json). This name appears in logs and in development tools. appName
is required and must match an AppD configuration containing the app's pubic key. (See Static Authentication later in this topic).digitalSigningKey
(string) - Provides a digital signing key that enables authenticated communication with Finsemble's interop service. If you specify this parameter, don't specify jwt
.windowName
(string) - You can give your app a specific window name. This name will appear in log messages. If you don't specify this parameter, Finsemble will generate it for you.routerAddress
(string) - You can provide a url for Finsemble's IAC. The default is http://127.0.0.1:3376.jwt
(string) - A JSON web token (see Static Authentication Using a JSON Web Token later in this topic). If you specify this parameter, don't specify digitalSigningKey
.Example: Starting the JavaScript adapter with custom values
FSBLJSAdapter.startApp({
appName: "TradeBlotter",
digitalSigningKey: privateKey,
windowName: "TradeBlotterWebview",
routerAddress: "http://127.0.0.1:4765"
});
FSBLJSAdapter.startApp() is promisified. You can await
its return, which resolves when the JavaScript adapter is initialized. But, awaiting is completely optional: FDC3 calls are safe to use immediately.
FSBLJSAdapter.startApp() can also be preloaded/injected. We recommend this approach when you're loading 3rd party FDC3 apps into webviews.
If Finsemble is not yet running when your freestanding app is started then the JavaScript adapter will continue retrying until it connects. Your app's FDC3 calls will queue until that connection is established.
Your code can make FDC3 calls as soon as the JavaScript adapter is loaded and startApp() has been called.
Example: YourApp.html
FSBLJSAdapter.startApp();
const channels = await fdc3.getSystemChannels();
fdc3.joinChannel(channels[0].id);
fdc3.broadcast({type:"symbol", id: {"symbol":"AAPL"}});
Finsemble's full desktop API (
FSBL.Clients
) is not currently supported in the JavaScript adapter. Only FDC3 API calls are currently supported.
The JavaScript adapter adds the global variables FSBLJSAdapter
and fdc3
to your freestanding app. Since these variables aren't imported, Typescript needs to be told where to find the type declarations that describe them. Those type declarations are available in the "@finsemble/finsemble-core" module (add this module using yarn add
or npm install
).
To gain access to these type definitions, add the following line to your tsconfig.json file:
"typeRoots": [
"node_modules/@types",
"node_modules/@finsemble"
]
This configuration tells Typescript (and Visual Studio Code) to look in the @finsemble/finsemble-core package for ambient (non-imported) types. After making the change, restart your IDE (e.g. Visual Studio Code) so that it loads these types.
Finsemble only discovers the identity of freestanding apps if those apps provide an appName
parameter to FSBLJSAdapter.startApp(), but even if an identity is provided, Finsemble considers that identity as unverified unless a digitalSigningKey
parameter is also passed to FSBLJSAdapter.startApp(). This process of providing a digital signed authentication token is called "static authentication".
Static authentication is required only for freestanding apps that need to interact with other apps that use the (
authorize
)[tutorial-ControllingInformationFlow.html]selectConnect
rule to restrict data access.
To configure a statically authenticated app:
Run Finsemble (desktop) and then open a developer console. Enter into your console:
> FSBL.createKeys();
This command will generate a public key and a private key.
Enter into your console:
> copy(FSBLPublicKey)
This will copy the public key to your clipboard. Paste it into your app's entry in appd.json:
{
"name": "TradeBlotter",
"manifest": {
"signatureKey": {
"alg": "RS256",
"e": "AQAB",
"ext": true,
"key_ops": [
"verify"
],
"kty": "RSA",
"n": "kv8uq_7Or89Xdu2XZTImNBUiZ4SBN5Kx_dOUlIH0LyWaz4SG4epDZhQkJ0xs8kcRS5akBHF0dz0ZMlSOlkr1dXwv1PEgt2rbcqPSNXnSJbUaWGCcmuqfOeX2uh2SpVFeYpCLzjBwq6J-iVRjdqRXeRXBv_D95wuY8ebpnmg3TQk-NRQT3nQQ_IPeGrRYbag08ZGcSERH55MNhvOotF73HRk9xqgexmTkc706-x7qcmvONNbKNrAYr6x_Me9Txo5X93UyNl5472QQE3fhgnJt8wxC2yvkKydnZ3bfu3sob4kyXyGI4ZcuuMfUA2bOAPvManwWTL9cHRYe-wnwirQBWw"
}
}
}
Enter into your console:
> copy(FSBLPrivateKey)
This will copy the private key to your clipboard. This private key must be passed to your app at runtime in the digitalSigningKey
parameter. It is up to you to decide where to store your private key and how to get it into your app.
FSBLJSAdapter.startApp({
appName: "TradeBlotter",
digitalSigningKey: {
"alg": "RS256",
"d": "AxbEAyQ_G_hWRQRYHphvr2opnMBEO4fkdUouwJzR0J3Qg3-BmsS39ttXKSY_fICGz1j9MwSz_FTMhJS8oabgl-qXUg5hGsmZjplSWleoKq0EE-BahMeHt16VkTKm5BIc0s2-dQOVPTNpx6FTgmjmAlWKipSErrK9MN7NK6F9VqviEAN1zwL4A8J6c6R98p3KI2F6-vjSU0RVaL8zqGi8aoqqs6jWWJP7iJcY8IeILuDwwybfFCGNkV6eQbnLukimU60nXhDrc4qggmj2MWTHOQqVJt-OmuxOc7tVZRJJzgun_x9obSYe_XAk4PKz_M8-QNYYRRY3_Dec5M0Z-LfuuQ",
"dp": "BarnqWf2S4zqNYz6h3bbIPLcrFIDDiRzvlF7OYkYkCXqTFhG4Zu-iU6rYOwVSmNxk2UG9SBvGpxNSGPGTrEL4LAXAPnbRVoe3RGJaLJMfu4gUxItzfJ7SncJD2E-D--moG1Djg0tSOL4qqsEgDFWowrP3bXXOC15NxPomtUnEb8",
"dq": "bp0ylMki-WugVy6FLjgqum1n_X_WURBPwoTpWczw_op4jWRqpmm_ltvwio5gcDi1_X0jBxbKV13eAi34R6nvqJbnToeeIDUBvpJI5l4sbGNm2H8cU17RMeDI5FNse9pNoSTaboiSzNgMLAyN0wnMr36zFFIvPHp6Z16ZZIJx1sE",
"e": "AQAB",
"ext": true,
"key_ops": [
"sign"
],
"kty": "RSA",
"n": "kv8uq_7Or89Xdu2XZTImNBUiZ4SBN5Kx_dOUlIH0LyWaz4SG4epDZhQkJ0xs8kcRS5akBHF0dz0ZMlSOlkr1dXwv1PEgt2rbcqPSNXnSJbUaWGCcmuqfOeX2uh2SpVFeYpCLzjBwq6J-iVRjdqRXeRXBv_D95wuY8ebpnmg3TQk-NRQT3nQQ_IPeGrRYbag08ZGcSERH55MNhvOotF73HRk9xqgexmTkc706-x7qcmvONNbKNrAYr6x_Me9Txo5X93UyNl5472QQE3fhgnJt8wxC2yvkKydnZ3bfu3sob4kyXyGI4ZcuuMfUA2bOAPvManwWTL9cHRYe-wnwirQBWw",
"p": "x2A79GfXzvj1e7KCml4SYu-YjaEZcUw_ly9YOKh7SvSn9ELzesuqZiA_tBL-AOzlC3z05AWHOGUfA05InjXxrCx6t_n83kAQYMspZK7z8GuhEytnw5PjjFfhAHQsrwZCyxS_Lzuu72iCfhn-hHGd8Ii2jlHAPjSu3WOUpSMG21M",
"q": "vL6wMJnwjbic_9UazKINdFmY8o_IOyANWXPXd24urh7CyQEsYgka0zucOV4UkrJ1Iw21Dp9WGmVZGgw6KKV0UAbuYvV5JZzwuGpEhf4ZWFqQDJ-tyy26QliWMH-op0OEDH5zaXAlQUkO53oDhDySfBp3CQvqIQSUCOlX16kgiNk",
"qi": "TKQzuZHZOiPYyl1hKNuK7v01z9qHGea18a7XiL3hDSW61IH3mpdjAnWSaUfaqVIgdSCIcwrUAbR1WkB6XjNytH_9pcRz03qFFNSREw7A6AhTk-0aLdsyjekBcG2AqxInwJ6256j1RzCBIOvjlB7DZh8xJJTR1qIhAzJEikK4Yh4"
}
});
Your app will now be authenticated when it registers with Finsemble and will be able to interact with apps that have set the authorize
SelectConnect rule.
The private key is never transmitted. Finsemble's JavaScript adapter uses the browser's crypto API to locally generate a signed authentication token. Only that token is transmitted.
Static authentication requires that your app is running under HTTPS.
For even more security in static authentication, Finsemble supports using a JSON Web Token (JWT) that you generate remotely ahead of time. It's the app's responsibility to fetch its own JWT from a remote server and input it as a parameter in the app's startup sequence like this:
FSBLJSAdapter.startApp({
appName,
jwt,
});
You must generate the JWT input to FSBLJSAdapter.startApp
using the private key that matches the app's public key in Finsemble's AppD
configuration. The JWT payload data must include only the name of the app: { name: "testApp" }
. For Finsemble to verify the JWT, it must be created using a RSA signature with SHA-256 (RS256).
Note: you don't need to set an expiration time when creating a JWT. Finsemble uses its own 30-second expiration timer for all its authentication tokens, which are verified when an app registers. For a JWT, Finsemble uses the
iot
field (which is automatically set to the current time when the JTW is created) to calculate the expiration time.
Here's an example function that shows how to generate a JWT for the app named testApp
on the remote Node.js server using the jsonwebtoken module.
async function getJSONWebTokenForApp() {
const jwt = require('jsonwebtoken');
const cryptoAPI = require("crypto").webcrypto.subtle;
const signatureData = { name: "testApp" };
// For Finsemble public/private key generation see https://documentation.finsemble.com/tutorial-StaticAuthentication.html.
const digitalSigningKey = {
alg: "RS256",
d: "4VMHGzvRpw-Rzbct3TzG6GTkQzf8yyHLfB8SiK4HV8RWKQTGH3FzKyNz7n6eiUIytlpbx8SbPjOe7BYh6Wo9wOf4LNbwxFaK26lB7zJyRK8sipn76k0HOJmXPVNbY4VNT1S00JIvKiKzDPbhNCJZq6Syr5kzpGNQmze4JlU0IPleIXdtrdO1cV9lDKshViUpaJbfdPBG1Jsc0UWNNX_jFsYb3ij-QgquPQ8li32Lmoc0aL9tCbJdnJxGnGJpuy3on7lncFMZdZR_YyPxbNysPH1cuqpKI7jp6iP1hzVvfGe9PYKxcONohqHpl7tp-CRcYT5kp5rUd0__W6B5wYvB",
dp: "QYuflX5ucEoh0aqZje60sKIaTaKW9qRbKUdpXE0RaHwaZ3DNtE1r2dBV3weKsPy-NQFIKtOMHn6cT7LVGSrzTPqruPBhS4LJ3FKSmH1eFMMI-Tiao2b35pZLe5h73JxOjdkllvvP-tRO4Q25qUy_sseFAYEDl8tNzrD1VxhAvUE",
dq: "pZaLXAk2isRpcd8u0zIqrmfgWzKeMMW8Jmb9tnx7LBOTK86KtzOfJR1mSb2JTmwtAGqRIKuRNL2bG9gTwIrG3ODxt-U6o9Aq0aWI-UsydL_HsgmvL-aCTF37agqUpnT1fij0NXg13RFOiumusfmsxURvNEOJEP0CEOAFVHu_RTE",
e: "AQAB",
ext: true,
key_ops: ["sign"],
kty: "RSA",
n: "q0vCwCa2vlc2cRuLXmR0bBRfhnNckIfDpv3z9GiTf16XmymSQexKNIWCPSgjwM9VsPopiF0KFMKCeEU4ca_qFQSKa2ybiyPGX4-TvjUftlvw96g3ILcqx0WbJoRZfMVrQCYFz_AI6-hUhk0br22zc1tQpG4lDY_WLyhZOT7LtOGFAeMWdlFS_VeQ3lGNUOTNBYd2475ABQfy8j6nAajUudUBhLdU7srl5vYSLfWBvGRJQ0LUqSsVGPF5RR8GIQ56DEiH_O73OVg-GSImcE2Ig7zuzN39opLnYelFFghBN5egQL1Q46shpU_c5k8eeIxbrCncEPqJY0iQkml81r7mbQ",
p: "4m6yATANXnstYOh4DWPMcWO_ta-ATocMULsDKtK5Bw0Ispl_2KONgRNHcZLM7mjJJHDvmxznbW9aKNJ3sbvcNlPc6NvTMpBvLk4CY6sTH4YycfCkQKHeNE506bhyLSd1obmUSlhtcoC7Q9gKInFkdWPCpE5_XrOTlj3-PxgBC8E",
q: "wanvFxB6A-M4L0JkeyvLAhOe3qCGXZ4APPnu_mI2PVu2rpjo6OA2yelbF3_geisRsSCpoj1MqSNuuoTG3DHP5r6XT_GcmClwaVUv2qrE4is4T3YllBmX9g9sFzLjE7mXFcsy9XniYjyvodEnJCp2hSZN7GLi8mCJe9DO9HlbNa0",
qi: "a3VzKzraZmnDsiKnGjbMHSnUdHISo_mvYMqJ-3YHeS--KzRisKjZkmJjRqhO8ckfbUPn9NFZhH5xTm_HywW3HTLTphZrJMoKfAgGCUNwL9pLhlvHzzO1IvDNi1oq3ae3ZzJLlx5_RXp5EjRwtzF13FCQ4_rZSVKBXhe3G8caG3Q",
};
const importedPrivateKey = await cryptoAPI.importKey(
"jwk",
digitalSigningKey,
{
name: CRYPTO_ALGORITHM,
hash: CRYPTO_HASH,
},
true,
["sign"]
);
const jwToken = await jwt.sign(signatureData, importedPrivateKey, { algorithm: 'RS256' });
return jwToken;
}
The JavaScript adapter loops until it connects to a running Finsemble instance. The proprietary function fdc3.onDisconnect()
can be used to call a handler when the connection to Finsemble is lost. This would typically only occur if Finsemble is closed independent of your freestanding app.
The JavaScript adapter does not currently have logic for re-establishing fdc3 state, so if your app is disconnected it will have to call FSBLJSAdapter.startApp() again and reestablish any fdc3 context or intent listeners. It may be easier to trigger an app reload or ask your end user to restart the app if it disconnects unexpectedly.
Example onDisconnect() usage
fdc3.onDisconnect(() => {
alert("This app has lost connectivity. Please restart.");
});
fdc3.onDisconnect() is not a standard FDC3 function. This API may change in future versions.