A foundation for developing APIs with Node.js and Express.
UPDATE: A version of Footing which is designed for storing application data using MongoDB has been created. It can be found in the feature/mongodb-app
branch. The default version stores application data with MySQL. A command line interface to create Footing projects is currently in the works. It can be found on the cli
branch.
Footing is a foundation for developing APIs with Node.js and Express. The project is designed in a way to make it easy for developers to build secure APIs with minimal setup. Footing provides the ability to define public or private routes with or without CSRF protection.
Routes that are predefined and come with Footing include ones that allow registering users, authenticating users, and deleting users. Routes for testing CSRF and authentication functionality are also included.
Footing's purpose is to enable developers to create APIs without needing to implement an authentication system.
Footing includes...
dotenv
).csurf
).Routes that are private will require a Bearer token in the authentication header of the request. Upon a successful login request, an authentication token will be stored as a cookie, and also returned in the form of a JSON response. The token is in the form of a JWT, and it's secret is a unique ID that is stored in the user's session. The authentication system protects routes by first verifying that the token in the authentication header matches that of the cookie. Secondly, the system verifies the token with the secret that is stored in the user's session.
It's important to note that upon a successful login request, the user's session is regenerated and a new CSRF token will be returned. The CSRF token used to make the login request will no longer be valid.
The following list serves to warn users of what is not included. It does not serve as a comprehensive list of what is not included with Footing.
Footing does not include...
Requirements for developing REST APIs with Footing include...
Disclaimer: Integration tests have been tested for Node.js v10.15.3. The project was originally developed using Node.js v8.11.1; however, the integration tests will fail on v8.11.1 due to the version of npm package supertest
that v8.11.1 uses. That specific version of supertest
has an issue making requests and receiving responses that include more than one cookie.
cd
into the root of the project directory.npm install
to install the dependencies..env.dist
file and rename it to .env
.env
file and set the values for the environment variables (suggested/default values are included).npm test
to make sure the project is working correctly.npm start
to start the server.To configure environment variables, duplicate the .env.dist
file and rename it to .env
. Environment variables are predefined with default values. Change them as needed. The variables are used for...
Environment variables included are...
The routes that have already been defined are for...
/signup
./login
./delete_account
./c/tkn
./status
./test/csrf
./test/auth
./test/auth_csrf
.The routes above are defined in the src/config/routes.js
file. They are implemented in the src/routes/api/identification.js
file and the src/routes/api/health.js
file.
To change the default route endpoints, it is recommended that they are changed in the src/config/routes.js
file and not in the implementation file. This is recommended because the integration tests rely on the src/config/routes.js
file to test the correct routes.
Changing the route endpoints in the src/config/routes.js
file will ensure that the integration tests will still work correctly.
To define new routes, create a new routing file in the src/routes/api/
directory. Footing has been designed to automatically require
all files in that directory. The file should be set up as so...
/**
* Your routing file
*/
module.exports = function(app, config, routes) {
// Define routes here.
}
Each routing file is automatically passed the app, config and routes variables. This way, all routing files can access any global application variables.
To require requests to be authenticated, the route receiving the request will need to use the RequestAuthenticator function found in src/routes/middleware/auth_middleware.js
as middleware. This function requires an instance of the AuthHandler class found in the src/handlers/auth_handler.js
to be passed to it. See the example below.
/**
* Your routing file
*/
const AuthHandler = require('../../handlers/auth_handler.js');
const RequestAuthenticator = require('../middleware/auth_middleware.js');
module.exports = function(app, config, routes) {
const requestAuthenticator = RequestAuthenticator(new AuthHandler(config));
// Define routes here.
routes.protected.post('/example', requestAuthenticator, function(res, req) {
/* ... */
});
}
Unprotected routes (routes that do not include CSRF protection) are used by the router variables routes.unprotected
.
Protected routes (routes that include CSRF protection) are used by the router variables routes.protected
.
Example of defining a new PUBLIC route without CSRF protection.
routes.unprotected.post('/public_without_CSRF', function(res, req) {
return res.status(200).json({"200":"Unathenticated"});
});
Example of defining a new PUBLIC route with CSRF protection
routes.protected.post('/public_with_CSRF', function(res, req) {
return res.status(200).json({"200":"Unathenticated"});
});
Example of defining a new PRIVATE route without CSRF protection
routes.unprotected.post('/auth_without_CSRF', requestAuthethenticator, function(res, req) {
return res.status(200).json({"200":"Authenticated"});
});
Example of defining a new PRIVATE route with CSRF protection
routes.protected.post('/auth_with_CSRF', requestAuthethenticator, function(res, req) {
return res.status(200).json({"200":"Authenticated"});
});
Obtaining a CSRF token: GET: http://localhost:port/c/tkn
User Signup:
POST: http://localhost:port/signup
{
"email": "[email protected]",
"password": "password",
"confirmPassword": "password",
"_csrf": "N2MbkPwA-3cJSavajIlsW_61OPZ_5uoQr6QU"
}
User Login:
POST: http://localhost:port/login
{
"email": "[email protected]",
"password": "password",
"_csrf": "N2MbkPwA-3cJSavajIlsW_61OPZ_5uoQr6QU"
}
Private Route without CSRF protection:
POST: http://localhost:port/test/auth
HEADER: Authorization - Bearer {jwtAuthTokenValueHere}
Private Route with CSRF protection:
POST: http://localhost:port/test/auth
HEADER: Authorization - Bearer {jwtAuthTokenValueHere}
{
"_csrf": "N2MbkPwA-3cJSavajIlsW_61OPZ_5uoQr6QU"
}
Due to the amount of npm packages that offer data sanitization, and the lack of regulation/validity that is available for such packages, Footing does not offer XSS protection/data sanitization functions. However, comments are available in suggested locations for sanitizing input data. These comments are located in the src/controllers/user_controller.js
file.
It is recommended to implement a middleware function that sanitizes all input data for all requests.
The developer doc can be found in /docs
.