Files
moeka-project/packages/stage-ui/package.json
T
ff7f64ace8 feat(server): add Steam OpenID sign-in and account linking plugin (#2226)
## Summary

Adds a self-contained better-auth plugin
(`server/apps/api/src/libs/auth-plugins/steam.ts`) implementing Steam
OpenID 2.0 sign-in, account linking, and callback verification via "dumb
mode".

Steam's web login is OpenID 2.0, not OAuth2/OIDC, so it cannot be
registered as a `socialProviders` entry, and better-auth has no plugin
hook for extending its OAuth2 endpoints with a non-OAuth2 protocol. The
plugin therefore adds the endpoints Steam's protocol needs: `POST
/sign-in/steam`, `POST /link/steam`, and `GET /steam/callback`.

- Callback verification uses OpenID "dumb mode"
(`openid.mode=check_authentication`): one extra round trip to Steam
instead of managing RSA association state.
- New sign-ups get a placeholder `<steamid64>@steam.placeholder.local`
with `emailVerified: true`, mirroring Apple Sign In's
`<sub>@apple.placeholder.local`.
- The plugin's request/query schemas use Zod; a `// NOTICE:` documents
that better-auth's OpenAPI generator is Zod-native. Steam verification
uses `ofetch`.
- Wires Steam into `apps/ui-server-auth` sign-in and profile "Connected
accounts", plus the shared `OAuthProvider` / `defaultSignInProviders` in
`packages/stage-ui`.
- Linking routes through `/link/steam` via the client's `$fetch`;
unlinking needs no special-casing (`/unlink-account` already takes a
free-form `providerId`).

No Steam Web API key is required for this browser-based flow.

We intentionally do not depend on community Steam packages (e.g.
`better-auth-steam`) or the still-open upstream draft
([better-auth#4877](https://github.com/better-auth/better-auth/pull/4877)).
Steam never returns an email, and we need sign-up that does not ask the
user for one plus first-class account linking; the available options
either require an email at sign-in, lack linking, or are abandoned /
blocked — shipping a small in-tree plugin is the safer auth dependency
for this requirement.

## Test plan

- [x] `pnpm exec vitest run
server/apps/api/src/libs/auth-plugins/steam.test.ts` — 6/6 passing
- [x] `pnpm -F @proj-airi/ui-server-auth exec vitest run` — 32/32
passing
- [x] `pnpm -F @proj-airi/stage-ui exec vitest run
src/libs/steam-auth-client.test.ts
src/composables/use-linked-accounts.test.ts` — 5/5 passing
- [x] `pnpm -F @proj-airi/api-server typecheck`
- [x] `pnpm -F @proj-airi/ui-server-auth typecheck`
- [x] `pnpm -F @proj-airi/stage-ui typecheck`

## Follow-ups

- Desktop Steam ticket sign-in (top of this stack): silent startup
ticket exchange for Steam builds; the server resolves or creates the
AIRI user for the verified SteamID before issuing an OIDC code.
- Steam persona name/avatar via `GetPlayerSummaries` inside the plugin,
if display names beyond `Steam User <id>` are wanted.

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-08-05 23:57:55 +08:00

220 lines
8.1 KiB
JSON

{
"name": "@proj-airi/stage-ui",
"type": "module",
"version": "0.11.3",
"private": true,
"description": "Shared core for stage",
"author": {
"name": "Moeru AI Project AIRI Team",
"email": "airi@moeru.ai",
"url": "https://github.com/moeru-ai"
},
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/moeru-ai/airi.git",
"directory": "packages/stage-ui"
},
"exports": {
".": "./src/components/index.ts",
"./components/scenarios/chat": "./src/components/scenarios/chat/index.ts",
"./components/scenarios/settings/model-settings": "./src/components/scenarios/settings/model-settings/index.ts",
"./components/scenes": "./src/components/scenes/index.ts",
"./components/*": "./src/components/*.ts",
"./components": "./src/components/index.ts",
"./composables/*": "./src/composables/*.ts",
"./composables": "./src/composables/index.ts",
"./constants/*": "./src/constants/*.ts",
"./constants": "./src/constants/index.ts",
"./directives/*": "./src/directives/*.ts",
"./libs/inference/adapters/*": "./src/libs/inference/adapters/*.ts",
"./libs/inference": "./src/libs/inference/index.ts",
"./libs/*": "./src/libs/*.ts",
"./libs": "./src/libs/index.ts",
"./services/*": "./src/services/*.ts",
"./tools/mcp": "./src/tools/mcp.ts",
"./stores/providers/aliyun": "./src/stores/providers/aliyun/index.ts",
"./stores/analytics": "./src/stores/analytics/index.ts",
"./stores/analytics/client": "./src/stores/analytics/client.ts",
"./stores/analytics/posthog": "./src/stores/analytics/posthog.ts",
"./stores/analytics/privacy-policy": "./src/stores/analytics/privacy-policy.ts",
"./stores/character": "./src/stores/character/index.ts",
"./stores/character/orchestrator/spark-notify-agent": "./src/stores/character/orchestrator/spark-notify-agent.ts",
"./stores/settings/analytics": "./src/stores/settings/analytics.ts",
"./stores/settings": "./src/stores/settings/index.ts",
"./stores/modules/vision": "./src/stores/modules/vision/index.ts",
"./stores/mcp-tool-bridge": "./src/stores/mcp-tool-bridge.ts",
"./stores/*": "./src/stores/*.ts",
"./stores": "./src/stores/index.ts",
"./workers/vad": "./src/workers/vad/index.ts",
"./workers/*": "./src/workers/*.ts",
"./workers": "./src/workers/index.ts",
"./utils": "./src/utils/index.ts",
"./utils/tts": "./src/utils/tts.ts",
"./types/*": "./src/types/*.ts",
"./types": "./src/types/index.ts"
},
"scripts": {
"typecheck": "vue-tsc --noEmit",
"story:dev": "histoire dev",
"story:build": "histoire build",
"story:build-base": "histoire build --base /ui",
"story:preview": "histoire preview",
"build": "echo \"No build step required\"",
"test": "vitest",
"test:run": "vitest run"
},
"dependencies": {
"@better-fetch/fetch": "catalog:",
"@date-fns/utc": "catalog:",
"@formkit/auto-animate": "catalog:",
"@huggingface/transformers": "catalog:",
"@moeru/eventa": "catalog:",
"@moeru/std": "catalog:",
"@opentelemetry/api": "catalog:",
"@opentelemetry/core": "catalog:",
"@opentelemetry/sdk-trace-base": "catalog:",
"@pinia/colada": "catalog:",
"@proj-airi/audio": "workspace:^",
"@proj-airi/ccc": "workspace:^",
"@proj-airi/chromatic": "catalog:",
"@proj-airi/core-agent": "workspace:^",
"@proj-airi/core-character": "workspace:^",
"@proj-airi/drizzle-duckdb-wasm": "catalog:",
"@proj-airi/font-chillroundm": "workspace:^",
"@proj-airi/font-cjkfonts-allseto": "workspace:^",
"@proj-airi/font-xiaolai": "workspace:^",
"@proj-airi/i18n": "workspace:^",
"@proj-airi/model-driver-lipsync": "workspace:^",
"@proj-airi/pipelines-audio": "workspace:^",
"@proj-airi/server-sdk": "workspace:^",
"@proj-airi/server-sdk-shared": "workspace:^",
"@proj-airi/stage-shared": "workspace:^",
"@proj-airi/stage-ui-live2d": "workspace:^",
"@proj-airi/stage-ui-mmd": "workspace:^",
"@proj-airi/stage-ui-spine": "workspace:^",
"@proj-airi/stage-ui-tachie": "workspace:^",
"@proj-airi/stage-ui-three": "workspace:^",
"@proj-airi/ui": "workspace:^",
"@ricky0123/vad-web": "catalog:",
"@shikijs/rehype": "catalog:",
"@shopify/draggable": "catalog:",
"@vueuse/core": "catalog:",
"@vueuse/motion": "catalog:",
"@vueuse/shared": "catalog:",
"@xsai-ext/providers": "catalog:",
"@xsai-transformers/embed": "catalog:",
"@xsai-transformers/shared": "catalog:",
"@xsai/embed": "catalog:",
"@xsai/generate-speech": "catalog:",
"@xsai/generate-text": "catalog:",
"@xsai/generate-transcription": "catalog:",
"@xsai/model": "catalog:",
"@xsai/shared": "catalog:",
"@xsai/shared-chat": "catalog:",
"@xsai/stream-text": "catalog:",
"@xsai/stream-transcription": "catalog:",
"@xsai/tool": "catalog:",
"@xsai/utils-chat": "catalog:",
"animejs": "catalog:",
"async-mutex": "catalog:",
"better-auth": "catalog:",
"culori": "catalog:",
"d3": "catalog:",
"date-fns": "catalog:",
"dompurify": "catalog:",
"embla-carousel-autoplay": "catalog:",
"embla-carousel-vue": "catalog:",
"es-toolkit": "catalog:",
"gpuu": "catalog:",
"hono": "catalog:",
"html2canvas": "catalog:",
"idb-keyval": "catalog:",
"jszip": "catalog:",
"kokoro-js": "catalog:",
"localforage": "catalog:",
"mediabunny": "catalog:",
"nanoid": "catalog:",
"node-vibrant": "catalog:",
"ofetch": "catalog:",
"pinia": "catalog:",
"posthog-js": "catalog:",
"rehype-katex": "catalog:",
"rehype-parse": "catalog:",
"rehype-stringify": "catalog:",
"reka-ui": "catalog:",
"remark-math": "catalog:",
"remark-parse": "catalog:",
"remark-rehype": "catalog:",
"shiki": "catalog:",
"splitpanes": "catalog:",
"unified": "catalog:",
"unist-builder": "catalog:",
"unist-util-visit": "catalog:",
"unspeech": "catalog:xsai",
"unstorage": "catalog:",
"uuid": "catalog:",
"valibot": "catalog:",
"vaul-vue": "catalog:",
"vue-i18n": "catalog:",
"vue-router": "catalog:",
"vue-sonner": "catalog:",
"web-haptics": "catalog:",
"xast-util-to-xml": "catalog:",
"xastscript": "catalog:",
"xsschema": "catalog:",
"zod": "catalog:"
},
"devDependencies": {
"@fontsource-variable/comfortaa": "catalog:",
"@fontsource-variable/dm-sans": "catalog:",
"@fontsource-variable/jura": "catalog:",
"@fontsource-variable/nunito": "catalog:",
"@fontsource-variable/quicksand": "catalog:",
"@fontsource-variable/urbanist": "catalog:",
"@fontsource/dm-mono": "catalog:",
"@fontsource/dm-serif-display": "catalog:",
"@fontsource/gugi": "catalog:",
"@fontsource/kiwi-maru": "catalog:",
"@fontsource/m-plus-rounded-1c": "catalog:",
"@histoire/plugin-vue": "catalog:",
"@iconify-json/carbon": "catalog:",
"@iconify-json/eos-icons": "catalog:",
"@iconify-json/lucide": "catalog:",
"@iconify-json/mingcute": "catalog:",
"@iconify-json/ph": "catalog:",
"@iconify-json/simple-icons": "catalog:",
"@iconify-json/solar": "catalog:",
"@iconify-json/svg-spinners": "catalog:",
"@iconify-json/vscode-icons": "catalog:",
"@moeru/std": "catalog:",
"@pinia/testing": "catalog:",
"@proj-airi/api-server": "workspace:*",
"@proj-airi/lobe-icons": "catalog:",
"@proj-airi/stream-kit": "workspace:*",
"@types/audioworklet": "catalog:",
"@types/culori": "catalog:",
"@types/d3": "catalog:",
"@types/hast": "catalog:",
"@types/splitpanes": "catalog:",
"@types/unist": "catalog:",
"@types/ws": "catalog:",
"@unocss/reset": "catalog:",
"@vitejs/plugin-vue": "catalog:",
"@webgpu/types": "catalog:",
"clustr": "catalog:",
"csstype": "catalog:",
"histoire": "catalog:",
"is-network-error": "catalog:",
"unocss": "catalog:",
"unplugin-basemove": "catalog:",
"unplugin-info": "catalog:",
"unplugin-yaml": "catalog:",
"vite": "catalog:",
"vitest-browser-vue": "catalog:",
"vue": "catalog:",
"vue-tsc": "catalog:",
"ws": "catalog:"
}
}