@leisurelink/magicbus-authentic

Magicbus middleware for integration with authentic.

Usage no npm install needed!

<script type="module">
  import leisurelinkMagicbusAuthentic from 'https://cdn.skypack.dev/@leisurelink/magicbus-authentic';
</script>

README

magicbus-authentic

Magicbus middleware for integration with authentic. This module exports middleware for both producers and consumers. The producer side middleware adds an "Authorization" header that uses the same signature scheme as our HTTP "Authorization" header. The consumer side uses the "Authorization" header to retrieve an AuthContext representing the producer and exports it for use by downstream middleware.

Installation

$ npm install @leisurelink/magicbus-authentic

Usage

This document assumes you're familiar with the usage of Magicbus and focuses on the particulars of this middleware. Read the documentation on magicbus for more general information.

In a Producer

var util = require('util');
var magicbus = require('@leisurelink/magicbus');
var DateHeader = require('@leisurelink/magicbus-date-header');

var serviceDomainName = 'my-domain';
var appName = 'my-publisher';

var signatureSpec = {
  keyId: 'producer-key-id',
  key: 'producer-private-key'
};

var broker = magicbus.createBroker(serviceDomainName, appName, 'amqp://localhost/');
var publisher = magicbus.createPublisher(broker);
publisher.use(new SignatureAuthorizationHeader.Producer(signatureSpec).middleware);

var messageToSend = {
  sender: 'loves receiver'
};

publisher.publish(messageToSend);

you can optionally send an AuthContext or jwt in publish options.auth

Signature Spec Options

The constructor for the producer middleware takes a specification for how the signature should be generated.

keyId - Required - The id of a public key the producer has registered in Authentic.

key - Required - The private key corresponding to the keyId.

headers - Optional - An array containing the names of headers to include in the signature. If not specified, it will use the default value ['Date']. You need to make sure all headers specified by this property actually exist in the amqp properties.headers object before the middleware executes or it will fail. See magicbus-date-header and magicbus-content-length-header for easily adding headers you want to include in the signature.

algorithm - Optional - The algorithm to use for generating the signature. If not specified, it will use the default value 'rsa-sha256'.

In a Consumer

var util = require('util');
var magicbus = require('@leisurelink/magicbus');
var DateHeader = require('@leisurelink/magicbus-date-header');

var Broker = MagicBus.Broker;
var Subscriber = MagicBus.Subscriber;

var serviceDomainName = 'my-domain';
var appName = 'my-publisher';

var signatureSpec = {
  keyId: 'producer-key-id',
  key: 'producer-private-key'
};

var authenticClient = new AuthenticClient('authentic-url', 'consumer-key-id', 'consumer-private-key');
var authScope = new AuthScope({...});

var broker = magicbus.createBroker(serviceDomainName, appName, 'amqp://localhost/');
var subscriber = magicbus.createSubscriber(broker);
subscriber.use(
    new SignatureAuthorizationHeader.Consumer(authenticClient, authScope)
      .middleware
    );
subscriber.on('some-event', function(message) {
    console.log(receivedMessage);
  });

Constructor Parameters

The consumer middleware requires an AuthenticClient and an AuthScope to be able to contact Authentic and produce an AuthContext that represents the producer of the message.

Accessing the AuthContext

there can be up to two contexts accessed via: message.properties.auth = { endpoint, user, origin }

Invalid Signatures

The consumer middleware will reject any messages that don't have a valid signature. This includes rejecting messages that don't include any "Authorization" header.

To handle a mixture of messages that have a signature and messages that don't, this library will need to be changed to not reject anonymous messages. Enforcing that only authenticated messages are handled by the end consumer could be implemented as downstream middleware, or left to the end consumer itself.

Out of Scope

This middleware is independent of and should be compatible with any message envelope. Message envelopes and any related middleware should be defined seperately.

Contributing

Running Tests

Run all tests with the usual command:

$ npm test

This will run all tests, including integration tests that require a running RabbitMQ server and a running Authentic server. To exclude the integration tests (like on a build server without access to RabbitMQ and Authentic), run:

$ npm run-script test-ex-integration

Integration Test Environment Variables

The integration tests allow you to override the values they user for connection strings, key ids, keys, etc. using environment variables. Read through /test/integration/send-receive.integration-tests.js to figure out what you need to set to run the tests in your environment.

Remaining Work

The consumer middleware still contains a lot of stuff that should be refactored to vanilla javascript.

There is no way to plug in a logging library. The consumer middleware includes a lot of console.log that should be replaced with calls to an injected logging library.

There's no caching of calls to authentic and there probably should be.