npm

Need private packages and team management tools?Check out npm Orgs. »

express-authy

0.1.1 • Public • Published

Overview

ExpressAuth is a simplicity inspired RESTful Express middleware to handle authentication features. It's still a work in progress so hang on for the published version!

Usage

Installation

To install it for use, use npm:

# npm install express-authy --save 

Alternative with yarn:

# yarn add express-authy 

Basic w/ Default Options

You can use the express-authy library as follows:

const express = require('express');
const ExpressAuthy = require('express-authy');
const server = express();
server.use(new ExpressAuthy());
server.listen(process.env.PORT);

This will set up express-authy at the root endpoint. With the default options, the following endpoints will be created:

  • /access
  • /forgot
  • /login
  • /logout
  • /register
  • /verify

Customizations

Setting the Base Path

The following line of code creates the 6 endpoints, /access, /forgot, /login, /logout, /register, /verify:

const ExpressAuthy = reuqire('express-authy');
// ...
server.use(new ExpressAuthy());
// ...

To change this to /auth/:endpoints, use:

const ExpressAuthy = reuqire('express-authy');
// ...
server.use('/auth', new ExpressAuthy());
// ...

Changing the Default Options

A .get() and .set() method of the ExpressAuth object allows you to alter and retrieve different options via a cursor. The method signatures are as follows:

.get( selector : {Array, String} )

The :selector argument can be of an Array type or a String type and it points to the item in the options tree. For example, to get the required parameters of an endpoint, of which the structure is:

// Defaults
{
  // ...
  param: {
    access: {
      // getting this
    }
  }
  // ...
}

We use:

ExpressAuth.get(['param', 'access']);

.set( selector : {Array, String}, value : {Any} )

The :selector property works in the same way as in .get(). The :value argument indicates the value to assign to the selected option. For example, to set the value of the option at param.access.token to the string 'token-id', we use:

ExpressAuth.set(['param', 'access', 'token'], 'token-id');

Options Tree

The options are defined with a tree structure. The .get() and .set() methods as described above are used to select the nested property as desired. For an example tree:

{
  "a": {
    "b": {
      "c": "d",
      "e": {
        "f": 0
      }
    },
    "g": 1
  },
  "h": 2,
  "i": 3
}

Using .get(['a', 'b', 'c']) will return "d".

Using .get(['h']) will return 2.

Using .set(['a', 'b', 'e', 'f'], 4) will set the 0 to 4.

Using .set(['a'], 'a') will result in the "a" branch being replaced with the string literal, "a".

Defaults

The defaults are defined as follows:

var defaults = {
  /**
   * this defines the names of the keys used in the application
   * to refer to the different available actions
   */
  keys: [
    access: 'access',
    login: 'login',
    logout: 'logout',
    forgot: 'forgot',
    register: 'register',
    verify: 'verify',
  ],
  /**
   * this defines the http methods of the endpoints, use lowercase
   */
  method: {
    [defaults.keys.access]: 'get',
    [defaults.keys.login]: 'get',
    [defaults.keys.logout]: 'get',
    [defaults.keys.forgot]: 'get',
    [defaults.keys.register]: 'get',
    [defaults.keys.verify]: 'get',
  },
  /**
   * this defines the different model handlers available. models
   * encapsulate the logic behind CRUD operations on the user
   * accounts
   */
  model: {
    sequelize: {
      config: {
        /**
         * if your account table has extra columns that must be
         * filled to allow an insertion, use this to specify the
         * columns
         */
        extraColumns: {},
        /**
         * retrieves the sequelize configuration file as specified
         * in the :path property of this object
         */
        get: function() => {configuration},
        /**
         * returns a Sequelize model corresponding to the account
         * table
         */
        model: function(sequelize : Sequelize) => SequelizeModel,
        /**
         * specifies the column names for the email, password, nonce
         * and session fields
         */
        names: {
          columnEmail: 'email',
          columnPassword: 'password',
          columnNonce: 'nonce_token',
          columnSession: 'session_token',
        },
        /**
         * determines where to find the sequelize configuration as
         * used in the get() method of this object
         */
        path: './sequelize.json',
        /**
         * name of the table containing the accounts
         */
        table: 'Accounts',
        /**
         * length of token used for nonce generation
         */
        nonceTokenLength: defaults.options.nonceTokenLength
      },
      /**
       * these are the model handlers and are called by the Express
       * request handlers to make persistent changes
       */
      handle: {
        /**
         * verifies :token against tokens stored in the database,
         * resolves the promise with the identified account if the
         * token is valid, rejects the promise with a false boolean
         * otherwise
         */
        [defaults.keys.access]: function(token) : Promise,
        /**
         * selects a user identified with the email :email and
         * validates the user's password using :password. if both
         * :email and :password is specified, adds a session token.
         * if only :email is specified, sets the nonce token. in both 
         * cases, the promise is resolved with the user's account
         * on success. if user is not found or the password is invalid,
         * rejects the promise with a false boolean.
         */
        [defaults.keys.login]: function(email, password) : Promise,
        /**
         * removes the nonce and session token of the user. resolves with
         * a boolean true on successful removal of tokens, rejects with
         * a boolean false otherwise 
         */
        [defaults.keys.logout]: function(id) : Promise,
        /**
         * inserts a user account with the email :email and password
         * :password. resolves with the newly created account on success,
         * rejects with an error object otherwise. if :otherInfo is specified,
         * also inserts the specified key-values. :otherInfo should be a
         * key-value dictionary such that { [columnName]: value } is valid
         */
        [defaults.keys.register]: function(email, password, [ otherInfo ]) : Promise,
        /**
         * verifies if :token is associated with a user account. resolves
         * with the user account if it's found, rejects with a boolean
         * false otherwise
         */
        [defaults.keys.verify]: function(token) : Promise,
        /**
         * sets a new nonce for the email 
         */
        [defaults.keys.forgot]: function(email) : Promise,
      }
    }
  },
  handlers: {
    /**
     * if any of these are set to a function, the
     * handlers will call these with the intended
     * returned object
     */
    after: {
      [defaults.keys.access]: null,
      [defaults.keys.login]: null,
      [defaults.keys.logout]: null,
      [defaults.keys.register]: null,
      [defaults.keys.verify]: null,
      [defaults.keys.forgot]: null
    },
    /**
     * Express handler for the /access endpoint
     */
    [defaults.keys.access]: function(req, res, next) : void,
    /**
     * Express handler for the /login endpoint
     */
    [defaults.keys.login]: function(req, res, next) : void
    /**
     * Express handler for the /logout endpoint
     */
    [defaults.keys.logout]: function(req, res, next) : void
    /**
     * Express handler for the /register endpoint
     */
    [defaults.keys.register]: function(req, res, next) : void
    /**
     * Express handler for the /verify endpoint
     */
    [defaults.keys.verify]: function(req, res, next) : void
    /**
     * Express handler for the /forgot endpoint
     */
    forgot: function(req, res, next) : void
  },
  params: {
    required: {
      /**
       * defines the parameter name for the /access endpoint
       */
      [defaults.keys.access]: {
        token: 'token',
      },
      /**
       * defines the parameter names for the /login endpoint
       */
      [defaults.keys.login]: {
        userIdentifier: 'user-id',
        password: 'password',
      },
      /**
       * defines the parameter name for the /logout endpoint
       */
      [defaults.keys.logout]: {
        token: 'token',
      },
      /**
       * defines the parameter names for the /register endpoint
       */
      [defaults.keys.register]: {
        email: 'email',
        password: 'password',
        passwordConfirmation: 'password-confirmation',
      },
      /**
       * defines the parameter names for the /verify endpoint
       */
      [defaults.keys.verify]: {
        nonce: 'token',
      },
      /**
       * defines the parameter names for the /forgot endpoint
       */
      [defaults.keys.forgot]: {
        email: 'email',
      }
    },
    /**
     * returns the required parameters for a given
     * endpointId which can be any of the String values
     * defined in defaults.keys
     */
    get: function(endpointId) : Object
  },
  secret: {
    /**
     * relative locations to the private and public key files
     */
    keys: {
      private: './config/key.priv',
      public: './config/key.pub'
    },
    /**
     * this value is returned when calling symmetric()
     */
    password: 'password',
    /**
     * returns an object like: {
     *   decryptor : String,
     *   encryptor : String
     * } which is the file content from the
     * defaults.secret.keys.private and
     * defaults.secret.keys.public file
     */
    asymmetric: function(),
    /**
     * returns an object like: {
     *   decryptor : String,
     *   encryptor : String
     * } which is equal to defaults.secret.password
     */
    symmetric: function()
  },
  options: {
    /**
     * defines which model to use, which should
     * correspond to a key in defaults.model
     */
    modelStrategy: 'sequelize',
    /**
     * defines what type of encryption to use which
     * should be a key in defaults.secret
     */
    secretStrategy: 'symmetric',
    /**
     * defines what type of token to use which should
     * be a key in defaults.token
     */
    tokenStrategy: 'jwt',
    /**
     * determines the length of nonces used
     */
    nonceTokenLength: 32
  },
  /**
   * defines the endpoints as called by a front-end
   */
  slug: {
        access: 'access',
        login: 'login',
        logout: 'logout',
        forgot: 'forgot',
        register: 'register',
        verify: 'verify',
  },
  token: {
    jwt: {
      /**
       * returns a JSON web token given the payload
       * argument :payload
       */
      generate: function(payload : Object) : String,
      /**
       * returns the payload stored in the JSON web
       * token :token
       */
      validate: function(token : String) : Object
    }
  }
};

Under The Hood Notes

Sequelize

ExpressAuth currently only uses Sequelize to manage database operations so we can target more platforms.

Contributing

Fork this repo, create your stuff and issue a pull request(:

Getting Started

Clone the repo locally.

To install dependencies, use yarn install:

# yarn install 

To initialize the test environment, you can run the following command:

# npm run dev:setup 

This will set up the SQLite3 database, create a public/private key pair for the tests to use, and create and run database migrations.

Running Tests

You will need to run the above command to set up the test environment before the tests will work as expected!

Code Quality

You can run the code quality tests using:

# npm run eslint 

Unit Tests

You can run the Mocha unit tests using:

# npm test 

You can also run it continuously in the background watching for file changes with:

# npm test:watch 

To test a single file, you can run:

# npm run test:this -- /path/to/file 

To test and watch a single file, you can run:

# npm run test:watch:this -- /path/to/file 

Coverage

Coverage files will be generated in /coverage.

More notes on contributing coming soon!

Changelog

v0 (pre-release)

0.1.1

  • code refactor
  • documentation updates

0.1.0

  • initial commit

install

npm i express-authy

Downloadsweekly downloads

9

version

0.1.1

license

MIT

last publish

collaborators

  • avatar
Report a vulnerability