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

@globalid/issuer-toolkit

v1.4.1

Published

Library for credential issuers integrated with GlobaliD

Downloads

25

Readme

Issuer Toolkit

This is a library for credential issuers integrated with GlobaliD.

Installation

npm install @globalid/issuer-toolkit

Usage

The GidIssuerClient class is the primary component of the toolkit, providing several methods for issuing a credential.

The function to create a GidIssuerClient requires the client ID and secret of a developer app created in GlobaliD's developer portal.

const clientId = '...';
const clientSecret = '...';
const client = createGidIssuerClient(clientId, clientSecret);

The GidIssuerClient supports the typical flow for issuing a credential:

  1. Receive and validate a credential request.
  2. Encrypt and upload file claims (optional).
  3. Build and send a credential offer.

If anything goes wrong in that process, issuers can report an error, which notifies the prospective holder of a problem in the credential issuance.

Validating a Credential Request

The validateRequest method will check the validity of a CredentialRequest, which consists of the following properties:

  • data (optional) - Information about the credential being requested
  • gidUuid - UUID of the holder's GlobaliD identity
  • signature - Result of digitally signing the concatenation of the timestamp, threadId, and (if present) data, using the holder's private key
  • threadId - ID correlating interactions related to this credential request
  • timestamp - Time of the request as the number of milliseconds since the Unix epoch

Of those, the signature and timestamp are validated. The signature is verified using the public key corresponding to the holder's identity (identified by gidUuid). The timestamp must be no more than 5 minutes in the past or 1 minute in the future. If the credential request is invalid, an error is thrown.

This method also handles boilerplate error reporting. An InvalidSignatureError, StaleRequestError, or EagerRequestError is reported as a 600-16. All other errors are reported as a 600-7.

const threadId = '...';
const gidUuid = '...';
const credentialRequest = {
  threadId,
  gidUuid,
  timestamp: 1640995200000,
  signature: 'abcdefghijklmnopqrstuvwxyz',
  data: {
    givenName: 'Neville',
    birthDate: '1980-07-30'
  }
};

try {
  await client.validateRequest(credentialRequest);
} catch (error) {
  if (error instanceof IdentityNotFoundError) {
    // invalid identity (i.e., `gidUuid` does not exist)
  } else if (error instanceof PublicKeyNotFoundError) {
    // user has no public key
  } else if (error instanceof InvalidSignatureError) {
    // `signature` is invalid
  } else if (error instanceof StaleRequestError || error instanceof EagerRequestError) {
    // `timestamp` is outside acceptable range
  }
}

Uploading a File

The uploadFile method allows for encrypting and uploading a file to GlobaliD's S3 instance. The file is encrypted using AES and a randomly-generated 256-bit key, which is itself encrypted using the holder's public key.

const fileClaim = await client.uploadFile(gidUuid, {
  name: '8bfd3afe-8f0b-4583-836e-97cde534e304.foo.jpg',
  type: 'image/jpeg',
  content: Buffer.from(/* ... */)
});

The result of uploadFile is a FileClaimValue intended for use in a CredentialOffer (see Sending a Credential Offer). A FileClaimValue has the following properties:

  • decryptionKey - Symmetric key used to decrypt (via AES) the payload received by dereferencing the url. The key is encrypted using RSA and the holder's public key.
  • sha512sum - Checksum of the file's content
  • type - Media type of the file's content
  • url - Location of the encrypted file

Sending a Credential Offer

The sendOffer method allows sending an offer for a credential following a credential request. The method accepts a CredentialOffer, which has the following properties:

  • claims - Claims about the credential subject
  • contextUri - URI of a JSON-LD context describing the credential subject
  • description (optional) - Descriptive text about the credential being offered
  • name - Name of the credential being offered
  • schemaUri - URI of a JSON Schema describing the data schema of the credential subject's claims
  • subjectType - JSON-LD @type of the credential subject
  • threadId - ID correlating interactions related to this credential request
const claims = ;

const credentialOffer = {
  threadId,
  name: 'Government ID',
  description: 'Lorem ipsum dolor sit amet',
  contextUri: 'https://example.com/contexts/Person',
  schemaUri: 'https://example.com/schemas/Person',
  subjectType: 'Person',
  claims: {
    givenName: 'Neville',
    birthDate: '1980-07-30',
    avatar: fileClaim
  }
};

await client.sendOffer(credentialOffer);

Reporting an Error

If something goes wrong while fulfilling a credential request, you can report the error using the reportError method.

await client.reportError(threadId, '600-1');

Error Codes

| Code | Description | | -------- | ------------------------------------------------------------- | | 300-8 | Document unsupported | | 600-1 | General credential request failure | | 600-3 | Verification process was cancelled | | 600-7 | GlobaliD erred or is unavailable | | 600-8 | Issuer is unavailable | | 600-16 | Request validation failed |

Downloading a File

The toolkit offers the downloadFile utility function for downloading and optionally decrypting a file from a URL, presumably sent in the initial credential request. This function is essentially the inverse of GidIssuerClient's uploadFile.

In addition to a URL string, downloadFile accepts the following options:

  • decryptionKey - Symmetric key used to decrypt the downloaded file via AES. The file is assumed to be in plaintext if this option is absent.
  • privateKey - Asymmetric private key (typically the issuer's) used to decrypt the decryptionKey via RSA. The decryptionKey is assumed to be plaintext if this option is absent.
  • sha512sum - Checksum used to validate the integrity of the downloaded (and possibly decrypted) file
import { downloadFile } from '@globalid/issuer-toolkit';

const buffer1 = await downloadFile('http://example.com/unencrypted-file');
const buffer2 = await downloadFile('https://example.com/encrypted-file', {
  decryptionKey: request.data.avatar.key,
  privateKey: process.env.PRIVATE_KEY,
  sha512sum: request.data.avatar.checksum
});

Testing Utilities

Nock

The @globalid/issuer-toolkit/testing module provides functions for mocking the HTTP requests (using nock) made by GidIssuerClient. There are mock* functions for each GidIssuerClient method, as well as a clearMocks function for cleanup.

import * as GidIssuerClient from '@globalid/issuer-toolkit/testing';

afterEach(() => {
  GidIssuerClient.clearMocks();
});

test('request validation', async () => {
  GidIssuerClient.mockValidateRequest(gidUuid, publicKey);

  // call your code that uses GidIssuerClient#validateRequest...

  // assertions...
});

test('sending an offer', async () => {
  GidIssuerClient.mockSendOffer();
  // ...
});

Sinon

The @globalid/issuer-toolkit/testing/sinon allows Sinon users to create a GidIssuerClient stub.

import stubGidIssuerClient from '@globalid/issuer-toolkit/testing/sinon';
import sinon from 'sinon';

const GidIssuerClientStub = stubGidIssuerClient();

afterEach(() => {
  sinon.restore();
});

test('request validation', async () => {
  GidIssuerClientStub.validateRequest.withArgs(/* ... */).resolves();

  // call your code that uses GidIssuerClient#validateRequest...

  // assertions...
});

test('sending an offer', async () => {
  GidIssuerClientStub.sendOffer.withArgs(/* ... */).resolves();
  // ...
});

TypeScript

The issuer toolkit is written in TypeScript, so type declarations are bundled with the package.

Development

The following NPM scripts are available for development:

  • build – Runs the clean, genver, compile, lint, and format:check scripts to build the project
  • clean – Removes the output directory for a clean build
  • compile – Compiles TypeScript files with tsc
  • format – Formats the files with Prettier
  • format:check – Checks the formatting of the files with Prettier
  • genver - Generates a version module with genversion
  • lint – Lints the code with ESLint
  • lint:fix – Attempts to fix problems found by the linter
  • test – Tests the code with Jest
  • test:watch – Tests the code in watch mode