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

@sphereon/oidc4vci-client

v0.0.1-unstable.0

Published

oidc4vp lib

Downloads

6

Readme

IMPORTANT this package is in an early development stage and does not support all functionality from the OID4VCI spec yet!

Background

A client to request and receive Verifiable Credentials using the OpenID for Verifiable Credential Issuance ( OID4VCI) specification for receiving Verifiable Credentials as a Holder.

Flows

Authorized Code Flow

This flow isn't supported yet!

Pre-authorized Code Flow

The pre-authorized code flow assumes the user is using an out of bound mechanism outside the issuance flow to authenticate first.

The below diagram shows the steps involved in the pre-authorized code flow. Note that wallet inner functionalities (like saving VCs) are out of scope for this library. Also This library doesn't involve any functionalities of a VC Issuer Flow diagram

Issuance Initiation

Issuance is started from a so-called Issuance Initiation Request by the Issuer. This typically is URI, exposed as a link or a QR code. You can call the IssuanceInitiation.fromURI(uri) method to parse the URI into a Json object containing the baseUrl and the IssuanceInitiationRequest payload object

import IssuanceInitiation from './IssuanceInitiation';

const initiationURI =
  'https://issuer.example.com?issuer=https%3A%2F%2Fserver%2Eexample%2Ecom&credential_type=https%3A%2F%2Fdid%2Eexample%2Eorg%2FhealthCard&credential_type=https%3A%2F%2Fdid%2Eexample%2Eorg%2FdriverLicense&op_state=eyJhbGciOiJSU0Et...FYUaBy';

const initiationRequestWithUrl = IssuanceInitiation.fromURI(initiationURI);
console.log(initiationRequestWithUrl);

/**
 * {
 *    "baseUrl": "https://server.example.com",
 *    "issuanceInitiationRequest": {
 *      "credential_type": [
 *        "https://did.example.org/healthCard",
 *        "https://did.example.org/driverLicense"
 *      ],
 *      "issuer": "https://server.example.com",
 *      "op_state": "eyJhbGciOiJSU0Et...FYUaBy"
 *    }
 * }
 */

Acquiring the Access Token

Now you will need to get an access token from the oAuth2 Authorization Server, using some values from the IssuanceInitiationRequest payload

Interfaces

export interface IssuanceInitiationRequestPayload {
  issuer: string; //REQUIRED The issuer URL of the Credential issuer, the Wallet is requested to obtain one or more Credentials from.
  credential_type: string[] | string; //REQUIRED A JSON string denoting the type of the Credential the Wallet shall request
  pre_authorized_code?: string; //CONDITIONAL The code representing the issuer's authorization for the Wallet to obtain Credentials of a certain type. This code MUST be short lived and single-use. MUST be present in a pre-authorized code flow.
  user_pin_required?: boolean; //OPTIONAL Boolean value specifying whether the issuer expects presentation of a user PIN along with the Token Request in a pre-authorized code flow. Default is false.
  op_state?: string; //OPTIONAL String value created by the Credential Issuer and opaque to the Wallet that is used to bind the sub-sequent authentication request with the Credential Issuer to a context set up during previous steps
}

export interface CredentialRequest {
  type: string | string[];
  format: CredentialFormat | CredentialFormat[];
  proof: ProofOfPossession;
}

export interface CredentialResponse {
  credential: W3CVerifiableCredential;
  format: CredentialFormat;
}

export interface CredentialResponseError {
  error: CredentialResponseErrorCode;
  error_description?: string;
  error_uri?: string;
}

Functions

Several utility functions are available

convertJsonToURI:

Converts a Json object or string into an URI:

import { convertJsonToURI } from './Encoding';

const encodedURI = convertJsonToURI(
  {
    issuer: 'https://server.example.com',
    credential_type: ['https://did.example.org/healthCard', 'https://did.example1.org/driverLicense'],
    op_state: 'eyJhbGciOiJSU0Et...FYUaBy',
  },
  {
    arrayTypeProperties: ['credential_type'],
    urlTypeProperties: ['issuer', 'credential_type'],
  }
);
console.log(encodedURI);
// issuer=https%3A%2F%2Fserver%2Eexample%2Ecom&credential_type=https%3A%2F%2Fdid%2Eexample%2Eorg%2FhealthCard&credential_type=https%3A%2F%2Fdid%2Eexample%2Eorg%2FdriverLicense&op_state=eyJhbGciOiJSU0Et...FYUaBy

convertURIToJsonObject:

Converts a URI into a Json object with URL decoded properties. Allows to provide which potential duplicate keys need to be converted into an array.

import { convertURIToJsonObject } from './Encoding';

const decodedJson = convertURIToJsonObject(
  'issuer=https%3A%2F%2Fserver%2Eexample%2Ecom&credential_type=https%3A%2F%2Fdid%2Eexample%2Eorg%2FhealthCard&credential_type=https%3A%2F%2Fdid%2Eexample%2Eorg%2FdriverLicense&op_state=eyJhbGciOiJSU0Et...FYUaBy',
  {
    arrayTypeProperties: ['credential_type'],
    requiredProperties: ['issuer', 'credential_type'],
  }
);
console.log(decodedJson);
// {
//   issuer: 'https://server.example.com',
//   credential_type: ['https://did.example.org/healthCard', 'https://did.example1.org/driverLicense'],
//   op_state: 'eyJhbGciOiJSU0Et...FYUaBy'
// }

createProofOfPossession

Creates the ProofOfPossession object and JWT signature

The callback function created using jose

// Must be JWS
const signJWT = async (args: JWTSignerArgs): Promise<string> => {
  const { header, payload, keyPair } = args;
  return await new jose.CompactSign(u8a.fromString(JSON.stringify({ ...payload })))
    // Only ES256 and EdDSA are supported
    .setProtectedHeader({ ...header, alg: args.header.alg })
    .sign(keyPair.privateKey);
};
const verifyJWT = async (args: { jws: string | Uint8Array; key: KeyLike | Uint8Array; options?: VerifyOptions }): Promise<void> => {
  // Throws an exception if JWT is not valid
  await jose.compactVerify(args.jws, args.key, args.options);
};

The arguments requested by jose and oidc4vci

const keyPair = await jose.generateKeyPair('ES256');

const jwtArgs: JWTSignerArgs = {
  header: {
    alg: 'ES256',
    kid: 'did:example:ebfeb1f712ebc6f1c276e12ec21/keys/1',
  },
  payload: {
    iss: 's6BhdRkqt3',
    aud: 'https://server.example.com',
    iat: 1659145924,
    nonce: 'tZignsnFbp',
  },
  privateKey: keyPair.privateKey,
  publicKey: keyPair.publicKey,
};

The actual method call

const proof: ProofOfPossession = await vcIssuanceClient.createProofOfPossession({
  jwtSignerArgs: jwtArgs,
  jwtSignerCallback: (args) => signJWT(args),
  jwtVerifyCallback: (args) => verifyJWT(args),
});
console.log(proof);
// {
//   "proof_type": "jwt",
//     "jwt": "eyJhbGciOiJSUzI1NiIsImtpZCI6ImRpZDpleGFtcGxlOmViZmViMWY3MTJlYmM2ZjFjMjc2ZTEyZWMyMS9rZXlzLzEifQ.eyJpc3MiOiJzNkJoZFJrcXQzIiwiYXVkIjoiaHR0cHM6Ly9zZXJ2ZXIuZXhhbXBsZS5jb20iLCJpYXQiOjE2NTkxNDU5MjQsIm5vbmNlIjoidFppZ25zbkZicCJ9.btetOcsJ_VOePkwlFf2kyxm6hEUvPRimf3M-Dn3Lmzcmt5QiPToXNWxe_0fEJlRf4Ith55YGB43ScBe6ScZmD1gfLELYQF7LLg97yYlx_Iu8RLA2dS_7EWzLD3ZIzyUGf_uMq3HwXGJKL-ihroRpRBvxRLdZCy-j62nAzoTsBnlr6n79VjkGtlxIjN_CLGIQBhc3du3enghY6N4s3oXFrxWMl7UzGKdjCYN6vSagDb0MURjdiDCsK_yX4NyNd0nGpxqGhVgMpuhqEcqyU0qWPyHF-swtGG5JVAOJGd_YkJS5vbia8UdyOJXnAAdEE1E62a2yUPahNDxMh1iIpS0WO7y6QexWXdb5fmnWDst89T3ELS8Hj2Vzsw1XPyk9XR9JmiDzmEZdH05Wf4M9pXUG4-8_7StB6Lxc7_xDJdk6JPbzFgAIhJa4F_3rfPuwMseSEQvD6bDFowkIiUpt1vXGGVjVm3N4I4Th4_A2QpW4mDzcTKoZq9MKlDGXeLQBtiKXmqs10Jvzpp3O7kBwH7Qm6VUdBxk_-wsWplUZC4IvCfv23hy2SyFnh5zC6Wtw3UcbrSH6LcD7g-RNTKe4fRekyDxqLRdEm60BOozgBoTNhnetCrQ3e7HrApj9EP0vqNyXdtGGWCA011HVDnz6lVzf5yijJB8hOPpkgYGRmHdRQwI"
// }