{"_id":"@aardwin/auth-server","_rev":"7-21347e55261e3d32de5fd38ec37fbd9a","name":"@aardwin/auth-server","dist-tags":{"latest":"0.4.0"},"versions":{"0.1.0":{"name":"@aardwin/auth-server","version":"0.1.0","keywords":["oauth","oauth2","authentication","server","sdk","aardwin","aardpro"],"license":"MIT","_id":"@aardwin/auth-server@0.1.0","maintainers":[{"name":"aardpro","email":"chileehong@outlook.com"}],"homepage":"https://github.com/aardpro/aardwin-sdk#readme","bugs":{"url":"https://github.com/aardpro/aardwin-sdk/issues"},"dist":{"shasum":"9c89dfcbcdde686443c42613f720dcb0049f7244","tarball":"https://registry.npmjs.org/@aardwin/auth-server/-/auth-server-0.1.0.tgz","fileCount":20,"integrity":"sha512-4IZOflRyxVkrmwt1GhcGytZ3xxohSmmB22VZehPR86fh2yyzm4JDNyEmoznl5yWX6GGc7iD8CBY/egC4gWrPpA==","signatures":[{"sig":"MEUCIQDTjKI/1cejVnYWSwFwwfllpfVp/QhnUcFaSYoHuz3XHwIgYRBloiDbouFCKBh3/UJF3sqkYzRy+6NfY5Rccb8AqGw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":41782},"main":"./dist/index.mjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.mjs","engines":{"node":">=18"},"exports":{".":{"node":"./dist/index.mjs","types":"./dist/index.d.ts","default":"./dist/index.mjs"},"./package.json":"./package.json"},"gitHead":"31401dd8ec75ebdad7559b86126b9a5c38d6b7ed","scripts":{"dev":"bun --watch src/index.ts","test":"bun test","build":"bun run build:dist && bun run build:types","clean":"rm -rf dist tsconfig.tsbuildinfo","typecheck":"tsc --noEmit","build:dist":"bun build src/index.ts --outfile dist/index.mjs --target node --minify","build:types":"tsc --emitDeclarationOnly --declaration --outDir dist","prepublishOnly":"bun run clean && bun run build && bun test"},"_npmUser":{"name":"aardpro","email":"chileehong@outlook.com"},"repository":{"url":"git+https://github.com/aardpro/aardwin-sdk.git","type":"git"},"_npmVersion":"10.9.8","description":"aardwin server SDK — framework-agnostic API client (exchangeCode + future methods)","directories":{},"_nodeVersion":"22.22.3","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/auth-server_0.1.0_1783497754394_0.17923751010808275","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@aardwin/auth-server","version":"0.2.0","keywords":["oauth","oauth2","authentication","server","sdk","aardwin","aardpro"],"license":"MIT","_id":"@aardwin/auth-server@0.2.0","maintainers":[{"name":"aardpro","email":"chileehong@outlook.com"}],"homepage":"https://github.com/aardpro/aardwin-sdk/tree/main/server-sdk#readme","bugs":{"url":"https://github.com/aardpro/aardwin-sdk/issues"},"dist":{"shasum":"69f0769186d4e89d7d55168cccef1fee709f0277","tarball":"https://registry.npmjs.org/@aardwin/auth-server/-/auth-server-0.2.0.tgz","fileCount":22,"integrity":"sha512-d6CtRZvLnvoq1uHcm8Viqst1++HzPo4u1ayjjMPpfGOHFYeyXGGpERY6T+Tk6z2CW9TEwRY68rgLLOUama69lw==","signatures":[{"sig":"MEYCIQDPqbFJeGAgbVP7TUa9buSdY3e3mJ5V6ErGfrJqfneaOAIhAPUnyWY2KFeckHjnTsEup6HdxGK3ne7osxQg/JzVVFeP","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aardwin%2fauth-server@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":45378},"main":"./dist/index.mjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.mjs","engines":{"node":">=18"},"exports":{".":{"node":"./dist/index.mjs","types":"./dist/index.d.ts","default":"./dist/index.mjs"},"./package.json":"./package.json"},"gitHead":"1bed13dfd56af89a4901a867cd4608ab12d1a622","scripts":{"dev":"bun --watch src/index.ts","test":"bun test","build":"bun run build:dist && bun run build:types","clean":"rm -rf dist tsconfig.tsbuildinfo","typecheck":"tsc --noEmit","build:dist":"bun build src/index.ts --outfile dist/index.mjs --target node --minify","build:types":"tsc -p tsconfig.build.json","prepublishOnly":"bun run clean && bun run build && bun test"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:cc113f45-f55e-4bc6-9776-a5c71a854393"}},"repository":{"url":"git+https://github.com/aardpro/aardwin-sdk.git","type":"git","directory":"server-sdk"},"_npmVersion":"12.0.1","description":"aardwin server SDK — framework-agnostic API client (exchangeCode + future methods)","directories":{},"_nodeVersion":"22.23.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.5.4"},"_npmOperationalInternal":{"tmp":"tmp/auth-server_0.2.0_1784631702921_0.7440235617997821","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"name":"@aardwin/auth-server","version":"0.2.1","keywords":["oauth","oauth2","authentication","server","sdk","aardwin","aardpro"],"license":"MIT","_id":"@aardwin/auth-server@0.2.1","maintainers":[{"name":"aardpro","email":"chileehong@outlook.com"}],"homepage":"https://github.com/aardpro/aardwin-sdk/tree/main/server-sdk#readme","bugs":{"url":"https://github.com/aardpro/aardwin-sdk/issues"},"dist":{"shasum":"869eae5b99a66427023d584bf493623c0fc9653e","tarball":"https://registry.npmjs.org/@aardwin/auth-server/-/auth-server-0.2.1.tgz","fileCount":22,"integrity":"sha512-y3k5HLrfMZ5esQ1Ci9/VViYlAvVfwjoonXV1Yyx/kvyKXhWC3g0gbxOMbTk4SiOJ4LFaOvLlrbC7Sfc6QSIRlg==","signatures":[{"sig":"MEUCIQDz2kz0Ymdksq0gvkibNa4qocE0Cw8+RaoqJDqZPCBrFgIgGtLo6fB+1m3EWJQNRRyl45V45C9vG6tQJ7CNo6434OE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aardwin%2fauth-server@0.2.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":45378},"main":"./dist/index.mjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.mjs","engines":{"node":">=18"},"exports":{".":{"node":"./dist/index.mjs","types":"./dist/index.d.ts","default":"./dist/index.mjs"},"./package.json":"./package.json"},"gitHead":"3545f62d4e4950c3e025a33413f1a8b1c077e62c","scripts":{"dev":"bun --watch src/index.ts","test":"bun test","build":"bun run build:dist && bun run build:types","clean":"rm -rf dist tsconfig.tsbuildinfo","typecheck":"tsc --noEmit","build:dist":"bun build src/index.ts --outfile dist/index.mjs --target node --minify","build:types":"tsc -p tsconfig.build.json","prepublishOnly":"bun run clean && bun run build && bun test"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:cc113f45-f55e-4bc6-9776-a5c71a854393"}},"repository":{"url":"git+https://github.com/aardpro/aardwin-sdk.git","type":"git","directory":"server-sdk"},"_npmVersion":"12.0.1","description":"aardwin server SDK — framework-agnostic API client (exchangeCode + future methods)","directories":{},"_nodeVersion":"22.23.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.5.4"},"_npmOperationalInternal":{"tmp":"tmp/auth-server_0.2.1_1785206419694_0.4195227562121022","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@aardwin/auth-server","version":"0.3.0","keywords":["oauth","oauth2","authentication","server","sdk","aardwin","aardpro"],"license":"MIT","_id":"@aardwin/auth-server@0.3.0","maintainers":[{"name":"aardpro","email":"chileehong@outlook.com"}],"homepage":"https://github.com/aardpro/aardwin-sdk/tree/main/server-sdk#readme","bugs":{"url":"https://github.com/aardpro/aardwin-sdk/issues"},"dist":{"shasum":"1b4ce615e9c7465e56414c18d40f73ec00a27ded","tarball":"https://registry.npmjs.org/@aardwin/auth-server/-/auth-server-0.3.0.tgz","fileCount":22,"integrity":"sha512-r0zev6ykUV5PXWOGZBCeboFxdvTaxkBzRe7mSkr0Ht97k16JcPmLxq/F0+55n+8vVR5veEHiMbf63efYanRZ0g==","signatures":[{"sig":"MEQCIHJ66VoGNdbqfIw1xHrHFN8jzKdVNPYY/kSHuAQnE1wzAiAyTcnE6UCNxAgk9oN80o/owzraVJw9a0oB5B1FDXT6hw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aardwin%2fauth-server@0.3.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":47865},"main":"./dist/index.mjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.mjs","engines":{"node":">=18"},"exports":{".":{"node":"./dist/index.mjs","types":"./dist/index.d.ts","default":"./dist/index.mjs"},"./package.json":"./package.json"},"gitHead":"fe1731e536805c9b29a59f15516c1d29c8fab64e","scripts":{"dev":"bun --watch src/index.ts","test":"bun test","build":"bun run build:dist && bun run build:types","clean":"rm -rf dist tsconfig.tsbuildinfo","typecheck":"tsc --noEmit","build:dist":"bun build src/index.ts --outfile dist/index.mjs --target node --minify","build:types":"tsc -p tsconfig.build.json","prepublishOnly":"bun run clean && bun run build && bun test"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:cc113f45-f55e-4bc6-9776-a5c71a854393"}},"repository":{"url":"git+https://github.com/aardpro/aardwin-sdk.git","type":"git","directory":"server-sdk"},"_npmVersion":"12.0.1","description":"aardwin server SDK — framework-agnostic API client (exchangeCode + future methods)","directories":{},"_nodeVersion":"22.23.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.5.4"},"_npmOperationalInternal":{"tmp":"tmp/auth-server_0.3.0_1785317745253_0.6599983316926434","host":"s3://npm-registry-packages-npm-production"}},"0.3.1":{"name":"@aardwin/auth-server","version":"0.3.1","keywords":["oauth","oauth2","authentication","server","sdk","aardwin","aardpro"],"license":"MIT","_id":"@aardwin/auth-server@0.3.1","maintainers":[{"name":"aardpro","email":"chileehong@outlook.com"}],"homepage":"https://github.com/aardpro/aardwin-sdk/tree/main/server-sdk#readme","bugs":{"url":"https://github.com/aardpro/aardwin-sdk/issues"},"dist":{"shasum":"3c527848ee0bf3379bdb0fd655a92256e375214c","tarball":"https://registry.npmjs.org/@aardwin/auth-server/-/auth-server-0.3.1.tgz","fileCount":22,"integrity":"sha512-Jw60l1vE9oiMEvU1A5re4dALZ43gngXB0+OJiO+ERY9kqDLka6nftQzVN1Jvz1cIbZP+nDFlzxarMsMGki00PQ==","signatures":[{"sig":"MEQCIGurki2SB9XaktTZT2svT8c53EZikNzMmiNCD0KJjkLbAiAYfIfASZY9iW9dA5olPDJd0Cr0gjKmbT/c3y8aojNLnw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aardwin%2fauth-server@0.3.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":48147},"main":"./dist/index.mjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.mjs","engines":{"node":">=18"},"exports":{".":{"node":"./dist/index.mjs","types":"./dist/index.d.ts","default":"./dist/index.mjs"},"./package.json":"./package.json"},"gitHead":"4943a7ad012f2dd169be4912e89c5a123312e190","scripts":{"dev":"bun --watch src/index.ts","test":"bun test","build":"bun run build:dist && bun run build:types","clean":"rm -rf dist tsconfig.tsbuildinfo","typecheck":"tsc --noEmit","build:dist":"bun build src/index.ts --outfile dist/index.mjs --target node --minify","build:types":"tsc -p tsconfig.build.json","prepublishOnly":"bun run clean && bun run build && bun test"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:cc113f45-f55e-4bc6-9776-a5c71a854393"}},"repository":{"url":"git+https://github.com/aardpro/aardwin-sdk.git","type":"git","directory":"server-sdk"},"_npmVersion":"12.0.2","description":"aardwin server SDK — framework-agnostic API client (exchangeCode + future methods)","directories":{},"_nodeVersion":"22.23.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.5.4"},"_npmOperationalInternal":{"tmp":"tmp/auth-server_0.3.1_1785485279754_0.14908255534936","host":"s3://npm-registry-packages-npm-production"}},"0.3.2":{"name":"@aardwin/auth-server","version":"0.3.2","keywords":["oauth","oauth2","authentication","server","sdk","aardwin","aardpro"],"license":"MIT","_id":"@aardwin/auth-server@0.3.2","maintainers":[{"name":"aardpro","email":"chileehong@outlook.com"}],"homepage":"https://github.com/aardpro/aardwin-sdk/tree/main/server-sdk#readme","bugs":{"url":"https://github.com/aardpro/aardwin-sdk/issues"},"dist":{"shasum":"0b9d628799f8cf1ab6c28b9e4f5f27ea38878cd4","tarball":"https://registry.npmjs.org/@aardwin/auth-server/-/auth-server-0.3.2.tgz","fileCount":23,"integrity":"sha512-VAj+eVVMFxzyBuWdI3Az1zk/o1DyrVqLSNsxtBlpm3RK6xjWRD/MSJp3WGbsFrqcl3LzUq+dVPFvr5jJTHk+QA==","signatures":[{"sig":"MEUCIH4uuS/BTB4r5j62IHN3l0T1s70/gFsSCpDntk0osUxiAiEAmjyLmxbcqw031Cg6Nd/7n3lFqr+EvNN8PZjSMx5xmy4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aardwin%2fauth-server@0.3.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":62607},"main":"./dist/index.mjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.mjs","engines":{"node":">=18"},"exports":{".":{"node":"./dist/index.mjs","types":"./dist/index.d.ts","default":"./dist/index.mjs"},"./package.json":"./package.json"},"gitHead":"d650e408d90fc5fb1da0178810f354d682923752","scripts":{"dev":"bun --watch src/index.ts","test":"bun test","build":"bun run build:dist && bun run build:types","clean":"rm -rf dist tsconfig.tsbuildinfo","typecheck":"tsc --noEmit","build:dist":"bun build src/index.ts --outfile dist/index.mjs --target node --minify","build:types":"tsc -p tsconfig.build.json","prepublishOnly":"bun run clean && bun run build && bun test"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:cc113f45-f55e-4bc6-9776-a5c71a854393"}},"repository":{"url":"git+https://github.com/aardpro/aardwin-sdk.git","type":"git","directory":"server-sdk"},"_npmVersion":"12.0.2","description":"aardwin server SDK — framework-agnostic API client (exchangeCode + future methods)","directories":{},"_nodeVersion":"22.23.2","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.5.4"},"_npmOperationalInternal":{"tmp":"tmp/auth-server_0.3.2_1786982639420_0.11451636165149637","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@aardwin/auth-server","version":"0.4.0","description":"aardwin server SDK — framework-agnostic API client (exchangeCode + future methods)","license":"MIT","type":"module","main":"./dist/index.mjs","module":"./dist/index.mjs","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","node":"./dist/index.mjs","default":"./dist/index.mjs"},"./package.json":"./package.json"},"scripts":{"dev":"bun --watch src/index.ts","build":"bun run build:dist && bun run build:types","build:dist":"bun build src/index.ts --outfile dist/index.mjs --target node --minify","build:types":"tsc -p tsconfig.build.json","typecheck":"tsc --noEmit","test":"bun test","prepublishOnly":"bun run clean && bun run build && bun test","clean":"rm -rf dist tsconfig.tsbuildinfo"},"keywords":["oauth","oauth2","authentication","server","sdk","aardwin","aardpro"],"repository":{"type":"git","url":"git+https://github.com/aardpro/aardwin-sdk.git","directory":"server-sdk"},"homepage":"https://github.com/aardpro/aardwin-sdk/tree/main/server-sdk#readme","bugs":{"url":"https://github.com/aardpro/aardwin-sdk/issues"},"publishConfig":{"access":"public"},"engines":{"node":">=18"},"devDependencies":{"typescript":"^5.5.4"},"gitHead":"a34702f052aca4a8ff1752938c855ec2d3753d00","_id":"@aardwin/auth-server@0.4.0","_nodeVersion":"22.23.2","_npmVersion":"12.0.2","dist":{"integrity":"sha512-uJVKJexTqWgyJYBCASy0BcDjBziJYu3A5HIe6cTjw502gUzySEq1DA21liWNINV5q6qJFQFOx5WwgwKmI9hN9Q==","shasum":"46e62d2aa1fb4e7bf55f8877db6702f3a7814d42","tarball":"https://registry.npmjs.org/@aardwin/auth-server/-/auth-server-0.4.0.tgz","fileCount":23,"unpackedSize":61669,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aardwin%2fauth-server@0.4.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIBK6d68YnzoTd9EkHhD+gy3eWaXLHt8lsj7I899QTV7KAiB8nda6xCvfnKoV4h8GvD9PFmgSYy+QJSFlWj00RS1jHQ=="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:cc113f45-f55e-4bc6-9776-a5c71a854393"}},"directories":{},"maintainers":[{"name":"aardpro","email":"chileehong@outlook.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/auth-server_0.4.0_1786984823947_0.7998986209288377"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-08T08:02:34.226Z","modified":"2026-08-17T16:40:24.392Z","0.1.0":"2026-07-08T08:02:34.504Z","0.2.0":"2026-07-21T11:01:43.052Z","0.2.1":"2026-07-28T02:40:19.854Z","0.3.0":"2026-07-29T09:35:45.394Z","0.3.1":"2026-07-31T08:07:59.925Z","0.3.2":"2026-08-17T16:03:59.591Z","0.4.0":"2026-08-17T16:40:24.079Z"},"bugs":{"url":"https://github.com/aardpro/aardwin-sdk/issues"},"license":"MIT","homepage":"https://github.com/aardpro/aardwin-sdk/tree/main/server-sdk#readme","keywords":["oauth","oauth2","authentication","server","sdk","aardwin","aardpro"],"repository":{"type":"git","url":"git+https://github.com/aardpro/aardwin-sdk.git","directory":"server-sdk"},"description":"aardwin server SDK — framework-agnostic API client (exchangeCode + future methods)","maintainers":[{"name":"aardpro","email":"chileehong@outlook.com"}],"readme":"# aardwin browser SDK — 完整接入指南\n\n[English](./README.md) | **中文**\n\n[browser-sdk README](../browser-sdk/README.md) 是简短入门，本文档是完整接入指南：OAuth2 authorization-code 流程如何工作、`<aardwin-auth>` 与 `<aardwin-account>` 的行为，以及你的后端回调路由必须实现哪些步骤。\n\n---\n## 流程时序\n\n```text\n  ┌─────────────────┐\n  │   你的登录页     │  <aardwin-auth site-id=\"…\">\n  │（与 callbackUrl │  请求 GET /api/providers?site_id=…\n  │   同 host）     │  按 provider 渲染按钮\n  └────────┬────────┘\n           │ 点击\n           │ 设置 aard_win_auth_state cookie（SameSite=Lax）\n           ▼\n  ┌─────────────────┐      ┌──────────────┐      ┌──────────────┐\n  │ aardwin bff     │ ──▶  │   provider   │ ──▶  │  provider    │\n  │ /authorize      │ 扫码 │（微信/谷歌等）│ 授权 │  返回 code   │\n  └────────┬────────┘      └──────────────┘      └──────────────┘\n           │ 302 跳回你注册的 callbackUrl\n           │ ?code=<一次性码>&state=<随机数>\n           ▼\n  ┌─────────────────┐\n  │  你的回调路由    │  1. 读取 aard_win_auth_state cookie\n  │                 │  2. timingSafeEqual 比较 cookie 与 query 的 state\n  │                 │  3. POST /api/oauth/token {site_id,code,client_secret}\n  │                 │  4. 生成你自己的 session，跳回应用\n  └─────────────────┘\n```\n\n流程中**没有 iframe**，也**没有 postMessage**。provider 扫码通过整页跳转完成，一次性码交到你的后端回调路由。你的路由必须校验 `state` 随机数，并用 `@aardwin/auth-server` 换码。\n\n---\n## 快速开始\n\n### 1. 在 https://aard.win 注册站点\n\n你会收到 / 配置：\n\n- `siteId` —— 公开，放在 `<aardwin-auth>` 标签里。\n- `clientSecret` —— 仅服务端使用，用于 `exchangeCode()`。\n- 站点的 **provider 列表**（wechat / google / github / outlook / discord / email）。\n- 你的 **callbackUrl** —— 接收 `?code=&state=` 的路由。\n\nprovider 列表与 callbackUrl 存在站点记录中；标签会动态拉取 provider 列表。\n\n### 2. 安装\n\n```bash\nnpm install @aardwin/auth-browser\n```\n\n```ts\nimport '@aardwin/auth-browser'; // 注册 <aardwin-auth> 与 <aardwin-account>\n```\n\n### 3. 在登录页放置标签\n\n```html\n<aardwin-auth site-id=\"YOUR_SITE_ID\"></aardwin-auth>\n```\n\n### 4. 实现回调路由\n\n以下示例是框架无关的 Web Fetch API 版本。复制并适配到你的框架（Astro / Next.js / Hono / Express 等）。\n\n```ts\nimport { exchangeCode, AardwinError } from '@aardwin/auth-server';\nimport { timingSafeEqual } from 'node:crypto';\n\nasync function handleCallback(req: Request): Promise<Response> {\n  const url = new URL(req.url);\n  const stateParam = url.searchParams.get('state');\n  const code = url.searchParams.get('code');\n\n  // 1. 读取 <aardwin-auth> 在跳转前设置的 state cookie。\n  const cookieHeader = req.headers.get('cookie') ?? '';\n  const stateCookie = parseCookie(cookieHeader, 'aard_win_auth_state');\n\n  // 2. 常量时间比较。不匹配 → 400（不要调用 exchangeCode）。\n  if (!stateCookie || !code || !safeStateEqual(stateCookie, stateParam)) {\n    return new Response('bad state', { status: 400 });\n  }\n\n  // 3. 用一次性码换身份。一次性消费 —— 失败时不要重试。\n  try {\n    const user = await exchangeCode({\n      code,\n      siteId: process.env.AARD_SITE_ID!,\n      clientSecret: process.env.AARDWIN_CLIENT_SECRET,\n    });\n\n    // 4. 生成你自己的 session，设置 session cookie，然后跳转。\n    const session = await createSession(user.user_id);\n    const res = Response.redirect(new URL('/dashboard', url), 303);\n    res.headers.append('set-cookie',\n      `sid=${session.token}; HttpOnly; SameSite=Lax; Path=/; Max-Age=${session.ttl}`);\n    res.headers.append('set-cookie',\n      'aard_win_auth_state=; Max-Age=0; Path=/'); // 删除已消费的 state cookie\n    return res;\n  } catch (e) {\n    if (e instanceof AardwinError) {\n      return new Response('auth failed: ' + e.message, { status: 400 });\n    }\n    throw e;\n  }\n}\n\nfunction parseCookie(header: string, name: string): string | undefined {\n  for (const part of header.split(';')) {\n    const [k, ...v] = part.trim().split('=');\n    if (k === name) return decodeURIComponent(v.join('='));\n  }\n  return undefined;\n}\n\nfunction safeStateEqual(a: string, b: string): boolean {\n  const ab = Buffer.from(a);\n  const bb = Buffer.from(b);\n  if (ab.length !== bb.length) return false;\n  return timingSafeEqual(ab, bb);\n}\n\nasync function createSession(userId: string): Promise<{ token: string; ttl: number }> {\n  // ... 你的 session 存储 ...\n  return { token: '...', ttl: 86400 };\n}\n```\n\n后端换码 helper 在另一个包 [`@aardwin/auth-server`](./README.md) 中。browser 包不再提供服务端入口。\n\n---\n## state 校验是你的责任\n\n浏览器 SDK 只负责设置 cookie，**不会替你校验 state**。你的回调路由必须：\n\n1. 从请求中读取名为 `aard_win_auth_state` 的 cookie。\n2. 用常量时间比较把它和 `?state=` 查询参数对比。\n3. 成功换码后**一次性消费**：删除 cookie。\n4. 不匹配时返回 `400`，不要继续调用 `exchangeCode()`。\n\n`<aardwin-auth>` 设置的 cookie 属性：\n\n| 属性 | 值 |\n|------|-----|\n| 名称 | `aard_win_auth_state` |\n| Path | `/` |\n| SameSite | `Lax` |\n| Max-Age | `1800` 秒（30 分钟） |\n| Domain | 省略 —— host-only cookie |\n\n因为 cookie 是 host-only，登录页与回调 URL **必须是同一 host**。\n\n---\n## `<aardwin-auth>` 属性参考表\n\n只有 `site-id` 必填。\n\n| 属性 | 必填 | 类型 | 说明 |\n|------|------|------|------|\n| `site-id` | 是 | `string` | 在 aardwin 控制台创建的站点 ID |\n| `i18n` | 否 | `'zh' \\| 'en'` | 显式指定语言；省略或非法值时按 `navigator.language` 检测，默认英文 |\n| `callback-path` | 否 | `string` | 显式 OAuth / email 回调路径；非空时 SDK 会在 bff 跳转 URL 中追加 `return_url`，空串/缺省时不发，bff 回退站点注册 callbackUrl |\n\nReact 类型补全可 opt-in：`import '@aardwin/auth-browser/react.d.ts';`（React 18 / React 19 / Next.js 15）。Preact、Solid 或 Vue JSX 消费者请自行添加 `JSX.IntrinsicElements` 声明。\n\nCDN / 零构建：\n\n```html\n<script src=\"/aardwin-auth.iife.js\"></script>\n<aardwin-auth site-id=\"YOUR_SITE_ID\"></aardwin-auth>\n```\n\n稳定版 IIFE CDN URL 将在首个稳定构建发布后公布。本地测试时，可把 `dist/aardwin-auth.iife.js` 产物复制到项目的 `public/` 目录。\n\n---\n## `<aardwin-account>` 组件\n\n`<aardwin-account>` 是自包含的内联账号管理 Web Component，在 Shadow DOM 内渲染。它**没有托管的管理页**，也**没有 `manage-url`**。\n\n它需要一个由服务端通过 `@aardwin/auth-server` 的 `createAccountHandoff()` 铸造的一次性账号接管码（handoff code）。该码一次性消费，60 秒过期，因此应在用户打开账号页时按需铸造，而不是在登录时铸造。\n\n```html\n<aardwin-account site-id=\"YOUR_SITE_ID\" code=\"ONE_TIME_HANDOFF_CODE\"></aardwin-account>\n```\n\n```ts\nimport { createAardwinClient } from '@aardwin/auth-server';\n\nconst client = createAardwinClient({\n  siteId: process.env.AARDWIN_SITE_ID,\n  clientSecret: process.env.AARDWIN_CLIENT_SECRET, // 仅服务端\n});\n\nconst { code, expiresIn } = await client.createAccountHandoff({ userId: session.userId });\n// 把 code 传给浏览器，渲染 <aardwin-account site-id code>\n```\n\n### `<aardwin-account>` 属性\n\n| 属性 | 必填 | 说明 |\n|------|------|------|\n| `site-id` | 是 | 站点 ID；决定可绑定的 provider |\n| `code` | 是 | `createAccountHandoff()` 返回的一次性账号接管码（handoff code）。如果 `sessionStorage` 已有 token，则不消费该 code |\n| `i18n` | 否 | `'zh' \\| 'en'`，默认按 `navigator.language` 检测 |\n\n### 生命周期\n\n1. 解析 access token：优先复用 `sessionStorage` 中缓存的 token（key 为 `aardwin_account_token`）；如无 token 且提供了新鲜 `code`，则调用 `POST /api/account/session {code}` 并保存返回的 `access_token`。\n2. 如果页面 URL 带有 `?pending` 和 `?provider`（从 OAuth provider 回调回来），则携带 Bearer token 调用 `POST /api/account/link/:provider/confirm {pending_token}`，清除 URL 参数，并重新渲染成功/失败反馈条。\n3. 否则渲染当前状态：`GET /api/account/identities`（Bearer）→ 已绑 identity 列表（每项带**解绑**按钮），以及剩余站点 provider 的绑定按钮（排除 `email` 和已绑 provider）。\n\n绑定与解绑行为：\n\n- **绑定**：`POST /api/account/link/:provider {return_url: <本页 URL>}`（Bearer）→ 整页跳转到 provider 的 authorize 端点。OAuth 回调回到同一页并带 `?pending=&provider=`，由步骤 2 处理。\n- **解绑**：在原生 `confirm()` 确认后调用 `DELETE /api/account/identities/:identityId`（Bearer）。\n- **token 过期（401）**：清除缓存 token，显示「会话已过期，请刷新页面」。下次加载 dashboard 时重新铸造 handoff code。\n\n### 错误事件\n\n缺少 `code` 且无缓存 token、建会话 / 拉取 / 绑定 / 解绑失败、以及 token 过期（401）时，都会派发 `aardwin:account-error` 事件（`bubbles: true, composed: true`）。`detail.phase` 区分来源：\n\n```ts\nel.addEventListener('aardwin:account-error', (e) => {\n  console.log(e.detail.phase, e.detail.message);\n});\n```\n\n---\n## Provider 路由表\n\n组件不会硬编码 provider URL。它调用 `GET /api/providers?site_id=`，接收每个 provider 对应的 `authorizeEndpoint`。平台自动路由：\n\n| Provider | 区域 bff 节点 |\n|----------|--------------|\n| 微信（WeChat） | 国内节点（domestic bff） |\n| Google、GitHub、Outlook、Discord | 海外节点（overseas bff） |\n| email | 由配置的 bff origin 提供 email 入口 |\n\n你不需要自己处理这些路由。按钮点击会跳转到 `${authorizeEndpoint}/authorize?site_id=&provider=&state=&lang=`（`email` 走专用入口）。换码始终走 API origin 的 `POST /api/oauth/token`。\n\n---\n## 接口契约\n\n| 接口 | 调用方 | 用途 |\n|------|--------|------|\n| `GET /api/providers?site_id=` | 浏览器 SDK → API | 拉取 provider 列表及 `authorizeEndpoint`；校验 Origin |\n| `GET {authorizeEndpoint}/authorize?site_id=&provider=&state=` | 浏览器 → 区域 bff | 渲染扫码页；302→callbackUrl `?code=&state=` |\n| `POST /api/oauth/token` | 你的后端 → API | `{ site_id, code, client_secret }` → 用户身份 |\n| `POST /api/account/session` | 浏览器 SDK → API | `{ code }` → `{ access_token }` |\n| `GET /api/account/identities` | 浏览器 SDK → API | Bearer token → 已绑 identity 列表 |\n| `POST /api/account/link/:provider` | 浏览器 SDK → API | Bearer + `{ return_url }` → 跳转绑定 provider |\n| `POST /api/account/link/:provider/confirm` | 浏览器 SDK → API | Bearer + `{ pending_token }` → 确认绑定 |\n| `DELETE /api/account/identities/:identityId` | 浏览器 SDK → API | Bearer → 解绑 identity |\n\n---\n## 问题排查\n\n### 按钮没有渲染\n\n打开浏览器 DevTools 的 Network 面板，检查 `GET /api/providers?site_id=...`：\n\n- 确认响应状态为 **200**；\n- 确认响应体中 `data.providers` 数组非空。数组为空表示该站点在控制台未配置任何 provider。\n\n### iframe 或嵌入式 webview 阻止跳转\n\n`<aardwin-auth>` 通过 `window.location.href` 做整页跳转。如果登录页被加载到限制顶层导航的 iframe 或应用内 webview 中，OAuth provider 可能拒绝流程或跳转失败。请把登录页放在顶层浏览上下文。\n\n### state 不匹配\n\n- 检查 `aard_win_auth_state` cookie 是否已设置（`Path=/`、`SameSite=Lax`、`Max-Age=1800`）。\n- 确认 `?state=` 查询参数与 cookie 值完全一致。\n- 确认登录页与回调 URL 在**同一 host**。cookie 是 host-only（无 `Domain` 属性），跨 host 回调无法读取。\n\n### code 已消费（`40001`）\n\n`exchangeCode()` 在一次性码无效、过期、已消费或不匹配时抛出 `code: 40001` 的 `AardwinError`。该码是一次性原子消费，**不要重试**。提示用户重新登录，`<aardwin-auth>` 的重定向流程会生成一个新 code。\n\n### 监听生命周期事件\n\n在 `<aardwin-auth>` 上：\n\n```ts\nconst el = document.querySelector('aardwin-auth');\nel.addEventListener('aardwin:error', (e) => console.log(e.detail));\n// { phase: 'render' | 'start', message: string, provider?: string }\nel.addEventListener('aardwin:ready', () => console.log('rendered'));\n```\n\n在 `<aardwin-account>` 上：\n\n```ts\nconst el = document.querySelector('aardwin-account');\nel.addEventListener('aardwin:account-error', (e) => console.log(e.detail.phase, e.detail.message));\n```\n\n---\n## 样式覆盖\n\n`<aardwin-auth>` 暴露 `part=\"button\"`：\n\n```css\naardwin-auth::part(button) {\n  border-radius: 999px;\n  background: #07c160;\n  color: #fff;\n}\n```\n\n`<aardwin-account>` 的绑定按钮同样暴露 `part=\"button\"`。\n\n---\n## 相关链接\n\n- [browser-sdk README](../browser-sdk/README.md)\n- [LOCALDEV.md](../browser-sdk/LOCALDEV.md)\n- [RELEASING.md](../RELEASING.md)\n- [https://aard.win](https://aard.win) —— 开发者控制台\n","readmeFilename":"README.zh-CN.md"}