access-manager
© WeeHorse 2018, MIT license
Authentication, Sessions and ACL
Access Manager is a one-stop solution for implementing authenticated and anonymous sessions with user handling and whitelisted ACL. Keeps the same session regardless of authenticated state. Attaches itself to an express app as a middleware.
Install
$ npm install access-manager
Install ACL data
If you want some example data or wish to import your ACL from file, use the --import-acl switch when you start your app with access manager (for the first time). Note that your app will shut down once the import is done.
Use example data: (example-acl.json)
$ node app --import-acl
Or provide your own file:
$ node app --import-acl=file.json
The ACL data installs into the acl collection. Obviously you're free to populate the acl collection anyway you see fit.
Examples:
Typical init with basic dependencies
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const bcrypt = require('bcrypt');
const saltRounds = 10;
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/some_database');
const AccessManager = require('access-manager');
const accessManager = new AccessManager({
mongoose: mongoose,
expressApp: app
});
Configuration
You can optionally add your own schemas (Schema Objects) for users, sessions and acl, at the properties: userSchema, sessionSchema, aclSchema
Some properties in the schemas are required by the access-manager. Those details can be found at the bottom of this document.
You will propably want to supply your own userSchema, an example of doing that:
const accessManager = new AccessManager({
mongoose: mongoose,
expressApp: app,
userSchema: {
firstName: {type: String, required:true},
lastName: {type: String, required:true},
email: {type: String, required:true, unique:true},
password: {type: String, required:true},
roles: [String]
}
The models access manager uses are then avaliable from access manager:
const User = accessManager.models.user;
Now access manager will do its work seamlessly in the background,
but we need a user, so here's a registration route:
The example ACL will only allow anonymous users and super users create accounts
app.post('/register', async (req, res)=>{
req.body.password = await bcrypt.hash(req.body.password, saltRounds);
let user = await new User(req.body);
await user.save();
res.json({msg:'Registered'});
});
And login:
The example ACL will prevent this route if you are already logged in
app.post('/login', async (req, res)=>{
let user = await User.findOne({email: req.body.email});
if(user && await bcrypt.compare(req.body.password, user.password)){
req.session.user = user._id;
req.session.loggedIn = true;
await req.session.save();
res.json({msg:'Logged in'});
}else{
res.json({msg:'Failed login'});
}
});
To logout:
The example ACL will prevent this route if you are already logged out
app.all('/logout', async (req, res)=>{
req.user = {};
req.session.loggedIn = false;
await req.session.save();
res.json({msg:'Logged out'});
});
A restricted example route:
The example ACL will only allow this route on logged in users
app.get('/messages', async (req, res)=>{
res.json({msg:'Here are your messages'});
});
Wildcard route (that takes any method) so we can test that the ACL blocks anything not allowed):
app.all('*', (req, res)=>{
res.json({params: req.params, body: req.body});
});
Don't forget...
app.listen(3000,()=>{
console.log("Remember Mystery science theatre 3000!");
});
Access manager schemas requirements
The schemas used in access manager must contain the properties detailed below. If you don't supply your own schemas these are the defaults:
The mininum required userSchema:
{
email: {type: String, required:true, unique:true},
password: {type: String, required:true},
roles: [String]
}
The mininum required sessionSchema:
{
loggedIn: {type:Boolean, default:false},
user: { type: this.mongoose.Schema.Types.ObjectId, ref: 'User' }
}
The mininum required aclSchema:
{
path: {type: String, unique: true},
roles: [
new this.mongoose.Schema({
role: String,
methods: [{type: String, enum: ['GET', 'POST', 'PUT', 'DELETE', 'ALL']}]
})
]
}