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

knoxxnxt-auth

v6.1.3

Published

Simple authentication module

Downloads

3

Readme

Auth

Simple authentication module for node.js applications

Installation

$ npm i --save knoxxnxt-auth

Usage

// default options shown here
var options = {
	db: require('./memorydb'),
	mail: {
		templates: { dir: __dirname + '/mail/templates' },
		transport: require('nodemailer-stub-transport')(),
		from: 'Default From <[email protected]>',
		url: {
			invite: {
				accept: 'http://example.com/auth/invite/accept?token=<%- user.activationToken %>&email=<%- user.email %>',
				reject: 'http://example.com/auth/invite/reject?token=<%- user.activationToken %>&email=<%- user.email %>'
			},
			activate: 'http://example.com/auth/activate?token=<%- user.activationToken %>&email=<%- user.email %>',
			reset: 'http://example.com/auth/reset?token=<%- user.resetToken %>&email=<%- user.email %>'
		},
		subject: {
			invite: 'You are invited',
			activate: 'Activate your account',
			reset: 'Reset Your account password'
		}
	},
	password: {
		// bcrypt rounds
		rounds: 10,
		length: { min: 8, max: 100 },
		entropy: { min: 24 }
	},
	// default user fields
	default: {
		properties: {},
		roles: []
	}
};

var auth = require('knoxxnxt-auth')(options);
var methods = auth.methods;

User Model

{
	// set by data store
	"_id": "...",
	// status of the user
	"status": "<invited|registered|enabled|disabled>",
	// login username and unique identifier
	"email": "...",
	// password hash - not accessible via api
	"hash": "...",
	// activation token - not accessible via api
	"activationToken": "...",
	// reset token and expiry - not accessible via api
	"resetToken": "...",
	"resetExpiry": "json date",
	// application specific properties
	"properties": { ... },
	// user roles
	"roles": [ ... ]
}

User Password

Password is validated using zxcvbn. It must obey the following rules:

  • Greater than 8 characters
  • Minimum 24 bits of entropy
  • No dictionary words or common passwords

Constants


exports.TOKEN_LENGTH = 64;

exports.RESET_TOKEN_EXPIRY = 1000 * 60 * 60 * 3; // 3 hours

exports.EMPTY_PASSWORD_HASH = '$2a$10$FISKBraDkuYqs5WNoyJiWe4.kgdJC1hHF0toHCzN80ZNLIRDKUBFS'; // password -> ''

exports.CURRENT_VERSION = require('../package.json').version;

exports.ROLE = {
  USER: 'user',
  USER_ADMIN: 'user admin'
};
exports.ROLES = ['user', 'user admin'];

exports.STATUS = {
  INVITED: 'invited',
  REGISTERED: 'registered',
  ENABLED: 'enabled',
  DISABLED: 'disabled'
};
exports.STATUSES = ['registered', 'enabled', 'disabled'];

exports.MESSAGE = {
	...
};

API

version

Current library version

isSupported(semver)

For plugins or wrappers, returns if the supported auth versions are compatible with the existing version.

admin(options)

Creates an admin console to call methods. options are passed straight to the repl.start function.

Additional options include:

  • sock: UNIX socket to bind to
  • port: Port to bind to
  • host: Host to bind to

Either sock or port must be provided.

auth.admin({ port: 9091, host: 'localhost' });

Client terminal

$ nc localhost 9091

auth > auth.methods.all({})
undefined
auth > [{"_id":0,"email":"[email protected]","roles":["user"],"status":"enabled","properties":{"name":{"first":"Test","last":"User"},"some":{"other":"property"},"__pass":"I like big hashes and I cannot lie"}}]

auth > methods.get({ email: '[email protected]' });
undefined
auth > {"_id":0,"email":"[email protected]","roles":["user"],"status":"enabled","properties":{"name":{"first":"Test","last":"User"},"some":{"other":"property"},"__pass":"I like big hashes and I cannot lie"}}
undefined
auth >

methods.get({ email })

Get a single user from the database.

var user = yield auth.methods.get({ email: '[email protected]' });

methods.all({ [query], [options] })

Get all the users in the database.

// no filter, no options
var allUsers = yield auth.methods.all();

var disabledUsers = yield auth.methods.all({
	query: { status: 'disabled' }
});

var fiveDisabledUsers = yield auth.methods.all({
	query: { status: 'disabled' },
	options: { limit: 5 }
});

methods.insert({ email, pass, [roles], [properties] })

Low-level method for bypassing registration and invitation mechanisms.

try {
	var user = yield auth.methods.insert({
		email: '[email protected]',
		pass: 'a',
		roles: ['developer', 'tester'],
		properties: {
			any: 'thing',
			even: { an: 'object' }
		}
	});
} catch(e) {}

methods.update(user)

Low-level method for updating user record directly.

Note: This completely replaces the user object in the database

try {
	var user = yield auth.methods.update({
		_id: 1,
		email: '[email protected]',
		hash: '<bcrypt hash>',
		roles: ['developer', 'tester'],
		status: 'enabled',
		properties: {
			any: 'thing',
			even: { an: 'object' }
		}
	});
} catch(e) {}

methods.remove({ email })

Low-level method for removing a user from the database. This is a permanent operation.

try {
	var user = yield auth.methods.remove({ email: '[email protected]' });
} catch(e) {}

methods.login({ email, pass })

Validates a user's credentials. If correct, returns the user record.

try {
	var user = yield auth.methods.login({ email: '[email protected]', pass: 'a' });
} catch(e) {}

methods.register({ email, pass, [roles], [properties], [mailOptions] })

Registers a new user. An email is sent to activate the user account. Until the account is activated, the status of the user is marked as 'registered'. Once activated, the user status changes to 'enabled'.

try {
	var user = yield auth.methods.register({ email: '[email protected]', pass: 'a' });
} catch(e) {}

methods.activate({ email, [token], [skipTokenVerification=false] })

Activates a user. The status of the user is changed from 'registered' to 'enabled'.

If skipTokenVerification is passed, token is not validated.

try {
	var user = yield auth.methods.activate({ email: '[email protected]', token: 'a' });

	var user = yield auth.methods.activate({ email: '[email protected]', skipTokenVerification: true });
} catch(e) {}

methods.resendActivationEmail({ email, [mailOptions] })

Creates a new activation token and sends the activation email.

try {
	var status = yield auth.methods.activate({ email: '[email protected]' });
} catch(e) {}

methods.invite({ email, [roles], [properties], [mailOptions] })

Invites a new user. The user status is marked as invited. The user is sent an email with instructions on how to activate their account.

try {
	var user = yield auth.methods.invite({ email: '[email protected]' });
} catch(e) {}

methods.resendInvitationEmail({ email, [mailOptions] })

Resends invitation email sent by invite().

try {
	var user = yield auth.methods.resendInvitationEmail({ email: '[email protected]' });
} catch(e) {}

methods.acceptInvite({ email, pass, [token], [skipTokenVerification=false] })

Accept an invitation. Changes user status from invited to enabled.

If skipTokenVerification is passed, token is not validated.

try {
	var user = yield auth.methods.acceptInvite({ email: '[email protected]', pass: 'a', token: 'a' });

	var user = yield auth.methods.acceptInvite({ email: '[email protected]', pass: 'b', skipTokenVerification: true });
} catch(e) {}

methods.rejectInvite({ email, [token], [skipTokenVerification=false] })

Reject an invitation. Removes user record from the system - this is permanent.

If skipTokenVerification is passed, token is not validated.

try {
	var user = yield auth.methods.rejectInvite({ email: '[email protected]', token: 'a' });

	var user = yield auth.methods.rejectInvite({ email: '[email protected]', skipTokenVerification: true });
} catch(e) {}

methods.getProperties({ email })

Get user properties

try {
	var properties = yield auth.methods.getProperties({ email: '[email protected]' });
} catch(e) {}

methods.setProperties({ email, properties })

Set user properties. User properties are replaced as is so ensure all properties are sent when updating.

try {
	yield auth.methods.properties({
		email: '[email protected]',
		properties: { a: 'a', b: 2 }
	});
} catch(e) {}

methods.changePassword({ email, oldPass, newPass })

Change a user's password

try {
	yield auth.methods.changePassword({ email: '[email protected]', oldPass: 'a', newPass: 'b' });
} catch(e) {}

methods.resetRequest({ email, [mailOptions] })

User is requesting a password reset. An email is sent to the user with a reset link.

try {
	yield auth.methods.resetRequest({ email: '[email protected]' });
} catch(e) {}

methods.resetResponse({ email, token, [pass], [skipTokenVerification=false] })

User is requesting a password reset. An email is sent to the user with a reset link.

If skipTokenVerification is passed, token is not validated.

try {
	yield auth.methods.resetResponse({ email: '[email protected]', token: 'a', pass: 'a' });
} catch(e) {}

methods.disable({ email })

Mark a user as disabled

try {
	yield auth.methods.disable({ email: '[email protected]' });
} catch(e) {}

methods.enable({ email })

Mark a user as enabled

try {
	yield auth.methods.enable({ email: '[email protected]' });
} catch(e) {}

Datastore

Any backend databased can be used such as MongoDB, Postgres, MySQL etc. It must support the following method signatures. All methods must be generator functions.

By default, a memory database is used and a warning is printed to the console.

.find([args])

An arguments hash passed from application level code is passed straight to the function. This could be used for various querying options.

.findById(id)

The user id is passed. It expects the entire user model to be returned.

.findByEmail(email)

Fetch a user by email. It expects the entire user model to be returned.

.insert(user)

A user json object is sent. The user object contains nested properties that must be handled appropriately by the data store. It expects a boolean indicating if the user was successfully inserted into the database.

It is left to the data store to assign the user with an id. This must be set as _id field on the user object

.bulkInsert(users)

An array of user json objects is sent. It expects a boolean indicating if the user was successfully inserted into the database.

.update(user)

The entire user object is passed to the data store. It expects a boolean indicating if the user was successfully inserted into the database.

.removeByEmail(email)

Remove a user from the database using their email address.

.reset()

Remove all the database records

Creating a datastore

See knoxxnxt-auth-mongodb for a sample implementaion.

Step 1

Create a new datastore that conforms to the above API.

Step 2

$ require knoxxnxt-auth --save-dev

Step 3

Run knoxxnxt-auth tests with the correct environment variable pointing to the new file

Debugging

In case of issues, debug by setting the environment variable DEBUG="knoxxnxt-auth:*".

$ cd node_modules/knoxxnxt-auth
$ DB_MODULE="$(pwd)/../../datastore.js" npm test