@dwk/vc 0.1.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +15 -0
- package/README.md +143 -0
- package/dist/config.d.ts +97 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +62 -0
- package/dist/config.js.map +1 -0
- package/dist/credential.d.ts +70 -0
- package/dist/credential.d.ts.map +1 -0
- package/dist/credential.js +139 -0
- package/dist/credential.js.map +1 -0
- package/dist/data-integrity.d.ts +102 -0
- package/dist/data-integrity.d.ts.map +1 -0
- package/dist/data-integrity.js +253 -0
- package/dist/data-integrity.js.map +1 -0
- package/dist/datetime.d.ts +26 -0
- package/dist/datetime.d.ts.map +1 -0
- package/dist/datetime.js +54 -0
- package/dist/datetime.js.map +1 -0
- package/dist/did-web.d.ts +93 -0
- package/dist/did-web.d.ts.map +1 -0
- package/dist/did-web.js +206 -0
- package/dist/did-web.js.map +1 -0
- package/dist/handler.d.ts +37 -0
- package/dist/handler.d.ts.map +1 -0
- package/dist/handler.js +362 -0
- package/dist/handler.js.map +1 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +46 -0
- package/dist/index.js.map +1 -0
- package/dist/jcs.d.ts +31 -0
- package/dist/jcs.d.ts.map +1 -0
- package/dist/jcs.js +67 -0
- package/dist/jcs.js.map +1 -0
- package/dist/log.d.ts +34 -0
- package/dist/log.d.ts.map +1 -0
- package/dist/log.js +32 -0
- package/dist/log.js.map +1 -0
- package/dist/multibase.d.ts +57 -0
- package/dist/multibase.d.ts.map +1 -0
- package/dist/multibase.js +165 -0
- package/dist/multibase.js.map +1 -0
- package/dist/status-list.d.ts +116 -0
- package/dist/status-list.d.ts.map +1 -0
- package/dist/status-list.js +241 -0
- package/dist/status-list.js.map +1 -0
- package/package.json +48 -0
- package/src/config.ts +158 -0
- package/src/credential.ts +188 -0
- package/src/data-integrity.ts +425 -0
- package/src/datetime.ts +57 -0
- package/src/did-web.ts +273 -0
- package/src/handler.ts +477 -0
- package/src/index.ts +133 -0
- package/src/jcs.ts +83 -0
- package/src/log.ts +35 -0
- package/src/multibase.ts +189 -0
- package/src/status-list.ts +356 -0
package/dist/did-web.js
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `did:web` ↔ URL mapping, DID-document construction, and verification-method
|
|
3
|
+
* resolution.
|
|
4
|
+
*
|
|
5
|
+
* The DID document itself (`/.well-known/did.json`) is a static artifact a static
|
|
6
|
+
* host can serve — {@link buildDidDocument} produces it — so this package does
|
|
7
|
+
* not need a Worker to *resolve* a DID. The Worker side uses
|
|
8
|
+
* {@link createDidWebResolver} during VC verification to fetch a credential
|
|
9
|
+
* issuer's DID document and locate the verification key referenced by a proof.
|
|
10
|
+
*
|
|
11
|
+
* Method/URL mapping follows the did:web rules: the first colon-separated
|
|
12
|
+
* component is the host (with a `%3A`-encoded port), remaining components are
|
|
13
|
+
* path segments, and a bare host resolves under `/.well-known/`.
|
|
14
|
+
*
|
|
15
|
+
* @see https://w3c-ccg.github.io/did-method-web/
|
|
16
|
+
*/
|
|
17
|
+
const DID_WEB_PREFIX = "did:web:";
|
|
18
|
+
/** The default did:web context documents emitted by {@link buildDidDocument}. */
|
|
19
|
+
export const DID_CONTEXT_V1 = "https://www.w3.org/ns/did/v1";
|
|
20
|
+
export const MULTIKEY_CONTEXT_V1 = "https://w3id.org/security/multikey/v1";
|
|
21
|
+
export const JWK_CONTEXT_V1 = "https://w3id.org/security/jwk/v1";
|
|
22
|
+
/**
|
|
23
|
+
* Convert a `did:web` identifier to the URL of its DID document. Throws if the
|
|
24
|
+
* input is not a `did:web` identifier.
|
|
25
|
+
*/
|
|
26
|
+
export function didWebToUrl(did) {
|
|
27
|
+
if (!did.startsWith(DID_WEB_PREFIX)) {
|
|
28
|
+
throw new Error(`@dwk/vc: "${did}" is not a did:web identifier`);
|
|
29
|
+
}
|
|
30
|
+
const components = did.slice(DID_WEB_PREFIX.length).split(":");
|
|
31
|
+
const host = components[0];
|
|
32
|
+
if (host === undefined || host.length === 0) {
|
|
33
|
+
throw new Error(`@dwk/vc: did:web identifier "${did}" has no host`);
|
|
34
|
+
}
|
|
35
|
+
const authority = decodeURIComponent(host);
|
|
36
|
+
const segments = components.slice(1).map((segment) => {
|
|
37
|
+
if (segment.length === 0) {
|
|
38
|
+
throw new Error(`@dwk/vc: did:web identifier "${did}" has an empty path segment`);
|
|
39
|
+
}
|
|
40
|
+
const decoded = decodeURIComponent(segment);
|
|
41
|
+
// Reject `.`/`..` so a crafted identifier cannot traverse out of its path
|
|
42
|
+
// when the URL is built and fetched.
|
|
43
|
+
if (decoded === "." || decoded === "..") {
|
|
44
|
+
throw new Error(`@dwk/vc: did:web identifier "${did}" has an invalid path segment "${decoded}"`);
|
|
45
|
+
}
|
|
46
|
+
return decoded;
|
|
47
|
+
});
|
|
48
|
+
const path = segments.length === 0
|
|
49
|
+
? "/.well-known/did.json"
|
|
50
|
+
: `/${segments.join("/")}/did.json`;
|
|
51
|
+
return `https://${authority}${path}`;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Derive the `did:web` identifier a URL (or host) would publish. A bare origin
|
|
55
|
+
* (or one whose path is `/.well-known/did.json`) maps to `did:web:<host>`; a
|
|
56
|
+
* deeper path maps its segments to colon-separated components.
|
|
57
|
+
*/
|
|
58
|
+
export function urlToDidWeb(input) {
|
|
59
|
+
const url = new URL(input.includes("://") ? input : `https://${input}`);
|
|
60
|
+
const host = url.host; // includes a non-default port
|
|
61
|
+
const encodedHost = host.replace(/:/g, "%3A");
|
|
62
|
+
let pathname = url.pathname;
|
|
63
|
+
if (pathname.endsWith("/did.json")) {
|
|
64
|
+
pathname = pathname.slice(0, -"/did.json".length);
|
|
65
|
+
}
|
|
66
|
+
const segments = pathname.split("/").filter((segment) => segment.length > 0);
|
|
67
|
+
if (segments.length === 0 ||
|
|
68
|
+
(segments.length === 1 && segments[0] === ".well-known")) {
|
|
69
|
+
return `${DID_WEB_PREFIX}${encodedHost}`;
|
|
70
|
+
}
|
|
71
|
+
const encoded = segments.map((segment) => encodeURIComponent(segment));
|
|
72
|
+
return `${DID_WEB_PREFIX}${encodedHost}:${encoded.join(":")}`;
|
|
73
|
+
}
|
|
74
|
+
function absoluteId(did, id) {
|
|
75
|
+
return id.startsWith("#") ? `${did}${id}` : id;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Build a `did:web` DID document from public verification methods. Emits the
|
|
79
|
+
* DID, Multikey, and (when a JWK method is present) JWK context documents, the
|
|
80
|
+
* `verificationMethod` array, and the requested verification relationships
|
|
81
|
+
* (defaulting to `assertionMethod`). Suitable for serialization to
|
|
82
|
+
* `/.well-known/did.json`.
|
|
83
|
+
*/
|
|
84
|
+
export function buildDidDocument(options) {
|
|
85
|
+
const { did } = options;
|
|
86
|
+
if (!did.startsWith(DID_WEB_PREFIX)) {
|
|
87
|
+
throw new Error(`@dwk/vc: "${did}" is not a did:web identifier`);
|
|
88
|
+
}
|
|
89
|
+
if (options.verificationMethods.length === 0) {
|
|
90
|
+
throw new Error("@dwk/vc: a DID document needs at least one verification method");
|
|
91
|
+
}
|
|
92
|
+
let hasJwk = false;
|
|
93
|
+
const methods = options.verificationMethods.map((input) => {
|
|
94
|
+
const id = absoluteId(did, input.id);
|
|
95
|
+
const method = {
|
|
96
|
+
id,
|
|
97
|
+
controller: did,
|
|
98
|
+
};
|
|
99
|
+
if (input.publicKeyMultibase !== undefined) {
|
|
100
|
+
method.type = input.type ?? "Multikey";
|
|
101
|
+
method.publicKeyMultibase = input.publicKeyMultibase;
|
|
102
|
+
}
|
|
103
|
+
else if (input.publicKeyJwk !== undefined) {
|
|
104
|
+
hasJwk = true;
|
|
105
|
+
method.type = input.type ?? "JsonWebKey";
|
|
106
|
+
method.publicKeyJwk = input.publicKeyJwk;
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
throw new Error(`@dwk/vc: verification method "${id}" needs publicKeyMultibase or publicKeyJwk`);
|
|
110
|
+
}
|
|
111
|
+
return { id, method };
|
|
112
|
+
});
|
|
113
|
+
const context = [DID_CONTEXT_V1, MULTIKEY_CONTEXT_V1];
|
|
114
|
+
if (hasJwk)
|
|
115
|
+
context.push(JWK_CONTEXT_V1);
|
|
116
|
+
const relationships = options.relationships ?? {};
|
|
117
|
+
const ids = methods.map((m) => m.id);
|
|
118
|
+
const document = {
|
|
119
|
+
"@context": context,
|
|
120
|
+
id: did,
|
|
121
|
+
verificationMethod: methods.map((m) => m.method),
|
|
122
|
+
};
|
|
123
|
+
if (relationships.assertionMethod ?? true)
|
|
124
|
+
document.assertionMethod = ids;
|
|
125
|
+
if (relationships.authentication ?? false)
|
|
126
|
+
document.authentication = ids;
|
|
127
|
+
if (options.alsoKnownAs !== undefined && options.alsoKnownAs.length > 0) {
|
|
128
|
+
document.alsoKnownAs = [...options.alsoKnownAs];
|
|
129
|
+
}
|
|
130
|
+
return document;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Locate a verification method in a DID document by its id. A method `id` that
|
|
134
|
+
* is a relative reference (`#key-0`) is resolved against the document's `id`
|
|
135
|
+
* before comparison, per DID Core — foreign documents commonly use relative ids.
|
|
136
|
+
*/
|
|
137
|
+
export function findVerificationMethod(didDocument, id) {
|
|
138
|
+
const docId = typeof didDocument.id === "string" ? didDocument.id : "";
|
|
139
|
+
const methods = didDocument.verificationMethod;
|
|
140
|
+
if (!Array.isArray(methods))
|
|
141
|
+
return undefined;
|
|
142
|
+
for (const entry of methods) {
|
|
143
|
+
if (entry === null || typeof entry !== "object" || Array.isArray(entry)) {
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
const entryId = entry.id;
|
|
147
|
+
if (typeof entryId !== "string")
|
|
148
|
+
continue;
|
|
149
|
+
const resolved = entryId.startsWith("#") ? `${docId}${entryId}` : entryId;
|
|
150
|
+
if (resolved === id) {
|
|
151
|
+
return entry;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return undefined;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Build a {@link VerificationMethodResolver} that resolves a `did:web`
|
|
158
|
+
* verification-method id by fetching the controller's DID document over HTTPS
|
|
159
|
+
* and locating the referenced method. Returns `undefined` for non-`did:web`
|
|
160
|
+
* ids, fetch failures, and unknown methods — verification treats that as an
|
|
161
|
+
* unresolvable key rather than throwing.
|
|
162
|
+
*/
|
|
163
|
+
export function createDidWebResolver(options = {}) {
|
|
164
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
165
|
+
if (fetchImpl === undefined) {
|
|
166
|
+
throw new Error("@dwk/vc: no fetch implementation available for did:web resolution");
|
|
167
|
+
}
|
|
168
|
+
return async (id) => {
|
|
169
|
+
const hashIndex = id.indexOf("#");
|
|
170
|
+
const did = hashIndex === -1 ? id : id.slice(0, hashIndex);
|
|
171
|
+
if (!did.startsWith(DID_WEB_PREFIX))
|
|
172
|
+
return undefined;
|
|
173
|
+
let url;
|
|
174
|
+
try {
|
|
175
|
+
url = didWebToUrl(did);
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
return undefined;
|
|
179
|
+
}
|
|
180
|
+
let response;
|
|
181
|
+
try {
|
|
182
|
+
response = await fetchImpl(url, {
|
|
183
|
+
headers: { accept: "application/did+json, application/json" },
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
catch {
|
|
187
|
+
return undefined;
|
|
188
|
+
}
|
|
189
|
+
if (!response.ok)
|
|
190
|
+
return undefined;
|
|
191
|
+
let document;
|
|
192
|
+
try {
|
|
193
|
+
document = await response.json();
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
return undefined;
|
|
197
|
+
}
|
|
198
|
+
if (document === null ||
|
|
199
|
+
typeof document !== "object" ||
|
|
200
|
+
Array.isArray(document)) {
|
|
201
|
+
return undefined;
|
|
202
|
+
}
|
|
203
|
+
return findVerificationMethod(document, id);
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
//# sourceMappingURL=did-web.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"did-web.js","sourceRoot":"","sources":["../src/did-web.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `@dwk/vc` fetch handler: VC-API-style issuance and verification endpoints,
|
|
3
|
+
* a status-update endpoint, and the served (signed) Bitstring Status List
|
|
4
|
+
* credentials. Routing matches the request pathname against each configured
|
|
5
|
+
* endpoint's path, so the handler is mountable under any prefix simply by
|
|
6
|
+
* configuring absolute endpoint URLs.
|
|
7
|
+
*
|
|
8
|
+
* The DID document itself is a static artifact ({@link ./did-web.buildDidDocument});
|
|
9
|
+
* this handler covers only the dynamic parts: signing credentials with the
|
|
10
|
+
* domain's key (a secret binding), verifying them, and flipping status bits in a
|
|
11
|
+
* strongly-consistent D1 store.
|
|
12
|
+
*/
|
|
13
|
+
import { type VcConfig } from "./config";
|
|
14
|
+
import { type VcStatusStoreEnv } from "./status-list";
|
|
15
|
+
/** Cloudflare bindings required by the `@dwk/vc` handler. */
|
|
16
|
+
export interface VcEnv extends Partial<VcStatusStoreEnv> {
|
|
17
|
+
/**
|
|
18
|
+
* The issuer's **private** signing key as a JWK JSON string (secret binding).
|
|
19
|
+
* Its public half is published in the DID document's verification method.
|
|
20
|
+
*/
|
|
21
|
+
readonly VC_SIGNING_KEY: string;
|
|
22
|
+
}
|
|
23
|
+
/** A `fetch`-compatible Worker handler. */
|
|
24
|
+
export type VcHandler = (request: Request, env: VcEnv, ctx: ExecutionContext) => Promise<Response>;
|
|
25
|
+
/**
|
|
26
|
+
* Build the `@dwk/vc` handler from configuration.
|
|
27
|
+
*
|
|
28
|
+
* The returned handler routes `POST {issueEndpoint}`, `POST {verifyEndpoint}`,
|
|
29
|
+
* `POST {statusEndpoint}`, and `GET {statusListEndpoint}/<purpose>`. Issuance and
|
|
30
|
+
* status changes are gated by the configured {@link AuthorizeOperation} hook (or
|
|
31
|
+
* the composing Worker's front door) and require the `VC_SIGNING_KEY` secret;
|
|
32
|
+
* status requires the `VC_STATUS_DB` D1 binding. Unmatched routes get `404`;
|
|
33
|
+
* wrong methods get `405`. The issuer credential / `issuerId` is recorded by host
|
|
34
|
+
* only; subjects, claims, keys, and proof values are never logged.
|
|
35
|
+
*/
|
|
36
|
+
export declare function createVc(config: VcConfig): VcHandler;
|
|
37
|
+
//# sourceMappingURL=handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../src/handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,EAAwC,KAAK,QAAQ,EAAE,MAAM,UAAU,CAAC;AAY/E,OAAO,EAWL,KAAK,gBAAgB,EACtB,MAAM,eAAe,CAAC;AAEvB,6DAA6D;AAC7D,MAAM,WAAW,KAAM,SAAQ,OAAO,CAAC,gBAAgB,CAAC;IACtD;;;OAGG;IACH,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAED,2CAA2C;AAC3C,MAAM,MAAM,SAAS,GAAG,CACtB,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,KAAK,EACV,GAAG,EAAE,gBAAgB,KAClB,OAAO,CAAC,QAAQ,CAAC,CAAC;AAoWvB;;;;;;;;;;GAUG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,QAAQ,GAAG,SAAS,CA8CpD"}
|
package/dist/handler.js
ADDED
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `@dwk/vc` fetch handler: VC-API-style issuance and verification endpoints,
|
|
3
|
+
* a status-update endpoint, and the served (signed) Bitstring Status List
|
|
4
|
+
* credentials. Routing matches the request pathname against each configured
|
|
5
|
+
* endpoint's path, so the handler is mountable under any prefix simply by
|
|
6
|
+
* configuring absolute endpoint URLs.
|
|
7
|
+
*
|
|
8
|
+
* The DID document itself is a static artifact ({@link ./did-web.buildDidDocument});
|
|
9
|
+
* this handler covers only the dynamic parts: signing credentials with the
|
|
10
|
+
* domain's key (a secret binding), verifying them, and flipping status bits in a
|
|
11
|
+
* strongly-consistent D1 store.
|
|
12
|
+
*/
|
|
13
|
+
import { hostFromUrl } from "@dwk/log";
|
|
14
|
+
import { resolveConfig } from "./config";
|
|
15
|
+
import { addProof, importSigner, verifyProof, } from "./data-integrity";
|
|
16
|
+
import { createDidWebResolver } from "./did-web";
|
|
17
|
+
import { checkValidityPeriod, validateCredential } from "./credential";
|
|
18
|
+
import { VcLogEvent } from "./log";
|
|
19
|
+
import { buildEncodedList, buildStatusEntry, buildStatusListCredential, createVcStatusStore, decodeBitstring, findStatusEntry, getBit, statusEntryIndex, } from "./status-list";
|
|
20
|
+
const JSON_HEADERS = { "content-type": "application/json" };
|
|
21
|
+
function json(body, status = 200) {
|
|
22
|
+
return new Response(JSON.stringify(body), { status, headers: JSON_HEADERS });
|
|
23
|
+
}
|
|
24
|
+
function problem(reason, description, status) {
|
|
25
|
+
return json({ error: reason, error_description: description }, status);
|
|
26
|
+
}
|
|
27
|
+
function emit(config, level, event, fields) {
|
|
28
|
+
config.logger[level](event, fields);
|
|
29
|
+
config.metrics.count(event, fields);
|
|
30
|
+
}
|
|
31
|
+
// Importing a key with crypto.subtle is comparatively expensive, and the secret
|
|
32
|
+
// binding is stable for the life of the isolate. Cache the resolved Signer by the
|
|
33
|
+
// raw JWK string so a hot issuance path imports the key once, not per request.
|
|
34
|
+
const signerCache = new Map();
|
|
35
|
+
/** Parse and validate the signing-key secret binding (fail loudly). */
|
|
36
|
+
async function loadSigner(env) {
|
|
37
|
+
if (!env.VC_SIGNING_KEY || typeof env.VC_SIGNING_KEY !== "string") {
|
|
38
|
+
throw new Error("@dwk/vc: missing required secret binding `VC_SIGNING_KEY`");
|
|
39
|
+
}
|
|
40
|
+
const cached = signerCache.get(env.VC_SIGNING_KEY);
|
|
41
|
+
if (cached !== undefined)
|
|
42
|
+
return cached;
|
|
43
|
+
let jwk;
|
|
44
|
+
try {
|
|
45
|
+
jwk = JSON.parse(env.VC_SIGNING_KEY);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
throw new Error("@dwk/vc: `VC_SIGNING_KEY` is not valid JWK JSON");
|
|
49
|
+
}
|
|
50
|
+
const signer = await importSigner(jwk);
|
|
51
|
+
signerCache.set(env.VC_SIGNING_KEY, signer);
|
|
52
|
+
return signer;
|
|
53
|
+
}
|
|
54
|
+
function getStore(env) {
|
|
55
|
+
return env.VC_STATUS_DB
|
|
56
|
+
? createVcStatusStore({ VC_STATUS_DB: env.VC_STATUS_DB })
|
|
57
|
+
: undefined;
|
|
58
|
+
}
|
|
59
|
+
/** The status list credential URL for a purpose, under this issuer. */
|
|
60
|
+
function statusListUrl(config, purpose) {
|
|
61
|
+
return `${config.statusListEndpoint}/${purpose}`;
|
|
62
|
+
}
|
|
63
|
+
async function isObjectBody(request) {
|
|
64
|
+
try {
|
|
65
|
+
const body = (await request.json());
|
|
66
|
+
if (body !== null && typeof body === "object" && !Array.isArray(body)) {
|
|
67
|
+
return body;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
/* fall through */
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
/** POST issue: sign a credential, optionally attaching a status entry. */
|
|
76
|
+
async function handleIssue(request, env, config) {
|
|
77
|
+
if (!(await config.authorize("issue", request))) {
|
|
78
|
+
emit(config, "warn", VcLogEvent.Rejected, { reason: "unauthorized" });
|
|
79
|
+
return problem("unauthorized", "not authorized to issue credentials", 401);
|
|
80
|
+
}
|
|
81
|
+
const body = await isObjectBody(request);
|
|
82
|
+
const credential = body?.credential;
|
|
83
|
+
if (credential === null ||
|
|
84
|
+
typeof credential !== "object" ||
|
|
85
|
+
Array.isArray(credential)) {
|
|
86
|
+
emit(config, "warn", VcLogEvent.Rejected, { reason: "malformed_request" });
|
|
87
|
+
return problem("malformed_request", "body must be { credential }", 400);
|
|
88
|
+
}
|
|
89
|
+
const doc = credential;
|
|
90
|
+
const errors = validateCredential(doc);
|
|
91
|
+
if (errors.length > 0) {
|
|
92
|
+
emit(config, "warn", VcLogEvent.Rejected, { reason: "malformed_request" });
|
|
93
|
+
return problem("malformed_request", errors.join("; "), 400);
|
|
94
|
+
}
|
|
95
|
+
// Attach a status entry when status is enabled, the request does not opt out,
|
|
96
|
+
// and the credential does not already carry one.
|
|
97
|
+
if (config.statusEnabled &&
|
|
98
|
+
body?.credentialStatus !== false &&
|
|
99
|
+
doc.credentialStatus === undefined) {
|
|
100
|
+
const store = getStore(env);
|
|
101
|
+
if (store !== undefined) {
|
|
102
|
+
await store.init();
|
|
103
|
+
const listUrl = statusListUrl(config, config.statusPurpose);
|
|
104
|
+
const index = await store.allocateIndex(listUrl, config.statusPurpose);
|
|
105
|
+
doc.credentialStatus = buildStatusEntry({
|
|
106
|
+
statusListCredential: listUrl,
|
|
107
|
+
statusListIndex: index,
|
|
108
|
+
statusPurpose: config.statusPurpose,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
let signer;
|
|
113
|
+
try {
|
|
114
|
+
signer = await loadSigner(env);
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
return problem("server_error", error.message, 500);
|
|
118
|
+
}
|
|
119
|
+
const verifiableCredential = await addProof(doc, signer, {
|
|
120
|
+
verificationMethod: config.verificationMethod,
|
|
121
|
+
});
|
|
122
|
+
emit(config, "info", VcLogEvent.Issued, { cryptosuite: signer.cryptosuite });
|
|
123
|
+
return json({ verifiableCredential });
|
|
124
|
+
}
|
|
125
|
+
/** Resolve a credential's status bit, best-effort. */
|
|
126
|
+
async function checkStatus(credential, env, config) {
|
|
127
|
+
const entry = findStatusEntry(credential, config.statusPurpose);
|
|
128
|
+
if (entry === undefined)
|
|
129
|
+
return { checked: false, revoked: false };
|
|
130
|
+
const index = statusEntryIndex(entry);
|
|
131
|
+
const listCredential = entry.statusListCredential;
|
|
132
|
+
if (index === undefined || typeof listCredential !== "string") {
|
|
133
|
+
return {
|
|
134
|
+
checked: false,
|
|
135
|
+
revoked: false,
|
|
136
|
+
error: "malformed credentialStatus",
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
// Our own list: read the authoritative D1 store directly.
|
|
140
|
+
const store = getStore(env);
|
|
141
|
+
if (store !== undefined &&
|
|
142
|
+
listCredential.startsWith(config.statusListEndpoint)) {
|
|
143
|
+
const revoked = await store.getStatus(listCredential, config.statusPurpose, index);
|
|
144
|
+
return { checked: true, revoked };
|
|
145
|
+
}
|
|
146
|
+
// Foreign list: fetch the published status list credential and decode it.
|
|
147
|
+
// Only https: is fetched — a foreign `statusListCredential` is attacker-
|
|
148
|
+
// controlled, so refusing other schemes blunts SSRF into internal services.
|
|
149
|
+
let listUrl;
|
|
150
|
+
try {
|
|
151
|
+
listUrl = new URL(listCredential);
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
return { checked: false, revoked: false, error: "status list malformed" };
|
|
155
|
+
}
|
|
156
|
+
if (listUrl.protocol !== "https:") {
|
|
157
|
+
return {
|
|
158
|
+
checked: false,
|
|
159
|
+
revoked: false,
|
|
160
|
+
error: "status list URL must use https",
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
try {
|
|
164
|
+
const response = await fetch(listUrl.toString(), {
|
|
165
|
+
headers: { accept: "application/json" },
|
|
166
|
+
});
|
|
167
|
+
if (!response.ok) {
|
|
168
|
+
return {
|
|
169
|
+
checked: false,
|
|
170
|
+
revoked: false,
|
|
171
|
+
error: "status list unreachable",
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
const listCred = (await response.json());
|
|
175
|
+
const subject = listCred.credentialSubject;
|
|
176
|
+
const encodedList = subject?.encodedList;
|
|
177
|
+
if (typeof encodedList !== "string") {
|
|
178
|
+
return { checked: false, revoked: false, error: "status list malformed" };
|
|
179
|
+
}
|
|
180
|
+
const bits = await decodeBitstring(encodedList);
|
|
181
|
+
return { checked: true, revoked: getBit(bits, index) };
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
return { checked: false, revoked: false, error: "status list unreachable" };
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/** POST verify: structural + validity + proof + status checks. */
|
|
188
|
+
async function handleVerify(request, env, config, resolver) {
|
|
189
|
+
const body = await isObjectBody(request);
|
|
190
|
+
const vc = body?.verifiableCredential ?? body?.credential;
|
|
191
|
+
if (vc === null || typeof vc !== "object" || Array.isArray(vc)) {
|
|
192
|
+
emit(config, "warn", VcLogEvent.Rejected, { reason: "malformed_request" });
|
|
193
|
+
return problem("malformed_request", "body must be { verifiableCredential }", 400);
|
|
194
|
+
}
|
|
195
|
+
const doc = vc;
|
|
196
|
+
const errors = [...validateCredential(doc)];
|
|
197
|
+
const warnings = [];
|
|
198
|
+
const proofResult = await verifyProof(doc, {
|
|
199
|
+
resolveVerificationMethod: resolver,
|
|
200
|
+
});
|
|
201
|
+
errors.push(...proofResult.errors);
|
|
202
|
+
const validity = checkValidityPeriod(doc);
|
|
203
|
+
if (validity !== null)
|
|
204
|
+
errors.push(`credential is ${validity}`);
|
|
205
|
+
const status = await checkStatus(doc, env, config);
|
|
206
|
+
if (status.error !== undefined)
|
|
207
|
+
warnings.push(status.error);
|
|
208
|
+
if (status.checked && status.revoked) {
|
|
209
|
+
errors.push(config.statusPurpose === "revocation"
|
|
210
|
+
? "credential is revoked"
|
|
211
|
+
: `credential status "${config.statusPurpose}" is set`);
|
|
212
|
+
}
|
|
213
|
+
const verified = errors.length === 0;
|
|
214
|
+
emit(config, verified ? "info" : "warn", VcLogEvent.Verified, { verified });
|
|
215
|
+
return json({ verified, errors, warnings });
|
|
216
|
+
}
|
|
217
|
+
/** POST status: flip a credential's status bit (e.g. revoke). */
|
|
218
|
+
async function handleStatusUpdate(request, env, config) {
|
|
219
|
+
if (!config.statusEnabled) {
|
|
220
|
+
emit(config, "warn", VcLogEvent.Rejected, { reason: "status_disabled" });
|
|
221
|
+
return problem("status_disabled", "status lists are not enabled", 404);
|
|
222
|
+
}
|
|
223
|
+
if (!(await config.authorize("status", request))) {
|
|
224
|
+
emit(config, "warn", VcLogEvent.Rejected, { reason: "unauthorized" });
|
|
225
|
+
return problem("unauthorized", "not authorized to change status", 401);
|
|
226
|
+
}
|
|
227
|
+
const store = getStore(env);
|
|
228
|
+
if (store === undefined) {
|
|
229
|
+
return problem("server_error", "missing D1 binding `VC_STATUS_DB`", 500);
|
|
230
|
+
}
|
|
231
|
+
const body = await isObjectBody(request);
|
|
232
|
+
if (body === null) {
|
|
233
|
+
emit(config, "warn", VcLogEvent.Rejected, { reason: "malformed_request" });
|
|
234
|
+
return problem("malformed_request", "body must be a JSON object", 400);
|
|
235
|
+
}
|
|
236
|
+
// Accept either a full credential (read its status entry) or an explicit index.
|
|
237
|
+
let index;
|
|
238
|
+
const purpose = (typeof body.statusPurpose === "string"
|
|
239
|
+
? body.statusPurpose
|
|
240
|
+
: config.statusPurpose);
|
|
241
|
+
const cred = body.credential;
|
|
242
|
+
if (cred !== undefined && typeof cred === "object" && !Array.isArray(cred)) {
|
|
243
|
+
const entry = findStatusEntry(cred, purpose);
|
|
244
|
+
if (entry !== undefined)
|
|
245
|
+
index = statusEntryIndex(entry);
|
|
246
|
+
}
|
|
247
|
+
if (index === undefined) {
|
|
248
|
+
const raw = body.statusListIndex ?? body.index;
|
|
249
|
+
const n = typeof raw === "string" ? Number(raw) : raw;
|
|
250
|
+
if (typeof n === "number" && Number.isInteger(n) && n >= 0)
|
|
251
|
+
index = n;
|
|
252
|
+
}
|
|
253
|
+
if (index === undefined) {
|
|
254
|
+
emit(config, "warn", VcLogEvent.Rejected, { reason: "malformed_request" });
|
|
255
|
+
return problem("malformed_request", "provide a credential with a status entry or a statusListIndex", 400);
|
|
256
|
+
}
|
|
257
|
+
const value = body.status === undefined ? true : body.status === true;
|
|
258
|
+
await store.init();
|
|
259
|
+
await store.setStatus(statusListUrl(config, purpose), purpose, index, value);
|
|
260
|
+
emit(config, "info", VcLogEvent.StatusChanged, {
|
|
261
|
+
statusPurpose: purpose,
|
|
262
|
+
value,
|
|
263
|
+
});
|
|
264
|
+
return json({ status: "ok", statusListIndex: index, value });
|
|
265
|
+
}
|
|
266
|
+
/** GET a signed status list credential for a purpose. */
|
|
267
|
+
async function handleStatusList(env, config, purposeSegment) {
|
|
268
|
+
if (!config.statusEnabled) {
|
|
269
|
+
return problem("status_disabled", "status lists are not enabled", 404);
|
|
270
|
+
}
|
|
271
|
+
const store = getStore(env);
|
|
272
|
+
if (store === undefined) {
|
|
273
|
+
return problem("server_error", "missing D1 binding `VC_STATUS_DB`", 500);
|
|
274
|
+
}
|
|
275
|
+
const purpose = purposeSegment;
|
|
276
|
+
const listUrl = statusListUrl(config, purpose);
|
|
277
|
+
await store.init();
|
|
278
|
+
const indices = await store.setIndices(listUrl, purpose);
|
|
279
|
+
const encodedList = await buildEncodedList(indices, config.statusListLength);
|
|
280
|
+
const unsigned = buildStatusListCredential({
|
|
281
|
+
id: listUrl,
|
|
282
|
+
statusPurpose: purpose,
|
|
283
|
+
encodedList,
|
|
284
|
+
issuer: config.did,
|
|
285
|
+
});
|
|
286
|
+
let signer;
|
|
287
|
+
try {
|
|
288
|
+
signer = await loadSigner(env);
|
|
289
|
+
}
|
|
290
|
+
catch (error) {
|
|
291
|
+
return problem("server_error", error.message, 500);
|
|
292
|
+
}
|
|
293
|
+
const signed = await addProof(unsigned, signer, {
|
|
294
|
+
verificationMethod: config.verificationMethod,
|
|
295
|
+
});
|
|
296
|
+
return json(signed);
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Build the `@dwk/vc` handler from configuration.
|
|
300
|
+
*
|
|
301
|
+
* The returned handler routes `POST {issueEndpoint}`, `POST {verifyEndpoint}`,
|
|
302
|
+
* `POST {statusEndpoint}`, and `GET {statusListEndpoint}/<purpose>`. Issuance and
|
|
303
|
+
* status changes are gated by the configured {@link AuthorizeOperation} hook (or
|
|
304
|
+
* the composing Worker's front door) and require the `VC_SIGNING_KEY` secret;
|
|
305
|
+
* status requires the `VC_STATUS_DB` D1 binding. Unmatched routes get `404`;
|
|
306
|
+
* wrong methods get `405`. The issuer credential / `issuerId` is recorded by host
|
|
307
|
+
* only; subjects, claims, keys, and proof values are never logged.
|
|
308
|
+
*/
|
|
309
|
+
export function createVc(config) {
|
|
310
|
+
const resolved = resolveConfig(config);
|
|
311
|
+
// Default verification resolver: did:web over the global fetch.
|
|
312
|
+
const resolver = resolved.resolveDid ?? createDidWebResolver();
|
|
313
|
+
return async (request, env, _ctx) => {
|
|
314
|
+
const url = new URL(request.url);
|
|
315
|
+
const path = url.pathname;
|
|
316
|
+
const method = request.method;
|
|
317
|
+
if (path === resolved.issuePath) {
|
|
318
|
+
if (method !== "POST")
|
|
319
|
+
return methodNotAllowed(resolved, "POST");
|
|
320
|
+
return handleIssue(request, env, resolved);
|
|
321
|
+
}
|
|
322
|
+
if (path === resolved.verifyPath) {
|
|
323
|
+
if (method !== "POST")
|
|
324
|
+
return methodNotAllowed(resolved, "POST");
|
|
325
|
+
return handleVerify(request, env, resolved, resolver);
|
|
326
|
+
}
|
|
327
|
+
if (path === resolved.statusPath) {
|
|
328
|
+
if (method !== "POST")
|
|
329
|
+
return methodNotAllowed(resolved, "POST");
|
|
330
|
+
return handleStatusUpdate(request, env, resolved);
|
|
331
|
+
}
|
|
332
|
+
if (path.startsWith(`${resolved.statusListPath}/`)) {
|
|
333
|
+
if (method !== "GET" && method !== "HEAD") {
|
|
334
|
+
return methodNotAllowed(resolved, "GET");
|
|
335
|
+
}
|
|
336
|
+
const segment = path.slice(resolved.statusListPath.length + 1);
|
|
337
|
+
if (segment.length === 0 || segment.includes("/")) {
|
|
338
|
+
return problem("not_found", "no such status list", 404);
|
|
339
|
+
}
|
|
340
|
+
const response = await handleStatusList(env, resolved, segment);
|
|
341
|
+
return method === "HEAD"
|
|
342
|
+
? new Response(null, {
|
|
343
|
+
status: response.status,
|
|
344
|
+
headers: response.headers,
|
|
345
|
+
})
|
|
346
|
+
: response;
|
|
347
|
+
}
|
|
348
|
+
emit(resolved, "warn", VcLogEvent.Rejected, {
|
|
349
|
+
reason: "not_found",
|
|
350
|
+
host: hostFromUrl(request.url),
|
|
351
|
+
});
|
|
352
|
+
return problem("not_found", "no such endpoint", 404);
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
function methodNotAllowed(config, allow) {
|
|
356
|
+
emit(config, "warn", VcLogEvent.Rejected, { reason: "method_not_allowed" });
|
|
357
|
+
return new Response(JSON.stringify({ error: "method_not_allowed" }), {
|
|
358
|
+
status: 405,
|
|
359
|
+
headers: { ...JSON_HEADERS, allow },
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
//# sourceMappingURL=handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.js","sourceRoot":"","sources":["../src/handler.ts"],"names":[],"mappings":"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"}
|