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
9 changes: 9 additions & 0 deletions .changeset/smart-buckets-end.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"agentcommercekit": minor
"@agentcommercekit/keys": minor
"@agentcommercekit/did": minor
---

- Upgrade legacy public key formats to use multibase in DID Documents
- Update base64 methods to be explicit that they use `base64url` encoding
- Simplify interface for public key encoding methods
14 changes: 9 additions & 5 deletions demos/payments/src/receipt-service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { serve } from "@hono/node-server"
import { logger } from "@repo/api-utils/middleware/logger"
import { colors, errorMessage, log, successMessage } from "@repo/cli-tools"
import {
colors,
errorMessage,
log,
logJson,
successMessage
} from "@repo/cli-tools"
import {
createPaymentReceipt,
getDidResolver,
Expand Down Expand Up @@ -77,10 +83,8 @@ app.post("/", async (c) => {

const paymentDetails = v.parse(paymentDetailsSchema, parsed.payload)

log(
colors.dim("Payment details:"),
colors.cyan(JSON.stringify(paymentDetails, null, 2))
)
log(colors.dim("Payment details:"))
logJson(paymentDetails, colors.cyan)

log(colors.dim("Verifying payment token..."))
// Verify the payment token is not expired, etc.
Expand Down
4 changes: 2 additions & 2 deletions examples/issuer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ This example showcases a **Credential Issuer** for [ACK-ID](https://www.agentcom

The API allows for the issuance, verification, and revocation of the following credential types:

- `ControllerCredential`: ACK-ID credentials that prove DID ownership heirarchies.
- `PaymentReceiptCredential`: ACK-Pay creddentials that provide proof of payment that satisfies a given Payment Request.
- `ControllerCredential`: ACK-ID credentials that prove DID ownership hierarchies.
- `PaymentReceiptCredential`: ACK-Pay credentials that provide proof of payment that satisfies a given Payment Request.

This issuer supports credential revocation using [StatusList2021](https://www.w3.org/community/reports/credentials/CG-FINAL-vc-status-list-2021-20230102/), which is a privacy-preserving, space-efficient mechanism for maintaining a credential revocation list.

Expand Down
2 changes: 1 addition & 1 deletion examples/issuer/src/test-helpers/did-web-with-signer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export async function createDidWebWithSigner(
keypair,
baseUrl,
controller,
format: "jwk"
encoding: "jwk"
})
const signer = createJwtSigner(keypair)

Expand Down
2 changes: 1 addition & 1 deletion examples/local-did-host/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ When someone attempts to resolve a `did:web` document, the resolver fetches `.we

This example is a basic [Hono](https://hono.dev) server with dynamic `.well-known/did.json` routes, served at subpaths, by default on the host `0.0.0.0:3458`. There are 2 identities, `agent` and `controller`, served from the subpaths `/agent/.well-known/did.json` and `/controller/.well-known/did.json`, respectively.

Per the `did:web` spec, the `did.json` file is a full DID Document. By its nature, the document does not chane often, and should typically be served as a static file from the server. In this example, we are building the DID Document dynamically per-request, which is not suitable for production environments.
Per the `did:web` spec, the `did.json` file is a full DID Document. By its nature, the document does not change often, and should typically be served as a static file from the server. In this example, we are building the DID Document dynamically per-request, which is not suitable for production environments.

## Getting Started

Expand Down
4 changes: 2 additions & 2 deletions examples/verifier/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

This example showcases a **Credential Verifier** for [ACK-ID](https://www.agentcommercekit.com/ack-id) and [ACK-Pay](https://www.agentcommercekit.com/ack-pay) Verifiable Credentials. This API is built with [Hono](https://hono.dev).

- `ControllerCredential`: ACK-ID credentials that prove DID ownership heirarchies.
- `PaymentReceiptCredential`: ACK-Pay creddentials that provide proof of payment that satisfies a given Payment Request.
- `ControllerCredential`: ACK-ID credentials that prove DID ownership hierarchies.
- `PaymentReceiptCredential`: ACK-Pay credentials that provide proof of payment that satisfies a given Payment Request.

This verifier uses [StatusList2021](https://www.w3.org/community/reports/credentials/CG-FINAL-vc-status-list-2021-20230102/), to check if a credential is revoked.# Installation

Expand Down
2 changes: 1 addition & 1 deletion packages/agentcommercekit/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ To learn more about the Agent Commerce Kit, check out the [documentation](https:

## Installation

We recommend installing the `agentcommercekit` package, which is tree-shakeable and contains everything you need to build for the ACK protiocol, even if you choose to only target ACK-ID or ACK-Pay. This is the simplest way to get started, prevent version conflicts or duplication, and makes it easy to manage updates.
We recommend installing the `agentcommercekit` package, which is tree-shakeable and contains everything you need to build for the ACK protocol, even if you choose to only target ACK-ID or ACK-Pay. This is the simplest way to get started, prevent version conflicts or duplication, and makes it easy to manage updates.

```sh
npm i agentcommercekit
Expand Down
2 changes: 1 addition & 1 deletion packages/did/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ const did = createDidKeyUri(keypair)
const didDocument = createDidDocumentFromKeypair({
did,
keypair,
format: "hex", // Optional, defaults to "jwk"
encoding: "jwk", // Optional, defaults to "jwk"
controller: "did:web:controller.example.com" // Optional
})

Expand Down
168 changes: 61 additions & 107 deletions packages/did/src/create-did-document.test.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,34 @@
import {
formatPublicKey,
encodePublicKeyFromKeypair,
generateKeypair,
keypairAlgorithms,
publicKeyFormats
publicKeyEncodings
} from "@agentcommercekit/keys"
import { beforeEach, describe, expect, test } from "vitest"
import {
createDidDocument,
createDidDocumentFromKeypair,
keyConfig
} from "./create-did-document"
import type { DidDocument } from "./did-document"
import type { Keypair, PublicKeyFormat } from "@agentcommercekit/keys"
import type { Keypair, PublicKeyEncoding } from "@agentcommercekit/keys"

const keyTypeMap = {
secp256k1: "EcdsaSecp256k1VerificationKey2019",
Ed25519: "Ed25519VerificationKey2018"
} as const

const formatToPropertyMap = {
hex: "publicKeyHex",
const encodingMap = {
hex: "multibase",
jwk: "jwk",
multibase: "multibase",
base58: "multibase"
}

const encodingToPropertyMap = {
hex: "publicKeyMultibase",
jwk: "publicKeyJwk",
multibase: "publicKeyMultibase",
base58: "publicKeyBase58"
base58: "publicKeyMultibase"
} as const

describe("createDidDocument() and createDidDocumentFromKeypair()", () => {
Expand All @@ -41,87 +47,51 @@ describe("createDidDocument() and createDidDocumentFromKeypair()", () => {
} as const

describe.each(keypairAlgorithms)("algorithm: %s", (algorithm) => {
describe.each(publicKeyFormats)("format: %s", (format: PublicKeyFormat) => {
test(`generates matching documents with ${algorithm} and ${format} format`, () => {
const keypair = keypairMap[algorithm]()

const documentFromKeypair = createDidDocumentFromKeypair({
did,
keypair,
format
})

let document: DidDocument
switch (format) {
case "hex":
document = createDidDocument({
did,
publicKey: {
format: "hex",
algorithm,
value: formatPublicKey(keypair, "hex")
}
})
break
case "jwk":
document = createDidDocument({
did,
publicKey: {
format: "jwk",
algorithm,
value: formatPublicKey(keypair, "jwk")
}
})
break
case "multibase":
document = createDidDocument({
did,
publicKey: {
format: "multibase",
algorithm,
value: formatPublicKey(keypair, "multibase")
describe.each(publicKeyEncodings)(
"encoding: %s",
(encoding: PublicKeyEncoding) => {
test(`generates matching documents with ${algorithm} and ${encoding} encoding`, () => {
const keypair = keypairMap[algorithm]()

const documentFromKeypair = createDidDocumentFromKeypair({
did,
keypair,
encoding
})

const document = createDidDocument({
did,
publicKey: encodePublicKeyFromKeypair(encoding, keypair)
})

const keyId = `${did}#${encodingMap[encoding]}-1`
const expectedDocument = {
"@context": [
"https://www.w3.org/ns/did/v1",
...keyConfig[algorithm].context
],
id: did,
verificationMethod: [
{
id: keyId,
type: keyTypeMap[algorithm],
controller: did,
[encodingToPropertyMap[encoding]]: expect.any(
encoding === "jwk" ? Object : String
) as unknown
}
})
break
case "base58":
document = createDidDocument({
did,
publicKey: {
format: "base58",
algorithm,
value: formatPublicKey(keypair, "base58")
}
})
break
}

const keyId = `${did}#${format}-1`
const expectedDocument = {
"@context": [
"https://www.w3.org/ns/did/v1",
...keyConfig[algorithm].context
],
id: did,
verificationMethod: [
{
id: keyId,
type: keyTypeMap[algorithm],
controller: did,
[formatToPropertyMap[format]]: expect.any(
format === "jwk" ? Object : String
) as unknown
}
],
authentication: [keyId],
assertionMethod: [keyId]
}

expect(document).toEqual(expectedDocument)
expect(documentFromKeypair).toEqual(expectedDocument)
expect(document.verificationMethod![0]!.id).toBe(keyId)
expect(documentFromKeypair.verificationMethod![0]!.id).toBe(keyId)
})
})
],
authentication: [keyId],
assertionMethod: [keyId]
}

expect(document).toEqual(expectedDocument)
expect(documentFromKeypair).toEqual(expectedDocument)
expect(document.verificationMethod![0]!.id).toBe(keyId)
expect(documentFromKeypair.verificationMethod![0]!.id).toBe(keyId)
})
}
)
})

test("includes controller when provided", () => {
Expand All @@ -135,11 +105,7 @@ describe("createDidDocument() and createDidDocumentFromKeypair()", () => {

const document = createDidDocument({
did,
publicKey: {
format: "jwk",
algorithm: "secp256k1",
value: formatPublicKey(secp256k1Keypair, "jwk")
},
publicKey: encodePublicKeyFromKeypair("jwk", secp256k1Keypair),
controller
})

Expand Down Expand Up @@ -181,19 +147,11 @@ describe("createDidDocument() and createDidDocumentFromKeypair()", () => {

const document1 = createDidDocument({
did: did1,
publicKey: {
format: "jwk",
algorithm: "secp256k1",
value: formatPublicKey(secp256k1Keypair, "jwk")
}
publicKey: encodePublicKeyFromKeypair("jwk", secp256k1Keypair)
})
const document2 = createDidDocument({
did: did2,
publicKey: {
format: "jwk",
algorithm: "secp256k1",
value: formatPublicKey(secp256k1Keypair, "jwk")
}
publicKey: encodePublicKeyFromKeypair("jwk", secp256k1Keypair)
})

expect(document1.verificationMethod![0]!.id).toBe(`${did1}#jwk-1`)
Expand All @@ -214,11 +172,7 @@ describe("createDidDocument() and createDidDocumentFromKeypair()", () => {

const document = createDidDocument({
did,
publicKey: {
format: "jwk",
algorithm: "secp256k1",
value: formatPublicKey(secp256k1Keypair, "jwk")
}
publicKey: encodePublicKeyFromKeypair("jwk", secp256k1Keypair)
})

const requiredFields = [
Expand Down
Loading