[{"data":1,"prerenderedAt":30},["ShallowReactive",2],{"doc:\u002Fcontracts\u002Foauth\u002F10-app-directory":3},{"title":4,"route":5,"toc":6,"segments":25,"source":29},"10 — 应用目录（生态一键登录 \u002F App Directory）","\u002Fcontracts\u002Foauth\u002F10-app-directory",[7,11,14,17,20,23],{"id":8,"text":9,"depth":10},"1-模型-每个-oauth-client-一个-opt-in-开关","1. 模型：每个 OAuth client 一个 opt-in 开关",2,{"id":12,"text":13,"depth":10},"2-端点-get-api-v1-oauth-ecosystem-公开-可缓存","2. 端点：`GET \u002Fapi\u002Fv1\u002Foauth\u002Fecosystem`（公开，可缓存）",{"id":15,"text":16,"depth":10},"3-下游接入-展示-生态-strip","3. 下游接入（展示「生态 strip」）",{"id":18,"text":19,"depth":10},"4-安全-隐私","4. 安全 \u002F 隐私",{"id":21,"text":22,"depth":10},"5-实施阶段","5. 实施阶段",{"id":24,"text":24,"depth":10},"变更摘要",[26],{"type":27,"html":28},"html","\u003Ch1 id=\"10-应用目录-生态一键登录-app-directory\" tabindex=\"-1\">10 — 应用目录（生态一键登录 \u002F App Directory）\u003C\u002Fh1>\n\u003Cblockquote>\n\u003Cp>🚧 \u003Cstrong>状态：后端只读端点 + 数据模型已实现；前端展示分阶段接入。\u003C\u002Fstrong> 让用户在注册\u002F登录时\n看到「拥有一个鲲 Galgame 账号，即可一键登录以下网站」——按 OAuth client 的开关（opt-in）\n列出生态内的站点。本文是下游（kungal \u002F moyu \u002F wiki）展示这条「生态 strip」的跨服务契约。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Cp>这对应业界成熟的 \u003Cstrong>App Launcher \u002F app directory\u003C\u002Fstrong> 模式（Salesforce、Cloudflare Access、\nMicrosoft 365 的「九宫格」），各家都是\u003Cstrong>私有实现 + 每个 app 一个可见性开关\u003C\u002Fstrong>——OAuth\u002FOIDC\n\u003Cstrong>没有\u003C\u002Fstrong>公开列举 client 的标准，所以这是产品元数据，不是协议缺口。本文的形态是「注册\u002F登录\n页的生态价值展示」，数据与 App Launcher 同源。\u003C\u002Fp>\n\u003Chr>\n\u003Ch2 id=\"1-模型-每个-oauth-client-一个-opt-in-开关\" tabindex=\"-1\">1. 模型：每个 OAuth client 一个 opt-in 开关\u003C\u002Fh2>\n\u003Cp>\u003Ccode>oauth_clients\u003C\u002Fcode> 新增（管理员在 OAuth 后台「OAuth 客户端」里配置）：\u003C\u002Fp>\n\u003Cdiv class=\"kun-table-wrap\">\u003Ctable>\u003Cthead>\n\u003Ctr>\n\u003Cth>字段\u003C\u002Fth>\n\u003Cth>含义\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ccode>listed\u003C\u002Fcode> (bool, 默认 \u003Ccode>false\u003C\u002Fcode>)\u003C\u002Ftd>\n\u003Ctd>是否出现在公开应用目录里。\u003Cstrong>opt-in\u003C\u002Fstrong>——内部\u002F管理类 client 默认不出现\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>logo_url\u003C\u002Fcode> (string)\u003C\u002Ftd>\n\u003Ctd>站点 logo（strip \u002F tile 用）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>tagline\u003C\u002Fcode> (string)\u003C\u002Ftd>\n\u003Ctd>一句话简介，如「Galgame 论坛」\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>display_order\u003C\u002Fcode> (int, 默认 0)\u003C\u002Ftd>\n\u003Ctd>排序（小在前，再按 name）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\u003C\u002Fdiv>\u003Cblockquote>\n\u003Cp>列表内容 = 所有 \u003Ccode>listed=true\u003C\u002Fcode> 的 client。复用已有的 \u003Ccode>name\u003C\u002Fcode> 与其所属 Site 的 \u003Ccode>domain\u003C\u002Fcode>。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Ch2 id=\"2-端点-get-api-v1-oauth-ecosystem-公开-可缓存\" tabindex=\"-1\">2. 端点：\u003Ccode>GET \u002Fapi\u002Fv1\u002Foauth\u002Fecosystem\u003C\u002Fcode>（公开，可缓存）\u003C\u002Fh2>\n\u003Cp>无需鉴权——这是\u003Cstrong>公开营销元数据\u003C\u002Fstrong>，只暴露展示字段，\u003Cstrong>不含\u003C\u002Fstrong> secret \u002F redirect_uris \u002F scope \u002F\n配额。建议前端缓存（内容很少变）。\u003C\u002Fp>\n\u003Cp>响应（\u003Ccode>data\u003C\u002Fcode>）：\u003C\u002Fp>\n\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\">\u003Ccode class=\"language-json\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">{\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">  \"apps\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: [\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    { \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\"name\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"鲲Galgame论坛\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\"site_domain\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"www.kungal.com\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\"logo_url\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"https:\u002F\u002F…\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\"tagline\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"Galgame 论坛\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\"auto_consent\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">true\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> },\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    { \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\"name\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"摸鱼galgame\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\"site_domain\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"moyu.moe\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\"logo_url\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"https:\u002F\u002F…\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\"tagline\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"Galgame 补丁站\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\"auto_consent\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">true\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">  ]\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">}\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>只返回 \u003Ccode>listed=true\u003C\u002Fcode> 的 client；排序 = \u003Cstrong>官方（\u003Ccode>auto_consent=true\u003C\u002Fcode>）在前\u003C\u002Fstrong>，再 \u003Ccode>display_order\u003C\u002Fcode>，再 \u003Ccode>name\u003C\u002Fcode>。\u003Ccode>auto_consent\u003C\u002Fcode> 复用已有的第一方标志（见 doc 05）：\u003Ccode>true\u003C\u002Fcode> = 第一方「官方」站点，下游据此展示「官方」标识并排在前面。\u003C\u002Fp>\n\u003Ch2 id=\"3-下游接入-展示-生态-strip\" tabindex=\"-1\">3. 下游接入（展示「生态 strip」）\u003C\u002Fh2>\n\u003Cp>同一个端点，三处复用：\u003C\u002Fp>\n\u003Col>\n\u003Cli>\u003Cstrong>OAuth 注册页\u003C\u002Fstrong>（\u003Ccode>oauth.kungal.com\u003C\u002Fcode> 自身，apps\u002Fweb）：注册卡片下方一条 logo strip——\n「拥有鲲 Galgame 账号，一键登录以下网站」。同源，直接调用。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>OAuth 授权页\u003C\u002Fstrong>（某个 client 的登录流程中）：高亮\u003Cstrong>当前正在登录的 client\u003C\u002Fstrong>，其余列为\n「也可用此账号登录：…」，把同意\u002F登录这一刻变成价值展示。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>下游登录\u002F注册 modal\u003C\u002Fstrong>（kungal \u002F moyu \u002F wiki）：\u003Ccode>fetch https:\u002F\u002Foauth.kungal.com\u002Fapi\u002Fv1\u002Foauth\u002Fecosystem\u003C\u002Fcode>\n渲染同样的 strip。\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp>\u003Cstrong>CORS（下游跨域读取必看）\u003C\u002Fstrong>：该端点是公开 GET，但下游浏览器跨域 \u003Ccode>fetch\u003C\u002Fcode> 时，\u003Cstrong>消费方的\norigin 必须在 OP 的 CORS 白名单内\u003C\u002Fstrong>（\u003Ccode>internal\u002Fmiddleware\u002Fcors.go\u003C\u002Fcode>：目前含 \u003Ccode>kungal.com\u003C\u002Fcode>、\n\u003Ccode>moyu.moe\u003C\u002Fcode> + 开发 origin）。接入子域（\u003Ccode>www.kungal.com\u003C\u002Fcode> \u002F \u003Ccode>wiki.kungal.com\u003C\u002Fcode>）时需把其 origin\n加进白名单，否则被 CORS 拦截。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>UX 建议\u003C\u002Fstrong>：logo + 名称、官方站点（\u003Ccode>auto_consent\u003C\u002Fcode>）显示 primary「官方」chip 并排在前、缓存\n端点；纯展示，不触碰认证流。参考实现：apps\u002Fweb 注册页（折叠为一排圆形 icon + 点击展开列表）、\nmoyu 登录 modal。\u003C\u002Fp>\n\u003Ch2 id=\"4-安全-隐私\" tabindex=\"-1\">4. 安全 \u002F 隐私\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>端点\u003Cstrong>只\u003C\u002Fstrong>返回 opt-in client 的公开展示字段——无 secret \u002F 内部布局，可安全公开 + 缓存。\u003C\u002Fli>\n\u003Cli>\u003Ccode>listed\u003C\u002Fcode> 默认 \u003Ccode>false\u003C\u002Fcode>（fail-closed）：内部 \u002F 管理 client 不会意外出现在公开目录。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>不在认证路径上\u003C\u002Fstrong>：纯只读元数据，不改 token \u002F session \u002F 重定向，安全面几乎为零。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2 id=\"5-实施阶段\" tabindex=\"-1\">5. 实施阶段\u003C\u002Fh2>\n\u003Col>\n\u003Cli>✅ 后端：\u003Ccode>oauth_clients\u003C\u002Fcode> 加 \u003Ccode>listed\u003C\u002Fcode>\u002F\u003Ccode>logo_url\u003C\u002Fcode>\u002F\u003Ccode>tagline\u003C\u002Fcode>\u002F\u003Ccode>display_order\u003C\u002Fcode> + \u003Ccode>GET \u002Foauth\u002Fecosystem\u003C\u002Fcode>（公开、按序）。（DB 迁移 \u003Ccode>kun_galgame_infra\u003C\u002Fcode>：\u003Ccode>cmd\u002Fmigrate\u003C\u002Fcode>）\u003C\u002Fli>\n\u003Cli>🚧 OAuth 后台：客户端创建\u002F编辑表单加这四个字段（管理员配置）。\u003C\u002Fli>\n\u003Cli>🚧 OAuth 注册页 strip + 授权页「当前 client 高亮」。\u003C\u002Fli>\n\u003Cli>🚧 下游 kungal \u002F moyu \u002F wiki 登录\u002F注册 modal 接入 strip（+ 按需扩 CORS 白名单）。\u003C\u002Fli>\n\u003Cli>⏳（可选）登录后的 \u003Cstrong>App Launcher\u003C\u002Fstrong>（账号中心九宫格）——同源数据，后续可叠加。\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Chr>\n\u003Ch2 id=\"变更摘要\" tabindex=\"-1\">变更摘要\u003C\u002Fh2>\n\u003Cblockquote>\n\u003Cp>\u003Cstrong>2026-06-25（设计 + 后端）\u003C\u002Fstrong>：新增本文。生态「一键登录」应用目录：\u003Ccode>oauth_clients\u003C\u002Fcode> 加\nopt-in \u003Ccode>listed\u003C\u002Fcode> + \u003Ccode>logo_url\u003C\u002Fcode>\u002F\u003Ccode>tagline\u003C\u002Fcode>\u002F\u003Ccode>display_order\u003C\u002Fcode>；公开只读 \u003Ccode>GET \u002Foauth\u002Fecosystem\u003C\u002Fcode>\n返回 \u003Ccode>listed\u003C\u002Fcode> client 的展示字段。前端 strip（OAuth 注册\u002F授权页 + 下游 modal）分阶段接入。\n对应业界 App Launcher 模式（无 OAuth 标准，属产品元数据）。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n","kun-galgame-infra\u002Fdocs\u002Fintegration\u002Foauth\u002F10-app-directory.md",1783514297233]