raveljs/ravel

View on GitHub
lib/auth/decorators/authenticated.js

Summary

Maintainability
A
35 mins
Test Coverage
A
100%
'use strict';

const Metadata = require('../../util/meta');

/**
 * The `@authenticated` decorator adds authentication middleware before an endpoint
 * within a `Routes` or `Resource` class, requiring that a user have valid credentials
 * before they can access the decorated endpoint(s).
 *
 * Assumes that you've `require`'d an `AuthenticationProvider`, and that you
 * have set up an `@authconfig` `Module`.
 *
 * If an object is passed as an argument to the decorator, it is used for configuration purposes,
 * and supports the following keys:
 * - `boolean config.redirect` true iff should automatically redirect to `app.get('login route')`
 *   if user is not signed in.
 * - `boolean config.register` register user automatically if they are not registered.
 *             (rather than failing and throwing an `$err.Authentication`).
 *
 * See [`authconfig`](#authconfig) and
 * [`AuthenticationProvider`](#authenticationprovider) for more information.
 *
 * @param {Class|Method|object} args - A configuration object (returning a decorator), or the Method
 *                                         or Class to directly apply this decorator to.
 *
 * @example
 * // Note: decorator works the same way on Routes or Resource classes
 *
 * const Routes = require('ravel').Routes;
 * const mapping = Routes.mapping;
 * const authenticated = Routes.authenticated;
 * // @Routes('/')
 * class MyRoutes {
 *   // @authenticated({redirect: true}) // works at the method-level, with or without arguments
 *   // @mapping(Routes.GET, 'app')
 *   async handler (ctx) {
 *     // will redirect to this.$params.get('login route') if not signed in
 *   }
 * }
 * @example
 * // Note: decorator works the same way on Routes or Resource classes
 *
 * const Resource = require('ravel').Resource;
 * const authenticated = Resource.authenticated;
 *
 * // @authenticated // works at the class-level as well (with or without arguments)
 * // @Resource('/')
 * class MyResource {
 *   async handler (ctx) {
 *     // will respond with a 401 if not signed in
 *   }
 * }
 */
function authenticated (...args) {
  if (args.length === 3 && typeof args[0].constructor === 'function') {
    Metadata.putMethodMeta(args[0], args[1], '@authenticated', 'config', Object.create(null));
  } else if (args.length === 1 && typeof args[0] === 'function') {
    // handle @authenticated at the class-level without arguments
    Metadata.putClassMeta(args[0].prototype, '@authenticated', 'config', Object.create(null));
  } else {
    const config = args[0] ? args[0] : Object.create(null);
    return function (target, key) {
      if (key === undefined) {
        Metadata.putClassMeta(target.prototype, '@authenticated', 'config', config);
      } else {
        Metadata.putMethodMeta(target, key, '@authenticated', 'config', config);
      }
    };
  }
}

/*!
 * Export the `@authenticated` decorator
 */
module.exports = authenticated;