# @bradford-tech/supabase-integrity-attest 中文文档

规范地址：https://npmcn.com/package/%40bradford-tech/supabase-integrity-attest/

版本：0\.9\.1
npm 元数据更新时间：2026-08-01T07:57:40.066Z
中文译文更新时间：2026-11-08T13:25:40.530Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40bradford-tech/supabase-integrity-attest
原文对照：https://npmcn.com/package/%40bradford-tech/supabase-integrity-attest/?lang=original
项目主页：https://integrity-attest.bradford.tech/
源码仓库：https://github.com/bradford-tech/supabase-integrity-attest

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：130 kB，43 个文件
- 模块格式：ES Module
- TypeScript 类型：未声明
- 运行依赖：3 个
- 首次发布：2026-03-08T03:10:14.222Z
- 最新版本发布：2026-08-01T07:57:39.731Z
- 近 30 天下载量：3839（2026-10-09 至 2026-11-07，npm 官方下载统计）
- 已发布版本数：15（近 12 个月 15 个）

## 中文简介

使用 WebCrypto 验证 App Attest 的证明和断言.

## 安装

```sh
npm install @bradford-tech/supabase-integrity-attest
```

也可以使用：`pnpm add @bradford-tech/supabase-integrity-attest`、`yarn add @bradford-tech/supabase-integrity-attest`、`bun add @bradford-tech/supabase-integrity-attest`

## README

# supabase-完整性测试

苹果App Attest服务器端用于边缘运行时间的验证,只使用WebCrypto.

## 安装

```bash
# Deno
deno add jsr:@bradford-tech/supabase-integrity-attest

# npm
npm install @bradford-tech/supabase-integrity-attest
```

## 快开始

校验设备验证并提取其公钥 :

```ts
import { verifyAttestation } from "@bradford-tech/supabase-integrity-attest";

const clientDataHash = new Uint8Array(
  await crypto.subtle.digest("SHA-256", new TextEncoder().encode(challenge)),
);

const { publicKeyPem, signCount } = await verifyAttestation(
  { appId: "TEAMID.com.example.app" },
  keyId,          // base64 key identifier from client
  clientDataHash, // SHA-256 of the challenge you issued
  attestation,    // base64 CBOR attestation from client
);
// publicKeyPem: "-----BEGIN PUBLIC KEY-----\nMFkw..."
// signCount: 0
```

存储 `publicKeyPem` 财务报告和审定财务报表 `signCount` 用于此设备。 使用它们来验证未来的断言.

## 为什么这个图书馆

现有的 App Attest 校验库依赖于 `node:crypto` 或者在边缘运行时崩溃的软件包. `appattest-checker-node` 用途 `X509Certificate.verify()`,该投出 `ERR_NOT_IMPLEMENTED` 在德诺. `pkijs` 在 Supabase Edge 函数的模块装入时崩溃, 因为 `self.crypto.name` 未定义. `@peculiar/x509` 停车 `tsyringe` 财务报告和审定财务报表 `reflect-metadata`,在模块初始化时依赖于全球副作用.

此库只使用 `crypto.subtle` 密码操作 `asn1js` x.509 解析和 `@noble/curves` 用于一个操作Deno's WebCrypto不支持(苹果公司中间证书上的P-384签名验证).

## 中间软件

下面的两个中间软件包都使用Supabase服务-作用客户端来访问数据库:

```ts
import { createClient } from "jsr:@supabase/supabase-js@2";

const supabase = createClient(
  Deno.env.get("SUPABASE_URL")!,
  Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!,
);
```

### `withAttestation` -- 设备注册

`withAttestation` 将一次性设备注册端口与自动挑战消耗、验证和设备密钥存储相包裹:

```ts
import { withAttestation } from "@bradford-tech/supabase-integrity-attest";

Deno.serve(withAttestation({
  appId: Deno.env.get("APP_ATTEST_APP_ID")!,
  consumeChallenge: async (challenge) => {
    const id = new TextDecoder().decode(challenge);
    const { data } = await supabase
      .from("attestation_challenges")
      .delete()
      .eq("id", id)
      .select("id");
    return (data?.length ?? 0) > 0;
  },
  storeDeviceKey: async ({ deviceId, publicKeyPem, signCount }) => {
    await supabase
      .from("device_attestations")
      .upsert({ key_id: deviceId, public_key_pem: publicKeyPem, sign_count: signCount });
  },
}, (_req, ctx) => {
  // ctx.deviceId, ctx.publicKeyPem, ctx.signCount, ctx.receipt, ctx.timings
  return Response.json({ deviceId: ctx.deviceId });
}));
```

`consumeChallenge` 必须是原子:返回 `true` 如果挑战有效、未使用和未到期(现已耗尽), `false` 否则。 使用 `DELETE ... RETURNING` 保证单用语义.

默认提取器读取一个JSON体:

```
POST /functions/v1/attest
Content-Type: application/json

{"keyId": "<base64>", "challenge": "<base64>", "attestation": "<base64>"}
```

### `withAssertion` -- 受保护的请求

`withAssertion` 将任何受保护的端点以自动确认、设备密钥查找并签名计数承诺来包装:

```ts
import { withAssertion } from "@bradford-tech/supabase-integrity-attest";

Deno.serve(withAssertion({
  appId: Deno.env.get("APP_ATTEST_APP_ID")!,
  getDeviceKey: async (deviceId) => {
    const { data } = await supabase
      .from("device_attestations")
      .select("public_key_pem, sign_count")
      .eq("key_id", deviceId)
      .single();
    return data
      ? { publicKeyPem: data.public_key_pem, signCount: data.sign_count }
      : null;
  },
  commitSignCount: async (deviceId, newSignCount) => {
    const { data } = await supabase
      .from("device_attestations")
      .update({ sign_count: newSignCount })
      .eq("key_id", deviceId)
      .lt("sign_count", newSignCount)
      .select("key_id");
    return (data?.length ?? 0) > 0;
  },
}, async (_req, { rawBody }) => {
  const payload = JSON.parse(new TextDecoder().decode(rawBody));
  return Response.json({ ok: true });
}));
```

客户端在信头中发送断言和装置ID. 请求机构是签名客户端数据:

```
POST /functions/v1/your-endpoint
X-App-Attest-Assertion: <base64-encoded assertion>
X-App-Attest-Device-Id: <base64-encoded keyId>
Content-Type: application/json

{"text": "Hello world", "voice": "en-US"}
```

### 签名计数解剖

`commitSignCount` **必须** 使用比较和扫描:如果当前值严格低于 `newSignCount`无条件 `UPDATE ... SET sign_count = $1` 当两个请求同时到达时, 静静地中断了 重放保护 .

```sql
UPDATE device_attestations
   SET sign_count = $1, last_seen_at = now()
 WHERE key_id = $2 AND sign_count < $1
```

返回时 `true` 如果行已更新。 图书馆转换 `false` 输入 `AssertionError(SIGN_COUNT_STALE)`。 。 。 .

### 共享选项

一旦您拥有多个受保护的功能, 请提取共享选项 :

```ts
// supabase/functions/_shared/attest.ts
import type { WithAssertionOptions } from "@bradford-tech/supabase-integrity-attest";

export const assertionOptions: WithAssertionOptions = {
  appId: Deno.env.get("APP_ATTEST_APP_ID")!,
  // ... getDeviceKey, commitSignCount as above
};
```

```ts
// supabase/functions/text-to-speech/index.ts
import { withAssertion } from "@bradford-tech/supabase-integrity-attest";
import { assertionOptions } from "../_shared/attest.ts";

Deno.serve(withAssertion(assertionOptions, async (_req, { rawBody }) => {
  const { text, voice } = JSON.parse(new TextDecoder().decode(rawBody));
  return Response.json({ audio: "..." });
}));
```

## Supabase 适配器

在Supabase上,跳过完全写回调. 这个 `/supabase` 子路径舰 `createSupabaseAdapter()`,它提供了所有四个存储回调 `issueChallenge()`,辅以两个邮递表:

```ts
import { withAttestation } from "@bradford-tech/supabase-integrity-attest/attestation";
import { createSupabaseAdapter } from "@bradford-tech/supabase-integrity-attest/supabase";

const adapter = createSupabaseAdapter(supabase); // service-role client

// Challenge endpoint
const { challengeBase64, expiresAt } = await adapter.issueChallenge("attestation");

// Registration endpoint -- adapter supplies consumeChallenge + storeDeviceKey
Deno.serve(withAttestation(
  { appId: Deno.env.get("APP_ATTEST_APP_ID")!, ...adapter },
  (_req, ctx) => Response.json({ deviceId: ctx.deviceId }),
));

// Protected endpoints -- adapter supplies getDeviceKey + commitSignCount
// withAssertion({ appId, ...adapter }, handler)
```

装有包裹的舰艇 [`sql/app_attest.sql`](https://npmcn.com/package/%40bradford-tech/supabase-integrity-attest/sql/app_attest.sql) (两个表格, RLS 锁定到服务角色, 过期索引, pg cron swearing of 过期的挑战). 表格名称和质疑 TTL 通过选项参数可以配置 . `@supabase/supabase-js` 不是一个依赖性——客户端在结构上被打入,所以任何最近的客户端都会工作.

App Attest 证明了设备的完整性,而不是用户身份:适配器和中间软件与Supabase Auth,AWS Cognitto,或任何无记名JWT并列.

## 低级 API

为了全面控制核查流量,使用 `verifyAttestation` 财务报告和审定财务报表 `verifyAssertion` 直接说.

上面的快速开始显示 `verifyAttestation`。注意: `clientDataHash` 必须是SHA-256的挑战,而不是原始的挑战。 客户端 SDKs( Expo) `attestKeyAsync`,当地 `DCAppAttestService.attestKey`)在传递到苹果公司之前先在内部将挑战进行散列;您必须生产相同的散列服务器侧. 这个 `withAttestation` 中件自动处理此操作 .

### 请求

```ts
import { verifyAssertion } from "@bradford-tech/supabase-integrity-attest";

const { signCount } = await verifyAssertion(
  { appId: "TEAMID.com.example.app" },
  assertion,         // base64 CBOR from client
  clientData,        // the request payload that was signed
  storedPublicKeyPem,
  storedSignCount,
);
// Update stored signCount to signCount
```

## 子路径导入

仅导入您需要的缩放捆绑大小 :

```ts
// Full library (attestation + assertion)
import { verifyAttestation, verifyAssertion } from "@bradford-tech/supabase-integrity-attest";

// Assertion only -- skips asn1js and @noble/curves
import { verifyAssertion, withAssertion } from "@bradford-tech/supabase-integrity-attest/assertion";

// Attestation only
import { verifyAttestation, withAttestation } from "@bradford-tech/supabase-integrity-attest/attestation";

// Supabase storage adapter
import { createSupabaseAdapter } from "@bradford-tech/supabase-integrity-attest/supabase";
```

## 处理错误

```ts
import {
  AttestationError,
  AssertionError,
} from "@bradford-tech/supabase-integrity-attest";

try {
  await verifyAttestation(appInfo, keyId, clientDataHash, attestation);
} catch (e) {
  if (e instanceof AttestationError) {
    console.log(e.code);
    // => "NONCE_MISMATCH"
  }
}
```

### 验证错误代码

| 代码 | 含义 |
| --- | --- |
| `INVALID_FORMAT` | CBOR 解码或结构验证失败 |
| `INVALID_CERTIFICATE_CHAIN` | X.509 证书链验证失败 |
| `NONCE_MISMATCH` | 计算出的非ce 不匹配证书的非ce |
| `RP_ID_MISMATCH` | RP ID 散列不符合应用程序 ID 的 SHA-256 |
| `KEY_ID_MISMATCH` | 公钥散列不符合所提供的密钥 ID |
| `INVALID_COUNTER` | 签名计数不是零( 需要证明) |
| `INVALID_AAGUID` | AAGUID 不符合预期环境 |
| `CHALLENGE_INVALID` | 缺少、过期或已经耗用的挑战`withAttestation` 仅限) |
| `INTERNAL_ERROR` | 存储回调或内部出错( Q)`withAttestation` 仅限) |

### 请求错误代码

| 代码 | 含义 |
| --- | --- |
| `INVALID_FORMAT` | CBOR 解码或结构验证失败 |
| `RP_ID_MISMATCH` | RP ID 散列不符合应用程序 ID 的 SHA-256 |
| `COUNTER_NOT_INCREMENTED` | 签名计数不大于存储值 |
| `SIGNATURE_INVALID` | ECDSA 签名验证失败 |
| `DEVICE_NOT_FOUND` | 没有给定设备ID的设备密钥( N)`withAssertion` 仅限) |
| `INTERNAL_ERROR` | 存储回调或内部出错( Q)`withAssertion` 仅限) |
| `SIGN_COUNT_STALE` | 同时提出的请求已经将计数器提前( Q)`withAssertion` 仅限) |

## 发展环境

对于使用苹果开发的 App Attest 环境的应用程序,通过 `developmentEnv: true`数字 :

```ts
await verifyAttestation(
  { appId: "TEAMID.com.example.app", developmentEnv: true },
  keyId,
  clientDataHash,
  attestation,
);
```

`withAttestation` 还接受 `developmentEnv` 在它的选择.

## 文档

页:1 [技术](https://integrity-attest.bradford.tech/)。 。 。 .

## 捐款

欢迎提出问题和拉动请求 [GitHub 图像](https://github.com/bradford-tech/supabase-integrity-attest)。 。 。 .

## 警卫

见 [警卫](https://npmcn.com/package/%40bradford-tech/supabase-integrity-attest/SECURITY.md) 脆弱性报告.

## 许可证

麻省理工学院

## 常见问题

### @bradford-tech/supabase-integrity-attest 是什么？

使用 WebCrypto 验证 App Attest 的证明和断言\.

### 如何安装 @bradford-tech/supabase-integrity-attest？

在项目目录运行 npm install @bradford-tech/supabase-integrity-attest，或使用 pnpm add / yarn add / bun add。

### @bradford-tech/supabase-integrity-attest 支持 TypeScript 吗？

该包的 package\.json 没有声明类型入口。需要类型提示时，可以在 npm 上查找社区维护的 @types/bradford-tech\_\_supabase-integrity-attest 是否存在。

### @bradford-tech/supabase-integrity-attest 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### @bradford-tech/supabase-integrity-attest 可以商用吗？

@bradford-tech/supabase-integrity-attest 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @bradford-tech/supabase-integrity-attest 的下载量有多少？

根据 npm 官方下载统计，@bradford-tech/supabase-integrity-attest 在 2026/10/09 – 2026/11/07 这 30 天内被下载 3,839 次，日均约 128 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @bradford-tech/supabase-integrity-attest 还在维护吗？

最新版本 0\.9\.1 发布于 2026/08/01（3 个月前）；至今共发布 15 个版本，其中过去 12 个月发布了 15 个；首次发布于 2026/03/08。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

