@meeco/sd-jwt
v1.2.4
Published
SD-JWT implementation in typescript
Keywords
Readme
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_sdarrays. Must match the algorithm declared in_sd_alg(defaultsha-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 byissueSDJWT.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 byverifySDJWT; 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. Packspayloadaccording todisclosureFrame, signs it with thesigneryou 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 callspackSDJWT.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 callsunpackSDJWT.packSDJWT(claims, disclosureFrame, hasher, opts)— lower-level building block used byissueSDJWT. Takesclaims+ adisclosureFrameand returns{ claims, disclosures }as plain objects, with_sddigests 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 byverifySDJWT. Resolves_sddigests 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.disclosureFramedecides 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 inpayloadshould 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 };algis recorded in the SD-JWT as_sd_alg, andcallbackis thehasherfunction used to compute the_sddigests (see BYOC above).cnf(optional) — Holder key material as aJWK, 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
_sdis sorted alphabetically by digest value —packSDJWTsorts 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"]v4EGS8JG9SuoSR5Fis the random salt for this disclosure, produced bygenerateSalt. 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]"]Kx9QzP2mYtNc4Rvwis 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 byissueSDJWTin 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_algfrom the SD-JWT payload, returns the matchinghasherused to resolve_sddigests.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:
- Functionality —
packSDJWTonly replaces selectively disclosable claims with_sddigests 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). - Arguments —
packSDJWThas noheaderand nosigner/cnf(there's nothing to sign or bind a holder key to at this level). Itshasheris passed directly as a plain function, rather than wrapped inissueSDJWT's{ hash: { alg, callback } }(there's no_sd_algto record, sincepackSDJWTdoesn'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) — thehasherfunction used to compute the_sddigests (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:
- Functionality —
unpackSDJWThashes each supplied Disclosure with thehasherfromgetHasher, and matches the resulting digest against the_sdvalues in the payload; only Disclosures whose digest is actually found in_sdget 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. - Arguments —
unpackSDJWTtakes noverifierand noopts.kb.verifier(there's no signature or key binding to check at this level). It also takes the already-decodedsdjwtpayload anddisclosuresarray directly, rather than the single compact SD-JWT stringverifySDJWTaccepts (splitting and decoding that string is exactly whatverifySDJWTdoes before callingunpackSDJWT).
The unpackSDJWT function takes the following arguments and returns the disclosed claims:
sdjwt(required) — an SD-JWT payload containing_sddigests.disclosures(required) — an array of Disclosure objects.getHasher(required) — a function that, given the_sd_algfrom the SD-JWT payload, returns the matchinghasherused to resolve_sddigests.
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:setupTest
Runs against examples in the test/examples directory
Examples are generated using sd-jwt-generate
npm run test