Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .changeset/cuddly-rocks-tell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@agentcommercekit/ack-pay": minor
"@agentcommercekit/vc": minor
"@agentcommercekit/ack-id": minor
"agentcommercekit": minor
---

Update credential signing to return only jwt; add domain and challenge to verifiable presentation signing.
5 changes: 2 additions & 3 deletions demos/e2e/src/credential-issuer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,10 @@ export class CredentialIssuer {
issuer: this.did
})

const { jwt } = await signCredential(credential, {
const jwt = await signCredential(credential, {
did: this.did,
signer: this.signer,
alg: "ES256K",
resolver: this.resolver
alg: "ES256K"
})

return jwt
Expand Down
5 changes: 2 additions & 3 deletions demos/e2e/src/receipt-issuer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,10 @@ export class ReceiptIssuer {
payerDid
})

const { jwt } = await signCredential(credential, {
const jwt = await signCredential(credential, {
did: this.did,
signer: this.signer,
alg: "ES256K",
resolver: this.resolver
alg: "ES256K"
})

return jwt
Expand Down
8 changes: 5 additions & 3 deletions demos/identity-a2a/src/issuer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
createJwtSigner,
generateKeypair,
getDidResolver,
parseJwtCredential,
signCredential
} from "agentcommercekit"
import type { DidUri } from "agentcommercekit"
Expand Down Expand Up @@ -37,12 +38,13 @@ export async function issueCredential({
issuer: issuerDid
})

const { verifiableCredential } = await signCredential(credential, {
const jwt = await signCredential(credential, {
did: issuerDid,
signer,
alg: "EdDSA",
resolver
alg: "EdDSA"
})

const verifiableCredential = await parseJwtCredential(jwt, resolver)

return verifiableCredential
}
8 changes: 5 additions & 3 deletions demos/identity/src/credential-issuer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
createDidWebDocumentFromKeypair,
createJwtSigner,
generateKeypair,
parseJwtCredential,
signCredential,
verifyJwt
} from "agentcommercekit"
Expand Down Expand Up @@ -88,13 +89,14 @@ export class CredentialIssuer {
issuer: this.did
})

const { verifiableCredential } = await signCredential(credential, {
const jwt = await signCredential(credential, {
did: this.did,
signer: this.signer,
alg: "ES256K",
resolver: this.resolver
alg: "ES256K"
})

const verifiableCredential = await parseJwtCredential(jwt, this.resolver)

return verifiableCredential
}
}
8 changes: 4 additions & 4 deletions demos/payments/src/receipt-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
createPaymentReceipt,
getDidResolver,
isDidPkhUri,
parseJwtCredential,
signCredential,
verifyJwt,
verifyPaymentToken
Expand Down Expand Up @@ -125,17 +126,16 @@ app.post("/", async (c) => {
payerDid: parsed.issuer
})

const { jwt, verifiableCredential } = await signCredential(receipt, {
const jwt = await signCredential(receipt, {
did: serverIdentity.did,
signer: serverIdentity.jwtSigner,
alg: "ES256K",
resolver: didResolver
alg: "ES256K"
})

log(successMessage("Receipt created successfully"))
return c.json({
receipt: jwt,
details: verifiableCredential
details: await parseJwtCredential(jwt, didResolver)
})
})

Expand Down
16 changes: 8 additions & 8 deletions examples/issuer/src/lib/credentials/build-signed-credential.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { makeRevocable, signCredential } from "agentcommercekit"
import {
makeRevocable,
parseJwtCredential,
signCredential
} from "agentcommercekit"
import { getStatusListPosition } from "@/db/utils/get-status-list-position"
import type { CredentialResponse, Issuer } from "../types"
import type { DatabaseCredential } from "@/db/schema"
Expand Down Expand Up @@ -34,13 +38,9 @@ export async function buildSignedCredential({
statusListUrl: `${baseUrl}/status/${statusListId}`
})

const { verifiableCredential, jwt } = await signCredential(
unsignedCredential,
{
...issuer,
resolver
}
)
const jwt = await signCredential(unsignedCredential, issuer)

const verifiableCredential = await parseJwtCredential(jwt, resolver)

return {
credential: verifiableCredential,
Expand Down
14 changes: 9 additions & 5 deletions examples/issuer/src/routes/status.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { apiSuccessResponse } from "@repo/api-utils/api-response"
import { notFound } from "@repo/api-utils/exceptions"
import { createStatusListCredential, signCredential } from "agentcommercekit"
import {
createStatusListCredential,
parseJwtCredential,
signCredential
} from "agentcommercekit"
import { Hono } from "hono"
import { env } from "hono/adapter"
import { getStatusList } from "@/db/queries/status-lists"
Expand Down Expand Up @@ -56,10 +60,10 @@ app.get(
issuer: issuer.did
})

const { verifiableCredential } = await signCredential(credential, {
...issuer,
resolver
})
const jwt = await signCredential(credential, issuer)

const verifiableCredential =
await parseJwtCredential<BitstringStatusListCredential>(jwt, resolver)

return c.json(apiSuccessResponse(verifiableCredential))
}
Expand Down
14 changes: 8 additions & 6 deletions packages/ack-pay/src/verify-payment-receipt.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import {
} from "@agentcommercekit/did"
import { createJwtSigner, curveToJwtAlgorithm } from "@agentcommercekit/jwt"
import { generateKeypair } from "@agentcommercekit/keys"
import { InvalidCredentialError, signCredential } from "@agentcommercekit/vc"
import {
InvalidCredentialError,
parseJwtCredential,
signCredential
} from "@agentcommercekit/vc"
import { beforeEach, describe, expect, it } from "vitest"
import { createPaymentReceipt } from "./create-payment-receipt"
import { createPaymentRequestBody } from "./create-payment-request-body"
Expand Down Expand Up @@ -65,14 +69,12 @@ describe("verifyPaymentReceipt()", () => {
)
})

const signed = await signCredential(unsignedReceipt, {
signedReceiptJwt = await signCredential(unsignedReceipt, {
did: receiptIssuerDid,
signer: createJwtSigner(receiptIssuerKeypair),
resolver
signer: createJwtSigner(receiptIssuerKeypair)
})

signedReceipt = signed.verifiableCredential
signedReceiptJwt = signed.jwt
signedReceipt = await parseJwtCredential(signedReceiptJwt, resolver)
})

it("validates a JWT receipt string", async () => {
Expand Down
4 changes: 4 additions & 0 deletions packages/vc/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { verifyPresentation } from "did-jwt-vc"

export * from "./create-credential"
export * from "./is-credential"
export * from "./signing/sign-credential"
Expand All @@ -13,3 +15,5 @@ export * from "./verification/types"
export * from "./verification/parse-jwt-credential"
export * from "./verification/verify-parsed-credential"
export * from "./verification/verify-proof"

export { verifyPresentation }
7 changes: 3 additions & 4 deletions packages/vc/src/signing/sign-credential.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,10 @@ test("signCredential creates a valid JWT and verifiable credential", async () =>
})

// Sign the credential
const { jwt, verifiableCredential } = await signCredential(credential, {
const jwt = await signCredential(credential, {
did: issuerDid,
signer: createJwtSigner(issuerKeypair),
alg: "ES256K",
resolver
alg: "ES256K"
})

// Verify the JWT using did-jwt verifier
Expand All @@ -58,5 +57,5 @@ test("signCredential creates a valid JWT and verifiable credential", async () =>
const payload = result.payload as JwtCredentialPayload

// Verify VC-specific payload elements
expect(verifiableCredential).toMatchObject(payload.vc)
expect(credential).toMatchObject(payload.vc)
})
17 changes: 8 additions & 9 deletions packages/vc/src/signing/sign-credential.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { isJwtString } from "@agentcommercekit/jwt"
import { createVerifiableCredentialJwt, verifyCredential } from "did-jwt-vc"

Check warning on line 2 in packages/vc/src/signing/sign-credential.ts

View workflow job for this annotation

GitHub Actions / check

'verifyCredential' is defined but never used. Allowed unused vars must match /^_/u
import type { SignOptions } from "./types"
import type { Signer } from "./types"
import type { Verifiable, W3CCredential } from "../types"
import type { Resolvable } from "@agentcommercekit/did"

Check warning on line 5 in packages/vc/src/signing/sign-credential.ts

View workflow job for this annotation

GitHub Actions / check

'Resolvable' is defined but never used. Allowed unused vars must match /^_/u
import type { JwtString } from "@agentcommercekit/jwt"

type SignedCredential<T extends W3CCredential> = {

Check warning on line 8 in packages/vc/src/signing/sign-credential.ts

View workflow job for this annotation

GitHub Actions / check

'SignedCredential' is defined but never used. Allowed unused vars must match /^_/u
/**
* The signed {@link Verifiable<W3CCredential>} credential
*/
Expand All @@ -22,18 +23,16 @@
* @param options - The {@link SignCredentialOptions} to use
* @returns A {@link SignedCredential}
*/
export async function signCredential<T extends W3CCredential>(
credential: T,
options: SignOptions
): Promise<SignedCredential<T>> {
export async function signCredential(
credential: W3CCredential,
signer: Signer
): Promise<JwtString> {
// options.alg is already a JwtAlgorithm, no conversion needed
const jwt = await createVerifiableCredentialJwt(credential, options)
const jwt = await createVerifiableCredentialJwt(credential, signer)

if (!isJwtString(jwt)) {
throw new Error("Failed to sign credential")
}

const { verifiableCredential } = await verifyCredential(jwt, options.resolver)

return { jwt, verifiableCredential: verifiableCredential as Verifiable<T> }
return jwt
}
107 changes: 65 additions & 42 deletions packages/vc/src/signing/sign-presentation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,57 +5,80 @@ import {
} from "@agentcommercekit/did"
import { createJwtSigner, verifyJwt } from "@agentcommercekit/jwt"
import { generateKeypair } from "@agentcommercekit/keys"
import { expect, test } from "vitest"
import { describe, expect, it } from "vitest"
import { createPresentation } from "../create-presentation"
import { signPresentation } from "./sign-presentation"
import type { Signer } from "./types"
import type { Verifiable, W3CCredential } from "../types"

test("signPresentation creates a valid JWT and verifiable presentation", async () => {
const resolver = getDidResolver()
const holderKeypair = await generateKeypair("secp256k1")
const holderDid = createDidWebUri("https://holder.example.com")
const resolver = getDidResolver()
const holderKeypair = await generateKeypair("secp256k1")
const holderDid = createDidWebUri("https://holder.example.com")

resolver.addToCache(
holderDid,
createDidDocumentFromKeypair({
did: holderDid,
keypair: holderKeypair
})
)

// Create a mock credential for the presentation
const mockCredential: Verifiable<W3CCredential> = {
"@context": ["https://www.w3.org/2018/credentials/v1"],
type: ["VerifiableCredential"],
issuer: { id: "did:example:issuer" },
credentialSubject: { id: "did:example:subject" },
issuanceDate: new Date().toISOString(),
proof: {
type: "Ed25519Signature2018"
}
resolver.addToCache(
holderDid,
createDidDocumentFromKeypair({
did: holderDid,
keypair: holderKeypair
})
)

const signer: Signer = {
did: holderDid,
signer: createJwtSigner(holderKeypair),
alg: "ES256K"
}

// Create a mock credential for the presentation
const mockCredential: Verifiable<W3CCredential> = {
"@context": ["https://www.w3.org/2018/credentials/v1"],
type: ["VerifiableCredential"],
issuer: { id: "did:example:issuer" },
credentialSubject: { id: "did:example:subject" },
issuanceDate: new Date().toISOString(),
proof: {
type: "Ed25519Signature2018"
}
}

// Generate an unsigned presentation
const presentation = createPresentation({
credentials: [mockCredential],
holder: holderDid,
id: "test-presentation",
type: "TestPresentation"
})
// Generate an unsigned presentation
const presentation = createPresentation({
credentials: [mockCredential],
holder: holderDid,
id: "test-presentation",
type: "TestPresentation"
})

// Sign the presentation
const { jwt, verifiablePresentation } = await signPresentation(presentation, {
did: holderDid,
signer: createJwtSigner(holderKeypair),
alg: "ES256K",
resolver
})
describe("signPresentation", () => {
it("creates a valid JWT and verifiable presentation", async () => {
// Sign the presentation
const jwt = await signPresentation(presentation, signer)

// Verify the JWT using did-jwt verifier
const result = await verifyJwt(jwt, {
resolver
})

// Verify the JWT using did-jwt verifier
const result = await verifyJwt(jwt, {
resolver
expect(result.payload.iss).toBe(holderDid)
expect(result.payload.nonce).toBeUndefined()
expect(result.payload.aud).toBeUndefined()
expect(presentation).toMatchObject(result.payload.vp)
})

expect(result.payload.iss).toBe(holderDid)
expect(verifiablePresentation).toMatchObject(result.payload.vp)
it("includes a challenge and domain", async () => {
const jwt = await signPresentation(presentation, signer, {
challenge: "test-challenge",
domain: "https://example.com"
})

const result = await verifyJwt(jwt, {
resolver,
policies: {
aud: false
}
})

expect(result.payload.nonce).toBe("test-challenge")
expect(result.payload.aud).toEqual(["https://example.com"])
})
})
Loading
Loading