Authentication

By default, Finsemble applications do not require authentication (i.e., user sign-on), but hooks are available for you to add an authentication component to match your specific application's needs. The Authentication Client API and underlying Authentication Service allow developers to add a custom authentication component which obliges end users to authenticate before proceeding. This API does not define a specific authentication protocol. It deals strictly with integration of authentication into the UX.

Finsemble itself is agnostic about which type of authentication you may choose to implement—the only requirement is to pass back to Finsemble the results using publishAuthorization(). If your authentication model requires handing control off to an external authentication server, then the "redirect page" you've specified to the external server must be the one to invoke publishAuthorization. Finsemble itself doesn't define what happens on authentication failure; it is up to the authentication component to determine how to handle failures (e.g., retry, terminate application, display error). Finsemble will simply not proceed with its start-up sequence until a successful authentication.

First, to define our terms:

  • Authentication - Checking an end user’s credentials to allow access to a system

  • Authorization - Asking another system whether an end user has been authenticated

Enabling authentication

You can have your users log in to Finsemble using a user name by password by using an authentication profile.

To do this, configure an authentication profile in your manifest-local.json. Add "startup" under "finsemble" and give it an adapter of "PASSWORD":

    "authentication": {
        "startup": {
             "adapter": "PASSWORD",
             "component": "defaultAuthentication"
         }
     }

The specified component will launch at start. You can use the existing defaultAuthentication component, which asks for username and password, or you can write your own. You must code that component to collect credentials and authenticate against your back end. Finsemble will block access to the overall application until your authentication dialog completes the process by calling AuthenticationClient.publishAuthorization. Once that function is called, the start-up process resumes.

Note: That authentication component can be spawned visible or hidden. If authentication is automated, keep the authentication component hidden by setting window.options.autoshow = "false" in its configuration.

The form of credentials required would typically be username/password, but this could be coded in any way that meets your security requirements. For instance, you can use SAML, HTTP authentication (Digest, NTLM, Negotiate, etc.), two-factor authentication, or any other schema.


The authentication start-up sequence

When a Finsemble application with authentication enabled launches, the following authentication sequence occurs:

  1. Finsemble checks for a "startup" profile. The "startup" profile tells Finsemble which authentication component to look for. This happens early in the startup process (before the Workspace Service starts).

  2. The Authentication Service spawns the authentication component and waits for a response.

  3. The spawned authentication component runs developer code to authenticate the user. When complete, the developer invokes FSBL.Clients.AuthenticationClient.publishAuthorization(username, credentials) with the authenticated username and credentials. This sets the user name for the current user. Credentials can be any object. The credentials will be available to Finsemble components that need access to it. Note that 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 invoke publishAuthorization.

  4. Upon receiving the authentication response from the component (i.e., when publishAuthorization is invoked), the service publishes the authenticated username and credentials to all listeners on SubPub topic "Authorization" (see below).

Any Finsemble component or service can be an "authorization listener" to receive the authorization results by using the following SubPub subscription:

	RouterClient.subscribe("Authorization", function (err, response) {
		// do something with auth data contained in "response.data"
	} );

Note: Once you know who the user is, you can use ConfigClient.processAndSet()to customise the configuration, e.g., populate a user's advanced app launcher, make certain workspaces available, provide credentials for certain applications, etc.

Using authentication credentials with a storage adapter

In order to use credentials from the Authentication Client with a storage adapter, you'll need to configure your Storage Adapter. Check out the Storing Data tutorial for more information.


Authorization through an external authentication website

OAuth 2.0/OpenID

With OAuth, applications (such as your Finsemble smart desktop) 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-built-in/components/authentication/oauthResponse.html",

// Optionally specify which component to use to spawn this profile
"component": "defaultAuthentication"
}

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.

OAuth response example

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.

Necessary Finsemble handler

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.

Backchannel endpoint

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.

Using OAuth to retrieve an access_token for API usage

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.


Session-based authentication

Typically, session-based authentication uses a browser cookie to store a session token. Each request back to the webserver includes the token cookie, which the webserver checks against its internal map of authenticated sessions.

When you log in through the authentication dialog, any cookies will be set for the domain where that dialog is hosted. Other components that Finsemble accesses which are hosted on that domain will automatically use that cookie. Session authentication will thus behave the way you would expect a normal web page to behave.

If you need the same session cookie to be used across domains then you should create a distributed store to share the cookie across domains.

Automatically authenticate with the user's Windows login credentials

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, authentication is requested of your servers using Chromium's Negotiate protocol. Finsemble runs on top of Electron, which uses the Chromium engine and can make use of Chromium's protocols.

The request will be fulfilled by Chromium via either Microsoft Kerberos or NTLM.

Overview of Windows authentication

The process is as follows:

  1. A Finsemble component or service requests content from your server (e.g., an HTML file, WebSocket connection, or API call).

  2. 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 is a protocol that is used to select the Kerberos or NTLM protocols on Windows.

  3. Chromium automatically responds with the Windows credentials if the server is on a trusted network. If your server is on the same LAN (same IP network/submask), or if your server is configured as a trusted server, then credentials will be sent (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.

  4. 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.

  5. Subsequent components connecting to this server will get their content immediately because Chromium will return valid credentials for any window that connects to the same, already-authorized, domain.

Example of Windows authentication

For most use cases, you will wish to capture the user’s details and credentials or session token. This will allow you to customize their experience via Dynamic Configuration and store their data via the Storage Service.

For security reasons, the user’s credentials (used by Chromium to perform authentication) are not automatically made available through JavaScript. Therefore, you will need to implement your own custom API call on your web server infrastructure to pass users' credential or session token back to Finsemble.

To use the returned credentials, implement a basic Finsemble component. Code it to initiate the API call and publish the response with AuthenticationClient.publishAuthorization().

Default Authentication component - You can adapt the Default Authentication component (found at src-built-in/components/authentication/defaultAuthentication.html) in the seed project to use the returned credentials. Just replace the fields and login button with an automated call to your API.

To enable your authentication component, configure an authentication profile in your manifest file ( /configs/application/manifest-local.json for local development). Add the following as a property of "finsemble":

   "authentication": {
        "startup": {
             "adapter": "PASSWORD",
             "component": "defaultAuthentication"
         }
     }

Replace defaultAuthentication with the name of your customized authentication component.

Note: If all you need to simply authenticate requests to resources on your network, then you can turn off the Authentication Service (do not create a "startup" authentication profile in your config). It isn't needed.

Display a "Logging in" message

A visible window can be used to provide a "Logging in..." message to users during the authentication process. Hide it afterwards by using WindowClient.close().

Alternatively, you can hide an authentication component's window throughout the authentication process by setting window.options.autoShow = false in its component configuration.

Use Dynamic Configuration

If you wish to customize each user’s experience via Dynamic Configuration, return their individual configuration via the Authentication API call you implemented. Use ConfigClient.processAndSet() to implement the desired behavior before calling AuthenticationClient.publishAuthorization() to continue the Finsemble start-up process.


check   Finsemble is agnostic about which type of authentication you may choose to implement, but comes with hooks for you to add whatever authentication component best fits your organization's needs.
 

Further reading

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: