Assisted Token JavaScript API

Overview of assisted-token.js

The Assisted token JavaScript API is a library that takes care of the complete communication with Curity. It will call the OAuth server to load a new Access Token, and if the user is authenticated invisibly update the browser memory with the token. It can patch the jQuery library so that each Ajax request is prepared with the access token. Note, in order to do CORS (Cross-Origin Resource Sharing) the API server needs to either support CORS or use a proxy library of some sort.

Preparation

The library is served by the Assisted Token endpoint in Curity. To enable this endpoint, configure it on the OAuth profile that is used and add it to the services that should expose it.

Once added the endpoint is configured with a base path (example: /oauth/v2/assisted-token). This path serves the Assisted Token-flow and is also the base for the library. If the endpoint is configured with the path /oauth/v2/assisted-token then the library is found under /oauth/v2/assisted-token/resources/js/assisted-token.min.js

Note

The assisted-token JavaScript library is located under the sub-path resources/js/assisted-token.min.js of the assisted-token endpoint.

Framing and security

The Assisted Token-flow is built around the ability to use iframing. Curity supports secure iframing that is configurable per client. The sites that are allowed to frame the Assisted Token-flow are configured through the client’s allowed-origins settings.

If there are multiple pages that must be able to frame the Assisted Token-flow, then multiple values, or possibly a wildcard allowed-origin value can be configured. In that case, it is required to include the for_origin-parameter when making a request to the Assisted Token-endpoint on the server (i.e. the request to /oauth/v2/assisted-token).

Please see the section on Client Framability to learn more about framability.

Third party scripts and security

Any code running on the client (such as the one handling assisted token) can be inspected or even modified by other scripts running on the same page. While certain protections can be (and are) utilized to hinder this, it remains imperative that third party scripts and their providers are trusted.

  • If you need to run third party scripts, see if you can avoid them at least on sensitive pages like those meant for authentication.
  • Perform at least a basic review of third party scripts running on your site.
  • Host any third party libraries and scripts on your own servers. Remember that anything hosted outside of your control can be changed at any time. This includes changes without malicious intent that could still interfere with your own scripts in unexpected ways.
  • If self-hosting is impossible or undesirable, investigate using the subresource integrity attribute for external scripts to make sure only code you have vetted is included in your pages.

Method summary of assisted-token.js

Retrieve tokens

  • fetchTokens() : Calls the oauth server to retrieve access tokens. Returns a CommonJS promise. No visible feedback
  • login() : Uses a visible iframe to show the login screen to allow user to enter credential
  • loginIfRequired() : First attempts to use a hidden iframe to authenticate the user using SSO, but if no SSO session was available, ut will revert to login(), showing a visible popover frame.

After initial tokens have been fetched

  • isAuthenticated() : returns the current token status (based on expiration given in the first request)
  • isExpired() : returns false as long as the token is valid
  • getAuthHeader() : returns a ready string that can be placed in the Authorization header in a HTTP request
  • prepareXHR(xhr) : takes an XMLHttpRequest and inserts the Authorization header into it.
  • prepareJQuery(jquery) : insert pre-send filter on jQuery object
  • getAdditionalData() : returns a dictionary containing any additional data about the currently issued tokens
  • revokeTokens() : Revokes the current access tokens.
  • logout() : Revokes the current access tokens, and terminates the active SSO Session.

Settings summary

To initialize the library a settings dictionary object is used with the following options

  • clientId : The OAuth client that should be used.
  • scope : An array of scopes to be requested
  • autoPrepareJqueryAjaxForOrigins : list of origins for which a pre-send filter will be applied to jQuery with the token, if present.
  • iframeSettings : Specific attributes that will be added to the iframe object used for login popover.
  • backdropSettings : Specific attributes that will be added to the backdrop object.
  • closeButtonSettings : Specific attributes that will be added to the close button object and the wrapper of the iframe.
  • for_origin : The value that is used to indicate the URL of the framing page, in case multiple allowed-origins are configured

Constants summary

When the assisted-token.js file is loaded, the following constants are available:

  • curity.token.constants.authentication_endpoint : The URI of the authentication endpoint, including the service provider ID (e.g., /dev/authn/authenticate?serviceProviderId=oauth-test)
  • curity.token.constants.hostUrl : The base URL of the server (e.g., https://localhost:8443)
  • curity.token.constants.logout_endpoint : The URI of the logout endpoint, including the service provider ID (e.g., /dev/authn/authenticate/logout?serviceProviderId=oauth-test)
  • curity.token.constants.revoke_endpoint : The URI of the assisted token revocation endpoint (e.g., /assisted-token/revoke)
  • curity.token.constants.token_endpoint : The assisted token endpoint URI (not to be confused with the regular OAuth token endpoint), such as /assisted-token

The library relies on the DOM to be loaded, so the script that initializes the library should be inside the <body>, or be run after the DOM is ready, i.e. inside jQuery.ready().

Example usage

The following section illustrates some example usages of the library.

Note

There are live examples that can be experimented with in the `<installation>/examples/clients/assisted-token-website`

Using existing SSO Session

This example does not let the user see anything, but assumes that the SSO session exists already that will auto-login the user when the authorize endpoint is reached. Commonly this happens with the user needed to authenticate in the first placed to reach the website at all.

<body>
...
<script>
        var settings = {
                clientId: "client-assisted-example",
                autoPrepareJqueryAjaxForOrigins: ['^/.*$'], //Inject tokens into jQuery ajax requests relative to where this file is hosted
        }

var assistant = curity.token.assistant(settings);

assistant.fetchTokens().then(function(){
                console.log("We are now logged in...");
                //Now get some data
                $.get(...);
        }).fail(function(err){
                console.log("Failed to login", err);
        });

Using fallback to popover iframe

<body>
...
<script>
        var settings = {
                clientId: "client-assisted-example",
                autoPrepareJqueryAjaxForOrigins: ['^/.*$'],
                iframeSettings: {
                        'width' : '320',
                        'height' : '600',
                        'style' : "z-index:100;position:absolute;top:20px;left:50%;margin-left:-160px;border:0;overflow:hidden;box-shadow: 0px 0px 10px #888888;"
                },
                backdropSettings: {
                        'visible' : 'static',
                        'class' : 'customBackdropClass',
                        'style' : "width:100%;height:100%;position:fixed;top:0;left:0;z-index:50;background:#8282ce;opacity: 0.4;filter: alpha(opacity=40);"
                }
        };

        var assistant = curity.token.assistant(settings);

        assistant.loginIfRequired().then(function(){
            console.log("We are now logged in...");
                //Now get some data
                $.get(...);
        }).fail(function(err){
                console.log("Failed to login", err);
        });

Testing if the library has valid tokens

Some times it’s useful to know if the library has valid tokens, and if not, fetch new ones.

...
var assistant = curity.token.assistant(settings)
...
if(!assistant.isAuthenticated() || !assistant.isExpired()){
        assistant.fetchTokens().then(...).fail(...);
}

Passing along the framing page

If a client configures multiple values for allowed-origins, the URL of the framing page must be provided. This can be done by including it in the settings:

<body>
    ...
    <script>
        var settings = {
            clientId: "client-assisted-example",
            autoPrepareJqueryAjaxForOrigins: ['^/.*$'],
            iframeSettings: {
                'width' : '320',
                'height' : '600',
                'style' : "z-index:100;position:absolute;top:20px;left:50%;margin-left:-160px;border:0;overflow:hidden;box-shadow: 0px 0px 10px #888888;"
            },
            for_origin: "https://main-page.example.com"
        }
        ...

This enables that the Assisted Token-flow can be executed from https://main-page.example.com. Note that it does require the client to have the value https://main-page.example.com configured for allowed-origins. Also note, that when the only configured value is https://main-page.example.com, it does not need to be explicitly provided.

Forced Authentication

This example enforces authentication even if there is an existing, valid SSO session.

<body>
...
<script>
        var settings = {
                clientId: "client-assisted-example",
                autoPrepareJqueryAjax: true, //default false
        }

        var assistant = curity.token.assistant(settings);

        assistant.login({
                force_auth: true //Enforcing authentication; ignoring any existing SSO session
        }).then(function(){
                console.log("We are now logged in...");
                //Now get some data
                $.get(...);
        }).fail(function(err){
                console.log("Failed to login", err);
        });
        ...

White-listing of API Origins

When tokens are automatically injected into jQuery Ajax requests it’s important to make sure those tokens aren’t sent to unintended remote origins. Even when such origins are trusted by the client, they might not be a trusted recipient of the token in question.

<body>
...
        <script>
                var settings = {
                        clientId: "client-assisted-example",
                        autoPrepareJqueryAjaxForOrigins: [
                                '^/.*$',                           // (1) Inject token in all requests relative to the origin of this file
                                '^https://example.com/api/*',      // (2) Inject token in all requests under 'https://example.com/api/'
                                '^https://example.com/api2/info$', // (3) Inject token in all requests to exactly 'https://example.com/api2/info'
                        ]
                };

                var assistant = curity.token.assistant(settings);
                ...
                $.get('/api').done(...);                              // Token injected; request matched in white-Listing #1
                $.get('https://example.com/api/').done(...);          // Token injected; request matched in white-Listing #2
                $.get('https://example.com/api/resource').done(...);  // Token injected; request matched in white-Listing #2
                $.get('https://example.com/api2/info').done(...);     // Token injected; request matched in white-Listing #3
                $.get('https://example.com/api2/resource').done(...); // Token not injected; request not matched in white-list
                $.get('https://evil.example.com/api/').done(...);     // Token not injected; request not matched in white-list
                ...
                assistant.prepareJQuery($, true);                 // Bypass white-listing
                $.get('https://evil.example.com/api/').done(...); // Token injected; white-list bypassed

Logout

This example shows how to revoke the current tokens and logout the active SSO session.

<body>
...
<script>
        var settings = {
                clientId: "client-assisted-example"
        }

        var assistant = curity.token.assistant(settings);

        assistant.logout().then(function(){
                console.log("We are now logged out");
        }).fail(function(err){
                console.log("Failed to logout", err);
        });
        ...

Third Party Cookies

The Assisted Token flow relies on integrating Curity’s authentication and token services into a JavaScript controlled application, both of which most likely run on their own DNS-domain. Having this separation may cause issues with modern browsers, that distinguish between requests to first parties and to third parties. An approach to overcome this being a problem, is described in the section Third Party Cookies.

When running both the example Assisted Token website as well as Curity from localhost, they will both be considered first party websites (they’re sourced from the same domain), so third party cookies are not an issue in that case. However when the example Assisted Token website is put on another domain, there are two things to do to enable preflighting to make Curity a first party:

1. Add the domain to the redirect url whitelist of the authentication service that is linked to the token service to which the Assisted Token endpoint belongs to

  1. uncomment the <script ...preflight.min.js"> tags in the assisted-token-website’s index.html.

JavaScript API Reference

settings

The settings object is used to initialize the library.

  • clientId: The OAuth client id of the website running this library
  • autoPrepareJqueryAjaxForOrigins: A list of RegExp() objects or strings representing a regular expression. For each endpoint matching a defined regexp, prepares the jQuery beforeSend with the token on the XHR objects authorization header.
  • iframeSettings: A dictionary object with attributes width, height, scrolling and style that will be applied on the visible iframes. It can also take targetElementId which should be an ID tag that the iframe will be mounted under. If not set the iframe will be mounted on the body tag.
  • backdropSettings: A dictionary object with attributes visible, backdropClass and style that will be applied on the backdrop object. Attribute visible is of type boolean or the string static. When set to static, clicking on the backdrop will not close the modal.
  • closeButtonSettings: A dictionary object with attributes visible, style, button, wrapperStyle and wrapperClass. A div element will wrap the iframe and be placed in the targetElementId (if configured in the iframeSettings), which will have its style and class as specified, and contain the html of the button attribute. When clicked, the button will close the modal.
authAndTokenParameters

A set of optional parameters that can be passed to token retrieval functions.

  • force_auth: Forces user authentication even if there already is an active session; effectively disabling SSO.
  • freshness: The maximum time, in seconds, that is allowed to pass since authentication was performed. If this value is exceeded, SSO is not performed, and the user must authenticate again.
  • scope: A string or a list of strings. The scope of the requested token.
  • acr: A string or a list of strings. The Authentication Context Class Reference(s) authentication should be performed with.
  • reuse: A boolean value, indicating whether a previously issued token should be re-issued, if one exists for the current session. Default: true.
curity.token.assistant(settings)

Creates a new assisted token assistant object that gets initialized with the server details

Arguments:
  • settings (settings) – A settings object with the init settings for the library
Returns:

A helper object that contains the initialized library.

assistant.login([params])

Opens a visible iframe and starts the authentication flow. This is used when the user is not assumed to be authenticated.

Arguments:
Returns:

A promise that will be resolved with a dictionary object with token and expiration.

assistant.fetchTokens([params])

Opens a hidden iframe and calls the OAuth servers assisted-token endpoint. It assumes an SSO session to be available

Arguments:
Returns:

A promise that will be resolved with a dictionary object with token and expiration, or an error object if rejected with a dictionary with an error_description field.

assistant.loginIfRequired([params])

First attempts to retrieve a token using a hidden frame, if this cannot be done, it retries using a visible frame to allow the user to enter its credentials and authenticate.

Arguments:
Returns:

A promise that will be resolved with a dictionary object with token and expiration, or an error object if rejected with a dictionary with an error_description field.

assistant.isAuthenticated()
Returns:True if the user is authenticated. This information is not fetched anew, but is the result of the last call to login, fetchTokens or loginIfRequired.
assistant.isExpired()
Returns:True if the token has expired.
assistant.getAuthHeader()
Returns:A string containing the token in the correct format to be put in the Authorization header for the HTTP request.
assistant.prepareXHR(xhr)

Patches the XMLHttpRequest object by adding the token to the Authorization header for the request.

Arguments:
  • xhr (XMLHttpRequest) – The XMLHttpRequest object that is about to be used.
assistant.prepareJQuery(jquery, ignoreWhitelist)

Patches jQuery by adding a beforeSend hook that makes sure that the Access token is available in the Authorization header.

Arguments:
  • jquery (jQuery) – The current jQuery object.
  • ignoreWhitelist (boolean) – Ignores any origins white-list defined via curity.token.assistant().
assistant.getAdditionalData()
Returns:a dictionary containing any additional data about the currently issued tokens
assistant.revokeTokens()

Revokes the current tokens.

assistant.logout()

Revokes the current tokens, and terminates the active SSO Session.