React SPA using Assisted Token Flow

React SPA using Assisted Token Flow

Code Examples / spa-integration

Overview

An example React application capable of acquiring an access token from the Curity Identity Server using the assisted token flow. The example code contains a Single Page Application for acquiring the token and an example API which can be called from the SPA using the access token.

Best Practice Update

For best results and strongest security it is no longer recommended to implement OAuth for SPAs solely in Javascript. See our resources on the Token Handler Pattern, for using only the latest secure cookies in the browser without losing any of the benefits of an SPA architecture.

Configure the Identity Server

In the Curity Identity Server, create an OAuth client with the following details, and also ensure that at least one working authenticator is configured against the client:

  • Client ID: client-assisted-example
  • Capabilities: Assisted Token Flow + Implicit Flow
  • Allowed Origins: http://localhost:3000
  • Redirect URI: http://localhost:3000
  • OAuth + OpenID Settings / Scope: openid

Configure the SPA

The React SPA can be downloaded from the GitHub link on this page and will then run at http://localhost:3000 by default. Edit the constants.js file in the SPA and set details to match your environment:

export const ISSUER = "https://localhost:8443/";
export const CLIENT_ID = "client-assisted-example";
export const API_URL = "http://127.0.0.1:8100";
export const AUTH_SERVER_ORIGIN = "http://127.0.0.1:8100";
export const OPENID_CONFIGURATION_URL = 'oauth/v2/oauth-anonymous/.well-known/openid-configuration'.

The SPA will use the Assisted Token Endpoint from the Curity Identity Server, and will download a library from a URL such as this at runtime:

  • https://localhost:8443/oauth/v2/oauth-assisted-token/resources/js/assisted-token.min.js

The SPA will then use the window.curity.token.assistant Javascript object to perform OAuth operations.

Run the SPA

Now that the setup is complete you can run npm install then npm start to browse to the SPA at http://localhost:3000. When the page loads, it makes an initial full screen redirect to the Identity Server in order to act as a user gesture, so that SSO cookies are then persisted by the browser. This is done in the App.js source file in the checkAuthorization method:

window.location.href = this.config.authorization_endpoint +
    `?response_type=id_token&scope=openid&client_id=${CLIENT_ID}` +
    `&redirect_uri=${window.origin}&prompt=none&nonce=${nonce}`;

Perform a Login

Next click the Login button to trigger sign in via an iframe, to prompt the end user to authenticate. The sample then uses secure iframing to invoke a login window without disrupting the current state within the SPA:

React Sign In

Triggering the login request only requires a single line of code when using the assisted token library, after which tokens are returned to the SPA:

this.tokenAssistant.loginIfRequired().then((msg) => {
    this.userToken = this.tokenAssistant.getAuthHeader();
}

Once login is complete, an authenticated session begins, and this is represented by an SSO cookie that the Identity Server issues to the SPA. This cookie is used by the assisted token library to retrieve tokens silently while the user’s authenticated session remains valid.

Call an API from the React SPA

After user sign in, the code sample provides a minimal screen as follows, to show the token returned from login and sent to the sample’s API:

React API Calls

The sample uses the popular axios library to call the API and note that the assisted token library manages overriding the XmlHttpRequest and adding the access token to the Authorization header in the correct manner.

callApi() {
    const isUserAuthenticated = this.tokenAssistant.isAuthenticated() && !this.tokenAssistant.isExpired();
    if (isUserAuthenticated) {
        axios.get(API_URL + '/api').then(response => {
            this.apiResponse = response.data.data;
    }
    else {
        this.tokenAssistant.loginIfRequired().then((token) => {
            this.callApi();
    }

The assisted token library also helps to deal with access token expiry conditions. If a token is expired or a 401 response is received from the API, the SPA can call loginIfRequired again to attempt to silently refresh the token, or to prompt the user to re-authenticate if the SSO cookie has expired.

API Cross Origin Request Handling

The React code example includes a simple NodeJS API which runs at http://localhost:8100 and is implemented in the server.js file. The API operation simply echoes the token back to the UI for display, but the API code shows how to set API response headers so that the browser is allowed to make cross origin calls to the API:

var origin = request.headers["origin"];
if (request.method === 'OPTIONS') {
    responseHeaders['Access-Control-Allow-Origin'] = origin;
    responseHeaders['Access-Control-Allow-Methods'] = "GET, HEAD, OPTIONS";
    responseHeaders['Access-Control-Allow-Headers'] = 'Authorization, WWW-Authenticate, Content-Type';
    responseHeaders["Access-Control-Allow-Credentials"] = "true";
    responseHeaders["Allow"] = "GET, HEAD, OPTIONS";
    response.writeHead(200, responseHeaders);
    response.end();
}
else {
    responseHeaders["Access-Control-Allow-Origin"] = origin;
    responseHeaders["Access-Control-Allow-Credentials"] = "true";
}

Lifecycle Events

The React SPA is only a basic sample to show how to fit the Assisted Token flow into a React coding style. For further details on dealing with lifecycle events, see the Javascript Assisted Token Sample, which covers aspects such as token refresh and multi tab browsing.

Conclusion

The Assisted Token Flow can be easily integrated into a React SPA, and adapted to the React coding style. For further programming details, see the Assisted Token Library API Reference.

Keep up with our latest articles and how-tos RSS feeds.