Finsemble provides a UI for authenticating end users, a "boot stage" for performing authentication related tasks (such as fetching entitlements or dynamically generating config), and APIs for sharing user credentials among cross-origin components.
Authentication involves the following:
Finsemble is capable of supporting any authentication protocol. This tutorial provides information to implement nearly any authentication scenario but focuses on the three most common approaches:
Prompting the user for login/password (session based authentication)
Automatic login with Windows credentials (aka "negotiate protocol")
Oauth/OpenID ("identify provider")
Skip to the section that matches your firm's authentication practices.
Finsemble's boot sequence runs an "authentication stage" before any components or workspaces are loaded. During this stage you can run authentication or initialization related code. You can run code that presents your users with a password prompt, or code that performs tasks invisibly.
To enable authentication at startup you must configure the "startup" authentication profile by making config entries.
authentication
is located at the top level of Finsemble's config (configs/application/config.json
). Adding a
"startup" entry will cause Finsemble to run the authentication stage.
Example: Configure Finsemble to prompt users for a username and password
"authentication": {
"startup": {
"component": "Authentication",
"adapter": "PASSWORD",
}
}
startup
- A special "authentication profile" that runs during Finsemble's boot sequence. (Creating additional
authentication profiles is an advanced topic covered later in this tutorial.).
component
- The name of the Finsemble Component that collects login and password (in
configs/application/components.json
). Your component can be fully customized. The only requirement is that it calls
FSBL.Clients.AuthenticationClient.publishAuthorization
when it is
complete (to allow Finsemble to finish its boot sequence).
adapter
- Finsemble's "authentication service" uses this to decide which authentication protocol to use. "PASSWORD"
and "OAUTH" are supported.
In Finsemble Seed, src/components/authentication
contains a sample authentication component built with React. It is
described by the config entry components.Authentication
(in components.json
). You can use this sample component as a
starting point or write your own.
When using the "PASSWORD" adapter, Finsemble expects your component to fully handle the process of authenticating your users. Your Authentication Component should collect and send the user's credentials (login, password, etc) to your back end server for authentication. The connection to your back end can use any web technology (e.g. AJAX, Websocket, etc).
Once your component has successfully completed authentication, it must tell Finsemble call
FSBL.Clients.AuthenticationClient.publishAuthorization(user, credentials)
so that Finsemble's boot sequence can resume:
user
- A string that identifies the user. This is used as a "key" by Finsemble's Storage Service to prevent storage
conflicts with saved data from other users. (This is particularly important if you support multiple user logins to the
same computer and use localStorage for data.)
credentials
- An object that may contain any useful values. The "credentials" object is publicly available to all
Finsemble components. Credentials are often used by components to perform Single Sign On (SSO) operations - because
they can trust the source of the credentials (Finsemble). (See below for information on subscribing and retrieving
credentials).
Note: Never rely on HTML components to store sensitive data. Browser data can be easily accessed by technical users. A
user can easily use Chrome tools to manually run publishAuthorization()
or to get access to the credentials
object.
You must program your desktop software just as you would program web software, using secure schemes such as session
tokens to protect access. Your application components must operate according to these principles unless your desktop
and browser are configured to use NTLM, Negotiate, Kerberos or another secure protocol.
Note: In cases where your authentication model requires handing control off to an authentication server (such as OAuth
2.0), then the "redirect page" from the authentication server should call publishAuthorization()
.
Note: Clicking on the "close icon" in the Authentication Component will cause Finsemble to shutdown. This prevents users from ever getting into a state where they cannot terminate your SmartDesktop.
Finsemble's example authentication component is built using the Finsemble React UI. The only important difference is
that instead of calling publishAuthorization()
imperatively, this component uses the authorize()
method provided by
Finsemble's useAuth()
React hook.
Your desktop may be able to authenticate users automatically (such as with "NTLM" or "negotiate" protocols) but you may still need to run code during Finsemble's authentication boot stage. Common use cases include fetching user entitlements or setting "dynamic config" (see below).
Finsemble's authentication component can be run "invisibly" by adding this setting to its config:
window.options.autoshow = false
.
Note: If your authentication process takes more than a few seconds you may want to avoid using invisible authentication. Instead, create a visible authentication component that simply provides a progress bar or some sort of visual indicator. This is a particularly good idea if it is possible for your authentication process to "block". The "close icon" in the authentication component provides users with a way of terminating your SmartDesktop if it fails to start.
"Dynamic Configuration" is our term for updating Finsemble's config at runtime by making calls to
FSBL.Clients.ConfigClient.processAndSet()
.
The most common use of "dynamic config" is to configure the user's desktop environment once the user has been identified. Typical environmental configs that are set dynamically include:
components
- The list of components available for launching can be dynamically generated based on the user's
identity and their entitlements. Often, the list is generated by a back end server that can output Finsemble
compatible config.
workspaces
- A firm may decide to provide example workspaces to its users. The authentication boot stage is a great
point to load those workspaces, particularly if the list of workspaces is keyed to user type.
The Finsemble's authentication boot stage provides a natural point to generate and set dynamic config. Typically, this
is done by making calls to processAndSet()
directly in your authentication component.
Any Finsemble component or service can subscribe to or retrieve authorization credentials.
Example: subscribe to authorization credentials:
FSBL.RouterClient.subscribe("Authorization", function (err, response) {
if (err) return /* handle error*/;
let credentials = response.data;
// ...
});
Example: retrieve authorization credentials:
FSBL.AuthenticationClient.getCurrentCredentials(function (err, credentials) {
if (err) return /* handle error*/;
// ...
});
Typical web session-based authentication uses a browser cookie to store a session token. The token is passed with each request back to the webserver as a cookie. The webserver checks the token against its internal map of authenticated sessions before returning data.
Browser cookies are "origin based". If the session cookie is set within your authentication component then the cookie will automatically available to any other component or service that is hosted on the same origin (domain). If you're building code within the Finsemble Seed project structure then this will mean that all of your components and services will share cookies (unless your server sets cookie path in your http headers).
In other words, cookies in Finsemble behave exactly as they behave in a web browser. (Please note that origins must match exactly! http://google.com and https://google.com are different origins)
If you need to share cookies among components that are hosted on different origins then you can shared them inside Finsemble's authentication credentials or with the distributed store.
When a SmartDesktop launches, the following authentication sequence occurs:
At Finsemble's "authentication" boot stage, config is checked for the existence of "authentication.startup"
. If it
exists, then steps 2, 3 & 4 are executed.
Finsemble's Authentication Service spawns the configured authentication component and waits for publishAuthorization() to be called.
Your code runs in the authentication component. This code should authenticate the user and then call
publishAuthorization(username, credentials)
.
Finsemble's Authentication Service publishes the username and credentials to all listeners on a PubSub topic
"Authorization" and makes it available to getCurrentCredentials()
.
If your organization has an authenticated network environment, you can use Integrated Windows Authentication (IWA) to automatically authenticate Finsemble using the user’s Windows login credentials.
When using Windows credentials on a trusted network, Finsemble itself does not actually play a role in authentication. Rather, your servers interact directly with Chromium via the negotiate protocol (Finsemble components are displayed by the Chromium engine).
The request will be invisibly validated by Chromium via either Microsoft Kerberos or NTLM.
When your network is configured for automatic authentication through Windows you should disable authentication in
Finsemble. Remove the authentication.startup
entry in your config (configs/application/config.json
). Your
SmartDesktop will now boot without prompting the user to log in.
However, there are still some considerations even when authentication is automatic.
You may still need to perform authentication related tasks during boot. In this case, you should use the "invisible authentication" techniques described above, completing authentication related tasks in an invisible Authentication Component that you create.
You may still need to fetch credentials. When using automatic authentication on Windows, your user’s credentials are not accessible in the browser. To fetch credentials requires implementing your own custom API into your web server's infrastructure. This can also be done inside an "invisible" Authentication Component as described above.
Please remember, when using invisible authentication components you must still call publishAuthorization()
for the
boot sequence to proceed.
A Finsemble component or service requests content from your server (e.g., an HTML file, WebSocket connection, or API call).
Your server responds with a request to authenticate with the Negotiate protocol specified. This is very similar to how HTTP basic or digest authorization work, but Negotiate does not result in a pop up to the user. Instead, if the server is on a trusted network, Chromium automatically makes a request for the user's credentials via Kerberos or NTLM protocols.
A trusted server is (a) on the same LAN (same IP network/submask), or (b) configured as a trusted server within Windows (this works the same way as Internet Explorer does). Your network administrator will likely configure your end users' machines to trust your server through group policy management.
Chromium automatically responds back to the server with the Windows credentials.
The server validates the credentials by making its own connection to the Windows LAN/Kerberos server. If validated, the content will be delivered; otherwise a 503 Unauthorized message will appear in the component window.
Subsequent components connecting to this server will receive their content immediately because Chromium will return valid credentials for any window that connects to the same, already-authorized, domain.
With OAuth, applications (such as your Finsemble SmartDesktop) can defer authentication to an external "identity provider." On the web, firms like Google and Facebook play the role of identity provider. Your firm may have its own identity service that supports OAuth. OAuth is made possible through a "shared secret" between the identity provider and the application. Behind the scenes, it is affected through a series of redirects.
Note: Finsemble’s implementation uses OAuth2 server flow (not implicit flow).
Set an authentication
config. We call this an authentication profile:
"startup": {
"adapter": "OAUTH2",
// The url of your identity provider’s login page. You can add additional query string parameters to this if necessary.
// For instance, MS ADFS sometimes requires a non-standard "resource" parameter.
// Another example would be https://accounts.google.com/o/oauth2/v2/auth?prompt=consent which controls how Google presents its OAuth page.
"authorization_endpoint": "https://openid.c2id.com/login",
// The type of OAuth request
"scope": "openid",
// This will be provided by your identity provider
"client_id": "XYZABC",
// Your OAuth server side script
"backchannel_endpoint": "https://yoursite/authenticate",
// Your redirect_uri should point to an `oauthResponse.html` page.
// Follow this tutorial to create an `oauthResponse.html` page (see below). The file name isn't dictated, but it must match the entry in *authentication*.
"redirect_uri": "$applicationRoot/src/components/authentication/oauthResponse.html",
// Optionally specify which component to use to spawn this profile
"component": "Authentication"
}
If you’re familiar with OAuth, Finsemble is automatically generating the following fields: state
, response_type
, and
redirect_uri
.
startup
is a special authentication profile which causes Finsemble to wait for authentication before proceeding. You
can create additional profiles, and call them using AuthenticationClient.beginAuthentication()
, such as when you have
a third-party component which requires OAuth.
Stage 1: With this configuration in place, Finsemble display the page at authorization_endpoint
as a modal when
it’s launched. The end user will log in to this modal. Upon successful login, the identity provider will redirect to
Finsemble’s built in OAuth component. This component will validate and process the response for the next stage.
Stage 2: Finsemble’s OAuth component will then make a post request to the URL you specified in
backchannel_endpoint
. This request will contain client_id
, grant_type
, code
and redirect_url
.
backchannel_endpoint
should point to a server page that you host which should add the client_secret
, and then
forward this information via a /token
request to your identity provider’s token_endpoint
. The results should be
returned back to the Finsemble request.
Finsemble will receive the results. It will process and publish the results as "credentials" via
FSBL.Clients.AuthenticationClient.publishAuthorization
. If the results include an id_token
then it will be decoded
and parsed. The parsed variables will be made available in the credentials object. If those results include a "sub
"
field, then that will be set as the Finsemble user. Otherwise, the access_token
will be set as the Finsemble user.
The application will start and your components can use the contents of the credentials object where needed.
Note: The client_id
and client_secret
should be encoded based on the specifications of your identity provider.
Some require that these be base64 encoded into an HTTP header. Some can accept them as post parameters.
Currently, credentials are available to all components.
During development, you can skip the server stage by including client_secret
in your authentication config. This can
make development easier but should not be done in production.
The redirect_uri
registered in the authentication
config must point to an oauthResponse.html page. The
oauthResponse.html response page can have HTML, CSS, and JavaScript for a consistent, branded experience for your
company. It will be useful to create handlers for window close and errors.
If the user attempts to close the window without completing the OAuth sign-in process, 1) they will not be authorized in Finsemble and 2) the application will take the action that you specify. In this example, when the end user closes the authentication window and the user isn't logged in, we assume that the user failed to sign in via OAuth; therefore we shut down the entire app. Otherwise, if we were just using the window to authenticate a client, we close the window but keep Finsemble running.
Example:
closeThisWindow() {
FSBL.Clients.AuthenticationClient.getCurrentCredentials((err, credentials) => {
if (!credentials) {
FSBL.shutdownApplication();
} else {
FSBL.Clients.WindowClient.getCurrentWindow().close();
}
});
}
Your desired user experience may vary. For example, instead of executing FSBL.shutdownApplication();
, you may choose
to prompt the end user to attempt the authentication process again.
There is one specific FSBL method that must be used to publish the user's authorization for the application:
FSBL.Clients.AuthenticationClient.completeOAUTH()
. Here is an example:
FSBL.Clients.AuthenticationClient.completeOAUTH(null, null, (err, response) => {
if (err) {
showError(err);
} else {
const credentials = response;
runBusinessLogic(credentials, () => {
// In the special case of "startup", we publish the authorization, which releases Finsemble and lets it start.
if (credentials.config.name === "startup") {
// "sub" is the user ID provided by the OpenID provider. Not all OpenID providers support this. If not supported, then just set the user to the access token returned by the OAuth service.
const user = credentials.sub;
if (!user) {
user = credentials.access_token;
}
// Finish the authentication process
FSBL.Clients.RouterClient.transmit("AuthenticationService.authorization", {
user: user,
credentials: credentials,
});
}
closeThisWindow();
});
}
});
Note that showError
, runBusinessLogic
, and closeThisWindow
are not FSBL methods—they are functions you may
write to streamline the handler code. For example, you may need runBusinessLogic
to do more work than only publish the
end user's authorization by coupling the workflow to additional services.
The remaining piece in the workflow is the backchannel endpoint registered in the authentication
config. This will
point to your backend service that will use your OAuth secret token to fulfill the OAuth request to the identity
provider. The logic needed by this endpoint is dependent upon your provider.
Note: Remember, your OAuth secret token must remain secured in a server-side application and never exposed in JavaScript.
OAuth can be used to access third-party resources. In this scenario, the end user is presented with login credentials
from the third party. Once authenticated through a series of redirects, the client application is provided with an
access_token
which it can use to access the third-party resources.
Create an OAuth2 profile as described above but with a specific name. You can then use
AuthenticationClient#beginAuthentication
to kick off the process of retrieving an access_token
. When that process is
complete, the callback will be returned with the access_token
and other credentials. See
AuthenticationClient#completeOAUTH for implementation details.
The Authentication Client documentation provides additional context.
Authentication is empowered by dynamic configuration which provides end users the applications and workspace layouts to which they are entitled.
For more information on how Finsemble handles start-up, check out Finsemble's Lifecycle Events.
The following sources are useful as references for the authentication process:
This document describes the browser authentication protocols in more detail: