Skip to content

Conversation

GianfrancoMS
Copy link

@GianfrancoMS GianfrancoMS commented Oct 15, 2025

@GianfrancoMS GianfrancoMS requested a review from a team as a code owner October 15, 2025 20:43
Copy link

changeset-bot bot commented Oct 15, 2025

🦋 Changeset detected

Latest commit: 11a773a

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 19 packages
Name Type
@credo-ts/core Patch
@credo-ts/drizzle-storage Patch
@credo-ts/openid4vc Patch
@credo-ts/action-menu Patch
@credo-ts/anoncreds Patch
@credo-ts/askar-to-drizzle-storage-migration Patch
@credo-ts/askar Patch
@credo-ts/cheqd Patch
@credo-ts/didcomm Patch
@credo-ts/drpc Patch
@credo-ts/hedera Patch
@credo-ts/indy-sdk-to-askar-migration Patch
@credo-ts/indy-vdr Patch
@credo-ts/node Patch
@credo-ts/question-answer Patch
@credo-ts/react-native Patch
@credo-ts/redis-cache Patch
@credo-ts/tenants Patch
@credo-ts/webvh Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@GianfrancoMS GianfrancoMS changed the title Add support JWT VC Issuer Metadata feat: Add support JWT VC Issuer Metadata Oct 15, 2025
@GianfrancoMS GianfrancoMS changed the title feat: Add support JWT VC Issuer Metadata feat: Add support for JWT VC Issuer Metadata Oct 15, 2025
@GianfrancoMS GianfrancoMS force-pushed the sd-jwt-vc-issuer-metadata branch 2 times, most recently from b52fd78 to 4b523bd Compare October 15, 2025 20:52
@GianfrancoMS GianfrancoMS reopened this Oct 16, 2025
Comment on lines +621 to +625
return {
method: 'jwk',
jwk: result.jwk,
issuer: result.issuer,
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking whether we want to call the method something else than jwk. Since with JWK we really only verify the binding that the credential is issued by a JWK, but with the jwt vc issuer metadata, you also verify the domain of the issuer where the issuer metadata is hosted.

Should we thus call this maybe well-known-issuer-metadata or well-known-issuer? I'm open to other names, if we can come up with something simple that does capture the origin of the key (like with x5c and did)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other options:

  • 'jwt-vc-issuer-metadata'
  • 'jwt-vc'

const operation = { operation: 'verify', algorithm: options.algorithm } as const
const kms =
backend || typeof options.key !== 'string'
backend || typeof options.key.keyId !== 'string'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah thanks for cathing this 👍

Comment on lines +64 to +79
export const JwksSchema = z.object({
keys: z.array(vJwk),
})

export const SdJwtVcIssuerMetadataSchema = z.union([
z.object({
issuer: z.string().url(),
jwks_uri: z.string().url(),
jwks: z.never().optional(),
}),
z.object({
issuer: z.string().url(),
jwks_uri: z.never().optional(),
jwks: JwksSchema,
}),
])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It probably makes sense to also move to zod gradually for other modules, but are these validation schemas also used somewhere? Or are they just used for types? I think it would be good to use this in resolveSigningPublicJwkFromJwtVcIssuerMetadata.

You can use the zParseWithErrorHandling util method on the returned JSON payload.

Copy link
Author

@GianfrancoMS GianfrancoMS Oct 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are used at resolveSigningPublicJwkFromJwtVcIssuerMetadata

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They just use the types as a generic to the fetch method, but they are not actually used for validation. So we don't get runtime validation, only build time validation, and we cas the .json() to the provided type.

// Configure endpoints
configureIssuerMetadataEndpoint(endpointRouter)
configureJwksEndpoint(endpointRouter, this.config)
configureJwtVcIssuerMetadataEndpoint(endpointRouter)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While i understand to add it to the openid4vc router, it does tightly couple the credential format with the issuance protocol. We could add this to the openid4vc module, so it could just register some additional routes, but we would need a new record type for it in that case, and now we can just use the openid4vc issuer record.

I think for this is fine, and we can always make it more flexible later on 👍

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm after thinking about this a bit more, I'm not so sure, since it would mean the jwt-vc-issuer would use the same url as the openid4vc-issuer. I'd expect you'd more often use an url like example.com over example.com/oid4vci/<uuid> as the value for iss.

Wouldn't it make more sense to just host the issuer metadata separately for now? We have the same for did:web, where you can create the did document in Credo, but you need to host it yourself on your webserver on the well konwn path

Copy link
Author

@GianfrancoMS GianfrancoMS Oct 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can support both use cases where we can host our own endpoint and have issuers host on their own servers.

Copy link
Author

@GianfrancoMS GianfrancoMS Oct 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this the same thing for the other well-known endpoints?
We host oauth and openid well-known endpoints.
It's the same logic here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if someone wants to issue a jwt vc over didcomm?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We host oauth and openid well-known endpoints.

oauth and openid well-known endpoints are inherently tied to openid4vci. You can issue a credenetial over oid4vci without these metadata files. For jwt-vc-issuer metadata it's different. There's no inherent relation between the two, except that you can choose to issue an SD-JWT VC credential over openid4vci (but as @rmlearney-digicatapult, it could theoretically also be done over DIDComm for example)

import type { OpenId4VcIssuanceRequest } from './requestContext'

export function configureJwtVcIssuerMetadataEndpoint(router: Router) {
router.get('/.well-known/jwt-vc-issuer', async (_request: OpenId4VcIssuanceRequest, response: Response, next) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This won't be hosted at the correct path looking at the spec. It will be hosted at <url>/oid4vci/<uuid>/.well-known/jwt-vc-issuer, while it should be hosted at <url>/.well-known/jwt-vc-issuer/oid4vci/<uuid>

We have this issue for other documents as well: #2221

Copy link
Author

@GianfrancoMS GianfrancoMS Oct 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it's the same issue for other documents, but I think we should leave it here until we figure out how to host all the documents correctly.
Plus, I've added support for both URLs.

Copy link
Contributor

@TimoGlastra TimoGlastra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @GianfrancoMS, thanks a lot for this PR! It's a great addition.

I'm thinking whether we should get the support into sd-jwt-vc merged, and hold off on the oid4vci integration and just require it to be hosted separately for now. It's quite easy to add this, and I'm not convinced yet that we should bind this to the oid4vci record yet. Currently the issuance method (did/x5c) is separate from the oid4vci protocol.

@GianfrancoMS
Copy link
Author

@TimoGlastra I tried to generate the migrations for drizzle, but got an error, so I'm not including those migrations in this PR. Hopefully you can help me with that 🙏

GianfrancoMS and others added 4 commits October 17, 2025 12:13
Signed-off-by: gianfrancoms <gianfrancomonsal@gmail.com>
Signed-off-by: Timo Glastra <timo@animo.id>
Signed-off-by: gianfrancoms <gianfrancomonsal@gmail.com>
Signed-off-by: gianfrancoms <gianfrancomonsal@gmail.com>
@GianfrancoMS GianfrancoMS force-pushed the sd-jwt-vc-issuer-metadata branch from 42bef4d to 11a773a Compare October 17, 2025 17:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants