An open API service indexing awesome lists of open source software.

https://github.com/oada/oada-certs

Generate and verify oauth dynamic client registration metadata
https://github.com/oada/oada-certs

Last synced: 12 months ago
JSON representation

Generate and verify oauth dynamic client registration metadata

Awesome Lists containing this project

README

          

# @oada/certs

[![npm](https://img.shields.io/npm/v/@oada/certs)](https://www.npmjs.com/package/@oada/certs)
[![Downloads/week](https://img.shields.io/npm/dw/@oada/certs.svg)](https://npmjs.org/package/@oada/certs)
[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://github.com/prettier/prettier)
[![License](https://img.shields.io/github/license/OADA/oada-certs)](LICENSE)

Use this to create/sign/interact with OADA developer certificates and any other
keys or signatures in the OADA and Trellis ecosystems.

## Installation

```shell
# If you want command-line tool:
yarn global add @oada/certs

# If you just want to use the JS libs in your project:
yarn add @oada/certs
```

## Command-line: setup an OADA domain folder

```shell
cd domain_folder
oada-certs --create-keys
oada-certs
```

NOTE: default is to look in the current folder for signing keys

## Command-line: sign a certificate

```shell
# creates signed_software_statement.js in current folder
oada-certs --signkey="./some_path_to_privatekey_pem" --sign=./some_path_to_unsigned_cert.js

# If you are hosting signing key with a jku:
oada-certs --signkey="./some_path_to_privatekey_pem" --signjku="https://some.jku.url" --signkid="someKeyIdAtThatJKU" --sign="./path_to_unsigned_cert.js"
```

## Command-line: validate/debug a certificate

```shell
# Note: caching is in-memory and therefore unused here
oada-certs --validate="signed_software_statement.js"

# If there are errors, they will print here. It will
# also tell you if the certificate is trusted
```

## Include library in TypeScript/JavaScript

```typescript
import * as oadacerts from '@oada/certs';

// If you don't pass a jku, it puts the public jwk for the
// sign key into the JWT automatically
try {
const signed_jwt_cert = await oadacerts.sign(payload, signkey, {
jku: 'https://url.to.some.jwkset',
kid: 'someKeyidInThatSet',
});
} catch (error: unknown) {
console.log(error, 'Error in signing certificate');
}

// Returns a promise:
const { trusted, payload, valid, details } = await oadacerts.validate(
signed_jwt_cert
);
// trusted = true if cert was signed by key on a trusted list
// payload = JSON object that is the decoded client certificate
// valid = true if cert was decodable with a correct signature
// details = array of details about the validation process to help with debugging a cert
// NOTE: if the certificate is untrusted, it cannot use a jku in the signature,
// it must use a jwk to be considered valid. This avoids fetching potentially malicious URL's.

// Self-explanatory utilities for working with JWK key sets (jwks):
oadacerts.jwksUtils.isJWK(key);
oadacerts.jwksUtils.isJWKset(set);
oadacerts.jwksUtils.findJWK(kid, jwks);
// jwkForSignature attempts to figure out the correct public JWK to use in
// validating a given signature. Uses an intelligent cache for remote-hosted
// jwk's.
// What makes this function tricky is the "hint":
// It was designed to make it simple to say something like:
// "hint: I looked up the JKU ot JWK on the signature, and it was from a trusted source."
// (i.e. hint = truthy),
// and it's truthy value is then either the JWKS (object) from the trusted source,
// or the jku (string) of the trusted source's jwks
// or
// "hint: I looked at my trusted sources and this one doesn't have a jwk or jku that matches."
// (i.e. hint === false)
// which means "just use the header on the signature because I have no outside reference that verifies it"
//
// - If boolean false, use the jku from the jose header and if no jku then use jose's jwk
// - If boolean true, throw error (it should have been either an object or a string)
// - If string, assume string is a jku uri, go get that URI and then check jose's jwk against it
// - If object and looks like a jwks, look for jose's jwk in the set
// - If object and looks like a jwk, compare that jwk with jose's jwk
const jwk = oadacerts.jwksUtils.jwkForSignature(jwt, hint, { timeout: 1000 });
```

## API

### _async_ `sign(payload, key, options)`

Generates a JWT with the given payload, signed with the given key.
Key should be a JWK, but it can also be a PEM string.

- `payload` _required_: The JSON payload to sign in the JWT.
- `key` _required_: JWK private key to sign with. If this is a string instead
of a JWK, it is assumed to be a PEM string.
- `options` _optional_: If you need to specify a given `kid` (key id) and `jku`
(JWK set URL) because your key is trusted, you can pass them here in
`options.header`. `options.header` is passed to `jose.JWS.createSign`.

### _async_ `validate(signature, options)`

- `sig` _required_: the signed JWT to validate
- `options` _optional_:
- `options.timeout` (default: 1000 ms): How long to wait on remote URL
requests.
- `options.trustedListCacheTime` (default: 3600 sec): How long to use
immediately use the cached value for the trusted list before waiting on the
request to finish.
- `options.additionalTrustedListURIs`: Any additional URLs (array of
strings) to include in the search for a trusted key.
- `options.disableDefaultTrustedListURI`: Only use the
additionalTrustedListURI's, not the default one.

Returns `{ trusted, payload, valid, header, details }`

- `trusted`: `true|false`: true if signing key was on the trusted list
- `payload`: the decoded payload
- `valid`: `true|false`: true if the JWT was a valid JWT and the signature
matched, says nothing about whether it was trusted.
- `details`: array of strings about the validation process that can be helpful
for debugging.

### `validate.TRUSTED_LIST_URI`

Exports the core `TRUSTED_LIST_URI` string for convenience as a property on the validate function.

### `validate.clearCache()`

Mainly for testing, you can clear the internal in-memory cache for all trusted lists with this function.

### `jwksUtils.isJWKSet(set)`

Return `true` if `set` looks like a JWK set (`jwks`)

### `jwksUtils.isJWK(key)`

Return `true` if `key` looks like a `jwk`.

### `jwksUtils.findJWK(kid, jwks)`

Search for the given `kid` (key id) in the `jwks` (JWK set)

### `jwksUtils.decodeWithoutVerify(jwt)`

Given a JWT, decode the header, payload, and signature without verifying them.
Returns `{ header, payload, signature }`

### _async_ `jwksUtils.jwkForSignature(jwt, hint, options)`

Returns to you the JWK necessary to validate a given JWT. Described above in detail in the javascript example.

### `jwksUtils.clearJWKSCache()`

Mainly for testing, clear the internal JWK key cache (i.e. previous `jku`-based keys that it has looked up). This is not the trusted list cache.

### `jwksUtils.getJWKSCache()`

Returns the JWK cache to you if you want to see it. Mainly for testing.

### `jwksUtils.cachePruneOldest()`

Eliminates the oldest entry from the JWK cache. Mainly for testing.

### _async_ `keys.create()`

Returns `{ public, private }`. Both are JWK's. You can use `private` to sign things.

### _async_ `keys.pubFromPriv(priv)`

Given a private JWK, return the corresponding public key as a JWK.

### `jose`

Exports the internally-used `node-jose` module for convenience.