Authentication
Authenticating users, whether on start-up or during use, is an essential step for most applications. Single sign-on (SSO) allows a user to sign in to multiple independent apps by using a single set of credentials, which can greatly improve a user's experience of using the smart desktop.
With Finsemble you can use any authentication system in your apps that you can use in a browser or native application, including SAML, OAuth and Integrated Windows Auth.
Finsemble also has built-in support for using an authentication system to:
- Control access to the smart desktop,
- Identify the user (for personalizing the desktop configuration and experience),
- Establish an auth session at startup (typically via an SSO system) so that apps don't need to present a login screen when launched,
- Retrieve and distribute credentials for other systems, such as access keys for use with an API platform.
Authentication support is achieved by providing the ability to launch an app (which usually presents a UI) for authenticating end users, a "boot stage" for performing authentication related tasks on startup (such as fetching entitlements or dynamically generating config), and APIs for sharing user credentials among cross-origin components.
Authenticating at startup
Authentication at startup involves:
- Finsemble's Authentication Client API
- Finsemble's Authentication Service
- Authentication components (that you might need to build or customize)
Finsemble's boot sequence runs an authentication stage before it loads any apps or workspaces. During this stage you can run authentication or initialization related code. For example, you can run code that presents your users with a password prompt. Or you can run code that performs tasks invisibly.
When a smart desktop launches, this authentication sequence occurs:
- At Finsemble's authentication boot stage, the Authentication Service checks whether
finsemble.authentication.startup
is configured. If it is, the Authentication service executes steps 2, 3 & 4. - 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 must authenticate the user, perform any additional initialization or entitlement related work, and then call
publishAuthorization(username, credentials)
. - Finsemble's Authentication Service publishes the username and the credentials you shared to all listeners on the PubSub topic
Authorization
. Credentials are also available at any time by callinggetCurrentCredentials()
.
All apps running in Finsemble have access to credentials shared via the Authorization
PubSub topic or publishAuthorization()
/ getCurrentCredentials()
. If you need to limit access to the credentials, use another mechanism to share them, such as a Router PubSub topic with subscribe and publish callbacks or LocalStorage (limited to apps running on the same origin as your Authentication component).
Enabling authentication at startup
To enable authentication at startup you must configure a startup
authentication profile by making config entries. You can add an authentication
element at the top level of Finsemble's config (public/configs/application/config.json). Adding a startup
entry to it causes Finsemble to run the authentication boot stage.
Example: Configure Finsemble to launch a component to handle authentication:
"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 topic.
component
- the name of the Finsemble component (in the components
element in /public/configs/application/config.json) that collects login and password or otherwise performs authentication (e.g. the 'negotiate' protocol for Integrated Windows Auth). You can fully customize your component. The only requirement is that it calls FSBL.Clients.AuthenticationClient.publishAuthorization when complete.
adapter
- Finsemble's authentication service uses this parameter to decide which authentication adapter to use. Finsemble currently supports:
- PASSWORD - simple adapter that launches a custom component app to handle authentication and awaits an API call to complete the process.
- OAUTH - adapter, provided for convenience, that supports the OAuth2 model. You can use it without a custom authentication component.
The Finsemble CLI can automatically add Finsemble's Authentication component template and the required config to your project. To do so, run: yarn finsemble template Authentication
See Building custom authentication components later in this topic for more details.
The three most common approaches to authentication are:
- Custom authentication components (that is, collecting username and password and performing session-based authentication and/or SAML)
- Automatic login with Windows credentials (negotiate protocol)
- OAuth/OpenID Connect (identity provider)
Go directly to the section that matches your firm's authentication practices.
Authenticating at runtime
In addition to using auth at startup, authentication can be triggered at runtime, for example to impersonate another user for support purposes or to authenticate for access to third-party resources that are not always needed.
To implement an authentication operation at runtime, create an authentication profile as described earlier but with a specific name (that is, name it something other than startup, for example otherResource
):
"authentication": {
"startup": {
...
},
"otherResource": {
"component": "Authentication",
"adapter": "PASSWORD",
}
}
You can then use AuthenticationClient.beginAuthentication({profile: "otherResource"})
to kick off the process with your specified authentication component. When that process is complete call AuthenticationClient.publishAuthorization()
to end the authentication process and share any credentials retrieved with apps running in your smart desktop. If you don't wish to make the new credentials available to all applications use another appropriate method of communication.
Building custom authentication components
In your Finsemble project, you can generate a template for a custom authentication component (built with React) by running:
yarn finsemble template Authentication
Finsemble generates the template in the /src/Authentication folder of your project. It then adds a matching configuration for the component to /public/configs/application/config.json as finsemble.components.Authentication
. You can use this template component as a starting point or you can build your own. Finally, the process adds a top level finsemble.authentication.startup
entry that points to the new component.
When using the "PASSWORD" adapter, Finsemble expects your component to fully handle the process of authenticating your users. Your authentication component must 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, such as AJAX, a Websocket, and so on.
When your component has successfully completed authentication, you must call FSBL.Clients.AuthenticationClient.publishAuthorization(user, credentials)
so that Finsemble's boot sequence can resume. Here are the parameters:
user
- a string that identifies the user. Finsemble's Storage Service passes it to storage adapters for use in generated keys or data tables to keep stored data for different users separate. This is particularly important if you support multiple user logins to the same computer and use the built-in indexedDB storage adapter.credentials
- an object that can contain any useful values, such as access keys or user ids. The credentials object is publicly available to all Finsemble components. See later in this topic for information about subscribing and retrieving credentials.
Never rely on HTML components to store sensitive data. Any technically savvy user can easily access browser data. A user might use dev 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. Use 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.
In cases where your authentication model requires handing control off to an authentication server (such as OAuth 2.0), make sure the redirect page from the authentication server calls publishAuthorization()
.
If a user clicks the Close icon in the authentication component template, Finsemble shuts down. This is because the generated authentication template is most commonly used for authentication at startup, and if authentication is not completed, Finsemble cannot continue the startup process. Custom authentication components used at startup should retain this feature. However, authentication components used at runtime might only need to close themselves.
Building an authentication component using React
Finsemble's example authentication component is built using the Finsemble React UI. The only important difference is that instead of calling publishAuthorization()
on the AuthenticationClient
directly, this component uses the method provided by Finsemble's useAuth()
React hook, which makes the calls for you.
Invisible authentication
Your desktop and applications might be able to authenticate users automatically (such as with NTLM or negotiate protocols) but you might still need to run code during Finsemble's authentication boot stage. Common use cases include fetching user entitlements or setting "dynamic config" (see later).
You can run a Finsemble Authentication component invisibly by adding this setting to its config:
window.options.autoshow = false;
If your authentication process takes more than a few seconds, we recommend that you avoid using invisible authentication. Instead, create a visible authentication component that simply provides a progress bar or some other 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 smart desktop if it fails to start.
Dynamic config
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 after the user has been identified. Typical environmental configs that are set dynamically include:
- applications - You can dynamically configure the list of apps available for launch based on the user's identity and their entitlements. Often, a back end server that can output Finsemble compatible config generates this list.
- workspaces - A firm can decide to provide example workspaces to its users. If the list of workspaces is keyed to user type, use the
authentication
boot stage to add those workspaces to the configuration.
Finsemble's authentication
boot stage provides a natural point to generate and set dynamic config. Typically, you do this by making calls to processAndSet()
directly in your authentication component. For more info, see the Dynamic Configuration tutorial.
Session tokens and cookies
Typical web session-based authentication uses a browser cookie to store a session token. With each request, the browser passes the token 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, the cookie is automatically available to any other component or service that is hosted on the same origin (domain, protocol and port). In other words, cookies in Finsemble behave exactly the same as they behave in a web browser. Note that origins must match exactly! For example, http://yourdomain.com
and https://yourdomain.com
are different origins.
If you need to share cookies among components that are hosted on different origins, you can share them inside Finsemble's FSBL.Clients.publishAuthorization(...)
API call or via a [Router PubSub topic](../how-finsemble-works/TheRouter/#third-supported-model-pubsub].
Automatic authentication with Windows login credentials
If your organization has an authenticated network environment, you can use Integrated Windows Authentication (IWA) to automatically authenticate apps running in Finsemble by using the user’s Windows login credentials.When using Windows credentials on a trusted network, Finsemble itself might not need to play a role in authentication. Rather, your servers can interact directly with apps running in Chromium via the negotiate protocol (Finsemble web components are displayed by the Chromium engine). Chromium and your server invisibly validate the request via either Microsoft Kerberos or NTLM.
Configuring Finsemble For Windows Authentication
When your network is configured for automatic authentication through Windows and you don't need to control access to the smart desktop or customize configuration, you don't need to enable authentication at startup in Finsemble. Your smart desktop boots without prompting the user to log in.
However, there are still some considerations even when authentication is automatic:
- You might still need to perform authentication-related tasks during boot. In this case, you should use the "invisible authentication" techniques described earlier in this topic, completing authentication related tasks in an invisible Authentication Component that you create.
- You might still need to fetch credentials. When you use automatic authentication on Windows, your user’s credentials are not accessible in the browser. Fetching credentials requires implementing a custom authenticated API call in your web server's infrastructure. You can do this inside an invisible Authentication Component as described earlier. You should authenticate the user and return details of the username and any other required credentials in the response for the authentication component to use.
When using invisible authentication components you must still call publishAuthorization()
for the boot sequence to proceed.
How Windows authentication works step-by-step
- A Finsemble application or service requests content from your server, for example an HTML file, WebSocket connection, or API call.
- Your server responds with a request to authenticate with the Negotiate protocol specified. This is similar to how HTTP basic or digest authorization work, but Negotiate doesn’t 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 either on the same LAN (same IP network/submask), or it is configured as a trusted server within Windows. This works the same way as it does in a web browser. Usually, your network administrator configures your end users' machines to trust your server through group policy management.
- Chromium automatically responds 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 server delivers the content; otherwise the app window receives a 503 Unauthorized message.
- Subsequent applications connecting to this server receive their content immediately because Chromium returns valid credentials for any window that connects to the same, already-authorized, domain.
OAuth
OAuth 2.0/OpenID Connect
Using OAuth, your smart desktop can use an external identity provider to perform authentication and authorize your applications. On the web, firms such as Google and Facebook serve as identity providers. Your firm can also have its own identity service that supports OAuth. OAuth is made possible through a shared secret between the identity provider and the application. Recall that OAuth doesn't directly use passwords. It relies on authorization tokens instead, and uses shared secrets to prevent request forgery. Behind the scenes, the request is redirected to the authentication provider's login page and then back to your application which processes data returned and uses it to retrieve an access token and any other details requested, such as a refresh token or details from the user profile. (More info about how OAuth works.)
OAuth has 4 main flows:
- Implicit flow. The client app asks the user if they want to link their login to a public authorization endpoint such as those provided by Google or Facebook. When an app sends an authorization request to the authorization endpoint, the user is redirected to a dialog from that endpoint and asked to confirm the permissions requested. Next, the authorization endpoint redirects the user back to the app and the client app receives an access token directly (encoded into the redirect URL). This flow used to be popular but its use is less frequent now because of security concerns.
- Authorization code. Also known as the server flow, this flow is similar to implicit flow, except instead of passing the access token in the URL, the OAuth server returns an authorization code. The app can then exchange this code , through a separate backchannel to the auth provider, to retrieve an access token. As the app’s webserver (the backchannel) retrieves the access token using a client secret rather than sending it directly through the user's browser, this flow is more secure than the implicit flow.
- Resource Owner Password Credentials. This flow is again similar to the implicit flow, with the exception that the client app collects the username and password itself and sends them on, rather than redirecting to a dialog from the authorization endpoint. Use this flow only with highly trusted clients because it exposes the user credentials.
- Client credentials. In this flow, no user is involved. The backend server directly authenticates with the OAuth server and gets a token. The token is not available for a user, only for publicly accessible info.
Finsemble's OAuth adapter in the Authentication service works with the Implicit and Authorization flows. To implement any other flow, follow the instructions for implementing a custom auth component.
The Implicit flow has a security vulnerability because an astute adversary who can see the URL of the redirect can grab it and use it to impersonate the legitimate user. You must assess your specific environment and your risk tolerance before you decide to implement this flow.
Authorization Code Flow is also vulnerable to the interception of authorization code by a third party. The Authorization Code Flow with Proof Key for Code Exchange (or PKCE, pronounced “pixie”), described in OAuth 2.0 RFC 7636, is an extension to the Authorization Code flow that helps to mitigate this kind of attack.
Finsemble currently doesn’t support PKCE, but it is on our roadmap. In the meantime, if you need to use PKCE implement a custom auth component.
To configure authentication with OAuth 2.0 via the Finsemble OAuth adapter, set an authentication profile. In a moment we’ll take a look at how to configure an authentication profile for the Authorization code flow or Implicit flow
OAuth flow: Authorization code grant
Add this configuration to the finsemble.authentication
element as described earlier:
"startup": {
"adapter": "OAUTH2",
// The url of your identity provider’s login page.
// You can add more query string parameters if necessary.
// For example, MS ADFS sometimes requires a non-standard "resource" parameter.
// Another example is 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 is provided by your identity provider
"client_id": "XYZABC",
// Your OAuth server side script
"backchannel_endpoint": "https://yoursite/authenticate",
// Your redirect_uri must 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/OauthResponse/index.html",
// Optionally specify which component to use to spawn this profile
"component": "Authentication"
}
If you’re familiar with OAuth, Finsemble is automatically generating these fields: state
, response_type
, and redirect_uri
.
startup
is a special authentication profile that 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 displays the page at authorization_endpoint
as a modal when it’s launched. The end user logs in to this modal. Upon successful login, the identity provider redirects to Finsemble’s built-in OAuth component, specified via the redirect_uri
. This component validates and processes the response for the next stage.
The URL redirected to will contain client_id
, grant_type
, code
and redirect_url
. The code
is the authorization code that your OAuth identity provider returns. This is instead of the token returned in the implicit flow. This code can be exchanged for an access token in the next stage.
Stage 2: Finsemble’s OAuth component makes a post request to the URL you specified in backchannel_endpoint
. The backchannel_endpoint
must point to a server page that you host, which will receive the form data from the post request, add the client_secret
, and then forward it on to your identify provider's token_endpoint
. The results returned by the identity provider must be forwarded on as the response to the original post request.
You must encode the client_id
and client_secret
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.
During development, you can skip the server stage by including client_secret
in your authentication config. This can make development easier but make sure you remove it in production.
When Finsemble receives the results, it processes and publishes them as "credentials" via FSBL.Clients.AuthenticationClient.publishAuthorization
. If the results include an id_token
, it is decoded and parsed. The parsed variables become available in the credentials object. If those results include a "sub"
field, that is set as the Finsemble user. Otherwise, the access_token
is set as the Finsemble user.
The application starts and your components can use the contents of the credentials object where needed.
Currently, Finsemble makes the received credentials available to all applications running in it. If you need to control access to credentials, implement a custom auth component instead.
OAuth flow: Implicit grant
The Implicit grant is a simplified OAuth flow where the access token is returned immediately without an extra authorization code exchange step. Be aware that the implicit grant is less secure than the other options.
Add this configuration to the finsemble.authentication
element as described earlier:
"startup": {
"adapter": "OAUTH2",
// The url of your identity provider’s login page.
//You can add more query string parameters to this if necessary.
// For example, MS ADFS sometimes requires a non-standard "resource" parameter.
// Another example is 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 is provided by your identity provider
"client_id": "XYZABC",
// Your redirect_uri must 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/OauthResponse/index.html",
// Specify the component to use to spawn this profile
"component": "Authentication"
}
In this flow, a backchannel_endpoint
and the client_secret
that it protects are not used to authenticate and receive a token (resulting in this flow being less secure).
OAuth response example
The redirect_uri
registered in the authentication config must point to an Oauth response page. This page can have HTML and CSS for a consistent, branded experience for your company. It is useful to create handlers for window close and errors.
You can add Finsemble's default Oauth response page by running the command yarn finsemble template OauthResponse
, or you can create your own.
If the user tries to close the window without completing the OAuth sign-in process, they won’t be authorized in Finsemble, and 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, so we shut down the entire smart desktop. 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.System.exit();
} else {
FSBL.Clients.WindowClient.close();
}
});
}
The user experience you want may vary. For example, instead of executing FSBL.System.exit()
, you can choose to prompt the end user to try the authentication process again.
Necessary Finsemble handler
There is one specific FSBL method that you must use 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 yours doesn’t, set the user to the access token that the OAuth service returned or retrieve the username by another means.
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();
});
}
});
Notice that showError
, runBusinessLogic
, and closeThisWindow
are not FSBL methods—they are functions you can implement in your authentication component to streamline the handler code. For example, you might 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 must point to your backend service that uses your OAuth client secret to fulfill the OAuth request to the identity provider. The logic needed by this endpoint depends on your provider.
Your OAuth client secret must remain secured in a server-side application. ever expose it in JavaScript.
Using OAuth to retrieve an access_token for API usage
You can use OAuth to access third-party resources by authenticating at runtime. Create an OAuth2 profile as described earlier 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 returns with the access_token
and other credentials.
Subscribing to and retrieving credentials
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*/;
// ..
});
See also
The Authentication Client documentation provides additional context.
Authentication enables dynamic configuration to apply entitlements, providing end users with the apps and workspace layouts that you want them to have access to.
These sources are useful as references for the authentication process:
- https://connect2id.com/learn/openid-connect
- https://developers.google.com/identity/protocols/OpenIDConnect
- http://blog.scottlogic.com/2015/03/09/OAUTH2-Authentication-with-ADFS-3.0.html
This document describes the browser authentication protocols in more detail:
More info about how OAuth works