@oada/certs
Version:
Generate and verify JWT signatures (OAuth dynamic client registration certificates and Trellis document integrity signatures) in the Open Ag Data Alliance (OADA) and Trellis ecosystems
193 lines (139 loc) • 7.14 kB
Markdown
# /certs
[](https://www.npmjs.com/package/@oada/certs)
[](https://npmjs.org/package/@oada/certs)
[](https://github.com/prettier/prettier)
[](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 /certs
# If you just want to use the JS libs in your project:
yarn add /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 '/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.