npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

client-http-keypair-authorization

v1.0.5

Published

Use Cavage public/private key message signing on HTTP Requests

Downloads

10

Readme

HTTP Cavage public key / private key authorization headers for authorizations

Cavage HTTP Key-Pair Authorization functions as an alternative for API tokens, OAuth, or JWT for REST and GraphQL APIs or web applications.

It works by having a web client sign HTTP headers and/or create a hash digest of the HTTP message body. In doing so, it verifies that the web client is the true creator of the message and that the message has not been tampered with during transport.

It can be used for:

  • Authentication and resource access restriction
  • Access throttling
  • Collecting usage statistics
  • and much, much more

Just like in traditional API token or OAuth system, a server expects to verify the the client has permission to access a resource such as a URL endpoint. However, with this system the client can also know if the server is the true creator of the HTTP response and that the message has not been tampered with during transport. Therefore this system creates two-way security for web applications.

How Traditional API Authorization Works

With API tokens, OAuth and JWT, the server creates a token that is given to the client. The client must store the token and send it to the server with each subsequent request that is verified by the server. If the client loses the token, it no longer has access to the resource. If another client else gains access to the token, the other client can access the resource as if they are the owner of the token.

How Key-Pair Authorization Works

With this HTTP key-pair authorization, the client generates a public key and private key. The private key is stored locally but does not need to be sent across the network. The public key is sent one time to the server, where it is stored and given an ID. This id is shared with the client. From there, only the public key ID is used to communicate about the client's public key.

When the client accesses a resource from a server, it makes a list of HTTP headers which will be used to create a cryptographic signature. This signature is signed using the client's private key and the list of headers is sent as a part of the signature. The server sends this list and the key ID it received when it registered its public key with the server in the Authorization HTTP header. The server then uses these same headers to verify the signature using the stored public key for that client.

Furthermore, the client can create a hash digest of the message body. The server can verify the hash to know that the message has not been altered since it was created. It can incorporate the digest in the signature to further verify that the digest has not been altered and that the creator of both the digest and the HTTP message body is the client.

This system has the added benefit of being able to work the other way around. It ensures that, once a server's public key is registered in a client application, all subsequent HTTP responses originated from the server.

How does Key-Pair Authorization Affect HTTP Requests

A normal HTTP Request might look like this:

POST /foo?param=value&pet=dog
Host: example.com
Content-Length: 34
Date: Mon, 11 Jan 2021 20:54:32 GMT
Content-Type: application/json; encoding=utf-8
Accept: application/json

{"hello":"world"}

In this example, the HTTP client is POSTING some JSON data to the url http://example.com/foo?param=value&pet=dog. The HTTP headers include the date and content type.

In HTTP keypair authorization, a subset of HTTP headers are used to create a message that is signed using a private key on the client. This signature and other information necessary to verify the signature are then described in the Authorization and/or Signature headers.

The client must share its public key with authorizing sever prior to using HTTP key-pair authorization. This public key is given an ID by the server, which is shared with the client, and which the client uses as a shorthand to tell the server which public key to use when verifying authorization.

Optionally, a digest of the HTTP message body may be included in the Digest header and used to create the signature also, to add an extra layer of security. If so, the algorithm is prepended to the digest with the format ALGORITHM=DIGEST.

POST /foo?param=value&pet=dog
Host: example.com
Content-Length: 34
Date: Mon, 11 Jan 2021 20:54:32 GMT
Content-Type: application/json; encoding=utf-8
Accept: application/json
Digest: SHA512=U0hBLTUxMj16RllORkk1anErY3FoT3ZIK3JSNzFHNmRZMU85bkNjMk9xczdWK0xCbkpYSWVrdEVwWTg4U0swdStjK29LR2xpaEp3NFFMdjc2d21NUHJlTEZmMms5Zz09
Authorization: algorithm="rsa-sha256",keyId="client-public-key-id",expires=1611235402,headers="(request-path) (expires) host content-length date digest",signature="TiJZTTihhUYAIlOm2PpnvJa/+15WOX2U0iKJ2LXsLecvohhRIWnwFfdHy4ci10mcv/UQgf2+bFf9lfFZUlPPdzckBNfXIqAjafM8XquJiw/t1v+pEGtJpaGASlzuWuL37gp3k8ux3l6zBKKbBVPPASkHVhz37uY1AXeMblfRbFE="

{"hello":"world"}

The server may then:

  • Use the keyId in the Authorization header to load a locally stored copy of the client's private key,
  • Reproduce the singing message by assembling the header and authorization data from the headers key
  • Verify the signature data using the client's public key, and the signing message, using the algorithm described in the Authorization header.

Why this library exists

This JavaScript module was created to give "Cavage" HTTP Signatures capabilities AJAX and REST API requests.

This enables HTTP authorization based on public key/private key encryption as an alternative to session cookies or API tokens.

For more information see Draft Cavage HTTP Signatures 12

Using Django Rest Framework on the server? Try the DRF Keypair authorization header library.

Installation

NPM:

npm install --save client-http-keypair-authorization-headers

Yarn:

yarn install --save client-http-keypair-authorization-headers

Browser:

Browser file can be found in dist.browser/http-keypair-auth.js

<script src="/path/to/http-keypair-auth.js"></script>

Creating and Exchanging Key Pairs

In this authorization system, the client must generate its own public and private keys, and then share the public key with the server. The server must store the public key and associate it with the client, and return to the client an ID for that public key. Of course the client and server must both support the key pair encryption and signature hashing algorithms.

Therefore, it is important to know how to generate a key pair, plus how to export that key pair to a format that can be stored or transferred across a network.

Generating Key Pairs

In Node:

let keyPair = {}
const { generateKeyPair } = require('crypto');
generateKeyPair('rsa', {
  modulusLength: 4096,
  publicKeyEncoding: {
    type: 'spki',
    format: 'pem'
  },
  privateKeyEncoding: {
    type: 'pkcs8',
    format: 'pem',
    cipher: 'aes-256-cbc',
    passphrase: 'top secret'
  }
}, (err, publicKey, privateKey) => {
  keyPair = { publicKey: publicKey, privateKey: privateKey };
});

In the browser

Of course there are many algorithms that can be used to generate a key pair. This example creates an ECDSA-P256 key pair which can be extracted (necessary for saving and sharing) and can be used to both sign and verify signatures (necessary for authorization).

var crypto = window.crypto || window.msCrypto

var keyPair = await crypto.subtle.generateKey(
  {
    name: 'ECDSA',
    namedCurve: 'P-256'
  }, true, ['sign', 'verify']
)
// resulting keyPair data structure will be: { privateKey: CrytoKey, publicKey: CryptoKey }

More information about SubtleCrypto.generateKey

Exporting Keys

In Node:

To export a key in Node, the crypto.PublicKey.export() and crypto.PrivateKey.export() methods may be used.

// export a public key, for storage and sharing with a server
const publicKeyPem = publicKey.export({
    type: 'spki',
    format: 'pem'
})

// export a public key, for storing locally
const publicKeyPem = privateKey.export({
    type: 'pkcs1',
    format: 'pem',
})

In the browser

// export a public key, for storage and sharing with a server
var publicKeyPem = HttpKeyPairAuthorizer.exportPublicKeyToPemString(
  keyPair.publicKey,
)

// export a public key, for storing locally
var privateKeyPem = HttpKeyPairAuthorizer.exportPrivateKeyToPemString(
  keyPair.privateKey,
)

Importing Keys

In Node:

const privateKeyString: string = '-----BEGIN PRIVATE KEY-----\n...'
const passphrase: string = 'I6lL3W7o3HAnpXldcdWm';  // Create a secret passphrase
const cipher: string = 'aes-256-cbc'; // pick a hash from cipher.getHashes()
const privateKey: typeof crypto.PrivateKeyObject = crypto.createPrivateKey({
  key: privateKeyString,
  encoding: 'utf-8'
});

const publicKeyString = '-----BEGIN PUBLIC KEY-----\n...';
const publicKey: typeof crypto.PublicKeyObject = crypto.createPublicKey({
  key: publicKeyString,
  encoding: 'utf-8'
});

In the browser

On the browser, algorithmParameters must be provided, which are compatible with the importKey supported formats

// Get a PEM private key, for example this ECDSA P-256 key
var edcsaP256KeyPem = '-----BEGIN EC PRIVATE KEY-----\n' +
  'MHcCAQEEIG65UDNLeeH2M0FJMq5sS66Zgbfo5HmeiYvSF0rvx+fLoAoGCCqGSM49\n' +
  'AwEHoUQDQgAE+YwQJ7xak48kmy4IhOLo3krj998lCeN95dCTA72TWaHQtwMraLPO\n' +
  'Kc2Z9V6olwQNiezfiSNq83Ln7EL3AOpp9g==\n' +
  '-----END EC PRIVATE KEY-----'

var algorithmParameters = {
  name: 'ECDSA',
  namedCurve: 'P-256'
}

var privateKey = HttpKeyPairAuthorizer.importPrivateKeyFromPemString(
  edcsaP256KeyPem,
  algorithmParameters
)

Send Requests to a Server

Typically requests are sent from a client to a server, but with this authorization mechanism, it is possible to send authorized messages the other way around.

Clients can create a HTTP header that includes a cryptographic signature of some HTTP headers. This header will include the keyId, the server's ID of the client's public key stored on the server.

For added security, the client can create a hash digest of the HTTP body to verify the HTTP body was not altered in transport.

Create an Authorization header

A client can sign HTTP headers to secure HTTP requests. Each header included provides a different layer of security.

| Header or property | Reason to include |Allowed on CORS | |--------------------|-------------------------------------------------------|----------------| | (request-target) | Verifies which endpoint the request was intended for |Yes | | Host | Verifies which server the request was intended for |No | | Date | Verifies that the request is unique |No | | (created) | Helps verify that the signature was not forged |Yes | | (created) | Helps verify that the signature was not forged |Yes | | Digest | Helps verify the message was not altered |Yes | | Content-Length | Helps verify the message was not altered |No |

Minimally, the Date header should be included in the HTTP request and in the signature creation.

Note: On cross-origin requests (CORS), Date, Host, and Content-Length headers are disallowed

Default headers example

The default signature will include only the HTTP Date header by default. As this header should change with each request, it guarantees that each request has a unique signature.

In Node:

// if not using in the browser:
import { HttpKeyPairAuthorizer, HttpMethod } from 'client-http-keypair-authorization-headers'

// Load a locally-stored private key
const privateKeyString = '-----BEGIN ENCRYPTED PRIVATE KEY-----...'
const passphrase = 'passphrase'
const privateKey = crypto.createPrivateKey({
  key: privateKeyString,
  passphrase: passphrase,
  cipher: 'aes-256-cbc'
})
const httpBody = '{"hello": "world"}'
const hashAlgorithm = 'SHA256'
const now = new Date();
const httpRequest = {
  method: HttpMethod.Post,
  path: '/foo?param=value&pet=dog',
  headers: {
    'Date': now.toUTCString(),
  },
  body: httpBody
}

// describe the
const authorizationParameters = {
  keyId: 'Test', // server's ID for client public key
  algorithm: 'SHA256',
  headers: []
}
const updatedHttpRequest = HttpKeyPairAuthorizer.createAuthorizationHeader(
  httpRequest,
  privateKey,
  authorizationParameters,
)
console.log(updatedHttpRequest.headers['Authorization']);
// 'algorithm="SHA256",keyId="Test",signature="iKKFBCekw5snRmcyEnpWLFXBXG8miig...",headers="date"'

In the browser:

// Load a private key
const privateKeyString = '-----BEGIN PRIVATE KEY-----...'
var algorithmParameters = {
  name: 'RSASSA-PKCS1-v1_5',
  hash: 'SHA-256'
}
var privateKey = HttpKeyPairAuthorizer.importPrivateKeyFromPemString(privateKeyString, algorithmParameters)

// Build a HTTP request
var now = new Date()
var httpBody = '{"hello": "world"}'
var httpRequest = {
  method: 'POST',
  path: '/foo?param=value&pet=dog',
  headers: {
    Date: now.toUTCString(),
  },
  body: httpBody
}

// Define the authorization parameters for building a signature
// it should be noted that CORS restricts the Date, Host,
// and Content-Length HTTP headers
// Do not include them in cross-origin requests
// keyId is the public key's identifier on the server
var authorizationParameters = {
  keyId: 'Test',
  algorithm: 'SHA256',
  headers: []
}
var digestHashAlgorithm = 'SHA256'

// Update the HTTP request with the header signature
var updatedHttpRequest;
HttpKeyPairAuthorizer.createAuthorizationHeader(
  httpRequest,
  privateKey,
  authorizationParameters,
  digestHashAlgorithm
).then(response => {
  updatedHttpRequest = response
})
console.log(updatedHttpRequest.headers['Authorization']);
// 'algorithm="SHA256",keyId="Test",signature="iKKFBCekw5snRmcyEnpWLFXBXG8miig...",headers="date"'

Full headers example

This example shows the full headers including a Digest (explained below)

In Node:

// if not using in the browser:
import { HttpKeyPairAuthorizer, HttpMethod } from 'client-http-keypair-authorization-headers'

// load the private key from memory
const privateKeyString = '-----BEGIN ENCRYPTED PRIVATE KEY-----...'
const passphrase = 'passphrase'
const privateKey = crypto.createPrivateKey({
  key: privateKeyString,
  passphrase: passphrase,
  cipher: 'aes-256-cbc'
})

// build a HTTP request
const httpBody = '{"hello": "world"}'
const now = new Date();
const httpRequest = {
  method: HttpMethod.Post,
  path: '/foo?param=value&pet=dog',
  headers: {
    'Host': 'example.com',
    'Date': now.toUTCString(),
    'Content-Type': 'application/json; encoding=utf-8',
    'Accept': 'application/json',
    'Content-Length': (httpBody.length * 2).toString(),
  },
  body: httpBody
}
// Configure signer
const authorizationParameters = {
  keyId: 'Test',
  algorithm: 'SHA256',
  created: Math.floor(now / 1000) - (60 * 60 * 24),
  expires: Math.floor(now / 1000) + (60 * 60 * 24),
  headers: [
    '(request-target)',
    '(created)',
    '(expires)',
    'host',
    'date',
    'digest',
    'content-type',
    'content-length'
  ]
}
const digestHashAlgorithm = 'SHA256'

// Apply signature headers to HTTP request
const updatedHttpRequest = HttpKeyPairAuthorizer.createAuthorizationHeader(
  httpRequest,
  privateKey,
  authorizationParameters,
  digestHashAlgorithm
)
console.log(updatedHttpRequest.headers['Authorization']);
// 'algorithm="SHA256",keyId="keyId",signature="iKKFBCekw5snRmcyEnpWLFXBXG8miig...",created=1234567890,expires=1234567890,headers="(request-target) (created) (expires) host date digest content-type content-length"'

In the browser:

// if not using in the browser:
// load the private key from memory
const privateKeyString = '-----BEGIN ENCRYPTED PRIVATE KEY-----...'
// Load a private key
var privateKey = HttpKeyPairAuthorizer.importPrivateKeyFromPemString(edcsaP256KeyPem, algorithmParameters)

// Build a HTTP request
var now = new Date()
var httpBody = '{"hello": "world"}'
var httpRequest = {
  method: 'POST',
  path: '/foo?param=value&pet=dog',
  headers: {
    Host: 'example.com',
    Date: now.toUTCString(),
    'Content-Type': 'application/json; encoding=utf-8',
    'Accept': 'application/json',
    'Content-Length': (httpBody.length * 2).toString(),
  },
  body: httpBody
}

// Define the authorization parameters for building a signature
// it should be noted that CORS restricts the Date, Host,
// and Content-Length HTTP headers
// Do not include them in cross-origin requests
// keyId is the public key's identifier on the server
var authorizationParameters = {
  keyId: 'Test',
  algorithm: 'SHA256',
  created: Math.floor(now / 1000) - (60 * 60 * 24),
  expires: Math.floor(now / 1000) + (60 * 60 * 24),
  headers: [
    '(request-target)',
    '(created)',
    '(expires)',
    'host',
    'date',
    'digest',
    'content-type',
    'content-length'
  ]
}
var digestHashAlgorithm = 'SHA256'

// Update the HTTP request with the header signature
var updatedHttpRequest;
HttpKeyPairAuthorizer.createAuthorizationHeader(
  httpRequest,
  privateKey,
  authorizationParameters,
  digestHashAlgorithm
).then(response => {
  updatedHttpRequest = response
})
console.log(updatedHttpRequest.headers['Authorization']);
// 'algorithm="SHA256",keyId="keyId",signature="iKKFBCekw5snRmcyEnpWLFXBXG8miig...",created=1234567890,expires=1234567890,headers="(request-target) (created) (expires) host date digest content-type content-length"'

Create a message digest

A message digest can be created to further validate the message body.

The digest will be included in the Digest HTTP header and can be bound to the Authorization header to make ensure the digest and message body are cryptographically signed.

In Node:

// if not using in the browser:
import HttpKeyPairAuthorizer from 'client-http-keypair-authorization-headers'

const httpBody = '{"hello": "world"}'
const hashAlgorithm = 'SHA-256'
const digest = HttpKeyPairAuthorizer.createDigestHeader(httpBody, hashAlgorithm)

console.log(digest)
// SHA256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=

In the Browser:

var digestHashAlgorithm = 'SHA-256'
HttpKeyPairAuthorizer.createDigestHeader(httpRequest.body, digestHashAlgorithm)
HttpKeyPairAuthorizer.createDigestHeader(httpRequest.body, digestHashAlgorithm).then(response => {
  httpRequest.headers['Digest'] = response
  console.log(response)
})
// SHA256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=

Using with fetch

// it should be noted that with CORS requests,
// `Date`, 'Host', and `Content-Length` headers are disabled
// and therefore cannot be included in the signature parameters
const response = await fetch(
  'http://example.com/foo?param=value&pet=dog', {
  mode: 'cors',
  headers: httpRequest.headers,
  body: httpRequest.httpBody
})

Using with Axios

// it should be noted that with CORS requests,
// `Date`, 'Host', and `Content-Length` headers are disabled
// and therefore cannot be included in the signature parameters
const response = await axios({
  method: 'POST',
  url: 'http://example.com/foo?param=value&pet=dog',
  data: httpBody,
  headers: { common: httpRequest.headers }
})

Using with http

const http = require('http')

let req = http.request(httpRequest, (chunk) => {
  // do something  with the inbound data
})

req.write(httpRequest.body)
req.end()

Verify Requests from a Client

When a message is received, it can verify the Authorization header to know if the sender and message are valid, authorized, and unmodified. Typically the sender is a client and the receiver is a server, but that is not strictly the case with this authorization mechanism.

Prior to receiving a request, the server must have a local copy of the client's public key and both client and server must agree on a unique ID for that key, as the client must tell the server which public key must be used to verify the signature it created. This guarantees the identify client to the server.

A signature which incorporates a digest makes it possible to verify the authenticity of the request body as well.

Verify the Signature

A client is expected to send the same data in an Authorization and Signature HTTP header, which will includes:

  • The public key ID as known on the server
  • The algorithm used to create the signature
  • The HTTP headers and other information used to generate the signed message
  • A base64-encoded cryptographic signature
  • Possibly the creation and expiration time of the signature

This can all be parsed by the HttpKeyPairAuthorizer class to verify the Authorization header.

In Node:

// if not using in the browser:
import HttpKeyPairAuthorizer from 'client-http-keypair-authorization-headers'
// crypto library required
import crypto from 'crypto'

// authorizationHeader pulled from a HTTP request
// public key loaded from a locally referenced public key ID, which
// was extracted from the authorizationHeader key="<public key id>"

// look up publicKeyString from keyId, provided in authorization header
const publicKeyString = '-----BEGIN PUBLIC KEY-----...'
// create a crypto.PublicKeyObject
const publicKey = crypto.createPublicKey({
  key: publicKeyString,
  type: keyTypeFromMemory,
  format: 'pem',
  encoding: 'base64'
})

// build a HttpRequest interface
// this example uses hard-coded data
// the actual header data required will be specified in the `headers=""`
// string within the `authorizationHeader`
HttpRequest = {
  method: 'get',
  path: '/local/path?query=parameters',
  headers: {
    'Host': 'example.com',
    'Date': 'Mon, 11 Jan 2021 20:54:32 GMT',
    'Content-Type': 'application/json; encoding=utf-8',
    'Accept': 'application/json',
    'Content-Length': '{"hello": "world"}'.length * 2,
    'Digest': 'abc123',
    'Authorization': authorizationHeader,
    'Signature': authorizationHeader
  },
  body: '{"hello": "world"}'
}

// verify the Authorization header
const doesVerify: string = HttpKeyPairAuthorizer.doesHttpRequestVerify(
  authorizationHeader,
  httpRequest,
  publicKey
)
console.log(doesVerify)
// true

In the browser:

// authorizationHeader pulled from a HTTP request
// public key loaded from a locally referenced public key ID, which
// was extracted from the authorizationHeader key="<public key id>"

// look up publicKeyString from keyId, provided in authorization header
const publicKeyString = '-----BEGIN PUBLIC KEY-----...'
// create a CryptoKey object
const algorithmParameters = {
  name: 'RSASSA-PKCS1-v1_5',
  hash: 'SHA-256'
}
const publicKey = await HttpKeyPairAuthorizer.importPublicKeyFromPemString(
  publicKeyString,
  algorithmParameters
)

// build a HttpRequest interface
// this example uses hard-coded data
// the actual header data required will be specified in the `headers=""`
// string within the `authorizationHeader`
httpRequest = {
  method: 'get',
  path: '/local/path?query=parameters',
  headers: {
    'Host': 'example.com',
    'Date': 'Mon, 11 Jan 2021 20:54:32 GMT',
    'Content-Type': 'application/json; encoding=utf-8',
    'Accept': 'application/json',
    'Content-Length': '{"hello": "world"}'.length * 2,
    'Digest': 'abc123',
    'Authorization': authorizationHeader,
    'Signature': authorizationHeader
  },
  body: '{"hello": "world"}'
}

// verify the Authorization header
const doesVerify = await HttpKeyPairAuthorizer.doesHttpRequestVerify(
  authorizationHeader,
  httpRequest,
  publicKey
)
console.log(doesVerify)
// true

The server is responsible for managing the lookup function for the keyId and loading a locally stored public key belonging to the client that sent the request.

Verify Digest

This feature is currently not available in the browser.

A request may contain a Digest header, which verifies the request body was not altered during transit. It is not cryptographically signed, so a middle man may be able to create a new digest.

However, when the Authorization header is signed using the Digest header, the request recipient can know that the digest (and therefore the request body) has not been tampered with.

In Node

// if not using in the browser:
import HttpKeyPairAuthorizer from 'client-http-keypair-authorization-headers'

const staticDigestHeader = 'SHA256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE='
const httpBody = '{"hello": "world"}'
const doesDigestVerify = HttpKeyPairAuthorizer.doesDigestVerify(httpBody, staticDigestHeader)
console.log(doesDigestVerify)
// true

In the browser:

const staticDigestHeader = 'SHA256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE='
const httpBody = '{"hello": "world"}'
const doesDigestVerify = await HttpKeyPairAuthorizer.doesDigestVerify(httpBody, staticDigestHeader)
console.log(doesDigestVerify)
// true

Known Issues

Right now, HTTP headers in the HttpRequest and HttpHeaders objects are case-sensitive, and are expected to be in Title-Case. lower-case, UPPER-CASE, or mIXed-cAse will result in the HttpKeyPairAuthorizer being unable to find the right headers when generating signatures.

Building

$ npm run-script build

Running unit tests

$ npm test

  Gets (request-target) from HttpRequest
    ✓ Can create a (request-target) from HttpRequest

  Passphrase
    Generate passphrase:
      ✓ passphrases should exist
      ✓ passphrases should be a string
      ✓ passphrases should be of length `.defaultPassphraseLength`
    Save passphrase:
      ✓ can store a passphrase

  Digests
    Can create a digest from a HttpRequest body
      ✓ Generates valid SHA256 hash
    Verifies digests
      ✓ Matching digests return true
      ✓ Mismatched digest returns false

  Signing messages
    Throws error if no "date" header
      ✓ Error thrown
    Can create a "default" singing message from HttpRequest
      ✓ message verifies
    Can create a "basic" singing message HttpRequest
      ✓ message verifies
    Can create an "all headers" singing message HttpRequest
      ✓ message verifies

  HTTP authorization signatures
    Can parse authorization signatures
      ✓ Parses "default" signature
      ✓ Parses "basic" signature
      ✓ Parses "all headers" signature

  Message signatures
    Signature creation
      ✓ signature is a string
      ✓ signature is valid
    Signature verification
      ✓ Returns false if HTTP headers missing
      ✓ Returns false if authorization parameters headers missing
      ✓ Returns false if created in the future
      ✓ Returns false if expires in the past
      ✓ Returns false if no Date HTTP header and empty headers key
      ✓ Returns false if signature cannot be verified
      ✓ Returns true if if signature can be verified
    Verifies HTTP Requests
      ✓ Returns true on valid HttpRequest
      ✓ Returns false on on mismatched Authorization and Signature headers
      ✓ Returns false if authorization invalid

  Http authorization headers
    Header creation
      ✓ signature is a string
      ✓ signature is valid

  HttpRequest tests
    Creates and Verifies Digest
      ✓ Creates and verifies signature and digest on a HTTP request
    Signs and Verifies HTTP Request
      ✓ Signs and verifies HTTP request
      ✓ Signs and digests and verifies HTTP request