Ignite Reactor: Authentication Service


Installation | Developers Guide | Version History

Overview

This service is comprised of a suite of resources and endpoints which are utilized to provide a secure and robust authentication platform for all other Ignite Reactor services. It is the backbone of the services layer and is where users, applications and permissions are managed and authenticated.

Technology

The authentication service is built from the Ignite Reactor: Service Framework and utilizes the following base technologies:
- Node.js
- Express
- MySQL and/or Mongodb
- Mocha (Unit Testing)

Installation


Prerequisites | Step-by-Step Guide | Environment Setup

The following will assist you in getting your environment setup to run this service.

Prerequisites

Step-by-Step

  1. Make sure you meet the prerequisites above

  2. Clone the repo:

     > git clone git@github.com:IgniteSocialMedia/authentication
    
  3. Navigate to the cloned repo:

     > cd authentication
    
  4. Install application modules:


    > npm install

    Several things will download, install and build, watch for any errors and fix as necessary

  5. Using your favorite editor update ./config/unit_test.json and ./config/development.json with your local MySQL database information (MongoDB is currently disabled).

    You'll want to change settings in the db->mysql->connection portion of the document, especially YOUR_PORT, YOUR_USERNAME, and YOUR_PASSWORD as shown here in the unit_test.json example:

     {
       "server": {
         "port": "3001",
         "ssl": false
       },
       "db": {
         "mysql": {
           "connection": {
             "host": "127.0.0.1",
             "port": YOUR_PORT,
             "user": "YOUR_USERNAME",
             "password": "YOUR_PASSWORD",
             "connectionLimit": 10,
             "waitForConnections": true,
             "queueLimit": 5,
             "debug": false
           },
           "options": {
             "database": "unit_authentication",
             "create_database": true
           }
         },
       },
       "testing": {
         "delay": 2000,
         "timeout": 10000
       }
     }
    
  6. Configure git to ignore your local configuration changes (preventing you from accidentally overriding the default configurations):

     > git rm --cached ./config/*.json
    
  7. Run the unit tests (you should see non-error results and all tests should pass)


    > npm test

    This should generate output similar to the following:

    > authentication@0.0.1 test /Users/michael/Projects/ignite/dev/services/authentication
    > ./node_modules/.bin/mocha --ui bdd --reporter spec ./tests
    Initializing application
    Configuration environment: unit_test
    Creating resources/routes
    Ignite Reactor: Authentication Service listening on port 3001...

    Several tests will run similar to the following example:

    info.js -----------------
    info

     controller
       GET /info
         ◦ should return a 200 and JSON with the correct "version", "name" and "description": GET /info 200 1ms - 99b
         ✓ should return a 200 and JSON with the correct "version", "name" and "description"


    After all of the tests have run you will get a report (nothing should report as failed here):


    156 passing (4 seconds)
    2 pending

    If you observe failures when running the unit tests you may not meet all prerequisites and if you are certain you do please contact someone for assistance.

  8. Generate the unit test coverage report


    > npm run-script coverage

    This will generate a coverage.html file in /docs

  9. Set a local environment variable called NODE_ENV to development so when the application runs locally it will use the development.json configurations (see Environment Configurations for more information):

     > echo export NODE_ENV=development >> ~/.bashrc ; source ~/.bashrc
    
  10. Generate the API documentation


    > npm run-script docs

    This will generate a living API document in /docs/api

  11. Start the service:

    > npm start
    
    The following output will be displayed, once you see the "listening on port xxxx" message your server is running:
    Initializing application
    Configuration environment: development
    Ignite Reactor:  Authentication Service listening on port 3000...
    Ignite Reactor:  Authentication Service listening on port 3001 (SSL)...
    ...
    
  12. Visit http://localhost:3000/info in your browser to see a basic API response. If successful you should see the following:

    {
      "version": "x.x.x",
      "name": "authentication",
      "description": "Ignite Reactor: Authentication Service"
    }
    
  13. Visit http://localhost:3000/docs/ in your browser to review the documentation.

  14. Build:

    > npm run-script build
    
    [Building](#building) should be come a habit before and after every push/pull. It will automate the above process and ensure everything is in order before you commit code as well as before you make changes.

Environment Configurations

The framework looks at the NODE_ENV environment variable to determine which set of configurations to load from the ./config/ directory. All unit tests are run using the unit_test environment so local developers don't blow away any development specific data when running tests (likewise we don't want to annihilate any other environment data when running tests in different environments).

Mac OS X users:

> echo export NODE_ENV=development >> ~/.bashrc ; source ~/.bashrc

Development Guide


Getting Started | Building | Documentation | Unit Testing | Test Coverage

The following sections detail some development guidelines and examples for working on this service.

Getting Started

Building

The framework provides a build script that will automatically perform common actions for the user. It will ensure the latest modules are installed, unit tests are passing, generate documentation and perform any other build commands nevessary for the application to function. To build the project run:

> npm run-script build

It is common practice to build prior to pushing any code into the main repository and it is also common to build whenever you pull an update to the project source code. This ensures you are working with the latest modules and have the latest documentation available. It is also a useful sanity check ensuring that all unit tests are passing before you begin work on the code base.

Documentation

The framework uses YUIDoc to parse and generate API documentation from source code comments. As you are creating new code and documenting it you will find the need to view your documentation as parsed by YUIDoc. There are two ways to accomplish this:

The easy way:

> npm run-script docs_server

This will run a [local documentation server] (http://localhost:2999) which will re-parse your source code whenever you refresh a page. This is useful when making constant tweaks to inline documentation and reviewing the rendered results real-time.

The traditional way:


> npm run-script docs
> npm start

This will generate the actual documentation in docs/api/ and run the service which includes the [full documentation] (http://localhost:3000/docs/). This method is more cumbersone as you have to constantly stop/start the service but will accomplish the same effect as the easier method mentined above. This method is required when working on YUIDoc theme changes as detailed below.

Working on the theme:

The YUIDoc parser utilizes a theme written primarily with CSS and Handlebars which is located in /docs/theme/. If you are working on the theme you will unfortunately have to use the traditional method of generating the API docs above as the docs_server method does not dynamically re-read the theme source upon generation.

Unit Testing

The framework utilizes Mocha for unit testing. The easiest way to run all tests is to run the npm test script:

> npm test

This will run every unit test in the entire suite. Test results are displayed in the spec format showing a heirarchy of what is being tested.

Also, it is possible to run a single unit test. Here is an example of running the unit test for the user resource:


> ./node_modules/.bin/mocha --ui bdd --reporter spec ./tests/user

Here are the results of the user unit test:


Initializing application
Configuration environment: unit_test
Ignite Reactor: Authentication Service listening on port 3003...
Ignite Reactor: Authentication Service listening on port 3004 (SSL)...

 

user.js -----------------
Created connection pool for MySQL database: unit_authentication
Re-installing databases for testing
Created MySQL table: users
Created MySQL table: access_tokens
Created MySQL table: app_api_keys
Created MySQL table: applications
Created default administrator user
Created MySQL table: app_users
Created MySQL table: user_audit_log
Created MySQL table: app_audit_log
Creating unit testing application: Unit Test Application
Created unit testing unexpiring access_token: a13d139addca11e28035ea395d28b0fc
Created unit testing unexpired access_token: a13d7222ddca11e28035ea395d28b0fc
Created unit testing expired access_token: a13ddaaaddca11e28035ea395d28b0fc
Created unit testing api key: a13e6ce0ddca11e28035ea395d28b0fc
Running tests


Now that the test setup has completed, test specific data is created:


Creating test data...
Test user 1 created
Test user 2 created

The tests will now execute. Note the heiarchy of indentation showing different levels of testing, we are first testing the user resource and inside of that we test the library, model, and controller. Each of those sub categories contain specific methods being tested and each method contains individual tests each with several assertions:


user
library
encryptPassword
✓ should generate a password hash (86ms)
✓ should not generate a password hash using invalid salt
verifyPassword
✓ should confirm a password against the correct hash (171ms)
✓ should reject a password against an incorrect hash (171ms)
model
admin account
✓ should exist and have a password
findByKey
✓ should find the admin user by username
✓ should return null if no user is found
✓ should return an error for invalid keys
findById
✓ should find the admin user by id
✓ should return null if no user is found
findAll
✓ should find 3 users
addUser
✓ should return errors for invalid users
✓ should return errors if the user already exists
✓ should add a new user (89ms)
updateUser
◦ should return errors for invalid users: [ { field: 'username',
message: 'Missing required field',
code: 400 } ]
✓ should return errors for invalid users (90ms)
◦ should update a user: []
✓ should update a user (176ms)
✓ should return errors if the user to update is not found
deleteUser
✓ should delete a user (91ms)
✓ should return errors if the user to delete is not found
authenticateUser
✓ should return an error when credentials are missing
✓ should return an error when credentials are incorrect (87ms)
✓ should return an access_token when credentials are correct (103ms)
✓ should return an access_token with the default expiration of 7200 seconds (89ms)
controller
GET /users
◦ should return a 200 and JSON with a list of users: GET /users?access_token=a13d139addca11e28035ea395d28b0fc 200 5ms - 602b
✓ should return a 200 and JSON with a list of users
◦ should return a 400 if called with no access token: GET /users 400 1ms - 73b
✓ should return a 400 if called with no access token
GET /users/
◦ should find the admin user: GET /users/1?access_token=a13d139addca11e28035ea395d28b0fc 200 2ms - 104b
✓ should find the admin user
◦ should return a 400 if called with no access token: GET /users/1 400 0ms - 73b
✓ should return a 400 if called with no access token
◦ should return a 404 if the user does not exist: GET /users/0?access_token=a13d139addca11e28035ea395d28b0fc 404 16ms - 59b
✓ should return a 404 if the user does not exist
POST /users
◦ should return a 400 if no user is passed: POST /users/?access_token=a13d139addca11e28035ea395d28b0fc 400 2ms - 222b
✓ should return a 400 if no user is passed
◦ should return a 409 if the user already exists: POST /users/?access_token=a13d139addca11e28035ea395d28b0fc 409 2ms - 71b
✓ should return a 409 if the user already exists
◦ should add a new user: POST /users/?access_token=a13d139addca11e28035ea395d28b0fc 200 90ms - 107b
✓ should add a new user (96ms)
PUT /users/
◦ should return errors for invalid users: PUT /users/9?access_token=a13d139addca11e28035ea395d28b0fc 400 4ms - 100b
✓ should return errors for invalid users (101ms)
◦ should update a user: PUT /users/10?access_token=a13d139addca11e28035ea395d28b0fc 200 89ms - 118b
✓ should update a user (185ms)
◦ should return errors if the user to update is not found: PUT /users/98726345?access_token=a13d139addca11e28035ea395d28b0fc 404 1ms - 78b
✓ should return errors if the user to update is not found
DELETE /users/
◦ should delete a user: DELETE /users/11?access_token=a13d139addca11e28035ea395d28b0fc 200 2ms - 104b
✓ should delete a user (98ms)
◦ should return errors if the user to delete is not found: DELETE /users/98726345?access_token=a13d139addca11e28035ea395d28b0fc 404 1ms - 66b
✓ should return errors if the user to delete is not found
POST /users/authenticate
◦ should return an error when credentials are missing: POST /users/authenticate 404 1ms - 70b
✓ should return an error when credentials are missing
◦ should return an error when there is no such user: POST /users/authenticate 404 1ms - 70b
✓ should return an error when there is no such user
◦ should return an error when there credentials are incorrect: POST /users/authenticate 401 89ms - 84b
✓ should return an error when there credentials are incorrect (94ms)
◦ should return an access token when credentials are correct: POST /users/authenticate 200 89ms - 51b
✓ should return an access token when credentials are correct (96ms)
 
 
40 passing (3 seconds)

Different reports can be generated, see the Mocha documentation for examples.

For a simple example of a unit test see the ./tests/info.js test. More advanced tests are also available in the ./tests/ directory.

Test Coverage

The framework comes bundled with a unit test coverage report generator which provides a detailed (line by line) interactive HTML report displaying total percentage of lines that are executed by the unit testing suite. This tool helps developers ensure that the maximum percentage of source code is being executed and tested when the unit testing suite is run.

Generating the coverage report is done with an npm script:

> npm run-script coverage
This yields results similar to the following:
> authentication@0.0.1 coverage /Users/michael/Projects/ignite/dev/services/authentication
> node ./bin/test_coverage.js
 
Generating unit test coverage report...
 
Preparing project...
Locating JavaScript files...
Applying coverage to 20 file(s)...
    ./controllers/access_token.js
    ./controllers/api_key.js
    ./controllers/application.js
    ./controllers/authentication.js
    ./controllers/info.js
    ./controllers/user.js
    ./lib/access_token.js
    ./lib/api_key.js
    ./lib/application.js
    ./lib/authentication.js
    ./lib/config.js
    ./lib/db/mysql.js
    ./lib/user.js
    ./lib/validation.js
    ./models/access_token.js
    ./models/api_key.js
    ./models/application.js
    ./models/authentication.js
    ./models/user.js
    ./server.js
Generating coverage report (this may take a while)...
Cleaning up...
Report generated:  /Users/michael/Projects/ignite/dev/services/authentication/docs/coverage.html

The coverage.html report can be opened in your browser and will display an overview of test coverage for the application.

Version History


The following represents major changes in each release version of this service.

v0.0.1