WebApps 2

oauth2

The oauth2 object contains utility methods to perform OAuth2 operations. Available in a server side context to retrieve the object.

import oauth2 from '@sitevision/api/server/oauth2';

Methods

oauth2.createLoginLink(oauth2Config, redirectUrl, scopes)

Creates a login link used when getting the first access token for the current user or when upgrading scopes for the user.

The function returns a login link (string) for a specific OAuth2 config (sv:oAuth2Config JCR Node) using an array of scopes (strings) and a given redirect url (string)

oauth2.getAccessTokenStatus(oauth2Config)

Gets the current status of the users access token. Token will be refreshed if necessary.

The function returns a status object for a specific OAuth2 config (sv:oAuth2Config JCR Node).

  • status.isValid - true or false
  • status.scopes - Array of scopes for the token

Example

import oauth2 from '@sitevision/api/server/oauth2';
import appData from '@sitevision/api/server/appData';

const oAuth2Config = appData.getNode('oauth2-config');

const getLoginLink = () => {
   // Creates a login link used when getting the first access token
   // for the current user or when upgrading scopes for the user.
   return oauth2.createLoginLink(
      oAuth2Config, // A sv:oAuth2Configuration object
      '/', // Where the user should be redirected after successful login
      ['profile', 'email', 'CustomScope.Read'] // Array of scopes needed
   );
};

const getAccessTokenStatus = () => {
   // Gets the current status of the users access token.
   // Token will be refreshed if necessary
   return oauth2.getAccessTokenStatus(oAuth2Config);
   // status.isValid - true or false
   // status.scopes - Array of scopes for the token
};

The scopes in the loginLink will be a combination of the scopes requested by the app and the default scopes added to the sv:oAuth2Configuration

The list of scopes might not be the exact same values as requested. The Resource Server isn't even required to return any scopes at all.

Handling Requester failure

The most important use case is to create a loginLink where the user can be redirected. One frequent cause of request failure is the lack of required scope in the stored access token

import oauth2 from '@sitevision/api/server/oauth2';
import appData from '@sitevision/api/server/appData';
import requester from '@sitevision/api/server/Requester';

const oauth2Config = appData.get('oauth2-config');
const options = {
   oauth2: oauth2Config,
};
const neededScopes = ['profile', 'email', 'https://server.com/Resource.Read'];

const doRequest = () => {
   requester
      .get('https://server.com/resource', options)
      .then((result) => {
         // ...
      })
      .catch((message, status) => {
         // Different error format for different providers.
         // Find out if scopes are unsufficient
         const sufficient = isSufficient(status);
         if (!sufficient) {
            const redirectUrl = ...;
            const loginLink = oauth2.createLoginLink(
               oauth2Config,
               redirectUrl,
               neededScopes
            );
            // Redirect user or render the link
         }
      });
};

Pre page render hook example

The error handling above is absolutely crucial to handle all error cases. We do have an opportunity to limit the number of failed requests though by being proactive.

We can check the status of the user token in the pre page render hook phase. If the token isn't valid we redirect the user to the login page.

You might also want to check the scopes of the token before deciding whether to redirect.

// hooks.js
import { beforeRender } from '@sitevision/api/server/hooks';
import appData from '@sitevision/api/server/appData';
import oauth2 from '@sitevision/api/server/oauth2';
import url from '@sitevision/api/server/url';

beforeRender((req, res) => {
   const neededScopes = ['profile', 'email'];
   const oAuth2Config = appData.getNode('oauth2-config');
   const currentStatus = oauth2.getAccessTokenStatus(oAuth2Config);
   const isValid = currentStatus.isValid;
   const scopes = currentStatus.scopes;

   // Redirect if token is not valid
   // Optional: Inspect scopes and redirect if scopes is insufficient
   if (!isValid) {
      const currentUrl = url.get(req.path);
      const redirectUrl = oauth2.createLoginLink(
         oAuth2Config,
         currentUrl,
         neededScopes
      );
      res.redirect(redirectUrl);
      return;
   }
});

It might not always be the best user experience to redirect during pre page render. If your app isn't the main content on the page it might even annoy the user

Be careful when comparing scopes. The Authorization Server might rewrite the scope names or not provide them at all. Be careful with simple String equality checks. Know the system you're integrating to