JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 486
  • Score
    100M100P100Q148549F

Package Exports

  • @digitalbazaar/ezcap-express

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@digitalbazaar/ezcap-express) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

ezcap express library (@digitalbazaar/ezcap-express)

Node.js CI

zcap's gettin' you down? Get on the Ezcap Express! Woot WOoot! 🚇🎉

Connect middleware that provides easy Authorization Capability (zcap) support for express.js HTTP servers and more.

Table of Contents

Background

This library provides node.js express middleware that can be used to protect resources on HTTP servers using Authorization Capabilities (zcaps). The library is configured with secure and sensible defaults to help developers get started quickly and ensure that their server code is production-ready.

Security

The security characteristics of this library are largely influenced by design decisions made by client and server software. For clients, implementers should pay particular attention to secure private key management. For servers, security characteristics are largely dependent on how carefully the server manages zcap registrations, zcap invocations, and zcap delegations. Bugs or failures related to client key management, or server zcap validity checking will lead to security failures. It is imperative that implementers audit their implementations, preferably via parties other than the implementer.

Install

  • Node.js 14+ is required.
git clone git@github.com:digitalbazaar/ezcap-express.git
cd ezcap-express
npm install

Usage

Define getRootController

async function getRootController({
  req, rootCapabilityId, rootInvocationTarget
}) {
  // get associated capability controller from database
  let controller;
  try {
    const record = await database.getMyThingById({
      id: rootInvocationTarget
    });
    controller = record.controller;
  } catch(e) {
    if(e.type === 'NotFoundError') {
      const url = req.protocol + '://' + req.get('host') + req.url;
      throw new Error(
        `Invalid capability identifier "${rootCapabilityId}" ` +
        `for URL "${url}".`);
    }
    throw e;
  }

  return controller;
}

Define documentLoader

import didIo from 'did-io';
import didKeyDriver from 'did-method-key';
import jldl from 'jsonld-document-loader';

const _documentLoader = new jldl.JsonLdDocumentLoader();

// support did:key
didIo.use('key', didKeyDriver.driver());

async function documentLoader(url) {
  let document;
  if(url.startsWith('did:')) {
    document = await didIo.get({did: url, forceConstruct: true});
    return {
      contextUrl: null,
      documentUrl: url,
      document
    };
  }

  // finally, try the base document loader
  return _documentLoader(url);
}

Define authorizeMyZcapInvocation

const {authorizeZcapInvocation} = require('ezcap-express');

async function authorizeMyZcapInvocation({expectedTarget, expectedAction} = {}) {
  return authorizeZcapInvocation({
    expectedHost: 'ezcap.example',
    getRootController,
    documentLoader,
    expectedTarget,
    expectedAction,
  });
};

Use authorizeMyZcapInvocation

import express from 'express';
import asyncHandler from 'express-async-handler';

const app = express();

app.post('/foo',
  authorizeMyZcapInvocation(),
  asyncHandler(async (req, res) => {
    // your code goes here
    // req.zcap is available to provide authz information
  }));

API Reference

The ezcap approach is opinionated in order to make using zcaps a pleasant experience for developers. To do this, it makes two fundamental assumptions regarding the systems it interacts with:

  • The systems are HTTP-based and REST-ful in nature.
  • The REST-ful systems center around reading and writing resources.

If these assumptions do not apply to your system, the zcapld library might be a better, albeit more complex, solution for you.

Looking at each of these core assumptions more closely will help explain how designing systems to these constraints make it much easier to think about zcaps. Let's take a look at the first assumption:

The systems are HTTP-based and REST-ful in nature.

Many modern systems tend to have HTTP-based interfaces that are REST-ful in nature. That typically means that most resource URLs are organized by namespaces, collections, and items: /<root-namespace>/<collection-id>/<item-id>. In practice, this tends to manifest itself as URLs that look like /my-account/things/1. The ezcap approach maps the authorization model in a 1-to-1 way to the URL. Following along with the example, the root capability would then be /my-account, which you will typically create and have access to. You can then take that root capability and delegate access to things like /my-account/things to let entities you trust modify the things collection. You can also choose to be more specific and only delegate to /my-account/things/1 to really lock down access. ezcap attempts to keep things very simple by mapping URL hierarchy to authorization scope.

Now, let's examine the second assumption that makes things easier:

The REST-ful systems center around reading and writing resources.

There is an incredible amount of flexibility that zcaps provide. You can define a variety of actions: read, write, bounce, atomicSwap, start, etc. However, all that flexibility adds complexity and one of the goals of ezcap is to reduce complexity to the point where the solution is good enough for 80% of the use cases. A large amount of REST-ful interactions tend to revolve around reading and writing collections and the items in those collections. For this reason, there are only two actions that are exposed by default in ezcap: read and write. Keeping the number of actions to a bare minimum has allowed implementers to achieve very complex use cases with very simple code.

These are the two assumptions that ezcap makes and with those two assumptions, 80% of all use cases we've encountered are covered.

authorizeZcapInvocation(options) ⇒ function

Authorizes an incoming request.

Kind: global function
Returns: function - Returns an Express.js middleware route handler.

Param Type Description
options object Options hashmap.
options.documentLoader object Document loader used to load DID Documents, capability documents, and JSON-LD Contexts.
[options.expectedAction] string The expected action for the invoked capability.
options.expectedHost string The expected host for the invoked capability.
[options.expectedTarget] string | Array.<string> The expected target(s) for the invoked capability.
[options.getExpectedRootCapabilityId] function Used to return the expected root capability identifiers for the expected targets.
options.getRootController function Used to get the root capability controller for the given root capability ID.
[options.logger] object The logger instance to use.
[options.suite] object The expected cryptography suite to use when verifying digital signatures.

Contribute

See the contribute file!

PRs accepted.

If editing the Readme, please conform to the standard-readme specification.

Commercial Support

Commercial support for this library is available upon request from Digital Bazaar: support@digitalbazaar.com

License

New BSD License (3-clause) © Digital Bazaar