OAuth2 [@since 7.0]

The oauth2 object contains utility methods to perform OAuth2 operations. Use require('oauth2') in a server side context to retrieve the object.

const oauth2 = require('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

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

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

// Gets the current status of the users access token.
// Token will be refreshed if necessary
var status = 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

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

requester.get('https://server.com/resource', options)
  .done(function(result) {
    // ...
  })
  .fail(function(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
(function() {
  'use strict';

  const { beforeRender } = require('hooks');
  const neededScopes = ['profile', 'email'];

  beforeRender((req, res) => {
    const appData = require('appData');
    const oauth2 = require('oauth2');
    const url = require('url');
    
    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 yoy're integrating to