@bradford-tech/supabase-integrity-attest 0.3.1 → 0.4.1

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 (76) hide show
  1. package/README.md +1 -1
  2. package/esm/assertion.d.ts +22 -1
  3. package/esm/assertion.d.ts.map +1 -1
  4. package/esm/assertion.js +22 -2
  5. package/esm/attestation.d.ts +22 -0
  6. package/esm/attestation.d.ts.map +1 -1
  7. package/esm/attestation.js +22 -1
  8. package/esm/mod.d.ts +93 -1
  9. package/esm/mod.d.ts.map +1 -1
  10. package/esm/mod.js +93 -2
  11. package/esm/src/assertion.d.ts +14 -0
  12. package/esm/src/assertion.d.ts.map +1 -1
  13. package/esm/src/assertion.js +9 -0
  14. package/esm/src/attestation.d.ts +20 -1
  15. package/esm/src/attestation.d.ts.map +1 -1
  16. package/esm/src/attestation.js +15 -4
  17. package/esm/src/errors.d.ts +52 -4
  18. package/esm/src/errors.d.ts.map +1 -1
  19. package/esm/src/errors.js +47 -3
  20. package/esm/src/with-assertion.d.ts +63 -1
  21. package/esm/src/with-assertion.d.ts.map +1 -1
  22. package/esm/src/with-assertion.js +36 -3
  23. package/esm/src/with-attestation.d.ts +79 -0
  24. package/esm/src/with-attestation.d.ts.map +1 -0
  25. package/esm/src/with-attestation.js +135 -0
  26. package/package.json +4 -7
  27. package/esm/_dnt.test_polyfills.d.ts.map +0 -1
  28. package/esm/_dnt.test_shims.d.ts.map +0 -1
  29. package/esm/deps/jsr.io/@std/assert/1.0.19/almost_equals.d.ts.map +0 -1
  30. package/esm/deps/jsr.io/@std/assert/1.0.19/array_includes.d.ts.map +0 -1
  31. package/esm/deps/jsr.io/@std/assert/1.0.19/assert.d.ts.map +0 -1
  32. package/esm/deps/jsr.io/@std/assert/1.0.19/assertion_error.d.ts.map +0 -1
  33. package/esm/deps/jsr.io/@std/assert/1.0.19/equal.d.ts.map +0 -1
  34. package/esm/deps/jsr.io/@std/assert/1.0.19/equals.d.ts.map +0 -1
  35. package/esm/deps/jsr.io/@std/assert/1.0.19/exists.d.ts.map +0 -1
  36. package/esm/deps/jsr.io/@std/assert/1.0.19/fail.d.ts.map +0 -1
  37. package/esm/deps/jsr.io/@std/assert/1.0.19/false.d.ts.map +0 -1
  38. package/esm/deps/jsr.io/@std/assert/1.0.19/greater.d.ts.map +0 -1
  39. package/esm/deps/jsr.io/@std/assert/1.0.19/greater_or_equal.d.ts.map +0 -1
  40. package/esm/deps/jsr.io/@std/assert/1.0.19/instance_of.d.ts.map +0 -1
  41. package/esm/deps/jsr.io/@std/assert/1.0.19/is_error.d.ts.map +0 -1
  42. package/esm/deps/jsr.io/@std/assert/1.0.19/less.d.ts.map +0 -1
  43. package/esm/deps/jsr.io/@std/assert/1.0.19/less_or_equal.d.ts.map +0 -1
  44. package/esm/deps/jsr.io/@std/assert/1.0.19/match.d.ts.map +0 -1
  45. package/esm/deps/jsr.io/@std/assert/1.0.19/mod.d.ts.map +0 -1
  46. package/esm/deps/jsr.io/@std/assert/1.0.19/not_equals.d.ts.map +0 -1
  47. package/esm/deps/jsr.io/@std/assert/1.0.19/not_instance_of.d.ts.map +0 -1
  48. package/esm/deps/jsr.io/@std/assert/1.0.19/not_match.d.ts.map +0 -1
  49. package/esm/deps/jsr.io/@std/assert/1.0.19/not_strict_equals.d.ts.map +0 -1
  50. package/esm/deps/jsr.io/@std/assert/1.0.19/object_match.d.ts.map +0 -1
  51. package/esm/deps/jsr.io/@std/assert/1.0.19/rejects.d.ts.map +0 -1
  52. package/esm/deps/jsr.io/@std/assert/1.0.19/strict_equals.d.ts.map +0 -1
  53. package/esm/deps/jsr.io/@std/assert/1.0.19/string_includes.d.ts.map +0 -1
  54. package/esm/deps/jsr.io/@std/assert/1.0.19/throws.d.ts.map +0 -1
  55. package/esm/deps/jsr.io/@std/assert/1.0.19/unimplemented.d.ts.map +0 -1
  56. package/esm/deps/jsr.io/@std/assert/1.0.19/unreachable.d.ts.map +0 -1
  57. package/esm/deps/jsr.io/@std/internal/1.0.12/build_message.d.ts.map +0 -1
  58. package/esm/deps/jsr.io/@std/internal/1.0.12/diff.d.ts.map +0 -1
  59. package/esm/deps/jsr.io/@std/internal/1.0.12/diff_str.d.ts.map +0 -1
  60. package/esm/deps/jsr.io/@std/internal/1.0.12/format.d.ts.map +0 -1
  61. package/esm/deps/jsr.io/@std/internal/1.0.12/styles.d.ts.map +0 -1
  62. package/esm/deps/jsr.io/@std/internal/1.0.12/types.d.ts.map +0 -1
  63. package/esm/src/cose.d.ts.map +0 -1
  64. package/esm/tests/assertion-entry.test.d.ts.map +0 -1
  65. package/esm/tests/assertion.test.d.ts.map +0 -1
  66. package/esm/tests/attestation-entry.test.d.ts.map +0 -1
  67. package/esm/tests/attestation.test.d.ts.map +0 -1
  68. package/esm/tests/authdata.test.d.ts.map +0 -1
  69. package/esm/tests/certificate.test.d.ts.map +0 -1
  70. package/esm/tests/cose.test.d.ts.map +0 -1
  71. package/esm/tests/der.test.d.ts.map +0 -1
  72. package/esm/tests/errors.test.d.ts.map +0 -1
  73. package/esm/tests/fixtures/apple-attestation.d.ts.map +0 -1
  74. package/esm/tests/fixtures/generate-assertion.d.ts.map +0 -1
  75. package/esm/tests/utils.test.d.ts.map +0 -1
  76. package/esm/tests/with-assertion.test.d.ts.map +0 -1
package/README.md CHANGED
@@ -10,7 +10,7 @@ assertions using the WebCrypto API. Built for Deno and Supabase Edge Functions.
10
10
  deno add jsr:@bradford-tech/supabase-integrity-attest
11
11
 
12
12
  # npm
13
- npx jsr add @bradford-tech/supabase-integrity-attest
13
+ npm install @bradford-tech/supabase-integrity-attest
14
14
  ```
15
15
 
16
16
  ## Subpath imports
@@ -1,7 +1,28 @@
1
+ /**
2
+ * Lightweight assertion-only entry point. Avoids pulling in `asn1js` and
3
+ * `@noble/curves`, keeping the bundle minimal for assertion-only use cases.
4
+ *
5
+ * ```ts
6
+ * import {
7
+ * verifyAssertion,
8
+ * withAssertion,
9
+ * } from "@bradford-tech/supabase-integrity-attest/assertion";
10
+ *
11
+ * const { signCount } = await verifyAssertion(
12
+ * { appId: "TEAMID.com.example.app" },
13
+ * assertion,
14
+ * clientData,
15
+ * publicKeyPem,
16
+ * previousSignCount,
17
+ * );
18
+ * ```
19
+ *
20
+ * @module
21
+ */
1
22
  export { verifyAssertion } from "./src/assertion.js";
2
23
  export type { AppInfo, AssertionResult } from "./src/assertion.js";
3
24
  export { AssertionError, AssertionErrorCode } from "./src/errors.js";
4
25
  export { withAssertion } from "./src/with-assertion.js";
5
26
  export { DEFAULT_ASSERTION_HEADER, DEFAULT_DEVICE_ID_HEADER, } from "./src/with-assertion.js";
6
- export type { AssertionContext, DeviceKey, ExtractAssertionFn, WithAssertionOptions, } from "./src/with-assertion.js";
27
+ export type { AssertionContext, AssertionTimings, DeviceKey, ExtractAssertionFn, WithAssertionOptions, } from "./src/with-assertion.js";
7
28
  //# sourceMappingURL=assertion.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"assertion.d.ts","sourceRoot":"","sources":["../src/assertion.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,YAAY,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAGrE,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EACL,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACV,gBAAgB,EAChB,SAAS,EACT,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,yBAAyB,CAAC"}
1
+ {"version":3,"file":"assertion.d.ts","sourceRoot":"","sources":["../src/assertion.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,YAAY,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAGrE,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EACL,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACV,gBAAgB,EAChB,gBAAgB,EAChB,SAAS,EACT,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,yBAAyB,CAAC"}
package/esm/assertion.js CHANGED
@@ -1,6 +1,26 @@
1
- // assertion.ts — lightweight entry point (no asn1js / @noble/curves)
1
+ /**
2
+ * Lightweight assertion-only entry point. Avoids pulling in `asn1js` and
3
+ * `@noble/curves`, keeping the bundle minimal for assertion-only use cases.
4
+ *
5
+ * ```ts
6
+ * import {
7
+ * verifyAssertion,
8
+ * withAssertion,
9
+ * } from "@bradford-tech/supabase-integrity-attest/assertion";
10
+ *
11
+ * const { signCount } = await verifyAssertion(
12
+ * { appId: "TEAMID.com.example.app" },
13
+ * assertion,
14
+ * clientData,
15
+ * publicKeyPem,
16
+ * previousSignCount,
17
+ * );
18
+ * ```
19
+ *
20
+ * @module
21
+ */
2
22
  export { verifyAssertion } from "./src/assertion.js";
3
23
  export { AssertionError, AssertionErrorCode } from "./src/errors.js";
4
- // withAssertion wrapper
24
+ // withAssertion middleware
5
25
  export { withAssertion } from "./src/with-assertion.js";
6
26
  export { DEFAULT_ASSERTION_HEADER, DEFAULT_DEVICE_ID_HEADER, } from "./src/with-assertion.js";
@@ -1,4 +1,26 @@
1
+ /**
2
+ * Full attestation entry point including certificate chain verification
3
+ * dependencies (`asn1js`, `@noble/curves`).
4
+ *
5
+ * ```ts
6
+ * import {
7
+ * verifyAttestation,
8
+ * withAttestation,
9
+ * } from "@bradford-tech/supabase-integrity-attest/attestation";
10
+ *
11
+ * const { publicKeyPem } = await verifyAttestation(
12
+ * { appId: "TEAMID.com.example.app" },
13
+ * keyId,
14
+ * challenge,
15
+ * attestation,
16
+ * );
17
+ * ```
18
+ *
19
+ * @module
20
+ */
1
21
  export { verifyAttestation } from "./src/attestation.js";
2
22
  export type { AppInfo, AttestationResult, VerifyAttestationOptions, } from "./src/attestation.js";
3
23
  export { AttestationError, AttestationErrorCode } from "./src/errors.js";
24
+ export { withAttestation } from "./src/with-attestation.js";
25
+ export type { AttestationContext, AttestationTimings, ExtractAttestationFn, WithAttestationOptions, } from "./src/with-attestation.js";
4
26
  //# sourceMappingURL=attestation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"attestation.d.ts","sourceRoot":"","sources":["../src/attestation.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,YAAY,EACV,OAAO,EACP,iBAAiB,EACjB,wBAAwB,GACzB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"attestation.d.ts","sourceRoot":"","sources":["../src/attestation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,YAAY,EACV,OAAO,EACP,iBAAiB,EACjB,wBAAwB,GACzB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAGzE,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,YAAY,EACV,kBAAkB,EAClB,kBAAkB,EAClB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,2BAA2B,CAAC"}
@@ -1,3 +1,24 @@
1
- // attestation.ts — full attestation entry point (includes cert chain deps)
1
+ /**
2
+ * Full attestation entry point including certificate chain verification
3
+ * dependencies (`asn1js`, `@noble/curves`).
4
+ *
5
+ * ```ts
6
+ * import {
7
+ * verifyAttestation,
8
+ * withAttestation,
9
+ * } from "@bradford-tech/supabase-integrity-attest/attestation";
10
+ *
11
+ * const { publicKeyPem } = await verifyAttestation(
12
+ * { appId: "TEAMID.com.example.app" },
13
+ * keyId,
14
+ * challenge,
15
+ * attestation,
16
+ * );
17
+ * ```
18
+ *
19
+ * @module
20
+ */
2
21
  export { verifyAttestation } from "./src/attestation.js";
3
22
  export { AttestationError, AttestationErrorCode } from "./src/errors.js";
23
+ // withAttestation middleware
24
+ export { withAttestation } from "./src/with-attestation.js";
package/esm/mod.d.ts CHANGED
@@ -1,3 +1,93 @@
1
+ /**
2
+ * Verify Apple App Attest attestations and assertions using WebCrypto.
3
+ *
4
+ * This library implements Apple's full
5
+ * [App Attest](https://developer.apple.com/documentation/devicecheck/establishing-your-app-s-integrity)
6
+ * server-side verification — CBOR decoding, X.509 certificate chain
7
+ * validation, nonce verification, ECDSA signature checks — using only
8
+ * WebCrypto APIs so it runs in Supabase Edge Functions, Deno Deploy,
9
+ * and other edge runtimes with no native dependencies.
10
+ *
11
+ * ## Attestation
12
+ *
13
+ * Verify a new device and extract its public key:
14
+ *
15
+ * ```ts
16
+ * import { verifyAttestation } from "@bradford-tech/supabase-integrity-attest";
17
+ *
18
+ * const { publicKeyPem, receipt, signCount } = await verifyAttestation(
19
+ * { appId: "TEAMID.com.example.app" },
20
+ * keyId,
21
+ * challenge,
22
+ * attestation,
23
+ * );
24
+ * // Store publicKeyPem and signCount for future assertion verification
25
+ * ```
26
+ *
27
+ * ## Assertion
28
+ *
29
+ * Verify ongoing requests from an already-attested device:
30
+ *
31
+ * ```ts
32
+ * import { verifyAssertion } from "@bradford-tech/supabase-integrity-attest";
33
+ *
34
+ * const { signCount } = await verifyAssertion(
35
+ * { appId: "TEAMID.com.example.app" },
36
+ * assertion,
37
+ * clientData,
38
+ * publicKeyPem,
39
+ * previousSignCount,
40
+ * );
41
+ * // Persist the updated signCount
42
+ * ```
43
+ *
44
+ * ## Middleware
45
+ *
46
+ * Use the {@linkcode withAttestation} and {@linkcode withAssertion}
47
+ * wrappers for automatic verification, callback-driven storage, and
48
+ * typed error handling:
49
+ *
50
+ * ```ts
51
+ * import {
52
+ * withAttestation,
53
+ * withAssertion,
54
+ * } from "@bradford-tech/supabase-integrity-attest";
55
+ *
56
+ * const attestHandler = withAttestation(
57
+ * {
58
+ * appId: "TEAMID.com.example.app",
59
+ * consumeChallenge,
60
+ * storeDeviceKey,
61
+ * },
62
+ * (_req, ctx) =>
63
+ * Response.json({ ok: true, deviceId: ctx.deviceId }),
64
+ * );
65
+ *
66
+ * const protectedHandler = withAssertion(
67
+ * {
68
+ * appId: "TEAMID.com.example.app",
69
+ * getDeviceKey,
70
+ * commitSignCount,
71
+ * },
72
+ * (_req, ctx) =>
73
+ * Response.json({ hello: ctx.deviceId, counter: ctx.signCount }),
74
+ * );
75
+ * ```
76
+ *
77
+ * ## Subpath imports
78
+ *
79
+ * For smaller bundles, import only what you need:
80
+ *
81
+ * - `@bradford-tech/supabase-integrity-attest/attestation` — attestation
82
+ * (`verifyAttestation`, `withAttestation`). Full crypto deps.
83
+ * - `@bradford-tech/supabase-integrity-attest/assertion` — assertion
84
+ * (`verifyAssertion`, `withAssertion`). Excludes `asn1js` and
85
+ * `@noble/curves` to keep the bundle minimal.
86
+ *
87
+ * Full documentation: {@link https://integrity-attest.bradford.tech}
88
+ *
89
+ * @module
90
+ */
1
91
  export type { AppInfo, AttestationResult, VerifyAttestationOptions, } from "./src/attestation.js";
2
92
  export type { AssertionResult } from "./src/assertion.js";
3
93
  export { verifyAttestation } from "./src/attestation.js";
@@ -5,5 +95,7 @@ export { verifyAssertion } from "./src/assertion.js";
5
95
  export { AssertionError, AssertionErrorCode, AttestationError, AttestationErrorCode, } from "./src/errors.js";
6
96
  export { withAssertion } from "./src/with-assertion.js";
7
97
  export { DEFAULT_ASSERTION_HEADER, DEFAULT_DEVICE_ID_HEADER, } from "./src/with-assertion.js";
8
- export type { AssertionContext, DeviceKey, ExtractAssertionFn, WithAssertionOptions, } from "./src/with-assertion.js";
98
+ export type { AssertionContext, AssertionTimings, DeviceKey, ExtractAssertionFn, WithAssertionOptions, } from "./src/with-assertion.js";
99
+ export { withAttestation } from "./src/with-attestation.js";
100
+ export type { AttestationContext, AttestationTimings, ExtractAttestationFn, WithAttestationOptions, } from "./src/with-attestation.js";
9
101
  //# sourceMappingURL=mod.d.ts.map
package/esm/mod.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../src/mod.ts"],"names":[],"mappings":"AAEA,YAAY,EACV,OAAO,EACP,iBAAiB,EACjB,wBAAwB,GACzB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EACL,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACV,gBAAgB,EAChB,SAAS,EACT,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,yBAAyB,CAAC"}
1
+ {"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../src/mod.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyFG;AAEH,YAAY,EACV,OAAO,EACP,iBAAiB,EACjB,wBAAwB,GACzB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EACL,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACV,gBAAgB,EAChB,gBAAgB,EAChB,SAAS,EACT,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,YAAY,EACV,kBAAkB,EAClB,kBAAkB,EAClB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,2BAA2B,CAAC"}
package/esm/mod.js CHANGED
@@ -1,7 +1,98 @@
1
- // mod.ts — public API surface
1
+ /**
2
+ * Verify Apple App Attest attestations and assertions using WebCrypto.
3
+ *
4
+ * This library implements Apple's full
5
+ * [App Attest](https://developer.apple.com/documentation/devicecheck/establishing-your-app-s-integrity)
6
+ * server-side verification — CBOR decoding, X.509 certificate chain
7
+ * validation, nonce verification, ECDSA signature checks — using only
8
+ * WebCrypto APIs so it runs in Supabase Edge Functions, Deno Deploy,
9
+ * and other edge runtimes with no native dependencies.
10
+ *
11
+ * ## Attestation
12
+ *
13
+ * Verify a new device and extract its public key:
14
+ *
15
+ * ```ts
16
+ * import { verifyAttestation } from "@bradford-tech/supabase-integrity-attest";
17
+ *
18
+ * const { publicKeyPem, receipt, signCount } = await verifyAttestation(
19
+ * { appId: "TEAMID.com.example.app" },
20
+ * keyId,
21
+ * challenge,
22
+ * attestation,
23
+ * );
24
+ * // Store publicKeyPem and signCount for future assertion verification
25
+ * ```
26
+ *
27
+ * ## Assertion
28
+ *
29
+ * Verify ongoing requests from an already-attested device:
30
+ *
31
+ * ```ts
32
+ * import { verifyAssertion } from "@bradford-tech/supabase-integrity-attest";
33
+ *
34
+ * const { signCount } = await verifyAssertion(
35
+ * { appId: "TEAMID.com.example.app" },
36
+ * assertion,
37
+ * clientData,
38
+ * publicKeyPem,
39
+ * previousSignCount,
40
+ * );
41
+ * // Persist the updated signCount
42
+ * ```
43
+ *
44
+ * ## Middleware
45
+ *
46
+ * Use the {@linkcode withAttestation} and {@linkcode withAssertion}
47
+ * wrappers for automatic verification, callback-driven storage, and
48
+ * typed error handling:
49
+ *
50
+ * ```ts
51
+ * import {
52
+ * withAttestation,
53
+ * withAssertion,
54
+ * } from "@bradford-tech/supabase-integrity-attest";
55
+ *
56
+ * const attestHandler = withAttestation(
57
+ * {
58
+ * appId: "TEAMID.com.example.app",
59
+ * consumeChallenge,
60
+ * storeDeviceKey,
61
+ * },
62
+ * (_req, ctx) =>
63
+ * Response.json({ ok: true, deviceId: ctx.deviceId }),
64
+ * );
65
+ *
66
+ * const protectedHandler = withAssertion(
67
+ * {
68
+ * appId: "TEAMID.com.example.app",
69
+ * getDeviceKey,
70
+ * commitSignCount,
71
+ * },
72
+ * (_req, ctx) =>
73
+ * Response.json({ hello: ctx.deviceId, counter: ctx.signCount }),
74
+ * );
75
+ * ```
76
+ *
77
+ * ## Subpath imports
78
+ *
79
+ * For smaller bundles, import only what you need:
80
+ *
81
+ * - `@bradford-tech/supabase-integrity-attest/attestation` — attestation
82
+ * (`verifyAttestation`, `withAttestation`). Full crypto deps.
83
+ * - `@bradford-tech/supabase-integrity-attest/assertion` — assertion
84
+ * (`verifyAssertion`, `withAssertion`). Excludes `asn1js` and
85
+ * `@noble/curves` to keep the bundle minimal.
86
+ *
87
+ * Full documentation: {@link https://integrity-attest.bradford.tech}
88
+ *
89
+ * @module
90
+ */
2
91
  export { verifyAttestation } from "./src/attestation.js";
3
92
  export { verifyAssertion } from "./src/assertion.js";
4
93
  export { AssertionError, AssertionErrorCode, AttestationError, AttestationErrorCode, } from "./src/errors.js";
5
- // withAssertion wrapper
94
+ // withAssertion middleware
6
95
  export { withAssertion } from "./src/with-assertion.js";
7
96
  export { DEFAULT_ASSERTION_HEADER, DEFAULT_DEVICE_ID_HEADER, } from "./src/with-assertion.js";
97
+ // withAttestation middleware
98
+ export { withAttestation } from "./src/with-attestation.js";
@@ -1,9 +1,23 @@
1
+ /** Identifies the app whose assertions are being verified. */
1
2
  export interface AppInfo {
3
+ /** Apple App ID in the format `TEAMID.bundleId`. */
2
4
  appId: string;
5
+ /** Set to `true` when verifying assertions from the development environment. */
3
6
  developmentEnv?: boolean;
4
7
  }
8
+ /** Successful assertion verification result. */
5
9
  export interface AssertionResult {
10
+ /** Updated sign count to persist for the next assertion. */
6
11
  signCount: number;
7
12
  }
13
+ /**
14
+ * Verify an Apple App Attest assertion.
15
+ *
16
+ * Validates the CBOR-encoded assertion against the expected app ID,
17
+ * checks the monotonic sign counter, and verifies the ECDSA signature
18
+ * using the device's public key.
19
+ *
20
+ * @throws {AssertionError} If any verification step fails.
21
+ */
8
22
  export declare function verifyAssertion(appInfo: AppInfo, assertion: Uint8Array | string, clientData: Uint8Array | string, publicKeyPem: string, previousSignCount: number): Promise<AssertionResult>;
9
23
  //# sourceMappingURL=assertion.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"assertion.d.ts","sourceRoot":"","sources":["../../src/src/assertion.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAsB,eAAe,CACnC,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,UAAU,GAAG,MAAM,EAC9B,UAAU,EAAE,UAAU,GAAG,MAAM,EAC/B,YAAY,EAAE,MAAM,EACpB,iBAAiB,EAAE,MAAM,GACxB,OAAO,CAAC,eAAe,CAAC,CA2G1B"}
1
+ {"version":3,"file":"assertion.d.ts","sourceRoot":"","sources":["../../src/src/assertion.ts"],"names":[],"mappings":"AAaA,8DAA8D;AAC9D,MAAM,WAAW,OAAO;IACtB,oDAAoD;IACpD,KAAK,EAAE,MAAM,CAAC;IACd,gFAAgF;IAChF,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,gDAAgD;AAChD,MAAM,WAAW,eAAe;IAC9B,4DAA4D;IAC5D,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;GAQG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,UAAU,GAAG,MAAM,EAC9B,UAAU,EAAE,UAAU,GAAG,MAAM,EAC/B,YAAY,EAAE,MAAM,EACpB,iBAAiB,EAAE,MAAM,GACxB,OAAO,CAAC,eAAe,CAAC,CA2G1B"}
@@ -4,6 +4,15 @@ import { parseAssertionAuthData } from "./authdata.js";
4
4
  import { derToRaw } from "./der.js";
5
5
  import { AssertionError, AssertionErrorCode } from "./errors.js";
6
6
  import { concat, constantTimeEqual, decodeBase64Bytes, importPemPublicKey, toBytes, } from "./utils.js";
7
+ /**
8
+ * Verify an Apple App Attest assertion.
9
+ *
10
+ * Validates the CBOR-encoded assertion against the expected app ID,
11
+ * checks the monotonic sign counter, and verifies the ECDSA signature
12
+ * using the device's public key.
13
+ *
14
+ * @throws {AssertionError} If any verification step fails.
15
+ */
7
16
  export async function verifyAssertion(appInfo, assertion, clientData, publicKeyPem, previousSignCount) {
8
17
  const assertionBytes = decodeBase64Bytes(assertion);
9
18
  const clientDataBytes = toBytes(clientData);
@@ -1,14 +1,22 @@
1
+ /** Identifies the app being attested. */
1
2
  export interface AppInfo {
3
+ /** Apple App ID in the format `TEAMID.bundleId`. */
2
4
  appId: string;
5
+ /** Set to `true` when verifying attestations from the development environment. */
3
6
  developmentEnv?: boolean;
4
7
  }
8
+ /** Successful attestation verification result. */
5
9
  export interface AttestationResult {
10
+ /** PEM-encoded ECDSA P-256 public key extracted from the attestation. */
6
11
  publicKeyPem: string;
12
+ /** Raw App Attest receipt bytes for server-side refresh. */
7
13
  receipt: Uint8Array;
14
+ /** Initial sign count (always `0` for attestation). */
8
15
  signCount: number;
9
16
  }
17
+ /** Options for {@linkcode verifyAttestation}. */
10
18
  export interface VerifyAttestationOptions {
11
- /** Override date for certificate chain validation (for testing with expired certs) */
19
+ /** Override date for certificate chain validation (for testing with expired certs). */
12
20
  checkDate?: Date;
13
21
  }
14
22
  /**
@@ -32,6 +40,17 @@ export interface AttestationCbor {
32
40
  };
33
41
  authData: Uint8Array;
34
42
  }
43
+ /** Decode an Apple App Attest attestation object from raw CBOR bytes. */
35
44
  export declare function decodeAttestationCbor(data: Uint8Array): AttestationCbor;
45
+ /**
46
+ * Verify an Apple App Attest attestation.
47
+ *
48
+ * Implements the full server-side verification described in
49
+ * [Apple's documentation](https://developer.apple.com/documentation/devicecheck/validating_apps_that_connect_to_your_server):
50
+ * CBOR decode, certificate chain validation, nonce check, key extraction,
51
+ * AAGUID check, and credential ID verification.
52
+ *
53
+ * @throws {AttestationError} If any verification step fails.
54
+ */
36
55
  export declare function verifyAttestation(appInfo: AppInfo, keyId: string, challenge: Uint8Array | string, attestation: Uint8Array | string, options?: VerifyAttestationOptions): Promise<AttestationResult>;
37
56
  //# sourceMappingURL=attestation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"attestation.d.ts","sourceRoot":"","sources":["../../src/src/attestation.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAiB;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,UAAU,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,wBAAwB;IACvC,sFAAsF;IACtF,SAAS,CAAC,EAAE,IAAI,CAAC;CAClB;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE;QACP,GAAG,EAAE,UAAU,EAAE,CAAC;QAClB,OAAO,EAAE,UAAU,CAAC;KACrB,CAAC;IACF,QAAQ,EAAE,UAAU,CAAC;CACtB;AAsGD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,UAAU,GAAG,eAAe,CAyEvE;AAED,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,UAAU,GAAG,MAAM,EAC9B,WAAW,EAAE,UAAU,GAAG,MAAM,EAChC,OAAO,CAAC,EAAE,wBAAwB,GACjC,OAAO,CAAC,iBAAiB,CAAC,CAkJ5B"}
1
+ {"version":3,"file":"attestation.d.ts","sourceRoot":"","sources":["../../src/src/attestation.ts"],"names":[],"mappings":"AAaA,yCAAyC;AACzC,MAAM,WAAW,OAAO;IACtB,oDAAoD;IACpD,KAAK,EAAE,MAAM,CAAC;IACd,kFAAkF;IAClF,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,kDAAkD;AAClD,MAAM,WAAW,iBAAiB;IAChC,yEAAyE;IACzE,YAAY,EAAE,MAAM,CAAC;IACrB,4DAA4D;IAC5D,OAAO,EAAE,UAAU,CAAC;IACpB,uDAAuD;IACvD,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,iDAAiD;AACjD,MAAM,WAAW,wBAAwB;IACvC,uFAAuF;IACvF,SAAS,CAAC,EAAE,IAAI,CAAC;CAClB;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE;QACP,GAAG,EAAE,UAAU,EAAE,CAAC;QAClB,OAAO,EAAE,UAAU,CAAC;KACrB,CAAC;IACF,QAAQ,EAAE,UAAU,CAAC;CACtB;AAsGD,yEAAyE;AACzE,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,UAAU,GAAG,eAAe,CAyEvE;AAED;;;;;;;;;GASG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,UAAU,GAAG,MAAM,EAC9B,WAAW,EAAE,UAAU,GAAG,MAAM,EAChC,OAAO,CAAC,EAAE,wBAAwB,GACjC,OAAO,CAAC,iBAAiB,CAAC,CAkJ5B"}
@@ -20,10 +20,10 @@ function readCborUint(data, offset) {
20
20
  }
21
21
  if (additional === 26) {
22
22
  return {
23
- value: ((data[offset + 1] << 24) >>> 0) +
24
- (data[offset + 2] << 16) +
25
- (data[offset + 3] << 8) +
26
- data[offset + 4],
23
+ value: ((data[offset + 1] << 24) |
24
+ (data[offset + 2] << 16) |
25
+ (data[offset + 3] << 8) |
26
+ data[offset + 4]) >>> 0,
27
27
  end: offset + 5,
28
28
  };
29
29
  }
@@ -88,6 +88,7 @@ function findCborTextKey(data, key, startOffset) {
88
88
  }
89
89
  return -1;
90
90
  }
91
+ /** Decode an Apple App Attest attestation object from raw CBOR bytes. */
91
92
  export function decodeAttestationCbor(data) {
92
93
  // Verify top-level is a CBOR map
93
94
  const majorType = (data[0] >> 5) & 0x07;
@@ -154,6 +155,16 @@ export function decodeAttestationCbor(data) {
154
155
  const { value: authData } = readCborBytes(data, authDataValuePos);
155
156
  return { fmt, attStmt: { x5c, receipt }, authData };
156
157
  }
158
+ /**
159
+ * Verify an Apple App Attest attestation.
160
+ *
161
+ * Implements the full server-side verification described in
162
+ * [Apple's documentation](https://developer.apple.com/documentation/devicecheck/validating_apps_that_connect_to_your_server):
163
+ * CBOR decode, certificate chain validation, nonce check, key extraction,
164
+ * AAGUID check, and credential ID verification.
165
+ *
166
+ * @throws {AttestationError} If any verification step fails.
167
+ */
157
168
  export async function verifyAttestation(appInfo, keyId, challenge, attestation, options) {
158
169
  // Decode attestation bytes from base64 if string
159
170
  let attestationBytes;
@@ -1,29 +1,77 @@
1
+ /** Error codes returned by {@linkcode AttestationError}. */
1
2
  export declare enum AttestationErrorCode {
3
+ /** CBOR decoding or structural validation failed. */
2
4
  INVALID_FORMAT = "INVALID_FORMAT",
5
+ /** X.509 certificate chain verification failed. */
3
6
  INVALID_CERTIFICATE_CHAIN = "INVALID_CERTIFICATE_CHAIN",
7
+ /** Computed nonce does not match the nonce in the leaf certificate. */
4
8
  NONCE_MISMATCH = "NONCE_MISMATCH",
9
+ /** RP ID hash does not match SHA-256 of the app ID. */
5
10
  RP_ID_MISMATCH = "RP_ID_MISMATCH",
11
+ /** Public key hash does not match the provided key ID. */
6
12
  KEY_ID_MISMATCH = "KEY_ID_MISMATCH",
13
+ /** Sign count is not zero (required for attestation). */
7
14
  INVALID_COUNTER = "INVALID_COUNTER",
8
- INVALID_AAGUID = "INVALID_AAGUID"
15
+ /** AAGUID does not match the expected environment (production/development). */
16
+ INVALID_AAGUID = "INVALID_AAGUID",
17
+ /**
18
+ * The `consumeChallenge` callback returned `false`, meaning the challenge
19
+ * was missing, expired, or already consumed. Used by {@linkcode withAttestation}.
20
+ */
21
+ CHALLENGE_INVALID = "CHALLENGE_INVALID",
22
+ /**
23
+ * An internal or storage callback error occurred (used by
24
+ * {@linkcode withAttestation}). The original error is attached via
25
+ * `Error.cause` and is deliberately NOT reflected in the HTTP response
26
+ * body to avoid leaking database schema or driver diagnostics.
27
+ */
28
+ INTERNAL_ERROR = "INTERNAL_ERROR"
9
29
  }
30
+ /** Thrown when attestation verification fails. */
10
31
  export declare class AttestationError extends Error {
32
+ /** Machine-readable error code. */
11
33
  readonly code: AttestationErrorCode;
34
+ /** Discriminant for `instanceof` checks in catch blocks. Always `"AttestationError"`. */
12
35
  readonly name = "AttestationError";
13
- constructor(code: AttestationErrorCode, message: string);
36
+ /** Create an AttestationError with a machine-readable code and human-readable message. */
37
+ constructor(
38
+ /** Machine-readable error code. */
39
+ code: AttestationErrorCode, message: string, options?: {
40
+ cause?: unknown;
41
+ });
14
42
  }
43
+ /** Error codes returned by {@linkcode AssertionError}. */
15
44
  export declare enum AssertionErrorCode {
45
+ /** CBOR decoding or structural validation failed. */
16
46
  INVALID_FORMAT = "INVALID_FORMAT",
47
+ /** RP ID hash does not match SHA-256 of the app ID. */
17
48
  RP_ID_MISMATCH = "RP_ID_MISMATCH",
49
+ /** Sign count was not greater than the previously stored value. */
18
50
  COUNTER_NOT_INCREMENTED = "COUNTER_NOT_INCREMENTED",
51
+ /** ECDSA signature verification failed. */
19
52
  SIGNATURE_INVALID = "SIGNATURE_INVALID",
53
+ /** No device key found for the given device ID (used by {@linkcode withAssertion}). */
20
54
  DEVICE_NOT_FOUND = "DEVICE_NOT_FOUND",
21
- INTERNAL_ERROR = "INTERNAL_ERROR"
55
+ /** An internal or storage callback error occurred (used by {@linkcode withAssertion}). */
56
+ INTERNAL_ERROR = "INTERNAL_ERROR",
57
+ /**
58
+ * The `commitSignCount` callback returned `false`, meaning another
59
+ * concurrent request already advanced the stored counter past this value.
60
+ * Indicates an expected race under concurrent load, not a client bug.
61
+ * Used by {@linkcode withAssertion}.
62
+ */
63
+ SIGN_COUNT_STALE = "SIGN_COUNT_STALE"
22
64
  }
65
+ /** Thrown when assertion verification fails. */
23
66
  export declare class AssertionError extends Error {
67
+ /** Machine-readable error code. */
24
68
  readonly code: AssertionErrorCode;
69
+ /** Discriminant for `instanceof` checks in catch blocks. Always `"AssertionError"`. */
25
70
  readonly name = "AssertionError";
26
- constructor(code: AssertionErrorCode, message: string, options?: {
71
+ /** Create an AssertionError with a machine-readable code and human-readable message. */
72
+ constructor(
73
+ /** Machine-readable error code. */
74
+ code: AssertionErrorCode, message: string, options?: {
27
75
  cause?: unknown;
28
76
  });
29
77
  }
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/src/errors.ts"],"names":[],"mappings":"AAEA,oBAAY,oBAAoB;IAC9B,cAAc,mBAAmB;IACjC,yBAAyB,8BAA8B;IACvD,cAAc,mBAAmB;IACjC,cAAc,mBAAmB;IACjC,eAAe,oBAAoB;IACnC,eAAe,oBAAoB;IACnC,cAAc,mBAAmB;CAClC;AAED,qBAAa,gBAAiB,SAAQ,KAAK;aAGvB,IAAI,EAAE,oBAAoB;IAF5C,SAAkB,IAAI,sBAAsB;gBAE1B,IAAI,EAAE,oBAAoB,EAC1C,OAAO,EAAE,MAAM;CAIlB;AAED,oBAAY,kBAAkB;IAC5B,cAAc,mBAAmB;IACjC,cAAc,mBAAmB;IACjC,uBAAuB,4BAA4B;IACnD,iBAAiB,sBAAsB;IACvC,gBAAgB,qBAAqB;IACrC,cAAc,mBAAmB;CAClC;AAED,qBAAa,cAAe,SAAQ,KAAK;aAGrB,IAAI,EAAE,kBAAkB;IAF1C,SAAkB,IAAI,oBAAoB;gBAExB,IAAI,EAAE,kBAAkB,EACxC,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAIhC"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/src/errors.ts"],"names":[],"mappings":"AAEA,4DAA4D;AAC5D,oBAAY,oBAAoB;IAC9B,qDAAqD;IACrD,cAAc,mBAAmB;IACjC,mDAAmD;IACnD,yBAAyB,8BAA8B;IACvD,uEAAuE;IACvE,cAAc,mBAAmB;IACjC,uDAAuD;IACvD,cAAc,mBAAmB;IACjC,0DAA0D;IAC1D,eAAe,oBAAoB;IACnC,yDAAyD;IACzD,eAAe,oBAAoB;IACnC,+EAA+E;IAC/E,cAAc,mBAAmB;IACjC;;;OAGG;IACH,iBAAiB,sBAAsB;IACvC;;;;;OAKG;IACH,cAAc,mBAAmB;CAClC;AAED,kDAAkD;AAClD,qBAAa,gBAAiB,SAAQ,KAAK;IAKvC,mCAAmC;aACnB,IAAI,EAAE,oBAAoB;IAL5C,yFAAyF;IACzF,SAAkB,IAAI,sBAAsB;IAC5C,0FAA0F;;IAExF,mCAAmC;IACnB,IAAI,EAAE,oBAAoB,EAC1C,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAIhC;AAED,0DAA0D;AAC1D,oBAAY,kBAAkB;IAC5B,qDAAqD;IACrD,cAAc,mBAAmB;IACjC,uDAAuD;IACvD,cAAc,mBAAmB;IACjC,mEAAmE;IACnE,uBAAuB,4BAA4B;IACnD,2CAA2C;IAC3C,iBAAiB,sBAAsB;IACvC,uFAAuF;IACvF,gBAAgB,qBAAqB;IACrC,0FAA0F;IAC1F,cAAc,mBAAmB;IACjC;;;;;OAKG;IACH,gBAAgB,qBAAqB;CACtC;AAED,gDAAgD;AAChD,qBAAa,cAAe,SAAQ,KAAK;IAKrC,mCAAmC;aACnB,IAAI,EAAE,kBAAkB;IAL1C,uFAAuF;IACvF,SAAkB,IAAI,oBAAoB;IAC1C,wFAAwF;;IAEtF,mCAAmC;IACnB,IAAI,EAAE,kBAAkB,EACxC,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAIhC"}