npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

express-authy

v0.1.1

Published

Authentication module for Express that provides endpoints for a given route.

Downloads

5

Readme

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