@dwk/webauthn 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.
Files changed (48) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +111 -0
  3. package/dist/cbor.d.ts +34 -0
  4. package/dist/cbor.d.ts.map +1 -0
  5. package/dist/cbor.js +144 -0
  6. package/dist/cbor.js.map +1 -0
  7. package/dist/config.d.ts +108 -0
  8. package/dist/config.d.ts.map +1 -0
  9. package/dist/config.js +70 -0
  10. package/dist/config.js.map +1 -0
  11. package/dist/cose.d.ts +73 -0
  12. package/dist/cose.d.ts.map +1 -0
  13. package/dist/cose.js +191 -0
  14. package/dist/cose.js.map +1 -0
  15. package/dist/encoding.d.ts +28 -0
  16. package/dist/encoding.d.ts.map +1 -0
  17. package/dist/encoding.js +63 -0
  18. package/dist/encoding.js.map +1 -0
  19. package/dist/handler.d.ts +20 -0
  20. package/dist/handler.d.ts.map +1 -0
  21. package/dist/handler.js +101 -0
  22. package/dist/handler.js.map +1 -0
  23. package/dist/index.d.ts +29 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +28 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/log.d.ts +25 -0
  28. package/dist/log.d.ts.map +1 -0
  29. package/dist/log.js +26 -0
  30. package/dist/log.js.map +1 -0
  31. package/dist/rp.d.ts +21 -0
  32. package/dist/rp.d.ts.map +1 -0
  33. package/dist/rp.js +336 -0
  34. package/dist/rp.js.map +1 -0
  35. package/dist/verify.d.ts +135 -0
  36. package/dist/verify.d.ts.map +1 -0
  37. package/dist/verify.js +277 -0
  38. package/dist/verify.js.map +1 -0
  39. package/package.json +50 -0
  40. package/src/cbor.ts +168 -0
  41. package/src/config.ts +179 -0
  42. package/src/cose.ts +238 -0
  43. package/src/encoding.ts +68 -0
  44. package/src/handler.ts +135 -0
  45. package/src/index.ts +54 -0
  46. package/src/log.ts +25 -0
  47. package/src/rp.ts +492 -0
  48. package/src/verify.ts +471 -0
package/dist/cose.js ADDED
@@ -0,0 +1,191 @@
1
+ /**
2
+ * COSE_Key (RFC 9052) → JWK conversion and the Web Crypto parameters for the
3
+ * COSE signature algorithms a WebAuthn relying party accepts.
4
+ *
5
+ * Authenticators hand the relying party a credential public key as a COSE_Key
6
+ * (an integer-keyed CBOR map) and sign assertions with a COSE algorithm
7
+ * identifier. This module narrows that to the algorithms we support — the same
8
+ * asymmetric set as `@dwk/dpop` (ES256/ES384, RS256/PS256) — and produces a JWK
9
+ * plus the `importKey`/`verify` parameter objects, so verification stays on Web
10
+ * Crypto with no external dependency.
11
+ *
12
+ * Web Crypto's ECDSA `verify` expects a raw `r‖s` signature, but WebAuthn EC
13
+ * assertions are ASN.1 DER `SEQUENCE { r, s }`; {@link derToRawEcdsaSignature}
14
+ * bridges that gap.
15
+ */
16
+ import { bytesToBase64url } from "./encoding";
17
+ /** COSE_Key map labels (RFC 9052 §7, RFC 9053). */
18
+ const COSE_KTY = 1;
19
+ const COSE_ALG = 3;
20
+ const COSE_EC_CRV = -1;
21
+ const COSE_EC_X = -2;
22
+ const COSE_EC_Y = -3;
23
+ const COSE_RSA_N = -1;
24
+ const COSE_RSA_E = -2;
25
+ /** COSE key types we accept. */
26
+ const KTY_EC2 = 2;
27
+ const KTY_RSA = 3;
28
+ /** COSE elliptic curves (RFC 9053 §7.1). */
29
+ const CRV_P256 = 1;
30
+ const CRV_P384 = 2;
31
+ const CRV_P521 = 3;
32
+ /** COSE algorithm identifiers (the asymmetric subset we accept). */
33
+ export const COSE_ALG_ES256 = -7;
34
+ export const COSE_ALG_ES384 = -35;
35
+ export const COSE_ALG_ES512 = -36;
36
+ export const COSE_ALG_RS256 = -257;
37
+ export const COSE_ALG_PS256 = -37;
38
+ /** Default `pubKeyCredParams`, most-preferred first: ES256 then RS256. */
39
+ export const DEFAULT_COSE_ALGORITHMS = [
40
+ COSE_ALG_ES256,
41
+ COSE_ALG_RS256,
42
+ ];
43
+ /** Thrown when a COSE_Key is malformed or uses an unsupported algorithm. */
44
+ export class CoseError extends Error {
45
+ }
46
+ function intValue(map, label) {
47
+ const v = map.get(label);
48
+ return typeof v === "number" ? v : null;
49
+ }
50
+ function bytesValue(map, label) {
51
+ const v = map.get(label);
52
+ return v instanceof Uint8Array ? v : null;
53
+ }
54
+ function ecCurveName(crv) {
55
+ switch (crv) {
56
+ case CRV_P256:
57
+ return "P-256";
58
+ case CRV_P384:
59
+ return "P-384";
60
+ case CRV_P521:
61
+ return "P-521";
62
+ default:
63
+ return null;
64
+ }
65
+ }
66
+ /** Convert a decoded COSE_Key map to a JWK plus its declared algorithm. */
67
+ export function coseToKey(map) {
68
+ const kty = intValue(map, COSE_KTY);
69
+ const alg = intValue(map, COSE_ALG);
70
+ if (kty === null || alg === null) {
71
+ throw new CoseError("COSE_Key missing kty/alg");
72
+ }
73
+ if (kty === KTY_EC2) {
74
+ const crv = intValue(map, COSE_EC_CRV);
75
+ const x = bytesValue(map, COSE_EC_X);
76
+ const y = bytesValue(map, COSE_EC_Y);
77
+ if (crv === null || x === null || y === null) {
78
+ throw new CoseError("COSE EC2 key missing crv/x/y");
79
+ }
80
+ const curve = ecCurveName(crv);
81
+ if (curve === null)
82
+ throw new CoseError(`unsupported COSE curve ${crv}`);
83
+ return {
84
+ jwk: {
85
+ kty: "EC",
86
+ crv: curve,
87
+ x: bytesToBase64url(x),
88
+ y: bytesToBase64url(y),
89
+ },
90
+ alg,
91
+ };
92
+ }
93
+ if (kty === KTY_RSA) {
94
+ const n = bytesValue(map, COSE_RSA_N);
95
+ const e = bytesValue(map, COSE_RSA_E);
96
+ if (n === null || e === null) {
97
+ throw new CoseError("COSE RSA key missing n/e");
98
+ }
99
+ return {
100
+ jwk: { kty: "RSA", n: bytesToBase64url(n), e: bytesToBase64url(e) },
101
+ alg,
102
+ };
103
+ }
104
+ throw new CoseError(`unsupported COSE kty ${kty}`);
105
+ }
106
+ /**
107
+ * Resolve the `importKey`/`verify` parameters for a COSE algorithm, or `null`
108
+ * when it is outside the accepted set. The set mirrors `@dwk/dpop`: ES256/ES384
109
+ * (ECDSA), RS256 (RSASSA-PKCS1-v1_5), PS256 (RSA-PSS).
110
+ */
111
+ export function cryptoParamsForCoseAlg(alg) {
112
+ switch (alg) {
113
+ case COSE_ALG_ES256:
114
+ return {
115
+ importParams: { name: "ECDSA", namedCurve: "P-256" },
116
+ verifyParams: { name: "ECDSA", hash: "SHA-256" },
117
+ ec: true,
118
+ ecSignatureBytes: 64,
119
+ };
120
+ case COSE_ALG_ES384:
121
+ return {
122
+ importParams: { name: "ECDSA", namedCurve: "P-384" },
123
+ verifyParams: { name: "ECDSA", hash: "SHA-384" },
124
+ ec: true,
125
+ ecSignatureBytes: 96,
126
+ };
127
+ case COSE_ALG_RS256:
128
+ return {
129
+ importParams: { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" },
130
+ verifyParams: { name: "RSASSA-PKCS1-v1_5" },
131
+ ec: false,
132
+ ecSignatureBytes: 0,
133
+ };
134
+ case COSE_ALG_PS256:
135
+ return {
136
+ importParams: { name: "RSA-PSS", hash: "SHA-256" },
137
+ verifyParams: { name: "RSA-PSS", saltLength: 32 },
138
+ ec: false,
139
+ ecSignatureBytes: 0,
140
+ };
141
+ default:
142
+ return null;
143
+ }
144
+ }
145
+ /**
146
+ * Convert an ASN.1 DER `SEQUENCE { INTEGER r, INTEGER s }` ECDSA signature to
147
+ * the fixed-length raw `r‖s` form Web Crypto's ECDSA `verify` expects. DER
148
+ * integers are big-endian, minimally encoded, and may carry a leading `0x00`
149
+ * sign byte; each is left-padded (or its sign byte trimmed) to `size/2` bytes.
150
+ *
151
+ * @param der - the DER-encoded signature
152
+ * @param size - the raw signature length (64 for P-256, 96 for P-384)
153
+ * @throws CoseError if the DER structure is malformed
154
+ */
155
+ export function derToRawEcdsaSignature(der, size) {
156
+ let offset = 0;
157
+ const next = () => {
158
+ if (offset >= der.length)
159
+ throw new CoseError("malformed DER signature");
160
+ return der[offset++];
161
+ };
162
+ if (next() !== 0x30)
163
+ throw new CoseError("DER signature not a SEQUENCE");
164
+ // SEQUENCE length (short form is sufficient for ECDSA signatures).
165
+ next();
166
+ const readInteger = () => {
167
+ if (next() !== 0x02)
168
+ throw new CoseError("DER signature missing INTEGER");
169
+ const len = next();
170
+ const end = offset + len;
171
+ if (end > der.length)
172
+ throw new CoseError("malformed DER signature");
173
+ let value = der.subarray(offset, end);
174
+ offset = end;
175
+ // Drop a leading sign byte and any over-long left padding.
176
+ while (value.length > 1 && value[0] === 0x00)
177
+ value = value.subarray(1);
178
+ return value;
179
+ };
180
+ const r = readInteger();
181
+ const s = readInteger();
182
+ const half = size / 2;
183
+ if (r.length > half || s.length > half) {
184
+ throw new CoseError("DER signature integer too large");
185
+ }
186
+ const raw = new Uint8Array(size);
187
+ raw.set(r, half - r.length);
188
+ raw.set(s, size - s.length);
189
+ return raw;
190
+ }
191
+ //# sourceMappingURL=cose.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cose.js","sourceRoot":"","sources":["../src/cose.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Byte / base64url encoding helpers shared across the WebAuthn ceremonies.
3
+ *
4
+ * WebAuthn transports binary fields (challenges, credential ids, signatures,
5
+ * attestation objects) as base64url over JSON, so every ceremony boundary needs
6
+ * the same encode/decode pair. These are pure and runtime-agnostic — Web APIs
7
+ * (`atob`/`btoa`/`TextEncoder`) only.
8
+ */
9
+ /** Decode a base64url (or padded base64) string to raw bytes. */
10
+ export declare function base64urlToBytes(input: string): Uint8Array;
11
+ /** Encode raw bytes as an unpadded base64url string. */
12
+ export declare function bytesToBase64url(bytes: Uint8Array): string;
13
+ /**
14
+ * Strip any base64 padding and normalize base64 to base64url so two encodings
15
+ * of the same bytes compare equal. WebAuthn clients emit the challenge in
16
+ * `clientDataJSON` as unpadded base64url; this lets a comparison tolerate a
17
+ * padded or `+`/`/`-alphabet copy without a full decode/re-encode round trip.
18
+ */
19
+ export declare function normalizeBase64url(input: string): string;
20
+ /** Whether two byte arrays are equal (length + contents). Not constant-time. */
21
+ export declare function bytesEqual(a: Uint8Array, b: Uint8Array): boolean;
22
+ /** SHA-256 digest of the input bytes as a fresh `Uint8Array`. */
23
+ export declare function sha256(input: Uint8Array): Promise<Uint8Array>;
24
+ /** UTF-8 encode a string to bytes. */
25
+ export declare function utf8ToBytes(input: string): Uint8Array;
26
+ /** UTF-8 decode bytes to a string. */
27
+ export declare function bytesToUtf8(bytes: Uint8Array): string;
28
+ //# sourceMappingURL=encoding.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encoding.d.ts","sourceRoot":"","sources":["../src/encoding.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,iEAAiE;AACjE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAU1D;AAED,wDAAwD;AACxD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAS1D;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,gFAAgF;AAChF,wBAAgB,UAAU,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,GAAG,OAAO,CAMhE;AAED,iEAAiE;AACjE,wBAAsB,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAGnE;AAED,sCAAsC;AACtC,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAErD;AAED,sCAAsC;AACtC,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAErD"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Byte / base64url encoding helpers shared across the WebAuthn ceremonies.
3
+ *
4
+ * WebAuthn transports binary fields (challenges, credential ids, signatures,
5
+ * attestation objects) as base64url over JSON, so every ceremony boundary needs
6
+ * the same encode/decode pair. These are pure and runtime-agnostic — Web APIs
7
+ * (`atob`/`btoa`/`TextEncoder`) only.
8
+ */
9
+ /** Decode a base64url (or padded base64) string to raw bytes. */
10
+ export function base64urlToBytes(input) {
11
+ const b64 = input.replace(/-/g, "+").replace(/_/g, "/");
12
+ const padded = b64.length % 4 === 0 ? b64 : b64 + "=".repeat(4 - (b64.length % 4));
13
+ const binary = atob(padded);
14
+ const bytes = new Uint8Array(binary.length);
15
+ for (let i = 0; i < binary.length; i++) {
16
+ bytes[i] = binary.charCodeAt(i);
17
+ }
18
+ return bytes;
19
+ }
20
+ /** Encode raw bytes as an unpadded base64url string. */
21
+ export function bytesToBase64url(bytes) {
22
+ let binary = "";
23
+ for (const byte of bytes) {
24
+ binary += String.fromCharCode(byte);
25
+ }
26
+ return btoa(binary)
27
+ .replace(/\+/g, "-")
28
+ .replace(/\//g, "_")
29
+ .replace(/=+$/, "");
30
+ }
31
+ /**
32
+ * Strip any base64 padding and normalize base64 to base64url so two encodings
33
+ * of the same bytes compare equal. WebAuthn clients emit the challenge in
34
+ * `clientDataJSON` as unpadded base64url; this lets a comparison tolerate a
35
+ * padded or `+`/`/`-alphabet copy without a full decode/re-encode round trip.
36
+ */
37
+ export function normalizeBase64url(input) {
38
+ return input.replace(/-/g, "+").replace(/_/g, "/").replace(/=+$/, "");
39
+ }
40
+ /** Whether two byte arrays are equal (length + contents). Not constant-time. */
41
+ export function bytesEqual(a, b) {
42
+ if (a.length !== b.length)
43
+ return false;
44
+ for (let i = 0; i < a.length; i++) {
45
+ if (a[i] !== b[i])
46
+ return false;
47
+ }
48
+ return true;
49
+ }
50
+ /** SHA-256 digest of the input bytes as a fresh `Uint8Array`. */
51
+ export async function sha256(input) {
52
+ const digest = await crypto.subtle.digest("SHA-256", input);
53
+ return new Uint8Array(digest);
54
+ }
55
+ /** UTF-8 encode a string to bytes. */
56
+ export function utf8ToBytes(input) {
57
+ return new TextEncoder().encode(input);
58
+ }
59
+ /** UTF-8 decode bytes to a string. */
60
+ export function bytesToUtf8(bytes) {
61
+ return new TextDecoder().decode(bytes);
62
+ }
63
+ //# sourceMappingURL=encoding.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encoding.js","sourceRoot":"","sources":["../src/encoding.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,iEAAiE;AACjE,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IACxD,MAAM,MAAM,GACV,GAAG,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;IACtE,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;IAC5B,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC5C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,KAAK,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAClC,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,wDAAwD;AACxD,MAAM,UAAU,gBAAgB,CAAC,KAAiB;IAChD,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IACtC,CAAC;IACD,OAAO,IAAI,CAAC,MAAM,CAAC;SAChB,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;SACnB,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;SACnB,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACxB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAa;IAC9C,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACxE,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,UAAU,CAAC,CAAa,EAAE,CAAa;IACrD,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IACxC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAClC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAAE,OAAO,KAAK,CAAC;IAClC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,iEAAiE;AACjE,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,KAAiB;IAC5C,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,KAAqB,CAAC,CAAC;IAC5E,OAAO,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;AAChC,CAAC;AAED,sCAAsC;AACtC,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,OAAO,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACzC,CAAC;AAED,sCAAsC;AACtC,MAAM,UAAU,WAAW,CAAC,KAAiB;IAC3C,OAAO,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACzC,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * The stateless WebAuthn relying-party front door.
3
+ *
4
+ * It routes the four ceremony steps — `/register/options`, `/register/verify`,
5
+ * `/authenticate/options`, `/authenticate/verify` — to the per-relying-party
6
+ * Durable Object, which owns all challenge and credential state. The handler is
7
+ * mountable under any path prefix because it identifies the step by the *tail*
8
+ * of the request path, and it injects the trusted clock and forwarded config the
9
+ * DO needs (the DO cannot hold the injected logger/metrics/clock itself). It
10
+ * emits each ceremony outcome on the wired logger and metrics seams.
11
+ */
12
+ import { type WebAuthnConfig, type WebAuthnEnv } from "./config";
13
+ /** A `fetch`-compatible Worker handler. */
14
+ export type WebAuthnHandler = (request: Request, env: WebAuthnEnv, ctx: ExecutionContext) => Promise<Response>;
15
+ /**
16
+ * Create the stateless WebAuthn relying-party handler. All four ceremony steps
17
+ * are `POST`; anything else is `405`. Unmatched paths are `404`.
18
+ */
19
+ export declare function createWebAuthn(config: WebAuthnConfig): WebAuthnHandler;
20
+ //# sourceMappingURL=handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../src/handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,EAKL,KAAK,cAAc,EACnB,KAAK,WAAW,EAEjB,MAAM,UAAU,CAAC;AAElB,2CAA2C;AAC3C,MAAM,MAAM,eAAe,GAAG,CAC5B,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,WAAW,EAChB,GAAG,EAAE,gBAAgB,KAClB,OAAO,CAAC,QAAQ,CAAC,CAAC;AA6EvB;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,cAAc,GAAG,eAAe,CAwBtE"}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * The stateless WebAuthn relying-party front door.
3
+ *
4
+ * It routes the four ceremony steps — `/register/options`, `/register/verify`,
5
+ * `/authenticate/options`, `/authenticate/verify` — to the per-relying-party
6
+ * Durable Object, which owns all challenge and credential state. The handler is
7
+ * mountable under any path prefix because it identifies the step by the *tail*
8
+ * of the request path, and it injects the trusted clock and forwarded config the
9
+ * DO needs (the DO cannot hold the injected logger/metrics/clock itself). It
10
+ * emits each ceremony outcome on the wired logger and metrics seams.
11
+ */
12
+ import {} from "@dwk/log";
13
+ import { INTERNAL_HEADERS, forwardedConfig, resolveConfig, } from "./config";
14
+ /** Map a request path tail to a ceremony operation, or `null` if unmatched. */
15
+ function operationForPath(pathname) {
16
+ if (pathname.endsWith("/register/options"))
17
+ return "register/options";
18
+ if (pathname.endsWith("/register/verify"))
19
+ return "register/verify";
20
+ if (pathname.endsWith("/authenticate/options"))
21
+ return "authenticate/options";
22
+ if (pathname.endsWith("/authenticate/verify"))
23
+ return "authenticate/verify";
24
+ return null;
25
+ }
26
+ /** Fail loudly if a required Cloudflare binding is missing (no silent degradation). */
27
+ function assertBindings(env) {
28
+ if (!env.WEBAUTHN) {
29
+ throw new Error("@dwk/webauthn: missing required Durable Object binding `WEBAUTHN`");
30
+ }
31
+ }
32
+ /** Emit a structured event on both the logger and the metrics seam. */
33
+ function emit(config, level, event, fields) {
34
+ config.logger[level](event, fields);
35
+ config.metrics.count(event, fields);
36
+ }
37
+ /**
38
+ * Translate the Durable Object's outcome headers into a structured event on the
39
+ * injected seams, then strip those internal headers before the response reaches
40
+ * the client. A rejection (any non-2xx) logs at `warn` with its stable reason;
41
+ * a success logs at `info`.
42
+ */
43
+ function logOutcome(config, response) {
44
+ const event = response.headers.get(INTERNAL_HEADERS.event);
45
+ if (event) {
46
+ const reason = response.headers.get(INTERNAL_HEADERS.reason);
47
+ if (response.ok) {
48
+ emit(config, "info", event);
49
+ }
50
+ else {
51
+ emit(config, "warn", event, reason ? { reason } : undefined);
52
+ }
53
+ }
54
+ const headers = new Headers(response.headers);
55
+ headers.delete(INTERNAL_HEADERS.event);
56
+ headers.delete(INTERNAL_HEADERS.reason);
57
+ return new Response(response.body, {
58
+ status: response.status,
59
+ statusText: response.statusText,
60
+ headers,
61
+ });
62
+ }
63
+ /** Build the internal DO request: forwarded config, trusted clock, op, body. */
64
+ function internalRequest(request, config, op) {
65
+ const headers = new Headers({
66
+ "content-type": "application/json",
67
+ [INTERNAL_HEADERS.op]: op,
68
+ [INTERNAL_HEADERS.config]: JSON.stringify(forwardedConfig(config)),
69
+ [INTERNAL_HEADERS.now]: String(config.now()),
70
+ });
71
+ return new Request(request.url, {
72
+ method: "POST",
73
+ headers,
74
+ body: request.body,
75
+ });
76
+ }
77
+ /**
78
+ * Create the stateless WebAuthn relying-party handler. All four ceremony steps
79
+ * are `POST`; anything else is `405`. Unmatched paths are `404`.
80
+ */
81
+ export function createWebAuthn(config) {
82
+ const resolved = resolveConfig(config);
83
+ return async (request, env, _ctx) => {
84
+ assertBindings(env);
85
+ const op = operationForPath(new URL(request.url).pathname);
86
+ if (op === null) {
87
+ return new Response("Not Found", { status: 404 });
88
+ }
89
+ if (request.method.toUpperCase() !== "POST") {
90
+ return new Response("Method Not Allowed", {
91
+ status: 405,
92
+ headers: { allow: "POST" },
93
+ });
94
+ }
95
+ // One Durable Object per relying party, keyed by the rpId (no sharding).
96
+ const id = env.WEBAUTHN.idFromName(resolved.rpId);
97
+ const response = await env.WEBAUTHN.get(id).fetch(internalRequest(request, resolved, op));
98
+ return logOutcome(resolved, response);
99
+ };
100
+ }
101
+ //# sourceMappingURL=handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.js","sourceRoot":"","sources":["../src/handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * `@dwk/webauthn` — a WebAuthn / passkeys relying party.
3
+ *
4
+ * A stateless Worker front door over a per-relying-party Durable Object that is
5
+ * the consistency authority for short-TTL challenge state and credential
6
+ * records. It runs the four ceremony steps — registration (attestation) and
7
+ * authentication (assertion) options + verification — entirely on Web Crypto,
8
+ * with no external dependency beyond `@dwk/log`.
9
+ *
10
+ * The package is **not** protocol-agnostic: it is the WebAuthn endpoint. It is
11
+ * filed for completeness as an **exploratory, lowest-priority** package — a clean
12
+ * Workers fit, but a step toward generic authentication rather than the
13
+ * web-presence standards this cohort centers on.
14
+ *
15
+ * Attestation is verified for the `none` and `packed` self-attestation formats
16
+ * (the relying party requests `attestation: "none"`); full attestation-chain
17
+ * verification is intentionally out of scope.
18
+ *
19
+ * @see spec/packages/webauthn.md
20
+ * @packageDocumentation
21
+ */
22
+ export { createWebAuthn, type WebAuthnHandler } from "./handler";
23
+ export { WebAuthnObject } from "./rp";
24
+ export { type WebAuthnConfig, type WebAuthnEnv, type UserVerificationRequirement, } from "./config";
25
+ export { DEFAULT_COSE_ALGORITHMS, COSE_ALG_ES256, COSE_ALG_ES384, COSE_ALG_RS256, COSE_ALG_PS256, } from "./cose";
26
+ export { verifyRegistration, verifyAuthentication, parseClientData, parseAuthenticatorData, type RegistrationVerifyInput, type RegistrationVerifyResult, type AuthenticationVerifyInput, type AuthenticationVerifyResult, type VerifiedCredential, type VerifyFailureReason, } from "./verify";
27
+ export { WebAuthnLogEvent } from "./log";
28
+ export type { Logger, Metrics } from "@dwk/log";
29
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,cAAc,EAAE,KAAK,eAAe,EAAE,MAAM,WAAW,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,MAAM,CAAC;AAEtC,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,2BAA2B,GACjC,MAAM,UAAU,CAAC;AAElB,OAAO,EACL,uBAAuB,EACvB,cAAc,EACd,cAAc,EACd,cAAc,EACd,cAAc,GACf,MAAM,QAAQ,CAAC;AAEhB,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,eAAe,EACf,sBAAsB,EACtB,KAAK,uBAAuB,EAC5B,KAAK,wBAAwB,EAC7B,KAAK,yBAAyB,EAC9B,KAAK,0BAA0B,EAC/B,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,GACzB,MAAM,UAAU,CAAC;AAElB,OAAO,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAC;AACzC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,28 @@
1
+ /**
2
+ * `@dwk/webauthn` — a WebAuthn / passkeys relying party.
3
+ *
4
+ * A stateless Worker front door over a per-relying-party Durable Object that is
5
+ * the consistency authority for short-TTL challenge state and credential
6
+ * records. It runs the four ceremony steps — registration (attestation) and
7
+ * authentication (assertion) options + verification — entirely on Web Crypto,
8
+ * with no external dependency beyond `@dwk/log`.
9
+ *
10
+ * The package is **not** protocol-agnostic: it is the WebAuthn endpoint. It is
11
+ * filed for completeness as an **exploratory, lowest-priority** package — a clean
12
+ * Workers fit, but a step toward generic authentication rather than the
13
+ * web-presence standards this cohort centers on.
14
+ *
15
+ * Attestation is verified for the `none` and `packed` self-attestation formats
16
+ * (the relying party requests `attestation: "none"`); full attestation-chain
17
+ * verification is intentionally out of scope.
18
+ *
19
+ * @see spec/packages/webauthn.md
20
+ * @packageDocumentation
21
+ */
22
+ export { createWebAuthn } from "./handler";
23
+ export { WebAuthnObject } from "./rp";
24
+ export {} from "./config";
25
+ export { DEFAULT_COSE_ALGORITHMS, COSE_ALG_ES256, COSE_ALG_ES384, COSE_ALG_RS256, COSE_ALG_PS256, } from "./cose";
26
+ export { verifyRegistration, verifyAuthentication, parseClientData, parseAuthenticatorData, } from "./verify";
27
+ export { WebAuthnLogEvent } from "./log";
28
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,cAAc,EAAwB,MAAM,WAAW,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,MAAM,CAAC;AAEtC,OAAO,EAIN,MAAM,UAAU,CAAC;AAElB,OAAO,EACL,uBAAuB,EACvB,cAAc,EACd,cAAc,EACd,cAAc,EACd,cAAc,GACf,MAAM,QAAQ,CAAC;AAEhB,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,eAAe,EACf,sBAAsB,GAOvB,MAAM,UAAU,CAAC;AAElB,OAAO,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAC"}
package/dist/log.d.ts ADDED
@@ -0,0 +1,25 @@
1
+ /**
2
+ * The structured-logging vocabulary for `@dwk/webauthn`.
3
+ *
4
+ * The Durable Object cannot hold the injected logger/metrics (they do not cross
5
+ * the isolate boundary), so it names its ceremony outcome in a response header
6
+ * ({@link INTERNAL_HEADERS.event}) and the front door emits it on the wired
7
+ * seams. Event names are stable and dotted; callers attach only reason codes and
8
+ * non-sensitive facts — never challenges, signatures, or public keys.
9
+ */
10
+ /** Stable, dotted event names emitted on the logger and metrics seams. */
11
+ export declare enum WebAuthnLogEvent {
12
+ /** Registration options issued (a fresh challenge was minted). */
13
+ RegisterOptions = "webauthn.register.options",
14
+ /** A registration ceremony verified and a credential was stored. */
15
+ RegisterVerified = "webauthn.register.verified",
16
+ /** A registration ceremony was rejected. */
17
+ RegisterRejected = "webauthn.register.rejected",
18
+ /** Authentication options issued. */
19
+ AuthenticateOptions = "webauthn.authenticate.options",
20
+ /** An authentication ceremony verified. */
21
+ AuthenticateVerified = "webauthn.authenticate.verified",
22
+ /** An authentication ceremony was rejected. */
23
+ AuthenticateRejected = "webauthn.authenticate.rejected"
24
+ }
25
+ //# sourceMappingURL=log.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,0EAA0E;AAC1E,oBAAY,gBAAgB;IAC1B,kEAAkE;IAClE,eAAe,8BAA8B;IAC7C,oEAAoE;IACpE,gBAAgB,+BAA+B;IAC/C,4CAA4C;IAC5C,gBAAgB,+BAA+B;IAC/C,qCAAqC;IACrC,mBAAmB,kCAAkC;IACrD,2CAA2C;IAC3C,oBAAoB,mCAAmC;IACvD,+CAA+C;IAC/C,oBAAoB,mCAAmC;CACxD"}
package/dist/log.js ADDED
@@ -0,0 +1,26 @@
1
+ /**
2
+ * The structured-logging vocabulary for `@dwk/webauthn`.
3
+ *
4
+ * The Durable Object cannot hold the injected logger/metrics (they do not cross
5
+ * the isolate boundary), so it names its ceremony outcome in a response header
6
+ * ({@link INTERNAL_HEADERS.event}) and the front door emits it on the wired
7
+ * seams. Event names are stable and dotted; callers attach only reason codes and
8
+ * non-sensitive facts — never challenges, signatures, or public keys.
9
+ */
10
+ /** Stable, dotted event names emitted on the logger and metrics seams. */
11
+ export var WebAuthnLogEvent;
12
+ (function (WebAuthnLogEvent) {
13
+ /** Registration options issued (a fresh challenge was minted). */
14
+ WebAuthnLogEvent["RegisterOptions"] = "webauthn.register.options";
15
+ /** A registration ceremony verified and a credential was stored. */
16
+ WebAuthnLogEvent["RegisterVerified"] = "webauthn.register.verified";
17
+ /** A registration ceremony was rejected. */
18
+ WebAuthnLogEvent["RegisterRejected"] = "webauthn.register.rejected";
19
+ /** Authentication options issued. */
20
+ WebAuthnLogEvent["AuthenticateOptions"] = "webauthn.authenticate.options";
21
+ /** An authentication ceremony verified. */
22
+ WebAuthnLogEvent["AuthenticateVerified"] = "webauthn.authenticate.verified";
23
+ /** An authentication ceremony was rejected. */
24
+ WebAuthnLogEvent["AuthenticateRejected"] = "webauthn.authenticate.rejected";
25
+ })(WebAuthnLogEvent || (WebAuthnLogEvent = {}));
26
+ //# sourceMappingURL=log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.js","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,0EAA0E;AAC1E,MAAM,CAAN,IAAY,gBAaX;AAbD,WAAY,gBAAgB;IAC1B,kEAAkE;IAClE,iEAA6C,CAAA;IAC7C,oEAAoE;IACpE,mEAA+C,CAAA;IAC/C,4CAA4C;IAC5C,mEAA+C,CAAA;IAC/C,qCAAqC;IACrC,yEAAqD,CAAA;IACrD,2CAA2C;IAC3C,2EAAuD,CAAA;IACvD,+CAA+C;IAC/C,2EAAuD,CAAA;AACzD,CAAC,EAbW,gBAAgB,KAAhB,gBAAgB,QAa3B"}
package/dist/rp.d.ts ADDED
@@ -0,0 +1,21 @@
1
+ /**
2
+ * The per-relying-party Durable Object: the single-threaded consistency
3
+ * authority for WebAuthn challenge state and credential records.
4
+ *
5
+ * The stateless front door (`handler.ts`) routes a ceremony step to this object
6
+ * via the {@link INTERNAL_HEADERS.op} header and the request body; everything
7
+ * that must be strongly consistent — minting and single-use consumption of
8
+ * short-TTL challenges, and reading/writing credential records (public key,
9
+ * signature counter, transports) — happens here, where Cloudflare guarantees a
10
+ * single thread per relying party. Challenge and credential state are kept in DO
11
+ * SQLite (never KV: a stale challenge or counter is a security bug). Consumers
12
+ * bind this class as a Durable Object namespace.
13
+ */
14
+ import { DurableObject } from "cloudflare:workers";
15
+ import { type WebAuthnEnv } from "./config";
16
+ export declare class WebAuthnObject extends DurableObject<WebAuthnEnv> {
17
+ #private;
18
+ constructor(state: DurableObjectState, env: WebAuthnEnv);
19
+ fetch(request: Request): Promise<Response>;
20
+ }
21
+ //# sourceMappingURL=rp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rp.d.ts","sourceRoot":"","sources":["../src/rp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD,OAAO,EAGL,KAAK,WAAW,EACjB,MAAM,UAAU,CAAC;AAgClB,qBAAa,cAAe,SAAQ,aAAa,CAAC,WAAW,CAAC;;gBAGhD,KAAK,EAAE,kBAAkB,EAAE,GAAG,EAAE,WAAW;IA0BxC,KAAK,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC;CAwU1D"}