koa-neo4j
Version:
Rapidly create REST APIs, powered by Koa and Neo4j -- batteries included with built-in role based authentication via JWT and reusable backend components
125 lines (108 loc) • 5.08 kB
JavaScript
;Object.defineProperty(exports, "__esModule", { value: true });exports.Authentication = undefined;
var _koaPassport = require('koa-passport');
var _passportLocal = require('passport-local');
var _passportJwt = require('passport-jwt');
var _jsonwebtoken = require('jsonwebtoken');var _jsonwebtoken2 = _interopRequireDefault(_jsonwebtoken);
var _preprocess = require('./preprocess');function _interopRequireDefault(obj) {return obj && obj.__esModule ? obj : { default: obj };}
class Authentication {
constructor(neo4jConnection, { secret, passwordMatches, tokenExpirationInterval,
userCypherQueryFile, rolesCypherQueryFile } = {}) {
this.neo4jConnection = neo4jConnection;
this.passport = new _koaPassport.KoaPassport();
this.secret = secret;
this.passwordMatches = passwordMatches;
this.tokenExpirationInterval = tokenExpirationInterval || '1h';
this.userQuery = userCypherQueryFile;
this.rolesQuery = rolesCypherQueryFile;
this.passport.use(
new _passportLocal.Strategy((username, password, done) => this.getUser(username, password).
then(user => done(null, user)).
catch(done)));
// koa-passport uses generators which will be deprecated in koa v3,
// below block should be refactored accordingly
// The author of koa-passport has not considered the use cases of done(err),
// hence we need to wrap calls in a promise
this.authenticateLocal = (ctx, next) => new Promise(
(resolve, reject) => this.passport.authenticate('local',
(err, user) => resolve(user))(ctx, () => {}).
catch(reject)).
then(user => {
// koa-passport returns false if object is not formatted as {username, password}
if (user === false)
throw new Error('invalid POST data, expected {username, password[, remember]}');
return user;
}).
catch(error => ctx.throw(400, error)).
then(user => {
ctx.body = this.getToken(user, ctx.request.body.remember);
}).
catch(error => ctx.throw(422, error)).
then(next);
this.passport.use(new _passportJwt.Strategy(
{
jwtFromRequest: _passportJwt.ExtractJwt.fromAuthHeaderWithScheme('JWT'),
secretOrKey: secret },
(user, done) => {
// Check whether payload is user
if (!user.id)
done(new Error('invalid token'));else
done(null, user);
}));
this.authenticateJwt = (ctx, next) => new Promise((resolve, reject) =>
this.passport.authenticate('jwt',
{ session: false }, (err, user) => resolve(user))(ctx, () => {}).
catch(reject))
// TODO next line connects to DB, token already embodies roles,
// change when access token is implemented
.then(this.appendRoles)
// koa-passport's ctx.login(user) is just too much hassle, setting ctx.user instead
.then(user => {ctx.user = user;}).
then(next);
this.getUser = (username, password) => {
return this.neo4jConnection.executeCypher(this.userQuery, { username: username }).
then(response => {
const user = response[0];
if (!user)
throw new Error('invalid username or password');
const passwordsMatch = this.passwordMatches ?
this.passwordMatches(password, user.password) :
password === user.password;
if (!passwordsMatch)
throw new Error('invalid username or password');
delete user.password;
return user;
}).
then(this.appendRoles);
};
this.appendRoles = user => {
return this.neo4jConnection.executeCypher(
this.rolesQuery || 'MATCH (user) WHERE id(user) = {id} ' +
'RETURN {roles: labels(user)}',
{ id: (0, _preprocess.neo4jInt)(user.id) }, !this.rolesQuery).
then(response => {
let [{ roles }] = response;
if (!roles)
throw new Error(
"'rolesCypherQueryFile' returned an invalid object, " +
'expected { roles }');
roles = roles.map(role => role.toLowerCase());
user.roles = roles;
return user;
});
};
this.getToken = (user, remember) => {
const options = {};
if (!remember)
options.expiresIn = this.tokenExpirationInterval;
return {
token: `JWT ${_jsonwebtoken2.default.sign(user, this.secret, options)}`,
user: user };
};
this.login = (username, password, remember) => {
return this.getUser(username, password).
then(user => this.getToken(user, remember));
};
}} /**
* Created by keyvan on 8/20/16.
*/exports.
Authentication = Authentication;