npm package: @leisurelink/trusted-app
Description: An Expressjs derived engine for web applications participating in LeisureLink's federated security.
Author: Phillip Clark
Latest version: 4.1.1

# trusted-app

A convention-based web framework, building on [Express](http://expressjs.com/), for APIs and websites participating in LeisureLink's federated security.

## Why

Whereas [Express 4](http://expressjs.com/) is an un-opinionated web framework for Node.js, _`trusted-app` must have a few opinions in order to satisfy LeisureLink's security principles_. Therefore, while it builds on Express, it adds more convention to the process of starting up an application.

The _**trusted**_ part of `trusted-app` means that the application participates in LeisureLink's federated security; therefore, `trusted-app` comes with the appropriate middleware to ensure callers are _authenticated_, _authorized_, their actions are _non-repudiable_, and activity is _audited_.

We also think that _trusted_ means _well behaved_, so `trusted-app` establishes conventional error handlers late in the middleware chain to ensure that clients get predictable responses, even in the face of unexpected errors.

Beyond these basic notions of making an application _trusted_, this version of `trusted-app` endeavors to stay out of your way so you can use the underlying Express the way you want.

## Trust

By _**trust**_, we really mean _**secure**_. Moreover, we _trust_ that our callers are _secure_ because they provide us with evidence of such with each request.

> We use a combination of [HTTP Signature](https://github.com/joyent/node-http-signature/blob/master/http_signing.md) and [JWT](https://jwt.io/) to establish trust; you may want to familiarize yourself with these, but it is not a pre-requisite.

## Install

```bash
npm install @leisurelink/trusted-app
```

## Quick Start

### Simple Use Adding Nothing

Using `trusted-app` is very similar to using `express`:

```javascript
var trusted = require('@leisurelink/trusted-app');

// trusted is-a express app.
let app = trusted();

app.get('/hello/:who', require('./routes/hello/get'));

app.start(3000);
app.shutdownOnProcessEvents('SIGTERM', 'SIGINT');
```

Of course the example above adds very little to bald Express; what would be the point of that?

### Making it _Trusted_

To make the application _trusted_, we need to configure and wireup some middleware. For this we'll need a few keys because trust is accomplished in-part by digital signature:

```javascript
var path = require('path');
var trusted = require('@leisurelink/trusted-app');
var Loggins = require('@leisurelink/skinny-loggins');

// trusted is-a express app.
let app = trusted();

// establish who we are as an endpoint, and who we trust
let trustedAuthorityUri = 'http://api.leisurelink.com:2999/';
let endpointPrivateKeyFile = path.normalize(path.join(__dirname, '../test-key.pem'));
let trustedPublicKeyFile = path.normalize(path.join(__dirname, '../test-key.pub'));

let trustOptions = {
  endpointKeyId: 'test/me', // this app's HTTP Signature identity
  endpointPrivateKeyFile,   // the private key this app uses for HTTP signatures
  trustedAuthorityUri,      // the URI of our trusted authority
  trustedIssuer: 'test',    // the trusted JWT issuer (authority's well-known name)
  trustedAudience: 'test',  // this is the JWT audience (non-matching tokens are untrusted)
  trustedPublicKeyFile      // this is the JWT issuer's public key, used to verify signatures.
};

app.establishLogger(new Loggins());
app.establishTrust(trustOptions);

app.get('/hello/:who',

  // demand that callers are also trusted endpoints
  trusted.demand.authenticatedEndpoint,

  require('./routes/hello/get')
  );

app.start(3000);
app.shutdownOnProcessEvents('SIGTERM', 'SIGINT');
```

With _trust_ established, and the new _demand_ middleware in place, the API will enforce security.

> Don't be intimidated by the extra code in this example &mdash; most of it deals with settings required to configure the trust relationship between the app and the _trusted authority_ (aka `authentic-api`). Notably, version 3+ of this module no longer has a direct dependency on `env-configurator`, but most likely your app _should_; _**never hard code these values**_, this example does so for brevity.

## Debug

If you think you're getting unexpected results, turn on debug to get some insight into what's going on:

```bash
> DEBUG=trusted* node app.js
```

## Use

`trusted-app` extends Express by adding to it's API.

### .establishLogger(logger, skip)

Associates the specified `logger` with the application. A logging mechanism is required by several security related middleware. The logger will be exposed on the app as a readonly property `.log`.

_arguements:_
* `logger` : _object, required_ &ndash; an object exposing conventional methods for logging - asserts `info`, `warn`, and `error` methods are present on the object.
* `skip` : _function, optional_ &ndash; a function that determines if a particular request should be logged. The function is given the Express `request` object and should return a _truthy_ response if the logging should be skipped.

_returns:_
* The `app` for chaining calls.

_example:_
```javascript
var Loggins = require('@leisurelink/skinny-loggins');

app.logger(new Loggins(), (req) => {
  // skip logging for the heath-check route; it occurs too frequently
  return req.baseUrl === '/health';
});

app.log.info('A logger is attached!');
```

> NOTE: `.establishLogger` uses `morgan` to shuttle messages from Express to the logger. If you need to customize morgan's behavior, don't use this method. However, be aware that the security middleware expects the app to have a `.log` property that resolves to the logger &mdash; you may have to assign it directly.

### .establishTrust(options)

Sets up security related middleware using the specified `options`, ensuring the app's secure participation in LeisureLink's federated security.

> NOTE: In generic terms, a _trusted authority_ is an API we trust to sign _auth-tokens_. The only implementation of _trusted authority_ is LeisureLink's `authentic-api`; therfore you should record in your brain-hole that when we say _trusted authority_ we mean `authentic-api` because **`authentic-api` is-a _trusted authority_**.

_arguements:_
* `options` : _object, required_ &ndash; an object specifying _trust_ related configuration options.
  * `endpointKeyId` : _string, required_ &ndash; the identity of the application, used to compose digital signatures when this app (as a _trusted-endpoint_) communicates with other services.
  * `endpointPrivateKeyFile` : _string, required_ &ndash; file system path to the key this application uses when producing digital signatures. **_The corresponding public key must be registered with the trusted authority before signatures will be trusted_**.
  * `trustedAuthorityUri` : _string, required_ &ndash; the base-URI of the _trusted authority_ used to authenticate and authorize callers.
  * `trustedIssuer` : _string, required_ &ndash; specifies the trusted JWT issuer; this is the well-known name of the trusted authority, tokens bearing a non-matching issuer are untrusted.
  * `trustedAudience` : _string, required_ &ndash; specifies the trusted JWT audience; tokens bearing non-matching audience are untrusted.
  * `trustedPublicKeyFile` : _string, required_ &ndash; file system path to the _trusted authority_'s public key; used to verify the _trusted authority_'s digital signature.

_returns:_
* The `app` for chaining calls.

When `.establishTrust` returns successfully, `app` has additional trust-related properties:

* `.auth.scope` &ndash; an instance of the `AuthScope` class configured according to your `options`.
* `.auth.client` &ndash; an instance of the `AuthenticClient` class


```javascript
// Logger must already be established for this to succeed!

app.establishTrust({
  endpointKeyId: 'this-app/self',
  endpointPrivateKeyFile: '/path/to/private-key.pem',
  trustedAuthorityUri: `https://uri.to.trusted-authority/`,
  trustedIssuer: 'test',
  trustedAudience: 'test',
  trustedPublicKeyFile: '/path/to/issuer-public-key.pub'
});
```

### .start(httpPort, httpsPort, tlsOptions)

Starts the underlying HTTP(S) server(s) on the specified ports.

_arguements:_
* `httpPort` : _number, required_ &ndash; the port on which the app will accept HTTP requests.
* `httpsPort` : _number, optional_ &ndash; the port on which the app will accept HTTPS requests.
* `tlsOptions` : _object, optional_ &ndash; TLS options passed as-is to the underlying [`https.createServer`, see nodejs.org's documentation for more info](https://nodejs.org/dist/latest-v4.x/docs/api/https.html#https_https_createserver_options_requestlistener).

_returns:_
* The `app` for chaining calls.

_events:_
* `starting` &ndash; emitted before the app create's the HTTP(S) server(s)
* `listening` &ndash; emitted when a server, either HTTP or HTTPS begins listening on a port.

_example (without TLS):_
```javascript
app.start(3000);
```

_example (with TLS):_
```javascript
app.start(3000, 3443, {
  key: fs.readFileSync('my/tls/key.pem'),
  cert: fs.readFileSync('my/tls/cert.pem')
});
```

### .shutdown(exitProcess, exitCode, suicideTimeoutMs)

Attempts a normal/friendly shutdown; aborting after the specified `suicideTimeoutMs`.

_arguements:_
* `exitProcess` : _boolean, optional &ndash; indicates whether the process should exit as a result of the shutdown. Default _false_.
* `exitCode` : _number, optional_ &ndash; specifies an exit code to use if exiting the process as a result of shutting down. Default 0.
* `suicideTimeoutMs` : _number, optional_ &ndash; specifies the number of milliseconds to wait for the app to shut down normally before forcing a process exit. Default 15000 milliseconds.

_events:_
* `shutting-down` &ndash; emitted when the app begins it's shutdown process

```javascript
app.shutdown(true, -1);
```

### .shutdownOnProcessEvents(evt, ...)

Hooks the specified process events so that when the event is observed the app's `.shutdown()` method is called.

_arguements:_
* `evt` : _string(s)_ &ndash; the name of one or more process events to monitor.

_returns:_
* The `app` for chaining calls.

_example:_
```javascript
app.shutdownOnProcessEvents('SIGTERM', 'SIGINT');
```

### Middleware and Demands

`trusted-app` comes with middleware that you may use _a la carte_. These are exported under the `.middleware` property:

* `.middleware.domainContext` &ndash; middleware that establishes a domain context; if used it should be very early in the middleware chain.
* `.middleware.domainCorrelation` &ndash; middleware that helps propagate a `correlation-id` across calls to micro-services; if used it must follow `domainCorrelation` and `remoteAuth` in the middleware chain.
* `.middleware.errorHandler` &ndash; middleware that performs LeisureLink's conventional error handling.
* `.middleware.localAuth` &ndash; middleware that establishes the local service's authority.
* `.middleware.remoteAuth` &ndash; middleware that establishes the remote endpoint's authority and if a user's `auth-token` is propagated with the call; establishes the user's authority.