ideman v3.0.1
Identity Manager 3
Implement OAuth2.0 and basic authentication cleanly into your NodeJS server application using Bookshelf and knex as ORM and queries builder.
Summary
Database
This module requires a database infrastructure. To automate the creation of schemas and others boring jobs, ideman provides a node command line interface tool called ideman-cli.
So, before continue with the installation of this module, go to ideman-cli project and then install ideman. Otherwise you can create manually the database schema following the documentation below.
Tables
Diagram

Installation
WARNING:
Remember that before installing ideman you MUST create the database schemas, otherwise this module will not work (ideman-cli).
In your project root run from command line:
$ npm install -save idemanExample
Let's start! Install in your application Bookshelf and its dependency knex.
Create a new file in your project root like:
//file: ./ideman.js
var knex = require('knex')({
client: 'pg',
connection: 'postgres://postgres:postgres@localhost:5432/ideman?charset=utf-8&ssl=true',
});
var Bookshelf = require('bookshelf')(knex);
var ideman = require('ideman')(Bookshelf);
ideman.init({
token: {
life: 3600 //token expiration in seconds
},
oauth: {
authentications: ['bearer' /*, 'basic'*/], //enable bearer token
grants: ['password', 'refresh_token' /*, 'client_credentials' */] //enable user credentials and refresh token grants
}
});
module.exports = ideman;Then include this file everywhere you need ideman methods, for example in your Express application you could have:
//file: ./routes/index.js
var express = require('express');
var router = express.Router();
var ideman = require('../ideman');
router.route('/oauth2/token').post(ideman.isClientAuthenticated, ideman.token);
router.route('/protected/resource').post(ideman.isAuthenticated, function() {
res.json({
data: 'The protected resource'
});
});Call the endpoint /oauth2/token to retrieve an access token:
$ curl -H 'Accept: application/x-www-form-urlencoded' -X POST -d 'grant_type=password&client_id=clientId&client_secret=clientSecret&username=userId&password=userPassword' http://localhost:3000/oauth2/tokenIt will return a JSON response:
{
"access_token":"NgvhmoKm9ASMCa3KGLh2yjNPqhIhFLEgPacesMFiIOQPuZ1Mq19Xg"
/* ... */
}With the new access_token you can call the protected resource /protected/resource
$ curl -H 'Authorization: Bearer NgvhmoKm9ASMCa3KGLh2yjNPqhIhFLEgPacesMFiIOQPuZ1Mq19Xg' -X POST http://localhost:3000/protected/resourceDocumentation
Construction
require('ideman')( bookshelf [, config]) : Object
The ideman module is initialized by injecting an initialized Bookshelf instance. It can also accepts a configuration object for database customizations.
Arguments
bookshelf {Object} Bookshelf instance
[config] {Object} Optional models and tables configurationReturns
{Object} Singleton instanceThe configuration object allows you to redefine tables and models names. If you don't specify any configuration, it uses a default object:
{
prefix: '',
entities: {
user: {
table: 'users',
model: 'User'
},
client: {
table: 'clients',
model: 'Client'
},
token: {
table: 'tokens',
model: 'Token'
},
code: {
table: 'codes',
model: 'Code'
}
}
}Methods
- Core
- Authorization
init( options ) : void
Initialization of singleton instance.
Arguments
options {Object} Ideman parametersIf you don't specify any paramaters, it uses a default object:
{
oauth2: {
//Use mandatory client secret in the auth request
useClientSecret: false,
//Enables authentications strategies
authentications: ['basic', 'bearer'],
//Enables authorizations grants
grants: ['client_credentials', 'password', 'refresh_token', 'authorization_code']
},
validation: {
//Enables input validation
enabled: false,
//Regexp for username
username: /^[\w\.]{2,100}$/g,
//Regexp for password
password: /^(?=.*[A-Za-z])(?=.*\d)(?=.*[.)(=,|$@$!%*#?&])[A-Za-z\d.)(=, | $@ $!%*#?&]{8,255}$/g,
//Regexp for client name
clientId: /^[\w\.]{2,100}$/g,
//Regexp for client secret
clientSecret: /^(?=.*[A-Za-z])(?=.*\d)(?=.*[.)(=,|$@$!%*#?&])[A-Za-z\d.)(=, | $@ $!%*#?&]{8,255}$/g,
},
user: {
//Users' password are crypted and compared by the specified mode below
passwordEnc: 'bcrypt' //bcrypt|crypto|none
},
ldap: {
//Enable LDAP user binding
enabled: false,
//Search filters, ex. (|(cn=<username>)(mail=<username>))
authAttributes: ['cn', 'mail'],
//Returned attribute after search (returned value must match with username column for a successful login)
returnAttribute: 'dn',
//Ldapper module configuration
ldapper: null
},
//Crypton module configuration
crypton: null,
token: {
//Token life in seconds
life: 3600,
//Token length in bytes
length: 32, //bytes
//Delete active tokens on login
autoRemove: true,
jwt: {
//Enables jwt token instead the standard token
enabled: false,
//Check if IP caller are the same of jwt IP when it was created
ipcheck: false,
//Check if user-agent caller are the same of jwt user-agent when it was created
uacheck: false,
//Secret key for signing jwt token
secretKey: 'K7pHX4OASe?c&lm'
}
}
}getConfig() : Object
Gets the ideman initialization object.
Returns
{Object} Ideman parametersgetDbConfig() : Object
Gets the ideman database configuration object.
Returns
{Object} Ideman db configgetBookshelf() : Object
Gets the Bookshelf instance.
Returns
{Object} Bookshelf instancegetPassport() : Object
Gets the passport instance.
Returns
{Object} Passport instanceIt is useful when you need to initialize passport for Express without installing it in your application.
For example when you use the middlewares methods of ideman module, your Express application needs to be configured with:
var app = express();
app.use(passport.initialize());getModel( name ) : Object
Gets a Bookshelf model. Available default models are: User, Client, Token, Code.
Arguments
name {string} Model nameReturns
{Object} Bookshelf modelNow you can extend a Bookshelf model in your application:
var bookshelf = ideman.getBookshelf();
var User = ideman.getModel('User');
var UserExt = bookshelf.model('UserExt', User.extend({
test: function() {
console.log('hello world');
return;
}
}));
console.log(UserExt.forge().tableName);getModels() : Array
Gets all Bookshelf models.
Returns
{Array} All bookshelf modelsvalidateUserCredentials( username, password ) : Promise( Object )
Checks if user credentials are valid.
Arguments
username {string} Username
password {string} Clear passwordReturns
{Object} Returns a promise with bookshelf `User` modelvalidateClientCredentials( name, secret ) : Promise( Object )
Checks if client credentials are valid.
Arguments
name {string} Client name
secret {string} Clear client secretReturns
{Object} Returns a promise with bookshelf `Client` modelvalidateBearerToken( token [, ip, userAgent] ) : Promise( Object )
Checks if token is valid.
Arguments
token {string} Bearer token
[ip] {string} Optional IP address to check
[userAgent] {string} Optional user agent to checkReturns
{Object} Returns a promise with referred bookshelf `User` or `Client` modelexchangePassword( client, username, password [, ip, userAgent] ) : Promise( Object )
Exchanges user's credentials for an access token. The client input object must be an existing entity into database.
Arguments
client {Object} Bookshelf `Client` model
username {string} Username
password {string} Clear password
[ip] {string} Optional IP address to save with token
[userAgent] {string} Optional user agent to save with tokenReturns
{Object} Returns a promise with tokensExample of a common scenario:
var promise = ideman.validateClientCredentials('name', 'secret')
.then(function(client) {
return ideman.exchangePassword(client, 'username', 'password');
});The returned JSON object is like:
{
"access_token":"<token>",
"refresh_token":"<refreshtoken>",
"expires_in":3600,
"token_type":"Bearer"
}exchangeClientCredentials( client [, ip, userAgent] ) : Promise( Object )
Exchanges client's credentials for an access token. The client input object must be an existing entity into database.
Arguments
client {Object} Bookshelf `Client` model
[ip] {string} Optional IP address to save with token
[userAgent] {string} Optional user agent to save with tokenReturns
{Object} Returns a promise with tokensThe returned JSON object is like:
{
"access_token":"<token>",
"refresh_token":"<refreshtoken>",
"expires_in":3600,
"token_type":"Bearer"
}exchangeRefreshToken( client, refreshToken ) : Promise( Object )
Exchanges a refesh token for a new access token. The client input object must be an existing entity into database.
Arguments
client {Object} Bookshelf `Client` model
refreshToken {string} Refresh tokenReturns
{Object} Returns a promise with tokensThe returned JSON object is like:
{
"access_token":"<token>",
"refresh_token":"<refreshtoken>",
"expires_in":3600,
"token_type":"Bearer"
}revokeToken( token, force ) : Promise( bool )
Revokes a token. If force is specified it removes all active tokens associated to user or client.
Arguments
token {string} Access token
force {bool} Removes all tokensReturns
{bool} Returns trueldapAuthentication( username, password ) : Promise( string )
Checks credentials for the given user on LDAP.
Arguments
username {string} Username
password {string} PasswordReturns
{string} Returns null if user was not found, otherwise the attribute value specified in configurationThrows
{LDAPConnectionError|LDAPBindError|LDAPUnbindError|LDAPSearchError}Express middlewares
isAuthenticated
This middleware protects your endpoint and checks if request contains basic credentials or a valid bearer token.
See [about authorizations](https://github.com/thinkingmik/ideman#authorization_grants) for request's details.
Example
router.route('/protected/resource').post(ideman.isAuthenticated, function() {
res.json({
data: 'The protected resource'
});
});Request
# using HTTP Basic Authentication
$ curl -u userId:userPwd -X GET http://localhost:3000/protected/resource
# using Bearer token
$ curl -H 'Authorization: Bearer NgvhmoKm9ASMCa3KGLh2yjNPqhIhFLEgPacesMFiIOQPuZ1Mq19Xg' -X POST http://localhost:3000/protected/resourceisClientAuthenticated
This middleware has been used with [token](https://github.com/thinkingmik/ideman#token) endpoint and checks for valid client credentials before getting an access token.
See [about authorizations](https://github.com/thinkingmik/ideman#authorization_grants) for request's details.
Example
router.route('/oauth2/token').post(ideman.isClientAuthenticated, ideman.token);Request
# user credentials grant
$ curl -H 'Accept: application/x-www-form-urlencoded' -X POST -d 'grant_type=password&client_id=clientId&username=userId&password=userPassword' http://localhost:3000/oauth2/token
# user credentials grant with basic auth
$ curl -u clientId:clientSecret -H 'Accept: application/x-www-form-urlencoded' -X POST -d 'grant_type=password&username=userId&password=userPassword' http://localhost:3000/oauth2/token
# client credentials grant
$ curl -H 'Accept: application/x-www-form-urlencoded' -X POST -d 'grant_type=client_credentials&client_id=clientId&client_secret=clientSecret' http://localhost:3000/oauth2/token
# client credentials grant with basic auth
$ curl -u clientId:clientSecret -H 'Accept: application/x-www-form-urlencoded' -X POST -d 'grant_type=client_credentials' http://localhost:3000/oauth2/token
# refresh token grant
$ curl -H 'Accept: application/x-www-form-urlencoded' -u clientId:clientSecret -X POST -d 'grant_type=refresh_token&refresh_token=wRv7bQiR8W7mEYTTlHsSXw4HL0DqP2qM12gQTN7XbSq74Z7JkNyUTv3ZeN' http://localhost:3000/oauth2/tokenExpress endpoints
token
This endpoint has been used with [isClientAuthenticated](https://github.com/thinkingmik/ideman#isclientauthenticated) middleware and returns an access token.
Example
router.route('/oauth2/token').post(ideman.isClientAuthenticated, ideman.token);logout
This endpoint has been used with [isAuthenticated](https://github.com/thinkingmik/ideman#isauthenticated) middleware and revokes the current token.
Example
router.route('/oauth2/logout').post(ideman.isAuthenticated, ideman.logout);About authorizations
OAuth 2.0 is the next evolution of the OAuth protocol which was originally created in late 2006. OAuth 2.0 focuses on client developer simplicity while providing specific authorization flows for web applications, desktop applications, mobile phones, and living room devices.
Basic authentication
HTTP Basic authentication implementation is the simplest technique for enforcing access controls to web resources because it doesn't require cookies, session identifier and login pages. This authentication method uses static, standard fields in the HTTP header.
Use cases
- service calls
Example request
Send in the user credentials directly in the header to call a protected resource:
$ curl -u userId:userPwd -X GET http://localhost:3000/usersAuthorization Code
The Authorization Code grant type is used when the client wants to request access to protected resources on behalf of another user (i.e. a 3rd party). This is the grant type most often associated with OAuth.
Use cases
- calls on behalf of a third party
Example request
First, redirect the user to the following URL:
http://localhost:3000/oauth2/authorize?client_id=client&response_type=code&redirect_uri=http://localhost:3000A successful authorization will pass the client the authorization code in the URL via the supplied redirect_uri:
http://localhost:3000/?code=0tlpnc37ElYTa7ShOnce this is done, a token can be requested using the authorization code.
$ curl -u clientId:clientSecret -H 'Accept: application/x-www-form-urlencoded' -X POST -d 'grant_type=authorization_code&code=0tlpnc37ElYTa7Sh&redirect_uti=http://localhost:3000' http://localhost:3000/oauth2/tokenA successful token request will return a standard access token in JSON format:
{
"access_token":"NgvhmoKm9ASMCa3KGLh2yjNPqhIhFLEgPacesMFiIOQPuZ1Mq19XgEOprNnKuxi8DGcK59aAdUCBhqosKV8GDw4pwxdzoryjaViv0GQi04d0cWFZRxxqheF552h7Ok7SOCL8ndxPgLqf5WzSy22zHF5Hhg8SZPRCAPOos6QgDi4oZnR0EGkyhEgHCchKUDmfmHWxlqCuZDau2gswwx41h9ZhozvLVJJIB0rB4huVGg7sCyqtbo5Lg1M61BwjahWd",
"refresh_token":"wRv7bQiR8W7mEYTTlHsSXw4HL0DqP2qM12gQTN7XbSq74Z7JkNyUTHLgxg0CIBY13G2O46PAWNUtaoDgWRYONYR7u9npnkQeCAXqi2MFuXfJnrWOCFf3nJiWU76cZUV1jgQzR1I2YQngfTvvT96gO8qCrhLjikWDzMnv9yexwubEfX7LcG5eekzKQrW9UwZgP3kaas46nEUXs5G5VY8Fl79JnHk0uDvKZR1ERsdbtHwVWkZSmGC5TEiRfPVv3ZeN",
"expires_in":3600,
"token_type":"Bearer"
}User Credentials
The User Credentials grant type (a.k.a. Resource Owner Password Credentials) is used when the user has a trusted relationship with the client, and so can supply credentials directly.
Use cases
- when the client wishes to display a login form
- for applications owned and operated by the resource server (such as a mobile or desktop application)
- for applications migrating away from using direct authentication and stored credentials
Example request
Send in the user credentials directly to receive an access token:
# using POST Body
$ curl -H 'Accept: application/x-www-form-urlencoded' -X POST -d 'grant_type=password&client_id=clientId&client_secret=clientSecret&username=userId&password=userPassword' http://localhost:3000/oauth2/token
# using HTTP Basic Authentication
$ curl -u clientId:clientSecret -H 'Accept: application/x-www-form-urlencoded' -X POST -d 'grant_type=password&username=userId&password=userPassword' http://localhost:3000/oauth2/tokenA successful token request will return a standard access token in JSON format:
{
"access_token":"NgvhmoKm9ASMCa3KGLh2yjNPqhIhFLEgPacesMFiIOQPuZ1Mq19XgEOprNnKuxi8DGcK59aAdUCBhqosKV8GDw4pwxdzoryjaViv0GQi04d0cWFZRxxqheF552h7Ok7SOCL8ndxPgLqf5WzSy22zHF5Hhg8SZPRCAPOos6QgDi4oZnR0EGkyhEgHCchKUDmfmHWxlqCuZDau2gswwx41h9ZhozvLVJJIB0rB4huVGg7sCyqtbo5Lg1M61BwjahWd",
"refresh_token":"wRv7bQiR8W7mEYTTlHsSXw4HL0DqP2qM12gQTN7XbSq74Z7JkNyUTHLgxg0CIBY13G2O46PAWNUtaoDgWRYONYR7u9npnkQeCAXqi2MFuXfJnrWOCFf3nJiWU76cZUV1jgQzR1I2YQngfTvvT96gO8qCrhLjikWDzMnv9yexwubEfX7LcG5eekzKQrW9UwZgP3kaas46nEUXs5G5VY8Fl79JnHk0uDvKZR1ERsdbtHwVWkZSmGC5TEiRfPVv3ZeN",
"expires_in":3600,
"token_type":"Bearer"
}Client Credentials
The Client Credentials grant type is used when the client is requesting access to protected resources under its control (i.e. there is no third party).
Use cases
- service calls (machine-to-machine authentication).
- calls on behalf of the user who created the client.
Example request
Send in the client credentials directly to receive an access token:
# using POST Body
$ curl -H 'Accept: application/x-www-form-urlencoded' -X POST -d 'grant_type=client_credentials&client_id=clientId&client_secret=clientSecret' http://localhost:3000/oauth2/token
# using HTTP Basic Authentication
$ curl -u clientId:clientSecret -H 'Accept: application/x-www-form-urlencoded' -X POST -d 'grant_type=client_credentials' http://localhost:3000/oauth2/tokenA successful token request will return a standard access token in JSON format:
{
"access_token":"NgvhmoKm9ASMCa3KGLh2yjNPqhIhFLEgPacesMFiIOQPuZ1Mq19XgEOprNnKuxi8DGcK59aAdUCBhqosKV8GDw4pwxdzoryjaViv0GQi04d0cWFZRxxqheF552h7Ok7SOCL8ndxPgLqf5WzSy22zHF5Hhg8SZPRCAPOos6QgDi4oZnR0EGkyhEgHCchKUDmfmHWxlqCuZDau2gswwx41h9ZhozvLVJJIB0rB4huVGg7sCyqtbo5Lg1M61BwjahWd",
"refresh_token":"wRv7bQiR8W7mEYTTlHsSXw4HL0DqP2qM12gQTN7XbSq74Z7JkNyUTHLgxg0CIBY13G2O46PAWNUtaoDgWRYONYR7u9npnkQeCAXqi2MFuXfJnrWOCFf3nJiWU76cZUV1jgQzR1I2YQngfTvvT96gO8qCrhLjikWDzMnv9yexwubEfX7LcG5eekzKQrW9UwZgP3kaas46nEUXs5G5VY8Fl79JnHk0uDvKZR1ERsdbtHwVWkZSmGC5TEiRfPVv3ZeN",
"expires_in":3600,
"token_type":"Bearer"
}Refresh Token
The Refresh Token grant type is used to obtain additional access tokens in order to prolong the client’s authorization of a user’s resources.
Use cases
- to allow clients prolonged access of a user’s resources
- to retrieve additional tokens of equal or lesser scope for separate resource calls
Example request
First, a refresh token must be retrieved using the Authorizaton Code or User Credentials grant types:
$ curl -H 'Accept: application/x-www-form-urlencoded' -X POST -d 'grant_type=password&client_id=clientId&client_secret=clientSecret&username=userId&password=userPassword' http://localhost:3000/oauth2/tokenThe access token will then contain a refresh token:
{
"access_token":"NgvhmoKm9ASMCa3KGLh2yjNPqhIhFLEgPacesMFiIOQPuZ1Mq19XgEOprNnKuxi8DGcK59aAdUCBhqosKV8GDw4pwxdzoryjaViv0GQi04d0cWFZRxxqheF552h7Ok7SOCL8ndxPgLqf5WzSy22zHF5Hhg8SZPRCAPOos6QgDi4oZnR0EGkyhEgHCchKUDmfmHWxlqCuZDau2gswwx41h9ZhozvLVJJIB0rB4huVGg7sCyqtbo5Lg1M61BwjahWd",
"refresh_token":"wRv7bQiR8W7mEYTTlHsSXw4HL0DqP2qM12gQTN7XbSq74Z7JkNyUTHLgxg0CIBY13G2O46PAWNUtaoDgWRYONYR7u9npnkQeCAXqi2MFuXfJnrWOCFf3nJiWU76cZUV1jgQzR1I2YQngfTvvT96gO8qCrhLjikWDzMnv9yexwubEfX7LcG5eekzKQrW9UwZgP3kaas46nEUXs5G5VY8Fl79JnHk0uDvKZR1ERsdbtHwVWkZSmGC5TEiRfPVv3ZeN",
"expires_in":3600,
"token_type":"Bearer"
}This refresh token can then be used to generate a new access token of equal or lesser scope:
$ curl -H 'Accept: application/x-www-form-urlencoded' -u clientId:clientSecret -X POST -d 'grant_type=refresh_token&refresh_token=wRv7bQiR8W7mEYTTlHsSXw4HL0DqP2qM12gQTN7XbSq74Z7JkNyUTHLgxg0CIBY13G2O46PAWNUtaoDgWRYONYR7u9npnkQeCAXqi2MFuXfJnrWOCFf3nJiWU76cZUV1jgQzR1I2YQngfTvvT96gO8qCrhLjikWDzMnv9yexwubEfX7LcG5eekzKQrW9UwZgP3kaas46nEUXs5G5VY8Fl79JnHk0uDvKZR1ERsdbtHwVWkZSmGC5TEiRfPVv3ZeN' http://localhost:3000/oauth2/tokenA successful token request will return a standard access token in JSON format:
{
"access_token":"vLBojG5gsVvP7EwIfu9OEAE1daWsicRLN4KmS4goRUdoJPagEx1rvOce1UVbQc2S8EVEP47A9KmWGqofyT94AE7zVowigyE4eobqVmNvb6z6yRHZNT2oaTZ486yThtrJ078SuqRhPRM67KG37c6KJTLDZPECYYZN3fefBFlFG9EbOFeAChszT6kXI96Q9uunZKRuadMEcl8PqueqDfJh203DPzDwwX33lufJYPgZGnZdaVeY11c26NwOkk68g6wx",
"refresh_token":"h5odKWZh9p3ueYDK10RljCblXbsPOKNjX0HhaV0EcCOn4DNm5PX8NtpEoWo2LTL717rNcHXF8LoosrDtrNn9BOLZHJVpuItfzM8pHJFB8gMBBE8NVkDSin1qvaRs8ubWxxLN8PE9qbZSvo4NBzsbhwLS49HMmL4z963S4YXWQrtu5t829NuWGvYU2UBlSNYIUsrBOZe9bW0XZJ5xEBdHZ4tBg06tSDE4VZTyGwtjk8HTkMAqybGwA8FB6UggRNr7",
"expires_in":3600,
"token_type":"Bearer"
}Credits
- oauth2orize by Jared Hanson
- knex by Tim Griesser
- bookshelf by Tim Griesser
- ldapjs by Mark Cavage
License
The MIT License
Copyright (c) 2017 Michele Andreoli http://thinkingmik.com