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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@meeco/sd-jwt

v1.2.4

Published

SD-JWT implementation in typescript

Readme

npm npm

SD-JWT

This is an implementation of SD-JWT (I-D version 19) in TypeScript.

Functionalities

  • No cryptographic dependencies (BYOC):

    • [x] Hasher
    • [x] Signer
    • [x] Salt Generator
  • Issue SD-JWT:

    • [x] Support recursive disclosures (parent object and its keys)
    • [x] Support for nested objects
    • [x] Support for arrays
    • [x] Optional: public key binding (cnf)
    • [x] Optional: decoy digest
  • Verify SD-JWT:

    • [x] Support recursive disclosures (parent object and its keys)
    • [x] Support for nested objects
    • [x] Support for arrays
    • [x] Optional: public key binding (cnf) check against the key binding JWT if one was provided
    • [x] Optional: decoy digest
  • Additional:

    • [ ] Holder: a separate function for a holder to be able to create a key binding JWT (6.2.2)
  • Tests:

    • [x] Issuer SD-JWT tests
    • [x] Verifier SD-JWT tests
    • [x] Tests that check compatibility with SD-JWT generated by external libs
    • [x] e2e test
  • Release:

    • [x] Create CommonJS and ESM builds
    • [x] Documentation
    • [x] Publish on npm

BYOC (Bring Your Own Crypto)

This library implements the SD-JWT data structures and algorithms, but does not ship any cryptographic implementation itself. Instead, you pass in your own callback functions wherever crypto is needed. The library does not care which crypto library you use, only that your callback matches the expected shape — the snippets below are just examples of how you could implement each one (here using Node's crypto and jose); use whatever fits your runtime and key management instead. The right choice often depends on your platform: Node's built-in crypto module isn't available in the browser, so browser consumers typically reach for WebCrypto (crypto.subtle) or a library like jose that works across both Node and browser environments.

  • hasher(data: string) => string | Promise<string>. Hashes a disclosure string into the digest used in _sd arrays. Must match the algorithm declared in _sd_alg (default sha-256).

    Example implementation, using Node's crypto:

    import crypto from 'crypto';
    
    const hasher = (data) => {
      const digest = crypto.createHash('sha256').update(data).digest();
      return Buffer.from(digest).toString('base64url');
    };
  • signer(header, payload) => Promise<string>. Signs the issuer's JWT header + payload and returns just the signature part. Used by issueSDJWT.

    Example implementation, using jose:

    import { SignJWT, importJWK } from 'jose';
    
    const signer = async (header, payload) => {
      const issuerPrivateKey = await importJWK(ISSUER_KEYPAIR.PRIVATE_KEY_JWK, header.alg);
      return (await new SignJWT(payload).setProtectedHeader(header).sign(issuerPrivateKey)).split('.').pop();
    };
  • verifier(jwt: string) => Promise<unknown>. Verifies the signature of a compact JWT. Used by verifySDJWT; throws or rejects if the signature is invalid.

    Example implementation, using jose:

    import { jwtVerify } from 'jose';
    
    const verifier = async (jwt) => {
      const issuerPublicKey = await getIssuerKey();
      return jwtVerify(jwt, issuerPublicKey);
    };

API Overview

The library exposes functions at two levels:

  • issueSDJWT(header, payload, disclosureFrame, opts) — top-level function for issuers. Packs payload according to disclosureFrame, signs it with the signer you provide, and returns a compact, ready-to-send SD-JWT (JWT + disclosures). This is the function most consumers should call to issue a token. Internally it calls packSDJWT.
  • verifySDJWT(compactSDJWT, verifier, getHasher, opts) — top-level function for verifiers. Verifies the signature (and optional key binding) of a compact SD-JWT and returns the payload with disclosed claims resolved. Internally it calls unpackSDJWT.
  • packSDJWT(claims, disclosureFrame, hasher, opts) — lower-level building block used by issueSDJWT. Takes claims + a disclosureFrame and returns { claims, disclosures } as plain objects, with _sd digests but no signing or compact serialization. Useful if you need to pack claims without immediately signing/issuing.
  • unpackSDJWT(sdjwtPayload, disclosures, getHasher) — lower-level building block used by verifySDJWT. Resolves _sd digests in a payload against an array of disclosures, without doing any signature verification.
  • createSDMap(sdjwt, hasher) — utility that builds a map of which fields are selectively disclosable, and a lookup from digest to disclosure.

The disclosureFrame argument shared by issueSDJWT and packSDJWT is the main thing you construct by hand — the next section shows how.

Disclosure Frame

To pack or issue claims into a valid SD-JWT using packSDJWT() or issueSDJWT(), you build a Disclosure Frame that defines which properties/values should be selectively disclosable.
It should conform to the following type definition:

type ArrayIndex = number;
type DisclosureFrameSDAttributes = { _sd?: Array<string | ArrayIndex>; _sd_decoy?: number };
export type DisclosureFrame =
  | ({ [key: string | ArrayIndex]: DisclosureFrame } & DisclosureFrameSDAttributes)
  | DisclosureFrameSDAttributes;
};

_sd_decoy is an optional property that defines the number of decoy digests to add.

The examples below all call packSDJWT(claims, disclosureFrame, hasher) to turn claims + disclosureFrame into the resulting packed claims. Digest values are illustrative — the real ones depend on your hasher and randomly generated salts.

Examples:

set property as selectively disclosable

const claims = {
  firstname: 'John',
  lastname: 'Doe',
};

const disclosureFrame = {
  _sd: ['firstname'], // set firstname as selectively disclosable
};

const { claims: packed } = await packSDJWT(claims, disclosureFrame, hasher);

// packed
{
  _sd: ['LjgwZy8TNXmmPO9mNqVDtq3jiX5r3YS-P-qw2hBNYyU'],
  lastname: 'Doe',
}

nested property

const claims = {
  address: {
    street: '123 Main St',
    suburb: 'Anytown',
    postcode: '1234',
  },
};

const disclosureFrame = {
  address: {
    // set address.street and address.suburb as selectively disclosable
    _sd: ['street', 'suburb'],
  },
};

const { claims: packed } = await packSDJWT(claims, disclosureFrame, hasher);

// packed
{
  address: {
    _sd: [
      '02d7bUYevjfAzJ0Gr42ymHy66ezQVL7huNGBO68xSfs',
      'ai7P4vgPZ-Jk1QwL55BLQqtN2gwWy31-pi2VGWiIggs',
    ],
    postcode: '1234',
  },
}

Array item

const claims = {
  nicknames: ['Johnny', 'JD'],
};

const disclosureFrame = {
  nicknames: {
    _sd: [0, 1], // index of items in 'nicknames' Array
  },
};

const { claims: packed } = await packSDJWT(claims, disclosureFrame, hasher);

// packed
{
  nicknames: [
    { '...': 'yfhdm_aKTMgm666j79GoXr2mer2dBW0cFfap8iXnAzY' },
    { '...': 'EU0ORASnAlqNtRwttBXsGTISxQ6myFPMBHPE0Ds8aSE' },
  ],
}

Object in Arrays

const claims = {
  items: [
    {
      type: 'shirt',
      size: 'M',
    },
    'Towel',
    'Water Bottle',
  ],
};

const disclosureFrame = {
  items: {
    0: {
      _sd: ['size'], // `size` property of items[0]
    },
  },
};

const { claims: packed } = await packSDJWT(claims, disclosureFrame, hasher);

// packed
{
  items: [
    {
      _sd: ['7aGqCE9HepzELBi59BvxxriDiV7uiB4yHTyN1im_m4M'],
      type: 'shirt',
    },
    'Towel',
    'Water Bottle',
  ],
}

Array in Arrays

const claims = {
  colors: [
    ['R', 'G', 'B'],
    ['C', 'Y', 'M', 'K'],
  ],
};

const disclosureFrame = {
  colors: {
    0: {
      _sd: [0, 2], // `R` and `B` in colors[0]
    },
  },
};

const { claims: packed } = await packSDJWT(claims, disclosureFrame, hasher);

// packed
{
  colors: [
    [{ '...': '' }, 'G', { '...': '' }],
    ['C', 'Y', 'M', 'K'],
  ],
}

See packSDJWT Example below for the full function signature, including the hasher argument and options.

issueSDJWT Example

The issueSDJWT function takes the following arguments and returns a compact SD-JWT combined with the disclosures:

  • header — the JWT header, e.g. { alg: 'ES256', kid: 'issuer-key-id' }.
  • payload — the full set of claims to issue: typically some always-visible registered claims (e.g. iss, iat, exp, sub) plus any custom claims. disclosureFrame decides which of these (usually the custom ones) become selectively disclosable — the rest stay in plain view, left as regular JWT claims.
  • disclosureFrame — the Disclosure Frame above declaring which claims in payload should be selectively disclosable.
  • opts — an options object:
    • signer (required) — the signer function used to sign the JWT (see BYOC above).
    • hash (required){ alg: string; callback: Hasher }; alg is recorded in the SD-JWT as _sd_alg, and callback is the hasher function used to compute the _sd digests (see BYOC above).
    • cnf (optional) — Holder key material as a JWK, e.g. { jwk: holderKey }, embedded in the payload for public key binding.
    • generateSalt (optional) — a custom salt-generation function, used when creating disclosures and decoy digests.

Example using the jose library for the signer function and crypto for the hasher.

import crypto from 'crypto'
import { SignJWT, importJWK } from 'jose';

const signer = async (header, payload) => {
  const issuerPrivateKey = await importJWK(ISSUER_KEYPAIR.PRIVATE_KEY_JWK, header.alg);
  // Only the signature value should be returned.
  return (await new SignJWT(payload).setProtectedHeader(header).sign(issuerPrivateKey)).split('.').pop();
};

const hasher = (data) => {
  const digest = crypto.createHash('sha256').update(data).digest();
  const hash = Buffer.from(digest).toString('base64url');
  return Promise.resolve(hash);
};

const header = {
  alg: 'ES256',
  kid: 'issuer-key-id'
};

const payload = {
  iss: 'https://example.com/issuer',
  iat: 168300000,
  exp: 188300000,
  sub: 'subject-id',
  name: 'John Doe',
  email: '[email protected]'
};

const disclosureFrame = {
  _sd: ['name', 'email']
};

// Optional
const cnf = { jwk: holderKey }
const generateSalt = (length) => crypto.randomBytes(length).toString('hex');

const sdjwt = await issueSDJWT(header, payload, disclosureFrame, {
  hash: {
    alg: 'sha-256',
    callback: hasher,
  },
  signer,
  cnf,
  generateSalt
});

sdjwt is a string — a regular, signed JWT (<base64url-header>.<base64url-payload>.<signature>) with its disclosures appended, joined by ~:

<jwt>~<disclosure-for-name>~<disclosure-for-email>~

which, spelled out in full, is:

<base64url-header>.<base64url-payload>.<signature>~<disclosure-for-name>~<disclosure-for-email>~

For example:

eyJhbGciOiJFUzI1NiIsImtpZCI6Imlzc3Vlci1rZXktaWQiLCJ0eXAiOiJ2YytzZC1qd3QifQ.eyJpc3MiOiJodHRwczovL2V4YW1wbGUuY29tL2lzc3VlciIsImlhdCI6MTY4MzAwMDAwLCJleHAiOjE4ODMwMDAwMCwic3ViIjoic3ViamVjdC1pZCIsIl9zZCI6WyI2RGRid2ViY0Y2ZmlnTkUyTGpOVGFqVDFOVENWTVY1VHNMYUUycTFBeGZjIiwiamxKZnEwcXFrdnd3Z1BySGg2a2Z6TzJwN2hwRFlYMU12ZS02MmJIZ3BIRSJdLCJfc2RfYWxnIjoic2hhLTI1NiJ9.<signature>~WyJ2NEVHUzhKRzlTdW9TUjVGIiwibmFtZSIsIkpvaG4gRG9lIl0~WyJLeDlRelAybVl0TmM0UnZ3IiwiZW1haWwiLCJqb2huLmRvZUBleGFtcGxlLmNvbSJd~

Breaking that down:

  • JWT header (first, .-separated segment, base64url-decoded):

    {
      alg: 'ES256',
      kid: 'issuer-key-id',
      typ: 'vc+sd-jwt',
    }
  • JWT payload (middle, .-separated segment, base64url-decoded):

    {
      iss: 'https://example.com/issuer',
      iat: 168300000,
      exp: 188300000,
      sub: 'subject-id',
      _sd: [
        '6DdbwebcF6figNE2LjNTajT1NTCVMV5TsLaE2q1Axfc', // hash digest of the disclosure for the `email` claim
        'jlJfq0qqkvwwgPrHh6kfzO2p7hpDYX1Mve-62bHgpHE', // hash digest of the disclosure for the `name` claim
      ],
      _sd_alg: 'sha-256',
    }

    Note _sd is sorted alphabetically by digest value — packSDJWT sorts it so that the position of a digest in the array never leaks which claim it discloses.

  • Disclosure for name (the piece after the first ~, base64url-decoded):

    "WyJ2NEVHUzhKRzlTdW9TUjVGIiwibmFtZSIsIkpvaG4gRG9lIl0" // base64url encode of ["v4EGS8JG9SuoSR5F","name","John Doe"]

    v4EGS8JG9SuoSR5F is the random salt for this disclosure, produced by generateSalt. It's included so the digest can't be brute-forced from the (often guessable) key/value alone.

  • Disclosure for email (the piece after the second ~, base64url-decoded):

    "WyJLeDlRelAybVl0TmM0UnZ3IiwiZW1haWwiLCJqb2huLmRvZUBleGFtcGxlLmNvbSJd" // base64url encode of ["Kx9QzP2mYtNc4Rvw","email","[email protected]"]

    Kx9QzP2mYtNc4Rvw is this disclosure's own salt — every disclosure gets a fresh independent salt.

verifySDJWT Example

The verifySDJWT function takes the following arguments and returns the SD-JWT payload with all disclosed claims resolved:

  • sdjwt — the compact, combined SD-JWT string to verify (including optional disclosures & KB-JWT) — see the example produced by issueSDJWT in the previous section.
  • verifier (required) — the verifier function used to check the JWT signature (see BYOC above).
  • getHasher (required) — a function that, given the _sd_alg from the SD-JWT payload, returns the matching hasher used to resolve _sd digests.
  • opts — an options object:
    • kb.verifier (optional) — a Keybinding Verifier function that can verify the embedded holder key against the KB-JWT.

Example using the jose library for the verifier function and crypto for the hasher.

import { importJWK, jwtVerify } from 'jose';

const getHasher = (hashAlg) => {
  let hasher;
  // Default Hasher = Hasher for SHA-256
  if (!hashAlg || hashAlg.toLowerCase() === 'sha-256') {
    hasher = (data) => {
      const digest = crypto.createHash('sha256').update(data).digest();
      return base64encode(digest);
    };
  }
  return Promise.resolve(hasher);
};

const verifier = async (jwt) => {
  const key = await getIssuerKey(); // Get SD-JWT issuer public key
  return jwtVerify(jwt, key);
};

const keyBindingVerifier = (kbjwt, holderJWK) => {
  // check against kb-jwt.aud && kb-jwt.nonce
  const { header } = decodeJWT(kbjwt);
  const holderKey = await importJWK(holderJWK, header.alg);
  const verifiedKbJWT = await jwtVerify(kbjwt, holderKey);
  return !!verifiedKbJWT;
}

const opts = {
  kb: {
    verifier: keyBindingVerifier
  }
}

// The `sdjwt` string produced by the `issueSDJWT` example above.
const compactSDJWT =
  'eyJhbGciOiJFUzI1NiIsImtpZCI6Imlzc3Vlci1rZXktaWQiLCJ0eXAiOiJ2YytzZC1qd3QifQ.eyJpc3MiOiJodHRwczovL2V4YW1wbGUuY29tL2lzc3VlciIsImlhdCI6MTY4MzAwMDAwLCJleHAiOjE4ODMwMDAwMCwic3ViIjoic3ViamVjdC1pZCIsIl9zZCI6WyI2RGRid2ViY0Y2ZmlnTkUyTGpOVGFqVDFOVENWTVY1VHNMYUUycTFBeGZjIiwiamxKZnEwcXFrdnd3Z1BySGg2a2Z6TzJwN2hwRFlYMU12ZS02MmJIZ3BIRSJdLCJfc2RfYWxnIjoic2hhLTI1NiJ9.<signature>~WyJ2NEVHUzhKRzlTdW9TUjVGIiwibmFtZSIsIkpvaG4gRG9lIl0~WyJLeDlRelAybVl0TmM0UnZ3IiwiZW1haWwiLCJqb2huLmRvZUBleGFtcGxlLmNvbSJd~';

try {
  const sdJWTwithDisclosedClaims = await verifySDJWT(compactSDJWT, verifier, getHasher, opts);
} catch (e) {
  console.log('Could not verify SD-JWT', e);
}

Since compactSDJWT is the string issued in the issueSDJWT example above, sdJWTwithDisclosedClaims is a plain object with the _sd/_sd_alg digests resolved back into their original claims — i.e. the reverse of what issueSDJWT produced:

{
  iss: 'https://example.com/issuer',
  iat: 168300000,
  exp: 188300000,
  sub: 'subject-id',
  name: 'John Doe', // resolved from the disclosure
  email: '[email protected]', // resolved from the disclosure
}

packSDJWT Example

packSDJWT is the lower-level building block that issueSDJWT calls internally. Compared to issueSDJWT:

  • FunctionalitypackSDJWT only replaces selectively disclosable claims with _sd digests and returns the packed claims + disclosures as plain objects; it does not sign anything or produce a compact SD-JWT string. Use it directly if you need packed claims without immediately signing/issuing (e.g. to sign them yourself, or to inspect/store them before issuance).
  • ArgumentspackSDJWT has no header and no signer/cnf (there's nothing to sign or bind a holder key to at this level). Its hasher is passed directly as a plain function, rather than wrapped in issueSDJWT's { hash: { alg, callback } } (there's no _sd_alg to record, since packSDJWT doesn't produce a JWT payload).

The packSDJWT function takes the following arguments and returns the packed claims (with selective disclosures replaced by digests) and an array of disclosures:

  • claims (required) — the claims object (or array) to pack.
  • disclosureFrame (required) — the Disclosure Frame declaring which claims should be selectively disclosable.
  • hasher (required) — the hasher function used to compute the _sd digests (see BYOC above).
  • options (optional):
    • generateSalt (optional) — a custom salt-generation function, used when creating disclosures and decoy digests.

Basic Usage

import crypto from 'crypto';
import { packSDJWT } from 'sd-jwt';

const claims = {
  name: 'John Doe',
  email: '[email protected]',
};

const disclosureFrame = {
  _sd: ['name', 'email'],
};

const hasher = (data) => {
  const digest = crypto.createHash('sha256').update(data).digest();
  const hash = Buffer.from(digest).toString('base64url');
  return Promise.resolve(hash);
};

const options = {
  generateSalt: generateSaltFunction,
};

const { claims: packed, disclosures } = await packSDJWT(claims, disclosureFrame, hasher, options);

This will selectively disclose name and email — the same two claims disclosed in the issueSDJWT example above, so the digests and disclosures below match that example's exactly. packed is the original claims with those two replaced by _sd digests, and disclosures holds the corresponding Disclosure strings:

// packed
{
  _sd: ['6DdbwebcF6figNE2LjNTajT1NTCVMV5TsLaE2q1Axfc', 'jlJfq0qqkvwwgPrHh6kfzO2p7hpDYX1Mve-62bHgpHE'],
}

// disclosures
[
  'WyJ2NEVHUzhKRzlTdW9TUjVGIiwibmFtZSIsIkpvaG4gRG9lIl0', // base64url encode of ["v4EGS8JG9SuoSR5F","name","John Doe"]
  'WyJLeDlRelAybVl0TmM0UnZ3IiwiZW1haWwiLCJqb2huLmRvZUBleGFtcGxlLmNvbSJd', // base64url encode of ["Kx9QzP2mYtNc4Rvw","email","[email protected]"]
]

Partially Disclosing an Array

Unlike the Array item example above, which discloses every item in the array, disclosureFrame can target only some indices - the rest stay as plain values:

const claims = {
  nicknames: ['JD', 'Johnny', 'The Doe'],
};

const disclosureFrame = {
  nicknames: { _sd: [1] }, // only the item at index 1 ('Johnny') is disclosed
};

const { claims: packedClaims, disclosures } = await packSDJWT(claims, disclosureFrame, hasher);

'JD' and 'The Doe' remain as plain values in packedClaims, while 'Johnny' is replaced by its digest:

// packedClaims
{
  nicknames: [
    'JD',
    {
      '...': 'GPyjlbT6dC0gh5hnkqW8JeiDdpTBnRUUqX_zQmZO5LA',
    },
    'The Doe',
  ],
}

// disclosures
[
  'WyJsa2x4RjVqTVlsR1RQVW92TU5JdkNBIiwiSm9obm55Il0', // base64url encode of ["lklxF5jMYlGTPUovMNIvCA","Johnny"]
]

unpackSDJWT Example

unpackSDJWT is the lower-level building block that verifySDJWT calls internally. Compared to verifySDJWT:

  • FunctionalityunpackSDJWT hashes each supplied Disclosure with the hasher from getHasher, and matches the resulting digest against the _sd values in the payload; only Disclosures whose digest is actually found in _sd get resolved into claims. It does not check a JWT signature or a key binding JWT — so it doesn't verify that the SD-JWT was actually issued by whoever it claims to be from, only that the disclosures you handed it match the digests present in the payload. Use it directly if you already have a trusted/decoded SD-JWT payload and disclosures, and just need the digests resolved without any signature/key-binding verification step.
  • ArgumentsunpackSDJWT takes no verifier and no opts.kb.verifier (there's no signature or key binding to check at this level). It also takes the already-decoded sdjwt payload and disclosures array directly, rather than the single compact SD-JWT string verifySDJWT accepts (splitting and decoding that string is exactly what verifySDJWT does before calling unpackSDJWT).

The unpackSDJWT function takes the following arguments and returns the disclosed claims:

  • sdjwt (required) — an SD-JWT payload containing _sd digests.
  • disclosures (required) — an array of Disclosure objects.
  • getHasher (required) — a function that, given the _sd_alg from the SD-JWT payload, returns the matching hasher used to resolve _sd digests.

Basic Usage

import crypto from 'crypto';

const getHasher = (hashAlg) => {
  let hasher;
  // Default Hasher = Hasher for SHA-256
  if (!hashAlg || hashAlg.toLowerCase() === 'sha-256') {
    hasher = (data) => {
      const digest = crypto.createHash('sha256').update(data).digest();
      return base64encode(digest);
    };
  }
  return Promise.resolve(hasher);
};

const disclosures = [
  {
    // base64url encode of ["v4EGS8JG9SuoSR5F","name","John Doe"]
    disclosure: 'WyJ2NEVHUzhKRzlTdW9TUjVGIiwibmFtZSIsIkpvaG4gRG9lIl0', 
    key: 'name',
    value: 'John Doe',
  },
  {
    // base64url encode of ["Kx9QzP2mYtNc4Rvw","email","[email protected]"]
    disclosure: 'WyJLeDlRelAybVl0TmM0UnZ3IiwiZW1haWwiLCJqb2huLmRvZUBleGFtcGxlLmNvbSJd', 
    key: 'email',
    value: '[email protected]',
  },
];

const sdjwt = {
  _sd: ['6DdbwebcF6figNE2LjNTajT1NTCVMV5TsLaE2q1Axfc', 'jlJfq0qqkvwwgPrHh6kfzO2p7hpDYX1Mve-62bHgpHE'],
};

const result = await unpackSDJWT(sdjwt, disclosures, getHasher);

Each entry in disclosures is hashed and checked against sdjwt._sd: the hash of the name Disclosure matches jlJfq0qqkvwwgPrHh6kfzO2p7hpDYX1Mve-62bHgpHE, and the hash of the email Disclosure matches 6DdbwebcF6figNE2LjNTajT1NTCVMV5TsLaE2q1Axfc — so both are resolved back into result as plain claims. These are the same two claims disclosed throughout the issueSDJWT, verifySDJWT and packSDJWT examples above, so the digests and disclosures here match those exactly:

// result
{
  name: 'John Doe',
  email: '[email protected]',
}

Selectively Disclosable Map

createSDMap

createSDMap returns:

  • sdMap — an object representation of the SD claims in an SD-JWT.
  • disclosureMap — a map of hash values to get the disclosure and its parent disclosures if the SD claim was recursively packed.
  const { sdMap, disclosureMap } = await createSDMap(sdjwt, hasher);

  // sdMap
  {
    nationalities: [
      {
        '...': {}, // item array value (if any recursive sd is present)
        _sd: 'pFndjkZ_VCzmyTa6UjlZo3dh-ko8aIKQc9DlGzhaVYo'
      },
      { '...': {}, _sd: '7Cf6JkPudry3lcbwHgeZ8khAv1U1OSlerP0VkBJrWZ0' }
    ],
    updated_at: { _sd: 'CrQe7S5kqBAHt-nMYXgc6bdt2SH5aTY1sU_M-PgkjPI' },
    email: { _sd: 'JzYjH4svliH0R3PyEMfeZu6Jt69u5qehZo7F7EPYlSE' },
  }

  // disclosureMap
  {
    'JzYjH4svliH0R3PyEMfeZu6Jt69u5qehZo7F7EPYlSE': {
      disclosure: 'WyI2SWo3dE0tYTVpVlBHYm9TNXRtdlZBIiwgImVtYWlsIiwgImpvaG5kb2VAZXhhbXBsZS5jb20iXQ',
      value: '[email protected]',
      parentDisclosures: []
    },
    'CrQe7S5kqBAHt-nMYXgc6bdt2SH5aTY1sU_M-PgkjPI': {
      disclosure: 'WyJHMDJOU3JRZmpGWFE3SW8wOXN5YWpBIiwgInVwZGF0ZWRfYXQiLCAxNTcwMDAwMDAwXQ',
      value: 1570000000,
      parentDisclosures: []
    },
    'pFndjkZ_VCzmyTa6UjlZo3dh-ko8aIKQc9DlGzhaVYo': {
      disclosure: 'WyJsa2x4RjVqTVlsR1RQVW92TU5JdkNBIiwgIlVTIl0',
      value: 'US',
      parentDisclosures: []
    },
    '7Cf6JkPudry3lcbwHgeZ8khAv1U1OSlerP0VkBJrWZ0': {
      disclosure: 'WyJuUHVvUW5rUkZxM0JJZUFtN0FuWEZBIiwgIkRFIl0',
      value: 'DE',
      parentDisclosures: []
    }
  }

Development

Installation

git clone https://github.com/Meeco/sd-jwt
npm install

npm run dev:setup

Test

Runs against examples in the test/examples directory

Examples are generated using sd-jwt-generate

npm run test