authenticate.js

/**
 *
 * @fileOverview This file exports the <i>authenticate</i> module that allows simple-cas to redirect
 * to redirect user to CAS sign-in page, get a ticket, come back to this route again,
 * validate a ticket and set cas session once ticket is validated.
 */


const url = require('url');
const protocol1 = require('./protocol1');
const protocol2 = require('./protocol2');
const protocol3 = require('./protocol3');
const CAS = require('../index');

/**
 *
 * @param options CAS configuration object
 * @param route explicitly declared current route,
 * for example your.app.com/login, in this case route is login.
 * which will be used for redirection after authentication.
 * @return {Function}
 */
function authenticate(options, route) {
  return (req, res, next) => {
    // support dynamic options maker
    if (req.data) {
      options.host = req.data.host;
      options.service = req.data.service;
      CAS.configure({ host: req.data.host, service: req.data.service });
    }
    if (req.data.protocolVersion) {
      options.protocolVersion = req.data.protocolVersion;
      CAS.configure({ protocolVersion: req.data.protocolVersion });
    }
    if (!options.service || !route || !options.host) {
      throw new Error('Must supply a valid CAS parameters object(host and service declared) and redirect route');
    }
    switch (options.protocolVersion) {
      case 1:
        Object.defineProperty(options.paths, 'serviceValidate', { value: '/cas/serviceValidate' });
        Object.defineProperty(options, 'validateTicketProtocol', { value: protocol1 });
        break;
      case 2:
        Object.defineProperty(options.paths, 'serviceValidate', { value: '/cas/serviceValidate' });
        Object.defineProperty(options, 'validateTicketProtocol', { value: protocol2 });
        break;
      case 3:
        Object.defineProperty(options.paths, 'serviceValidate', { value: '/cas/p3/serviceValidate' });
        Object.defineProperty(options, 'validateTicketProtocol', { value: protocol3 });
        break;
      default:
        Object.defineProperty(options.paths, 'serviceValidate', { value: '/cas/serviceValidate' });
        Object.defineProperty(options, 'validateTicketProtocol', { value: protocol2 });
    }
    // got back with a ticket
    if (req.query.ticket) {
      // putting making url from config to send to ticket validate package as one object
      const urlFormatter = {};
      urlFormatter.protocol = options.protocol;
      urlFormatter.pathname = options.paths.serviceValidate;
      urlFormatter.query = {};
      urlFormatter.host = options.host;
      const fullValidateUrl = url.format(urlFormatter);
      const fullServiceUrl = options.service + route;
      const servicePackage = {};
      servicePackage.fullValidateUrl = fullValidateUrl;
      servicePackage.fullServiceUrl = fullServiceUrl;
      servicePackage.ticket = req.query.ticket;
      servicePackage.validate = options.validateTicketProtocol;
      // validate ticket and set session
      CAS.validateService(servicePackage).then((msg) => {
        req.session.cas = {};
        req.session.cas.user = msg.user;
        next();
      }).catch((err) => {
        throw new Error(err);
      });
    } else {
      // there is no ticket in route, redirect user to cas login page
      options.pathname = options.paths.login;
      options.query = options.query || {};
      options.query.service = options.service;
      res.redirect(307, `${url.format(options)}${route}&renew=false`);
    }
  };
}

module.exports = authenticate;