major-a
Simple user authentication and tracking middleware for Connect/Express.
The GitHub docs are much better
Major-A is user athentication/authorization, admin, and tracking middleware all rolled into one. It uses bcrypt to hash passwords and JSON web tokens for user authentication. It tracks user activities per session, with a new session beginning every time a user that has been inactive for 5 minutes makes a request. In addition to sessions, Major-A keeps an easily-interpretable running log of every users activity.
NOTE: MajorAnalytics can now also track resources that you have defined. For more information on resource tracking, see Tracking Resources
Table of Contents
- Overview Tracking
- Session Tracking
- Accessing User Tracking Data
- Tracking Resources
- Tracking resource with anonymous users
Getting Started:
$ npm install --save major-a
const m = require('major-a'); // Create majorA Utilitiesconst mRouter = m.majorRouter;const mAuth = m.majorAuth;const mAdmin = m.majorAdmin;const mTracking = majorA.majorAnalytics;
NOTE: You must connect to your MongoDB instance before you require Major-A
// Require mongooseconst mongoose = require('mongoose');// Connect to MONGO DBmongoose.connect('YOUR_MONGO_DB_STRING'); // Now you can require Major-Aconst m = require('major-a'); // Create majorA Utilitiesconst mRouter = m.majorRouter;const mAuth = m.majorAuth;const mAdmin = m.majorAdmin;const mTracking = majorA.majorAnalytics;
MajorRouter
MajorRouter contains three routes: one for registering a new user, one for logging in an existing user, and one that requires admin privileges and returns the tracking profile of the specified user.
Routes
/register
The /register
route supports the POST
HTTP verb and is used to register new users. Registration requires as email and a password. Data must be passed as JSON in the body of the request in an object whose key is 'authentication' like so:
{"authentication": { "email" : "example@example.com", "password" : "example" }}
Major-A supports user attributes besides just email and password. The following is an example of a complete user registration request:
{ "name": { "first": "FirstName", "last": "LastName" }, "username": "ASickName", "gender": "Male", "birthday": "3/1/1994", "authentication": { "email": "example@example.com", "password": "example" }}
This route creates a new user in the database and returns an authorization token in an object. The token is accessible through the 'token' key. This token should be saved on the client side and sent in the header of every request as the value of the key 'token'. This token represents the users credentials and is valid as long as the user has made a request in the last five minutes. Once the token has been invalidated, the user will have to sign back in.
/login
The /login
route supports the GET
HTTP verb and is used for logging in existing users. The email and password of the user must sent as a Base64 encoded string in the header of the request using Basic HTTP. The email and password MUST be separated by a colon BEFORE being encoded and the word 'Basic' with a space after it should prepend the encoded string. The following is an example of preparing a username and password for logging in.
// user email and passwordvar email = 'example@example.com';var password = 'password';// Concatenate username and password, separated with a colonvar authString = email + ':' + password;// Encode string in Base64authString = btoa(authString); // This is the final string that should be included in the headervar finalAuthString = 'Basic ' + authString;
This route returns an authorization token in an object. The token is accessible through the 'token' key. This token should be saved on the client side and sent in the headers of every request as the value of the key 'token'. This token represents the users credentials and is valid as long as the user has made a request in the last five minutes. Once the token has been invalidated, the user will have to sign back in.
/tracking/:id
The /tracking/:id
route supports the GET
HTTP verb and requires administrator privileges to access. The route returns the tracking information of the user whose _id corresponds to :id
in the route.
An example AJAX request to this point that will return the tracking info for a user with an _id of 12345678910
looks like this:
$.ajax.get('http://localhost:8888/tracking/12345678910', function(data) { // Log tracking data console.log(data);});
MajorAuth
The majorAuth middleware is used to grant or deny access to protected routes based on whether or not the user has an authorization token. mAuth is a function that must called in your middlware stack. It takes an optional paramater to allows non logged-in user to access the path, but this is primarily for resource tracking. Protecting a route is as easy as including majorAuth in your route middleware:
Getting Started:
NOTE: majorAuth should always be the first middle registered. DO NOT INCLUDE BOTH majorAdmin and majorAuth as middleware for the same route. majorAdmin takes care of checking the token. Including both majorAdmin and majorAuth would result in a two token checks which can screw up the tracking package.
const express = require('express');const mongoose = require('mongoose');// Create express appconst app = express();// Connect to MONGO DBmongoose.connect('YOUR_MONGO_DB_STRING');// require Major-Aconst m = require('major-a');// Create majorA Utilitiesconst mRouter = m.majorRouter;const mAuth = m.majorAuth;const mAdmin = m.majorAdmin; // Protected Routeapp.post('/someprotetedroute', mAuth(), (req, res) { // This will only run if the request the passes the authentication check // the user object is accessible through req.user // Do protected stuff here})
If the user making the request does not have an authorization token, a 401 Unauthorized will be returned and no further middleware will be executed.
NOTE: If you are not using majorAnalytics resource tracking, you can skip the next section
mAuth Optional Parameter
MajorAuth takes an optional boolean parameter, like mAuth(true)
or mAuth(false)
. This optional value only needs to be included on routes that retreive resources. A true
value will allows users that are not logged in to still access the resource, the analytics for the resource they access are just measured differently.
For more info about resource tracking, see Resource Tracking.
MajorAdmin
Getting Started
Major-A supports authentication for administrators through the use of a major.json file placed in the root directory of your project. You can add administrators to your project by placing their email address in an array with the key administrators
.
major.json
{ "administrators" : [ "admin@exmaple.com", "admin2@exmaple.com"]}
The majorAdmin middleware is used to grant or deny access to administrator routes based on whether or not the users email matches any of the in the major.json``` file. Making a route only accessible to administrators is as easy as:
NOTE: majorAdmin should always be the first middle registered. DO NOT INCLUDE BOTH majorAdmin and majorAuth as middleware for the same route. majorAdmin takes care of checking the token. Including both majorAdmin and majorAuth would result in a two token checks which can screw up the tracking package.
const express = require('express');const mongoose = require('mongoose');// Create express appconst app = express();// Connect to MONGO DBmongoose.connect('YOUR_MONGO_DB_STRING');// require Major-Aconst m = require('major-a');// Create majorA Utilitiesconst mRouter = m.majorRouter;const mAuth = m.majorAuth;const mAdmin = m.majorAdmin; // Protected Routeapp.post('/someprotetedroute', mAdmin, (req, res) { // This will only run if the request the passes the authentication check // the user object is accessible through req.user // Do protected stuff here})
If the user making the request does not have administrator privileges, a 401 Unauthorized will be returned and no further middleware will be executed.
majorAnalytics
Getting Started
MajorAnalytics is Major-A's built in analytics package. It will automatically create a useranalytics
collection in your database and stores records in it. It is broken into two parts: overview and sessions. All sessions belong to an overview and all overviews have exactly one owner, which is the user whose information the overview contains. A new session begins when the user logs in, and end after the user has not made a request for 5 minutes. The logout time for the session is then recorded as the time of the last request made during the session. MajorAnalytics tracks every request made by all users (user must be logged in) and generates the following information for each user.
The overview contains:
Record | As |
---|---|
The date/time the user joined | Date |
How many time the user has logged in | Number |
The total number of requests made by the user | Number |
The complete amount of time spent active by user | Time, milliseconds |
The date/time of the users most recent request | Date |
Each session contains
Record | As |
---|---|
The date/time the beginning of this session | Date |
The date/time of the last request made during this session | Number |
The number of requests made by the user during this session | Number |
The duration of this session | Time, milliseconds |
MajorAnalytics currently only supports tracking for logged in users. If someone submits an issue requesting tracking for non logged in users, I will make it a priority to add it.
Accessing user tracking information through API
A user with administrator privileges can access the tracking information of any user through the /tracking/:id
route in the majorRouter package where :id
is the id of the user whose data you wish to receive. For more information about the majorRouter tracking route see Tracking
Resource Tracking
MajorAnalytics provides an API for tracking resources. Resources can be anything that has a mongoose model and is stored in a MongoDB instance. Upon the creation of a new resource document, you must pass the _id of the document and the type of resource as a string to the majorAnalytics.createTracker
function like so:
NOTE: Resource tracking by default only works for resources whose access routes require mAuth or mAdmin. To use resource tracking on routes that do not require mAdmin or mAuth, see Using resource tracking without mAuth or mAdmin
const express = require('express');// Require Json Parser to handle POSTconst jsonParser = require('body-parser').json();// Require Event modelconst Event = require(__dirname + '/../models/event.js');// Require MajorAconst majorA = require('major-a');// Require MajorA Analyticsconst mTracking = majorA.majorAnalytics;// Require MajorA Authconst mAuth = majorA.majorAuth;// Require MajorA Adminconst mAdmin = majorA.majorAdmin; // Create new Express Router and exportconst eventRouter = module.exports = exports = express.Router(); //Create new eventeventRouter.post('/new', mAdmin, jsonParser, (req, res) => { // Create new event var newEvent = new Event(req.body); // Save params newEvent.name = req.body.name; newEvent.description = req.body.description; newEvent.date = req.body.date; newEvent.postedOn = new Date(); newEvent.owner_id = req.user._id; // Save new event newEvent.save((err, event) => { // Error or no data if(err || !event) { return res.status(500).json({ msg: 'Error creating event' }); } // Create New Tracker mTracking.createTracker(event._id, 'event'); // Return new event data res.status(200).json({ msg: 'Successfully Created', event: event }); })});
In this example, event._id
is passed the first parameter and the string event
is passed as the second. The Event
model is separate from MajorA and has been required in from /../models/event.js
. This will create a new tracking document and store it in the trackresources
collection of your Mongo instance.
After the document has been created, you must track every request made to the document. Pass the _id
of the resource requested and the _id
of the user making the request to the majorAnalytics.track
function. Here is an example of tracking an event resource when a user makes a request for it.
const express = require('express');// Require Json Parser to handle POSTconst jsonParser = require('body-parser').json();// Require Event modelconst Event = require(__dirname + '/../models/event.js');// Require MajorAconst majorA = require('major-a');// Require MajorA Analyticsconst mTracking = majorA.majorAnalytics;// Require MajorA Authconst mAuth = majorA.majorAuth;// Require MajorA Adminconst mAdmin = majorA.majorAdmin; // Create new Express Router and exportconst eventRouter = module.exports = exports = express.Router(); // Get single eventeventRouter.get('/detail/:id', mAuth(), (req, res) => { // Find event Event.findOne({_id: req.params.id}, (err, event) => { // Err finding event if(err) { return res.status(500).json({ msg: 'There was an error retrieving' }); } // No Event found if(!event) { return res.status(200).json({ msg: 'No event found' }); } // Track request mTracking.track(event._id, req.user._id); // Return event res.status(200).json({ event: event }); });})
We pass event._id
and req.user._id
to mTracking.track
to record the request. mTrack
updates the resource tracking document whose _id
corresponds to the event._id
that we passed as the first parameter.
Using resource tracking without mAuth or mAdmin
In order to use resource tracking on routes that do not require authorization, you must still include mAuth
and pass a true
parameter to mAuth
. This will allow non-logged in users to the access the path, the analytics will just be measured differently.
For more information about mAuth, see majorAuth