@bradford-tech/supabase-integrity-attest 0.3.0 → 0.3.2

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/README.md CHANGED
@@ -10,7 +10,22 @@ 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
+ ```
15
+
16
+ ## Subpath imports
17
+
18
+ If you only need assertion verification, import from the lighter entry point:
19
+
20
+ ```typescript
21
+ // Full library (attestation + assertion)
22
+ import { verifyAttestation, verifyAssertion } from "@bradford-tech/supabase-integrity-attest";
23
+
24
+ // Assertion only — skips asn1js and @noble/curves
25
+ import { verifyAssertion } from "@bradford-tech/supabase-integrity-attest/assertion";
26
+
27
+ // Attestation only
28
+ import { verifyAttestation } from "@bradford-tech/supabase-integrity-attest/attestation";
14
29
  ```
15
30
 
16
31
  ## Usage
@@ -30,7 +45,88 @@ const result = await verifyAttestation(
30
45
  // Store result.publicKeyPem and signCount (0) for this device
31
46
  ```
32
47
 
33
- ### Assertion (every protected request)
48
+ ### Protecting edge functions with `withAssertion`
49
+
50
+ `withAssertion` wraps a request handler so that assertion verification,
51
+ device lookup, and sign count updates happen before your business logic runs.
52
+
53
+ ```typescript
54
+ import { createClient } from "jsr:@supabase/supabase-js@2";
55
+ import { withAssertion } from "@bradford-tech/supabase-integrity-attest";
56
+
57
+ const supabase = createClient(
58
+ Deno.env.get("SUPABASE_URL")!,
59
+ Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!,
60
+ );
61
+
62
+ Deno.serve(withAssertion({
63
+ appId: Deno.env.get("APP_ATTEST_APP_ID")!,
64
+ developmentEnv: Deno.env.get("APP_ATTEST_ENV") === "development",
65
+ getDeviceKey: async (deviceId) => {
66
+ const { data } = await supabase
67
+ .from("device_attestations")
68
+ .select("public_key_pem, sign_count")
69
+ .eq("key_id", deviceId)
70
+ .single();
71
+ return data
72
+ ? { publicKeyPem: data.public_key_pem, signCount: data.sign_count }
73
+ : null;
74
+ },
75
+ updateSignCount: async (deviceId, newSignCount) => {
76
+ await supabase
77
+ .from("device_attestations")
78
+ .update({ sign_count: newSignCount })
79
+ .eq("key_id", deviceId);
80
+ },
81
+ }, async (_req, { rawBody }) => {
82
+ const { text, voice } = JSON.parse(new TextDecoder().decode(rawBody));
83
+ // business logic
84
+ return new Response(JSON.stringify({ audio: "..." }), {
85
+ headers: { "Content-Type": "application/json" },
86
+ });
87
+ }));
88
+ ```
89
+
90
+ The client sends the assertion and device ID in headers. The request body
91
+ is the client data that was signed:
92
+
93
+ ```
94
+ POST /functions/v1/your-endpoint
95
+ Headers:
96
+ Content-Type: application/json
97
+ X-App-Attest-Assertion: <base64-encoded assertion>
98
+ X-App-Attest-Device-Id: <base64-encoded keyId>
99
+ Body:
100
+ {"text": "Hello world", "voice": "en-US"}
101
+ ```
102
+
103
+ Once you have multiple protected functions, extract the shared options into
104
+ a helper:
105
+
106
+ ```typescript
107
+ // supabase/functions/_shared/attest.ts
108
+ import type { WithAssertionOptions } from "@bradford-tech/supabase-integrity-attest";
109
+
110
+ export const attestOptions: WithAssertionOptions = {
111
+ appId: Deno.env.get("APP_ATTEST_APP_ID")!,
112
+ // ... getDeviceKey, updateSignCount as above
113
+ };
114
+ ```
115
+
116
+ ```typescript
117
+ // supabase/functions/text-to-speech/index.ts
118
+ import { withAssertion } from "@bradford-tech/supabase-integrity-attest";
119
+ import { attestOptions } from "../_shared/attest.ts";
120
+
121
+ Deno.serve(withAssertion(attestOptions, async (_req, { rawBody }) => {
122
+ const { text, voice } = JSON.parse(new TextDecoder().decode(rawBody));
123
+ return new Response(JSON.stringify({ audio: "..." }));
124
+ }));
125
+ ```
126
+
127
+ ### Assertion (low-level)
128
+
129
+ For full control over the verification flow, use `verifyAssertion` directly:
34
130
 
35
131
  ```typescript
36
132
  import { verifyAssertion } from "@bradford-tech/supabase-integrity-attest";
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_dnt.test_polyfills.d.ts","sourceRoot":"","sources":["../src/_dnt.test_polyfills.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,KAAK;QACb,KAAK,CAAC,EAAE,OAAO,CAAC;KACjB;CACF;AAED,OAAO,EAAE,CAAC"}
@@ -1,4 +1,25 @@
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 { verifyAssertion } from "@bradford-tech/supabase-integrity-attest/assertion";
7
+ *
8
+ * const { signCount } = await verifyAssertion(
9
+ * { appId: "TEAMID.com.example.app" },
10
+ * assertion,
11
+ * clientData,
12
+ * publicKeyPem,
13
+ * previousSignCount,
14
+ * );
15
+ * ```
16
+ *
17
+ * @module
18
+ */
1
19
  export { verifyAssertion } from "./src/assertion.js";
2
20
  export type { AppInfo, AssertionResult } from "./src/assertion.js";
3
21
  export { AssertionError, AssertionErrorCode } from "./src/errors.js";
22
+ export { withAssertion } from "./src/with-assertion.js";
23
+ export { DEFAULT_ASSERTION_HEADER, DEFAULT_DEVICE_ID_HEADER, } from "./src/with-assertion.js";
24
+ export type { AssertionContext, DeviceKey, ExtractAssertionFn, WithAssertionOptions, } from "./src/with-assertion.js";
4
25
  //# 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"}
1
+ {"version":3,"file":"assertion.d.ts","sourceRoot":"","sources":["../src/assertion.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;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,SAAS,EACT,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,yBAAyB,CAAC"}
package/esm/assertion.js CHANGED
@@ -1,3 +1,23 @@
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 { verifyAssertion } from "@bradford-tech/supabase-integrity-attest/assertion";
7
+ *
8
+ * const { signCount } = await verifyAssertion(
9
+ * { appId: "TEAMID.com.example.app" },
10
+ * assertion,
11
+ * clientData,
12
+ * publicKeyPem,
13
+ * previousSignCount,
14
+ * );
15
+ * ```
16
+ *
17
+ * @module
18
+ */
2
19
  export { verifyAssertion } from "./src/assertion.js";
3
20
  export { AssertionError, AssertionErrorCode } from "./src/errors.js";
21
+ // withAssertion wrapper
22
+ export { withAssertion } from "./src/with-assertion.js";
23
+ export { DEFAULT_ASSERTION_HEADER, DEFAULT_DEVICE_ID_HEADER, } from "./src/with-assertion.js";
@@ -1,3 +1,20 @@
1
+ /**
2
+ * Full attestation entry point including certificate chain verification
3
+ * dependencies (`asn1js`, `@noble/curves`).
4
+ *
5
+ * ```ts
6
+ * import { verifyAttestation } from "@bradford-tech/supabase-integrity-attest/attestation";
7
+ *
8
+ * const { publicKeyPem } = await verifyAttestation(
9
+ * { appId: "TEAMID.com.example.app" },
10
+ * keyId,
11
+ * challenge,
12
+ * attestation,
13
+ * );
14
+ * ```
15
+ *
16
+ * @module
17
+ */
1
18
  export { verifyAttestation } from "./src/attestation.js";
2
19
  export type { AppInfo, AttestationResult, VerifyAttestationOptions, } from "./src/attestation.js";
3
20
  export { AttestationError, AttestationErrorCode } from "./src/errors.js";
@@ -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;;;;;;;;;;;;;;;;GAgBG;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"}
@@ -1,3 +1,19 @@
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 { verifyAttestation } from "@bradford-tech/supabase-integrity-attest/attestation";
7
+ *
8
+ * const { publicKeyPem } = await verifyAttestation(
9
+ * { appId: "TEAMID.com.example.app" },
10
+ * keyId,
11
+ * challenge,
12
+ * attestation,
13
+ * );
14
+ * ```
15
+ *
16
+ * @module
17
+ */
2
18
  export { verifyAttestation } from "./src/attestation.js";
3
19
  export { AttestationError, AttestationErrorCode } from "./src/errors.js";
package/esm/mod.d.ts CHANGED
@@ -1,6 +1,25 @@
1
+ /**
2
+ * Verify Apple App Attest attestations and assertions using WebCrypto.
3
+ *
4
+ * ```ts
5
+ * import { verifyAttestation, verifyAssertion } from "@bradford-tech/supabase-integrity-attest";
6
+ *
7
+ * const { publicKeyPem } = await verifyAttestation(
8
+ * { appId: "TEAMID.com.example.app" },
9
+ * keyId,
10
+ * challenge,
11
+ * attestation,
12
+ * );
13
+ * ```
14
+ *
15
+ * @module
16
+ */
1
17
  export type { AppInfo, AttestationResult, VerifyAttestationOptions, } from "./src/attestation.js";
2
18
  export type { AssertionResult } from "./src/assertion.js";
3
19
  export { verifyAttestation } from "./src/attestation.js";
4
20
  export { verifyAssertion } from "./src/assertion.js";
5
21
  export { AssertionError, AssertionErrorCode, AttestationError, AttestationErrorCode, } from "./src/errors.js";
22
+ export { withAssertion } from "./src/with-assertion.js";
23
+ export { DEFAULT_ASSERTION_HEADER, DEFAULT_DEVICE_ID_HEADER, } from "./src/with-assertion.js";
24
+ export type { AssertionContext, DeviceKey, ExtractAssertionFn, WithAssertionOptions, } from "./src/with-assertion.js";
6
25
  //# 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"}
1
+ {"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../src/mod.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;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,SAAS,EACT,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,yBAAyB,CAAC"}
package/esm/mod.js CHANGED
@@ -1,4 +1,22 @@
1
- // mod.ts — public API surface
1
+ /**
2
+ * Verify Apple App Attest attestations and assertions using WebCrypto.
3
+ *
4
+ * ```ts
5
+ * import { verifyAttestation, verifyAssertion } from "@bradford-tech/supabase-integrity-attest";
6
+ *
7
+ * const { publicKeyPem } = await verifyAttestation(
8
+ * { appId: "TEAMID.com.example.app" },
9
+ * keyId,
10
+ * challenge,
11
+ * attestation,
12
+ * );
13
+ * ```
14
+ *
15
+ * @module
16
+ */
2
17
  export { verifyAttestation } from "./src/attestation.js";
3
18
  export { verifyAssertion } from "./src/assertion.js";
4
19
  export { AssertionError, AssertionErrorCode, AttestationError, AttestationErrorCode, } from "./src/errors.js";
20
+ // withAssertion wrapper
21
+ export { withAssertion } from "./src/with-assertion.js";
22
+ export { DEFAULT_ASSERTION_HEADER, DEFAULT_DEVICE_ID_HEADER, } from "./src/with-assertion.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,26 +1,53 @@
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",
15
+ /** AAGUID does not match the expected environment (production/development). */
8
16
  INVALID_AAGUID = "INVALID_AAGUID"
9
17
  }
18
+ /** Thrown when attestation verification fails. */
10
19
  export declare class AttestationError extends Error {
20
+ /** Machine-readable error code. */
11
21
  readonly code: AttestationErrorCode;
12
22
  readonly name = "AttestationError";
13
- constructor(code: AttestationErrorCode, message: string);
23
+ constructor(
24
+ /** Machine-readable error code. */
25
+ code: AttestationErrorCode, message: string);
14
26
  }
27
+ /** Error codes returned by {@linkcode AssertionError}. */
15
28
  export declare enum AssertionErrorCode {
29
+ /** CBOR decoding or structural validation failed. */
16
30
  INVALID_FORMAT = "INVALID_FORMAT",
31
+ /** RP ID hash does not match SHA-256 of the app ID. */
17
32
  RP_ID_MISMATCH = "RP_ID_MISMATCH",
33
+ /** Sign count was not greater than the previously stored value. */
18
34
  COUNTER_NOT_INCREMENTED = "COUNTER_NOT_INCREMENTED",
19
- SIGNATURE_INVALID = "SIGNATURE_INVALID"
35
+ /** ECDSA signature verification failed. */
36
+ SIGNATURE_INVALID = "SIGNATURE_INVALID",
37
+ /** No device key found for the given device ID (used by {@linkcode withAssertion}). */
38
+ DEVICE_NOT_FOUND = "DEVICE_NOT_FOUND",
39
+ /** An internal or storage callback error occurred (used by {@linkcode withAssertion}). */
40
+ INTERNAL_ERROR = "INTERNAL_ERROR"
20
41
  }
42
+ /** Thrown when assertion verification fails. */
21
43
  export declare class AssertionError extends Error {
44
+ /** Machine-readable error code. */
22
45
  readonly code: AssertionErrorCode;
23
46
  readonly name = "AssertionError";
24
- constructor(code: AssertionErrorCode, message: string);
47
+ constructor(
48
+ /** Machine-readable error code. */
49
+ code: AssertionErrorCode, message: string, options?: {
50
+ cause?: unknown;
51
+ });
25
52
  }
26
53
  //# sourceMappingURL=errors.d.ts.map
@@ -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;CACxC;AAED,qBAAa,cAAe,SAAQ,KAAK;aAGrB,IAAI,EAAE,kBAAkB;IAF1C,SAAkB,IAAI,oBAAoB;gBAExB,IAAI,EAAE,kBAAkB,EACxC,OAAO,EAAE,MAAM;CAIlB"}
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;CAClC;AAED,kDAAkD;AAClD,qBAAa,gBAAiB,SAAQ,KAAK;IAGvC,mCAAmC;aACnB,IAAI,EAAE,oBAAoB;IAH5C,SAAkB,IAAI,sBAAsB;;IAE1C,mCAAmC;IACnB,IAAI,EAAE,oBAAoB,EAC1C,OAAO,EAAE,MAAM;CAIlB;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;CAClC;AAED,gDAAgD;AAChD,qBAAa,cAAe,SAAQ,KAAK;IAGrC,mCAAmC;aACnB,IAAI,EAAE,kBAAkB;IAH1C,SAAkB,IAAI,oBAAoB;;IAExC,mCAAmC;IACnB,IAAI,EAAE,kBAAkB,EACxC,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAIhC"}
package/esm/src/errors.js CHANGED
@@ -1,16 +1,27 @@
1
1
  // src/errors.ts
2
+ /** Error codes returned by {@linkcode AttestationError}. */
2
3
  export var AttestationErrorCode;
3
4
  (function (AttestationErrorCode) {
5
+ /** CBOR decoding or structural validation failed. */
4
6
  AttestationErrorCode["INVALID_FORMAT"] = "INVALID_FORMAT";
7
+ /** X.509 certificate chain verification failed. */
5
8
  AttestationErrorCode["INVALID_CERTIFICATE_CHAIN"] = "INVALID_CERTIFICATE_CHAIN";
9
+ /** Computed nonce does not match the nonce in the leaf certificate. */
6
10
  AttestationErrorCode["NONCE_MISMATCH"] = "NONCE_MISMATCH";
11
+ /** RP ID hash does not match SHA-256 of the app ID. */
7
12
  AttestationErrorCode["RP_ID_MISMATCH"] = "RP_ID_MISMATCH";
13
+ /** Public key hash does not match the provided key ID. */
8
14
  AttestationErrorCode["KEY_ID_MISMATCH"] = "KEY_ID_MISMATCH";
15
+ /** Sign count is not zero (required for attestation). */
9
16
  AttestationErrorCode["INVALID_COUNTER"] = "INVALID_COUNTER";
17
+ /** AAGUID does not match the expected environment (production/development). */
10
18
  AttestationErrorCode["INVALID_AAGUID"] = "INVALID_AAGUID";
11
19
  })(AttestationErrorCode || (AttestationErrorCode = {}));
20
+ /** Thrown when attestation verification fails. */
12
21
  export class AttestationError extends Error {
13
- constructor(code, message) {
22
+ constructor(
23
+ /** Machine-readable error code. */
24
+ code, message) {
14
25
  super(message);
15
26
  Object.defineProperty(this, "code", {
16
27
  enumerable: true,
@@ -26,16 +37,28 @@ export class AttestationError extends Error {
26
37
  });
27
38
  }
28
39
  }
40
+ /** Error codes returned by {@linkcode AssertionError}. */
29
41
  export var AssertionErrorCode;
30
42
  (function (AssertionErrorCode) {
43
+ /** CBOR decoding or structural validation failed. */
31
44
  AssertionErrorCode["INVALID_FORMAT"] = "INVALID_FORMAT";
45
+ /** RP ID hash does not match SHA-256 of the app ID. */
32
46
  AssertionErrorCode["RP_ID_MISMATCH"] = "RP_ID_MISMATCH";
47
+ /** Sign count was not greater than the previously stored value. */
33
48
  AssertionErrorCode["COUNTER_NOT_INCREMENTED"] = "COUNTER_NOT_INCREMENTED";
49
+ /** ECDSA signature verification failed. */
34
50
  AssertionErrorCode["SIGNATURE_INVALID"] = "SIGNATURE_INVALID";
51
+ /** No device key found for the given device ID (used by {@linkcode withAssertion}). */
52
+ AssertionErrorCode["DEVICE_NOT_FOUND"] = "DEVICE_NOT_FOUND";
53
+ /** An internal or storage callback error occurred (used by {@linkcode withAssertion}). */
54
+ AssertionErrorCode["INTERNAL_ERROR"] = "INTERNAL_ERROR";
35
55
  })(AssertionErrorCode || (AssertionErrorCode = {}));
56
+ /** Thrown when assertion verification fails. */
36
57
  export class AssertionError extends Error {
37
- constructor(code, message) {
38
- super(message);
58
+ constructor(
59
+ /** Machine-readable error code. */
60
+ code, message, options) {
61
+ super(message, options);
39
62
  Object.defineProperty(this, "code", {
40
63
  enumerable: true,
41
64
  configurable: true,
@@ -0,0 +1,51 @@
1
+ import { AssertionError } from "./errors.js";
2
+ /** Default HTTP header name for the base64-encoded assertion. */
3
+ export declare const DEFAULT_ASSERTION_HEADER = "X-App-Attest-Assertion";
4
+ /** Default HTTP header name for the device identifier. */
5
+ export declare const DEFAULT_DEVICE_ID_HEADER = "X-App-Attest-Device-Id";
6
+ /** Stored device key material returned by the `getDeviceKey` callback. */
7
+ export type DeviceKey = {
8
+ /** PEM-encoded ECDSA P-256 public key from attestation. */
9
+ publicKeyPem: string;
10
+ /** Last verified sign count for this device. */
11
+ signCount: number;
12
+ };
13
+ /** Context passed to the inner handler after successful assertion verification. */
14
+ export type AssertionContext = {
15
+ /** Device identifier from the request. */
16
+ deviceId: string;
17
+ /** Updated sign count after verification. */
18
+ signCount: number;
19
+ /** Raw request body bytes (the client data that was signed). */
20
+ rawBody: Uint8Array;
21
+ };
22
+ /** Custom function to extract assertion data from an incoming request. */
23
+ export type ExtractAssertionFn = (req: Request) => Promise<{
24
+ assertion: string;
25
+ deviceId: string;
26
+ clientData: Uint8Array;
27
+ }>;
28
+ /** Configuration for the {@linkcode withAssertion} middleware. */
29
+ export type WithAssertionOptions = {
30
+ /** Apple App ID in the format `TEAMID.bundleId`. */
31
+ appId: string;
32
+ /** Set to `true` for development environment attestations. */
33
+ developmentEnv?: boolean;
34
+ /** Retrieve the stored device key for a given device ID. Return `null` if not found. */
35
+ getDeviceKey: (deviceId: string) => Promise<DeviceKey | null>;
36
+ /** Persist the new sign count after successful verification. */
37
+ updateSignCount: (deviceId: string, newSignCount: number) => Promise<void>;
38
+ /** Override the default header-based assertion extraction. */
39
+ extractAssertion?: ExtractAssertionFn;
40
+ /** Custom error response handler. Defaults to JSON error responses. */
41
+ onError?: (error: AssertionError, req: Request) => Response | Promise<Response>;
42
+ };
43
+ /**
44
+ * Request handler middleware that verifies App Attest assertions.
45
+ *
46
+ * Wraps a handler function with automatic assertion verification,
47
+ * device key lookup, and sign count management. Returns a new handler
48
+ * that rejects unauthenticated requests with appropriate HTTP error responses.
49
+ */
50
+ export declare function withAssertion(options: WithAssertionOptions, handler: (req: Request, context: AssertionContext) => Response | Promise<Response>): (req: Request) => Promise<Response>;
51
+ //# sourceMappingURL=with-assertion.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"with-assertion.d.ts","sourceRoot":"","sources":["../../src/src/with-assertion.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAsB,MAAM,aAAa,CAAC;AAEjE,iEAAiE;AACjE,eAAO,MAAM,wBAAwB,2BAA2B,CAAC;AACjE,0DAA0D;AAC1D,eAAO,MAAM,wBAAwB,2BAA2B,CAAC;AAEjE,0EAA0E;AAC1E,MAAM,MAAM,SAAS,GAAG;IACtB,2DAA2D;IAC3D,YAAY,EAAE,MAAM,CAAC;IACrB,gDAAgD;IAChD,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,mFAAmF;AACnF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,0CAA0C;IAC1C,QAAQ,EAAE,MAAM,CAAC;IACjB,6CAA6C;IAC7C,SAAS,EAAE,MAAM,CAAC;IAClB,gEAAgE;IAChE,OAAO,EAAE,UAAU,CAAC;CACrB,CAAC;AAEF,0EAA0E;AAC1E,MAAM,MAAM,kBAAkB,GAAG,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC;IACzD,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,UAAU,CAAC;CACxB,CAAC,CAAC;AAEH,kEAAkE;AAClE,MAAM,MAAM,oBAAoB,GAAG;IACjC,oDAAoD;IACpD,KAAK,EAAE,MAAM,CAAC;IACd,8DAA8D;IAC9D,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,wFAAwF;IACxF,YAAY,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC;IAC9D,gEAAgE;IAChE,eAAe,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3E,8DAA8D;IAC9D,gBAAgB,CAAC,EAAE,kBAAkB,CAAC;IACtC,uEAAuE;IACvE,OAAO,CAAC,EAAE,CACR,KAAK,EAAE,cAAc,EACrB,GAAG,EAAE,OAAO,KACT,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;CACnC,CAAC;AAmCF;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,oBAAoB,EAC7B,OAAO,EAAE,CACP,GAAG,EAAE,OAAO,EACZ,OAAO,EAAE,gBAAgB,KACtB,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,GAChC,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAuErC"}
@@ -0,0 +1,81 @@
1
+ // src/with-assertion.ts
2
+ import { verifyAssertion } from "./assertion.js";
3
+ import { AssertionError, AssertionErrorCode } from "./errors.js";
4
+ /** Default HTTP header name for the base64-encoded assertion. */
5
+ export const DEFAULT_ASSERTION_HEADER = "X-App-Attest-Assertion";
6
+ /** Default HTTP header name for the device identifier. */
7
+ export const DEFAULT_DEVICE_ID_HEADER = "X-App-Attest-Device-Id";
8
+ async function defaultExtractAssertion(req) {
9
+ const assertion = req.headers.get(DEFAULT_ASSERTION_HEADER);
10
+ const deviceId = req.headers.get(DEFAULT_DEVICE_ID_HEADER);
11
+ if (!assertion || !deviceId) {
12
+ throw new AssertionError(AssertionErrorCode.INVALID_FORMAT, `Missing ${DEFAULT_ASSERTION_HEADER} or ${DEFAULT_DEVICE_ID_HEADER} header`);
13
+ }
14
+ const clientData = new Uint8Array(await req.arrayBuffer());
15
+ return { assertion, deviceId, clientData };
16
+ }
17
+ function defaultErrorResponse(error) {
18
+ const status = error.code === AssertionErrorCode.INTERNAL_ERROR
19
+ ? 500
20
+ : error.code === AssertionErrorCode.INVALID_FORMAT
21
+ ? 400
22
+ : 401;
23
+ return new Response(JSON.stringify({ error: error.message, code: error.code }), { status, headers: { "Content-Type": "application/json" } });
24
+ }
25
+ /**
26
+ * Request handler middleware that verifies App Attest assertions.
27
+ *
28
+ * Wraps a handler function with automatic assertion verification,
29
+ * device key lookup, and sign count management. Returns a new handler
30
+ * that rejects unauthenticated requests with appropriate HTTP error responses.
31
+ */
32
+ export function withAssertion(options, handler) {
33
+ const appInfo = {
34
+ appId: options.appId,
35
+ developmentEnv: options.developmentEnv ?? false,
36
+ };
37
+ const extract = options.extractAssertion ?? defaultExtractAssertion;
38
+ return async (req) => {
39
+ let deviceId;
40
+ let clientData;
41
+ let newSignCount;
42
+ // Steps 1-4: extract, verify, update sign count
43
+ try {
44
+ const extracted = await extract(req);
45
+ deviceId = extracted.deviceId;
46
+ clientData = extracted.clientData;
47
+ let deviceKey;
48
+ try {
49
+ deviceKey = await options.getDeviceKey(deviceId);
50
+ }
51
+ catch (err) {
52
+ throw new AssertionError(AssertionErrorCode.INTERNAL_ERROR, "Storage callback failed", { cause: err });
53
+ }
54
+ if (!deviceKey) {
55
+ throw new AssertionError(AssertionErrorCode.DEVICE_NOT_FOUND, "Device not found");
56
+ }
57
+ const result = await verifyAssertion(appInfo, extracted.assertion, clientData, deviceKey.publicKeyPem, deviceKey.signCount);
58
+ try {
59
+ await options.updateSignCount(deviceId, result.signCount);
60
+ }
61
+ catch (err) {
62
+ throw new AssertionError(AssertionErrorCode.INTERNAL_ERROR, "Failed to update sign count", { cause: err });
63
+ }
64
+ newSignCount = result.signCount;
65
+ }
66
+ catch (err) {
67
+ const error = err instanceof AssertionError
68
+ ? err
69
+ : new AssertionError(AssertionErrorCode.INTERNAL_ERROR, String(err), {
70
+ cause: err,
71
+ });
72
+ return options.onError?.(error, req) ?? defaultErrorResponse(error);
73
+ }
74
+ // Step 5: handler — outside try/catch, errors bubble up
75
+ return await handler(req, {
76
+ deviceId,
77
+ signCount: newSignCount,
78
+ rawBody: clientData,
79
+ });
80
+ };
81
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"assertion-entry.test.d.ts","sourceRoot":"","sources":["../../src/tests/assertion-entry.test.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"assertion-entry.test.d.ts","sourceRoot":"","sources":["../../src/tests/assertion-entry.test.ts"],"names":[],"mappings":"AACA,OAAO,2BAA2B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"assertion.test.d.ts","sourceRoot":"","sources":["../../src/tests/assertion.test.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"assertion.test.d.ts","sourceRoot":"","sources":["../../src/tests/assertion.test.ts"],"names":[],"mappings":"AACA,OAAO,2BAA2B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"attestation-entry.test.d.ts","sourceRoot":"","sources":["../../src/tests/attestation-entry.test.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"attestation-entry.test.d.ts","sourceRoot":"","sources":["../../src/tests/attestation-entry.test.ts"],"names":[],"mappings":"AACA,OAAO,2BAA2B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"attestation.test.d.ts","sourceRoot":"","sources":["../../src/tests/attestation.test.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"attestation.test.d.ts","sourceRoot":"","sources":["../../src/tests/attestation.test.ts"],"names":[],"mappings":"AACA,OAAO,2BAA2B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"authdata.test.d.ts","sourceRoot":"","sources":["../../src/tests/authdata.test.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"authdata.test.d.ts","sourceRoot":"","sources":["../../src/tests/authdata.test.ts"],"names":[],"mappings":"AACA,OAAO,2BAA2B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"certificate.test.d.ts","sourceRoot":"","sources":["../../src/tests/certificate.test.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"certificate.test.d.ts","sourceRoot":"","sources":["../../src/tests/certificate.test.ts"],"names":[],"mappings":"AACA,OAAO,2BAA2B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"cose.test.d.ts","sourceRoot":"","sources":["../../src/tests/cose.test.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"cose.test.d.ts","sourceRoot":"","sources":["../../src/tests/cose.test.ts"],"names":[],"mappings":"AACA,OAAO,2BAA2B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"der.test.d.ts","sourceRoot":"","sources":["../../src/tests/der.test.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"der.test.d.ts","sourceRoot":"","sources":["../../src/tests/der.test.ts"],"names":[],"mappings":"AACA,OAAO,2BAA2B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"errors.test.d.ts","sourceRoot":"","sources":["../../src/tests/errors.test.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"errors.test.d.ts","sourceRoot":"","sources":["../../src/tests/errors.test.ts"],"names":[],"mappings":"AACA,OAAO,2BAA2B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"utils.test.d.ts","sourceRoot":"","sources":["../../src/tests/utils.test.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"utils.test.d.ts","sourceRoot":"","sources":["../../src/tests/utils.test.ts"],"names":[],"mappings":"AACA,OAAO,2BAA2B,CAAC"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"with-assertion.test.d.ts","sourceRoot":"","sources":["../../src/tests/with-assertion.test.ts"],"names":[],"mappings":"AACA,OAAO,2BAA2B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bradford-tech/supabase-integrity-attest",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "Verify Apple App Attest attestations and assertions using WebCrypto.",
5
5
  "repository": {
6
6
  "type": "git",