ideman

3.0.1 • Public • Published

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

alt text

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 ideman

Example

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/token

It 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/resource

Documentation

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 configuration

Returns

{Object} Singleton instance

The 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

init( options ) : void

Initialization of singleton instance.

Arguments

options  {Object} Ideman parameters

If 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 parameters

getDbConfig() : Object

Gets the ideman database configuration object.

Returns

{Object} Ideman db config

getBookshelf() : Object

Gets the Bookshelf instance.

Returns

{Object} Bookshelf instance

getPassport() : Object

Gets the passport instance.

Returns

{Object} Passport instance

It 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 name

Returns

{Object} Bookshelf model

Now 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 models

validateUserCredentials( username, password ) : Promise( Object )

Checks if user credentials are valid.

Arguments

username  {string} Username
password  {string} Clear password

Returns

{Object} Returns a promise with bookshelf `User` model

validateClientCredentials( name, secret ) : Promise( Object )

Checks if client credentials are valid.

Arguments

name    {string} Client name
secret  {string} Clear client secret

Returns

{Object} Returns a promise with bookshelf `Client` model

validateBearerToken( 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 check

Returns

{Object} Returns a promise with referred bookshelf `User` or `Client` model

exchangePassword( 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 token

Returns

{Object} Returns a promise with tokens

Example 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 token

Returns

{Object} Returns a promise with tokens

The 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 token

Returns

{Object} Returns a promise with tokens

The 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 tokens

Returns

{bool} Returns true

ldapAuthentication( username, password ) : Promise( string )

Checks credentials for the given user on LDAP.

Arguments

username  {string}  Username
password  {string}  Password

Returns

{string} Returns null if user was not found, otherwise the attribute value specified in configuration

Throws

{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 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/resource

isClientAuthenticated

This middleware has been used with token endpoint and checks for valid client credentials before getting an access token. See about authorizations 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/token

Express endpoints

token

This endpoint has been used with 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 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/users

Authorization 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:3000

A successful authorization will pass the client the authorization code in the URL via the supplied redirect_uri:

http://localhost:3000/?code=0tlpnc37ElYTa7Sh

Once 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/token

A 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/token

A 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/token

A 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/token

The 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/token

A 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

License

The MIT License

Copyright (c) 2017 Michele Andreoli http://thinkingmik.com

Package Sidebar

Install

npm i ideman

Weekly Downloads

1

Version

3.0.1

License

MIT

Last publish

Collaborators

  • thinkingmik