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 toport
: Port to bind tohost
: 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