openwallet-foundation-labs / sd-jwt-js Goto Github PK
View Code? Open in Web Editor NEWA JavaScript implementation of the Selective Disclosure JWT (SD-JWT) spec.
Home Page: https://sdjwt.js.org/
License: Apache License 2.0
A JavaScript implementation of the Selective Disclosure JWT (SD-JWT) spec.
Home Page: https://sdjwt.js.org/
License: Apache License 2.0
Add test:cov command to measure test coverage with jest
OWF provide npm token for our repo. you can check it in discord channel.
We'll apply same way with https://github.com/openwallet-foundation/agent-framework-javascript/blob/main/.github/workflows/continuous-deployment.yml
But, I think it should be triggered by release
Wiki can't manage docs version by 1.x or 2.x. So I suggest we move the documentation to docs/.
We currently upload our architecture image to our GitHub repository, but it appears that npm is unable to fetch it.
We should upload our image to a server and use it's URL
Key binding not included when present. Add key binding is needed.
In standard version 07, we can find details of key binding JWT(https://www.ietf.org/archive/id/draft-ietf-oauth-selective-disclosure-jwt-07.html#name-key-binding)
Key Binding aims to ensure that the presenter of an SD-JWT credential is actually the legitimate Holder of the credential
Note that there may be other ways to send a Key Binding JWT to the Verifier or for the Holder to prove possession of the key material included in an SD-JWT. In these cases, inclusion of the Key Binding JWT in the SD-JWT is not required.
It looks like key binding is created in present time
To remove the noExplicitAny
rule from biome, we need to update the code: https://github.com/openwallet-foundation-labs/sd-jwt-js/blob/main/biome.json#L25
Fix typo in package name
Add README.md for sd-jwt-js repository
requiredClaimKeys is now an array of string.
Since the PresentationFrame become a object representation, I think requiredClaim checking param would be also a object representation for the consistency.
Feel free to share your thoughts :)
Thank you.
some type are using the object type to extend a type like type DisclosureFrame<T extends object> = Frame<T>;
This could be the reason for some errors we have in the veramo implementation. One of their devs suggested to use an extensible like
interface Extensible {
[key: string]?: unknown;
}
type DisclosureFrame<T extends Extensible> = Frame<T>;
in vitest.shared.js e2e test file is excluded.
Removing exclude value lead to activate e2e test
import { defineProject, mergeConfig } from 'vitest/config';
export const browserConfig = defineProject({
test: {
globals: true,
coverage: {
exclude: ['examples/**'],
reporter: ['json'],
},
environment: 'jsdom',
},
});
export const nodeConfig = defineProject({
test: {
globals: true,
coverage: {
//TODO: the exclude is not working, therefore the coverage result are not correct.
exclude: ['examples/**'],
reporter: ['json'],
},
environment: 'node',
},
});
export const allEnvs = mergeConfig(browserConfig, nodeConfig);
@cre8 Please share your thoughts :)
when using node:crpyto, use lower-case, no-hyphen string like https://nodejs.org/docs/latest-v18.x/api/crypto.html#cryptocreatehashalgorithm-options to support lower version of node
For a holder, you receive the Disclosures with digests and right now can't easily access the digests of disclosures (private member). The only way to access a digest, is via calling digest
which requires a HaserAndAlg as input
public async digest(hash: HasherAndAlg): Promise<string> {
const { hasher, alg } = hash;
if (!this._digest) {
const hash = await hasher(this.encode(), alg);
this._digest = Uint8ArrayToBase64Url(hash);
}
return this._digest;
}
I do think it would be beneficial to make either the variable directly accessible (public) or change the signature of the function to have hash
as an optional argument as it is only used when computing a new digest.
Custom signer, and verifier support is missing in sdjwtInstance.
There seems to be quite some type casting (also introduced by #80). This makes working within the library quite a bit more difficult. Most, if not all, type casts can be avoided but do require some restructuring of the functions. Over the coming weekend I will try to find some time to add some functionality to avoid the type casting and include some safety checks.
A CODEOWNERS
file is required to ensure that approvals are completed via the CODEOWNERS.
A MAINTAINERS.md
file is required. It should contain the content specified at https://tac.openwallet.foundation/governance/maintainers-file-content/.
Add security.md
Currently packages released like 2.0.4-next.61
We can check it here: https://www.npmjs.com/package/@sd-jwt/core?activeTab=versions
I remove all the tags but it didn't fixed.
To make the usage easier for developers, I would suggest also to publish an implementation for the signet and verifier functions like key generation, hashing etc.
I would like to include it in the src folder and not an example folder so a developer can easily import and use it.
We are not forcing someone to use it, but we offer a challenged implementation that is ready to be used in the browser or in nodejs.
Right now I can pass a signer function to request the required key on demand like this example with veramo:
const signer: Signer = async (data: string) => context.agent.keyManagerSign({ keyRef: key.kid, data })
const sdjwt = new SDJwtInstance({
signer,
hasher: this.algorithms.hasher,
saltGenerator: this.algorithms.salltGenerator,
signAlg: alg,
})
The problem is, that the signAlg is a static value so I am not able to use any other sign algorithm in my signer.
The signer will return not just the signature, but also the used signAlgorithm. This would allow a more dynamic approach
We could think about using this approach also for the hasher, beside the specification of sd-jwt is limited to sha-256. But It would make this approach more generalised when we change it now for the signature
Add badges
Revise coverage to 93%
right now the kbVerify
uses the same input as Verify (payload and signature of the jwt). However the payload does not include a reference to the holder public key, therefore we are not able to verify the signature
Fix the root README.
Add README.md in package folder
By following the common javascript convention, I suggest all function names to be camelCase (or at least start with lowercase).
Especially when remarks can be misunderstood as a type instead of a function when imported somewhere else.
current
import { Base64 } from 'js-base64';
export const Base64urlEncode = Base64.encodeURI;
export const Base64urlDecode = Base64.decode;
export const Uint8ArrayToBase64Url = (input: Uint8Array): string =>
Base64.fromUint8Array(input, true);
to-be
import { Base64 } from 'js-base64';
export const base64UrlEncode = Base64.encodeURI;
export const base64UrlDecode = Base64.decode;
export const uint8ArrayToBase64Url = (input: Uint8Array): string =>
Base64.fromUint8Array(input, true);
await keyword is missing at index.ts:138
const validated = this.validate(encodedSDJwt, publicKey);
if (!validated) { ^
return false;
}
It should be
const validated = await this.validate(encodedSDJwt, publicKey);
if (!validated) {
return false;
}
Right now we have only the reference to the issuer in either the iss
or issuer
field. But to determine the used key, we need to place a reference into the header
Right now a presentation request is defined like ['name', 'address.city', 'address.street']
defining the fields that should be disclosed. The equivalent object oriented presentation looks like this:
const obj = {
name: true,
address: {
city: true,
street: true,
};
However I have some concerns allowing the object oriented approach:
[names.2.value]
.Because of this I suggest to only allow the list presentation approach and not add or replace it with the object one.
Since we are writing tests, we can use the coverage feature and maybe some tools like: https://about.codecov.io/
Add key binding example in example project.
see: https://www.ietf.org/archive/id/draft-ietf-oauth-selective-disclosure-jwt-07.html#section-5.3.1
sd_hash = Hasher("<Issuer-signed JWT>~<Disclosure 1>~<Disclosure 2>~...~<Disclosure N>~", <algorithm from the disclosed JWT>)
When decoding an sd-jwt-vc credential with
const sdjwtvc = await sdjwt.decode(credential.credential as string);
console.log(sdjwtvc.jwt!.payload!.jti);
it will throw an error that jti comes from an index signature. It would be correct that we pass SdJwtVcPayload
to the decode
function so it knows that the default values from a JWT can be set.
Current solution is to cast it manually like console.log((sdjwtvc.jwt!.payload as SdJwtVcPayload).jti);
According to the SD-JWT-VC spec, jti is not a known value, but according to the JWT spec it is. Should we also add all values that are in a JWT also to the payload of a Sdjwtvc? Of course marking them all as optional.
Add links to npm
When the release job gets executed. It will update the package.json files to add the new version number. Unfortunately it will be formatted in a way biome will fail after the changes get pushed to the main branch.
Section 3.5 lists 3 options to receive the key of an issuer:
Right now the library defines "bring your own crypto". Meaning the the user has to implement the getVerifier(publicKeyJWK: object): Promise<(data: string, signatureBase64url: string) => Promise<boolean>>;
by himself. The data field includes the encoded header and payload field, therefore all three options can be implemented.
@lukasjhan do you think it makes sense? We can not implement to resolve all did elements, but we could give some examples for this.
What I found out while implementing PEX is that there are functions that directly modify objects received as input. I think this will confuse users, so I think I'll have to copy the object internally and use it.
I proposed:
selectDisclosures in @sd-jwt/present
unpack, unpackSync, unpackObj and unpackArray in @sd-jwt/decode
As we will merge these two libraries, there will likely be some issues transferred as well from berendsliedrecht/sd-jwt-ts. These issues should be resolved in this repository (https://github.com/berendsliedrecht/sd-jwt-ts/issues). I can check out if they actually transfer and reopen an issue here.
We should define the minimal node version in the package json.
Additional goal:
In this step, we should also consider to run the tests as matrix to validate that it is running for multiple node versions. Doing so we need to check which step should upload the codecov report since the test run in parallel (or check if we can split it up and merge the different coverage reports laster). Right now vitest is doing browser and node test in sequence and is merging the report in one final file.
Like the main branch someone should not be possible to directly push to the next branch since this bypasses the review process
coverage.exclude is not working in vitest.shared.js
Verification right now takes a JSON Object and re-encodes this for validation (see https://github.com/openwallet-foundation-labs/sd-jwt-js/blob/d9eaf4465bb5f50f21d90549678d6c426fea0ae8/packages/core/src/kbjwt.ts#L39C1-L41C41):
const header = Base64urlEncode(JSON.stringify(this.header));
const payload = Base64urlEncode(JSON.stringify(this.payload));
const data = `${header}.${payload}`;
This means that the original encoding of header/payload does not get preserved and signature validation might fail. A good example would be the sd-jwt-vc spec example created by the python reference implementation of sd-jwt. When debugging the calls for verification, header and payload change and signature validation fails (because the original encoding is not minified and the new encoding is minified).
Test Vectors to reproduce the problem with:
const publicKeyExampleJwt: JWK = {
kty: "EC",
crv: "P-256",
x: "b28d4MwZMjw8-00CG4xfnn9SLMVMM19SlqZpVb_uNtQ",
y: "Xv5zWwuoaTgdS6hV43yI6gBwTnjukmFQQnJ_kCxzqk8",
};
let encodedJwt: string | undefined =
"eyJhbGciOiAiRVMyNTYiLCAidHlwIjogInZjK3NkLWp3dCIsICJraWQiOiAiZG9jLXNpZ25lci0wNS0yNS0yMDIyIn0.eyJfc2QiOiBbIjA5dktySk1PbHlUV00wc2pwdV9wZE9CVkJRMk0xeTNLaHBINTE1blhrcFkiLCAiMnJzakdiYUMwa3k4bVQwcEpyUGlvV1RxMF9kYXcxc1g3NnBvVWxnQ3diSSIsICJFa084ZGhXMGRIRUpidlVIbEVfVkNldUM5dVJFTE9pZUxaaGg3WGJVVHRBIiwgIklsRHpJS2VpWmREd3BxcEs2WmZieXBoRnZ6NUZnbldhLXNONndxUVhDaXciLCAiSnpZakg0c3ZsaUgwUjNQeUVNZmVadTZKdDY5dTVxZWhabzdGN0VQWWxTRSIsICJQb3JGYnBLdVZ1Nnh5bUphZ3ZrRnNGWEFiUm9jMkpHbEFVQTJCQTRvN2NJIiwgIlRHZjRvTGJnd2Q1SlFhSHlLVlFaVTlVZEdFMHc1cnREc3JaemZVYW9tTG8iLCAiamRyVEU4WWNiWTRFaWZ1Z2loaUFlX0JQZWt4SlFaSUNlaVVRd1k5UXF4SSIsICJqc3U5eVZ1bHdRUWxoRmxNXzNKbHpNYVNGemdsaFFHMERwZmF5UXdMVUs0Il0sICJpc3MiOiAiaHR0cHM6Ly9leGFtcGxlLmNvbS9pc3N1ZXIiLCAiaWF0IjogMTY4MzAwMDAwMCwgImV4cCI6IDE4ODMwMDAwMDAsICJ2Y3QiOiAiaHR0cHM6Ly9jcmVkZW50aWFscy5leGFtcGxlLmNvbS9pZGVudGl0eV9jcmVkZW50aWFsIiwgIl9zZF9hbGciOiAic2hhLTI1NiIsICJjbmYiOiB7Imp3ayI6IHsia3R5IjogIkVDIiwgImNydiI6ICJQLTI1NiIsICJ4IjogIlRDQUVSMTladnUzT0hGNGo0VzR2ZlNWb0hJUDFJTGlsRGxzN3ZDZUdlbWMiLCAieSI6ICJaeGppV1diWk1RR0hWV0tWUTRoYlNJaXJzVmZ1ZWNDRTZ0NGpUOUYySFpRIn19fQ.QXgzrePAdq_WZVGCwDxP-l8h0iyckrHBNidxVqGtKJ0LMzObqgaXUD1cgGEf7d9TexPkBcgQYqjuzlfbeCxxuA~WyJRZ19PNjR6cUF4ZTQxMmExMDhpcm9BIiwgImFkZHJlc3MiLCB7InN0cmVldF9hZGRyZXNzIjogIjEyMyBNYWluIFN0IiwgImxvY2FsaXR5IjogIkFueXRvd24iLCAicmVnaW9uIjogIkFueXN0YXRlIiwgImNvdW50cnkiOiAiVVMifV0~WyI2SWo3dE0tYTVpVlBHYm9TNXRtdlZBIiwgImVtYWlsIiwgImpvaG5kb2VAZXhhbXBsZS5jb20iXQ~WyJlbHVWNU9nM2dTTklJOEVZbnN4QV9BIiwgImZhbWlseV9uYW1lIiwgIkRvZSJd~WyIyR0xDNDJzS1F2ZUNmR2ZyeU5STjl3IiwgImdpdmVuX25hbWUiLCAiSm9obiJd~eyJhbGciOiAiRVMyNTYiLCAidHlwIjogImtiK2p3dCJ9.eyJub25jZSI6ICIxMjM0NTY3ODkwIiwgImF1ZCI6ICJodHRwczovL2V4YW1wbGUuY29tL3ZlcmlmaWVyIiwgImlhdCI6IDE3MDk5OTYxODUsICJzZF9oYXNoIjogIjc4cFFEazJOblNEM1dKQm5SN015aWpmeUVqcGJ5a01yRnlpb2ZYSjlsN0kifQ.7k4goAlxM4a3tHnvCBCe70j_I-BCwtzhBRXQNk9cWJnQWxxt2kIqCyzcwzzUc0gTwtbGWVQoeWCiL5K6y3a4VQ";
Add data in package.json
Using eslint forces us to write better code.
We can add it after #68 or during this. After this would be easier to split tasks up
Add test for checking PR.
pnpm test
pnpm test:e2e
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.