Koa Escher Authentication Middleware
koa-escher-auth is a Koa middleware designed to integrate Escher authentication into Node.js applications. It restricts access to routes by verifying incoming HTTP requests using Escher signatures and a configurable key pool. The package is currently stable at version 4.0.0, released in January 2023, with updates occurring on an irregular basis, typically for dependency upgrades or minor feature enhancements. Key differentiators include its tight integration with the Koa framework and its reliance on the `escher-keypool` for managing authentication credentials, ensuring secure, signed request processing. It is explicitly designed to work downstream of a body-parser middleware to correctly process request bodies for authentication. Escher itself is a stateless API authentication protocol based on AWS Signature Version 4.
Common errors
-
TypeError: Cannot read properties of undefined (reading 'rawBody')
cause The `koa-bodyparser` middleware was not applied or was applied after `koa-escher-auth.authenticator`.fixEnsure `app.use(bodyParser())` is placed before `app.use(escherAuth.authenticator())` in your Koa middleware chain. -
Error: Invalid Escher key pool
cause The `keyPool` configuration provided to the authenticator is not a valid JSON string or is malformed.fixVerify that your `escherConfig.keyPool` (or `SUITE_ESCHER_KEY_POOL` environment variable) contains a valid JSON string, using `JSON.stringify()` if constructing it from an object. -
Authentication Failed: Signature mismatch
cause The Escher signature generated by the client does not match the signature computed by the server. This is commonly caused by incorrect `credentialScope`, `keyId`, `secret`, client-server clock skew, or discrepancies in how the request is signed (e.g., included headers, body content).fixDouble-check that the `credentialScope`, `keyId`, and `secret` in your `escherConfig` on the server match the client-side configuration. Ensure server and client clocks are synchronized. Validate that the client is signing the request body and headers exactly as expected by the server.
Warnings
- breaking Version 4.0.0 introduced a breaking change where its internal dependency, `escher-keypool`, now uses `@emartech/json-logger` instead of `logentries-logformat` for logging. This change may require adjustments if your logging infrastructure or tests directly interacted with or expected the format of the previous logger.
- gotcha The `koa-escher-auth` middleware *must* be used downstream of a body-parser middleware (e.g., `koa-bodyparser`) that defines `request.rawBody`. Incorrect ordering will lead to authentication failures, especially for requests with bodies.
- gotcha The `keyPool` configuration parameter (whether passed directly or via environment variable `SUITE_ESCHER_KEY_POOL`) must always be a valid JSON *string*. Providing an object directly or an invalid JSON string will result in configuration errors.
- gotcha Node.js engine support was updated in v3.5.0 to `Node.js >=10.13.0 <19`. Using the package with Node.js versions outside this range (e.g., Node.js 19+) may lead to unexpected behavior or runtime errors due to dependency incompatibilities or changes in Node.js APIs.
- gotcha Prior to version 3.4.0, handling of empty POST requests and body validation was based on `request.rawBody`. This could cause issues with certain request types or body parser configurations. Version 3.4.0 updated this to validate the body based on `request.body`.
Install
-
npm install koa-escher-auth -
yarn add koa-escher-auth -
pnpm add koa-escher-auth
Imports
- authenticator
const escherAuth = require('koa-escher-auth'); app.use(escherAuth);import { authenticator } from 'koa-escher-auth'; - authenticator
import escherAuth from 'koa-escher-auth';
const escherAuth = require('koa-escher-auth'); app.use(escherAuth.authenticator(escherConfig)); - ctx.escherAccessKeyId
app.use(function(ctx) { console.log(ctx.escherAccessKeyId); });
Quickstart
import Koa from 'koa';
import bodyParser from 'koa-bodyparser';
import { authenticator } from 'koa-escher-auth';
// Load Escher configuration from environment variables or provide directly
const escherConfig = {
credentialScope: process.env.SUITE_ESCHER_CREDENTIAL_SCOPE ?? 'eu/app-id/ems_request',
keyPool: process.env.SUITE_ESCHER_KEY_POOL ?? JSON.stringify([
{ 'keyId': 'app-id_suite_v1', 'secret': 'app-id-secret', 'acceptOnly': 0 }
])
};
const app = new Koa();
// IMPORTANT: koa-bodyparser must be used before koa-escher-auth
app.use(bodyParser());
// Apply the Escher authenticator middleware
app.use(authenticator(escherConfig));
// Define a protected route handler
app.use(async (ctx) => {
// If authentication passes, the access key ID is available on ctx.escherAccessKeyId
ctx.body = `Hello world, ${ctx.escherAccessKeyId}! Request authenticated successfully.`;
});
const port = process.env.PORT || 3000;
app.listen(port, () => {
console.log(`Server listening on http://localhost:${port}`);
});