[{"data":1,"prerenderedAt":43},["ShallowReactive",2],{"doc:\u002Fcontracts\u002Foauth\u002F02-user-profile":3},{"title":4,"route":5,"toc":6,"segments":38,"source":42},"用户自助资料管理","\u002Fcontracts\u002Foauth\u002F02-user-profile",[7,11,15,18,20,23,26,29,32,35],{"id":8,"text":9,"depth":10},"身份操作-vs-展示操作","身份操作 vs 展示操作",2,{"id":12,"text":13,"depth":14},"身份层-oauth-profile-专用-下游用跳转模式","身份层（OAuth profile 专用，下游用跳转模式）",3,{"id":16,"text":17,"depth":14},"展示层-任何接入站都可以代理-自己实现-ui","展示层（任何接入站都可以代理 \u002F 自己实现 UI）",{"id":19,"text":19,"depth":10},"端点速览",{"id":21,"text":22,"depth":10},"get-auth-me","GET \u002Fauth\u002Fme",{"id":24,"text":25,"depth":10},"patch-auth-me","PATCH \u002Fauth\u002Fme",{"id":27,"text":28,"depth":10},"post-auth-me-avatar","POST \u002Fauth\u002Fme\u002Favatar",{"id":30,"text":31,"depth":10},"post-auth-email-send-code","POST \u002Fauth\u002Femail\u002Fsend-code",{"id":33,"text":34,"depth":10},"put-auth-email","PUT \u002Fauth\u002Femail",{"id":36,"text":37,"depth":10},"put-auth-password","PUT \u002Fauth\u002Fpassword",[39],{"type":40,"html":41},"html","\u003Ch1 id=\"用户自助资料管理\" tabindex=\"-1\">用户自助资料管理\u003C\u002Fh1>\n\u003Cp>返回 \u003Ca href=\"\u002Fcontracts\u002Foauth\">README\u003C\u002Fa>\u003C\u002Fp>\n\u003Cblockquote>\n\u003Cp>\u003Cstrong>重要 — 身份层操作必须在 OAuth profile 完成，下游禁止代理\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cp>\u003Cstrong>改邮箱、改密码、注销账号、管理登录设备\u003C\u002Fstrong>这类操作\u003Cstrong>只能\u003C\u002Fstrong>走 OAuth 自己的前端（https:\u002F\u002Foauth.kungal.com\u002Fprofile）。\u003Cstrong>kungal \u002F moyu \u002F wiki 都不要在自己前端实现这些 UI\u003C\u002Fstrong>，即使技术上可以代理 JWT。详细分类见下表。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Ch2 id=\"身份操作-vs-展示操作\" tabindex=\"-1\">身份操作 vs 展示操作\u003C\u002Fh2>\n\u003Cp>OAuth 的用户自助 API 在设计上分两层。下游接入时\u003Cstrong>不要\u003C\u002Fstrong>把所有写操作都拉到自己前端做——身份层修改强制走 OAuth profile，展示层可以站内提供 UI。\u003C\u002Fp>\n\u003Ch3 id=\"身份层-oauth-profile-专用-下游用跳转模式\" tabindex=\"-1\">身份层（OAuth profile 专用，下游用跳转模式）\u003C\u002Fh3>\n\u003Cp>凡是涉及&quot;账号所有权&quot;的操作，必须由 OAuth 自己的前端承担。原因：流程敏感、需要集中审计、未来要加 2FA \u002F 异地通知 \u002F 安全日志时只改一处。\u003C\u002Fp>\n\u003Cdiv class=\"kun-table-wrap\">\u003Ctable>\u003Cthead>\n\u003Ctr>\n\u003Cth>操作\u003C\u002Fth>\n\u003Cth>对应端点\u003C\u002Fth>\n\u003Cth>为什么必须 OAuth\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>新用户注册\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>\u003Ca href=\"\u002Fcontracts\u002Foauth\u002F05-registration#post-authregister\">POST \u002Fauth\u002Fregister\u003C\u002Fa>\u003C\u002Ftd>\n\u003Ctd>单点身份写入；防 N 套限流 \u002F 邮箱去重逻辑；未来加 passkey \u002F 第三方登录时零下游成本 — 详见 \u003Ca href=\"\u002Fcontracts\u002Foauth\u002F05-registration\">05-registration.md\u003C\u002Fa>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>改邮箱\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>\u003Ca href=\"#post-authemailsend-code\">POST \u002Fauth\u002Femail\u002Fsend-code\u003C\u002Fa> + \u003Ca href=\"#put-authemail\">PUT \u002Fauth\u002Femail\u003C\u002Fa>\u003C\u002Ftd>\n\u003Ctd>验证码寄到\u003Cstrong>旧邮箱\u003C\u002Fstrong>，防 JWT 被窃后被攻击者改邮箱锁出。流程敏感，必须集中审计\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>改密码\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>\u003Ca href=\"#put-authpassword\">PUT \u002Fauth\u002Fpassword\u003C\u002Fa>\u003C\u002Ftd>\n\u003Ctd>需老密码或重置 token，账号所有权操作\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>重设密码（忘记密码）\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>POST \u002Fauth\u002Fpassword\u002Fforgot\u003C\u002Fcode> + \u003Ccode>POST \u002Fauth\u002Fpassword\u002Freset\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>匿名流程，发邮件 + 一次性 token\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>（未来）启用 \u002F 关闭 2FA\u003C\u002Ftd>\n\u003Ctd>待定\u003C\u002Ftd>\n\u003Ctd>强身份验证步骤\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>（未来）查看登录历史 \u002F 主动下线设备\u003C\u002Ftd>\n\u003Ctd>待定\u003C\u002Ftd>\n\u003Ctd>跨站点 session 管理\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>（未来）绑定 \u002F 解绑第三方登录\u003C\u002Ftd>\n\u003Ctd>待定\u003C\u002Ftd>\n\u003Ctd>身份联邦\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>（未来）注销账号\u003C\u002Ftd>\n\u003Ctd>待定\u003C\u002Ftd>\n\u003Ctd>不可逆，需冷静期 + 二次验证\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>（未来）管理已授权 OAuth Client（&quot;撤销 kungal 访问权限&quot;）\u003C\u002Ftd>\n\u003Ctd>待定\u003C\u002Ftd>\n\u003Ctd>OAuth 元操作；下游无权也无法管理别人的授权\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\u003C\u002Fdiv>\u003Cp>\u003Cstrong>下游正确做法\u003C\u002Fstrong>：在账号设置页放一个&quot;跳转到 OAuth 账号中心&quot;按钮，附带 \u003Ccode>return\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-vue\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">&#x3C;\u003C\u002Fspan>\u003Cspan style=\"color:#22863A;--shiki-dark:#85E89D\">NuxtLink\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">  :\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">to\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">=\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"`https:\u002F\u002Foauth.kungal.com\u002Fprofile?return=${\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">encodeURIComponent\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">(\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">currentUrl\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">)\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">}`\"\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">  external\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">  class\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">=\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"...\"\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\">  修改邮箱 \u002F 密码\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">  &#x3C;Icon name=\"lucide:external-link\" class=\"ml-1 size-3\" \u002F>\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">&#x3C;\u002F\u003C\u002Fspan>\u003Cspan style=\"color:#22863A;--shiki-dark:#85E89D\">NuxtLink\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>下面那些端点的文档保留为\u003Cstrong>完整性\u003C\u002Fstrong>目的——OAuth 自己的前端 (apps\u002Fweb) 是唯一应该调用它们的客户端。\u003Cstrong>kungal \u002F moyu \u002F wiki 前端不要直接 fetch 这些路径\u003C\u002Fstrong>。\u003C\u002Fp>\n\u003Ch3 id=\"展示层-任何接入站都可以代理-自己实现-ui\" tabindex=\"-1\">展示层（任何接入站都可以代理 \u002F 自己实现 UI）\u003C\u002Fh3>\n\u003Cdiv class=\"kun-table-wrap\">\u003Ctable>\u003Cthead>\n\u003Ctr>\n\u003Cth>操作\u003C\u002Fth>\n\u003Cth>端点\u003C\u002Fth>\n\u003Cth>说明\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>改显示名\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>PATCH \u002Fauth\u002Fme { name }\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>全局唯一，OAuth 后端拒重\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>改头像（URL 或 hash）\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>PATCH \u002Fauth\u002Fme\u003C\u002Fcode> 或 \u003Ccode>POST \u002Fauth\u002Fme\u002Favatar\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>后者一步走完上传 + 写库\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>改简介\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>PATCH \u002Fauth\u002Fme { bio }\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>纯展示，无安全性\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\u003C\u002Fdiv>\u003Cp>这些可以站内提供 UI（&quot;代理模式&quot;），也可以跳转（&quot;跳转模式&quot;，更一致），任选。\u003Ccode>PATCH \u002Fauth\u002Fme\u003C\u002Fcode> 和 \u003Ccode>POST \u002Fauth\u002Fme\u002Favatar\u003C\u002Fcode> 要求带终端用户 JWT，\u003Cstrong>不是\u003C\u002Fstrong> OAuth Client Basic Auth。\u003C\u002Fp>\n\u003Chr>\n\u003Ch2 id=\"端点速览\" tabindex=\"-1\">端点速览\u003C\u002Fh2>\n\u003Cdiv class=\"kun-table-wrap\">\u003Ctable>\u003Cthead>\n\u003Ctr>\n\u003Cth>端点\u003C\u002Fth>\n\u003Cth>方法\u003C\u002Fth>\n\u003Cth>层级\u003C\u002Fth>\n\u003Cth>鉴权\u003C\u002Fth>\n\u003Cth>用途\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ccode>\u002Fauth\u002Fme\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>GET\u003C\u002Ftd>\n\u003Ctd>—\u003C\u002Ftd>\n\u003Ctd>Bearer\u003C\u002Ftd>\n\u003Ctd>读自己完整资料\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>\u002Fauth\u002Fme\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>PATCH\u003C\u002Ftd>\n\u003Ctd>展示\u003C\u002Ftd>\n\u003Ctd>Bearer\u003C\u002Ftd>\n\u003Ctd>改 name \u002F avatar \u002F bio\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>\u002Fauth\u002Fme\u002Favatar\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>POST\u003C\u002Ftd>\n\u003Ctd>展示\u003C\u002Ftd>\n\u003Ctd>Bearer\u003C\u002Ftd>\n\u003Ctd>上传头像 multipart\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>\u002Fauth\u002Femail\u002Fsend-code\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>POST\u003C\u002Ftd>\n\u003Ctd>身份\u003C\u002Ftd>\n\u003Ctd>Bearer（仅 OAuth 前端）\u003C\u002Ftd>\n\u003Ctd>发送邮箱变更验证码到\u003Cstrong>旧\u003C\u002Fstrong>邮箱\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>\u002Fauth\u002Femail\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>PUT\u003C\u002Ftd>\n\u003Ctd>身份\u003C\u002Ftd>\n\u003Ctd>Bearer（仅 OAuth 前端）\u003C\u002Ftd>\n\u003Ctd>用验证码确认改邮箱\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>\u002Fauth\u002Fpassword\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>PUT\u003C\u002Ftd>\n\u003Ctd>身份\u003C\u002Ftd>\n\u003Ctd>Bearer（仅 OAuth 前端）\u003C\u002Ftd>\n\u003Ctd>改密码（需旧密码）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\u003C\u002Fdiv>\u003Chr>\n\u003Ch2 id=\"get-auth-me\" tabindex=\"-1\">GET \u002Fauth\u002Fme\u003C\u002Fh2>\n\u003Cp>获取当前登录用户的完整资料。与 \u003Ccode>\u002Foauth\u002Fuserinfo\u003C\u002Fcode> 的区别：\u003Ccode>\u002Fauth\u002Fme\u003C\u002Fcode> 是面向 OAuth 自己前端的内部端点，无 scope 过滤、字段更全（含 moemoepoint）。下游服务若用得着也可以调。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>请求头\u003C\u002Fstrong>：\u003Ccode>Authorization: Bearer &lt;access_token&gt;\u003C\u002Fcode>\u003C\u002Fp>\n\u003Cp>\u003Cstrong>成功响应\u003C\u002Fstrong>：\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\">  \"code\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">0\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">  \"data\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">    \"uuid\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"550e8400-e29b-...\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\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\">\"kun\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">    \"email\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"kun@kungal.com\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">    \"avatar\"\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>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">    \"bio\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"...\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">    \"moemoepoint\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">1234\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">    \"status\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">0\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">    \"roles\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: [\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"user\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"admin\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">],\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">    \"created_at\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"2024-01-01T00:00:00Z\"\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\u003Chr>\n\u003Ch2 id=\"patch-auth-me\" tabindex=\"-1\">PATCH \u002Fauth\u002Fme\u003C\u002Fh2>\n\u003Cp>修改当前登录用户的展示字段。所有字段都可选，不传的字段保持不变。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>请求头\u003C\u002Fstrong>：\u003Ccode>Authorization: Bearer &lt;access_token&gt;\u003C\u002Fcode>\u003C\u002Fp>\n\u003Cp>\u003Cstrong>请求体\u003C\u002Fstrong>：\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\">  \"name\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"newname\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">  \"avatar\"\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>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">  \"avatar_image_hash\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"abc123...\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">  \"bio\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"新简介\"\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">}\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cdiv class=\"kun-table-wrap\">\u003Ctable>\u003Cthead>\n\u003Ctr>\n\u003Cth>字段\u003C\u002Fth>\n\u003Cth>类型\u003C\u002Fth>\n\u003Cth>约束\u003C\u002Fth>\n\u003Cth>说明\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>name\u003C\u002Ftd>\n\u003Ctd>string?\u003C\u002Ftd>\n\u003Ctd>1..17 字符；全局唯一；允许 Unicode 字母\u002F数字 + \u003Ccode>!~_@#$%^&amp;*()+=-\u003C\u002Fcode>，禁止零宽 \u002F 不可见空白等 50+ 种字符（同注册规则，详见 \u003Ca href=\"\u002Fcontracts\u002Foauth\u002F05-registration#post-authregistersend-code\">05-registration.md\u003C\u002Fa>）\u003C\u002Ftd>\n\u003Ctd>用户名\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>avatar\u003C\u002Ftd>\n\u003Ctd>string?\u003C\u002Ftd>\n\u003Ctd>≤255 字符\u003C\u002Ftd>\n\u003Ctd>头像 URL（legacy；image_service 普及前继续用）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>avatar_image_hash\u003C\u002Ftd>\n\u003Ctd>string?\u003C\u002Ftd>\n\u003Ctd>≤64 字符\u003C\u002Ftd>\n\u003Ctd>头像的 image_service 哈希；前端 resolveAvatarUrl 优先用此字段\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>bio\u003C\u002Ftd>\n\u003Ctd>string?\u003C\u002Ftd>\n\u003Ctd>≤107 字符\u003C\u002Ftd>\n\u003Ctd>个人简介\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\u003C\u002Fdiv>\u003Cp>字段都用指针类型语义：\u003Cstrong>没传 = 不动；传了 = 设为该值\u003C\u002Fstrong>（包括传空字符串 = 清空）。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>成功响应\u003C\u002Fstrong>：返回更新后的完整 \u003Ccode>UserResponse\u003C\u002Fcode>（同 GET \u002Fauth\u002Fme 的 \u003Ccode>data\u003C\u002Fcode> shape）。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>错误响应\u003C\u002Fstrong>：\u003C\u002Fp>\n\u003Cdiv class=\"kun-table-wrap\">\u003Ctable>\u003Cthead>\n\u003Ctr>\n\u003Cth>HTTP\u003C\u002Fth>\n\u003Cth>code\u003C\u002Fth>\n\u003Cth>触发条件\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>400\u003C\u002Ftd>\n\u003Ctd>1\u003C\u002Ftd>\n\u003Ctd>JSON 格式错误\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>400\u003C\u002Ftd>\n\u003Ctd>7\u003C\u002Ftd>\n\u003Ctd>字段约束未通过（name 长度、bio 长度等）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>400\u003C\u002Ftd>\n\u003Ctd>10007\u003C\u002Ftd>\n\u003Ctd>name 与其他用户重复\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>401\u003C\u002Ftd>\n\u003Ctd>10001\u002F10002\u002F10003\u003C\u002Ftd>\n\u003Ctd>未提供 \u002F 无效 \u002F 过期 token\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\u003C\u002Fdiv>\u003Cp>\u003Cstrong>修改 email 不在这里\u003C\u002Fstrong> —— email 必须走 \u003Ccode>\u002Fauth\u002Femail\u002Fsend-code\u003C\u002Fcode> + \u003Ccode>\u002Fauth\u002Femail\u003C\u002Fcode>（带验证码的两步流程，防止账号被劫持）。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>修改 password 也不在这里\u003C\u002Fstrong> —— password 必须走 \u003Ccode>\u002Fauth\u002Fpassword\u003C\u002Fcode>（需要旧密码或重置 token）。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>举例\u003C\u002Fstrong>：仅改头像 hash（image_service 上传完毕之后）：\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-bash\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">curl\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\"> -X\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\"> PATCH\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\"> https:\u002F\u002Foauth.kungal.com\u002Fapi\u002Fv1\u002Fauth\u002Fme\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\"> \\\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">  -H\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\"> \"Authorization: Bearer &#x3C;access_token>\"\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\"> \\\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">  -H\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\"> \"Content-Type: application\u002Fjson\"\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\"> \\\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">  -d\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\"> '{\"avatar_image_hash\":\"abc123def456...\"}'\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Chr>\n\u003Ch2 id=\"post-auth-me-avatar\" tabindex=\"-1\">POST \u002Fauth\u002Fme\u002Favatar\u003C\u002Fh2>\n\u003Cblockquote>\n\u003Cp>\u003Cstrong>2026-05-23 新增\u003C\u002Fstrong>：一次性&quot;上传头像图片 → 写入用户记录&quot;端点，\u003Cstrong>避免下游 kungal \u002F moyu 自己维护 image_service client\u003C\u002Fstrong>。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Cp>直接接收图片二进制（multipart），OAuth 内部转发到 image_service，并在响应返回前把拿到的 hash 写入当前用户的 \u003Ccode>avatar_image_hash\u003C\u002Fcode>。\u003Cstrong>调用方拿到响应时数据库已经更新\u003C\u002Fstrong>，无需再调 \u003Ccode>PATCH \u002Fauth\u002Fme\u003C\u002Fcode>。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>请求头\u003C\u002Fstrong>：\u003Ccode>Authorization: Bearer &lt;access_token&gt;\u003C\u002Fcode>\u003C\u002Fp>\n\u003Cp>\u003Cstrong>请求 body\u003C\u002Fstrong>：\u003Ccode>multipart\u002Fform-data\u003C\u002Fcode>\u003C\u002Fp>\n\u003Cdiv class=\"kun-table-wrap\">\u003Ctable>\u003Cthead>\n\u003Ctr>\n\u003Cth>字段\u003C\u002Fth>\n\u003Cth>必填\u003C\u002Fth>\n\u003Cth>说明\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>file\u003C\u002Ftd>\n\u003Ctd>是\u003C\u002Ftd>\n\u003Ctd>图片文件，MIME 必须 \u003Ccode>image\u002F*\u003C\u002Fcode>；建议 ≤ 4 MiB（fiber 默认 body 上限）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\u003C\u002Fdiv>\u003Cp>\u003Cstrong>成功响应\u003C\u002Fstrong>：直接透传 image_service 的上传结果。\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\">  \"code\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">0\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">  \"data\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">    \"hash\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"abc123def456...\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">    \"url\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"https:\u002F\u002Fimage.kungal.iloveren.link\u002Fab\u002Fc1\u002Fabc123def456....webp\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">    \"variant_urls\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">      \"256\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"https:\u002F\u002Fimage.kungal.iloveren.link\u002Fab\u002Fc1\u002Fabc123def456..._256.webp\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">      \"100\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"https:\u002F\u002Fimage.kungal.iloveren.link\u002Fab\u002Fc1\u002Fabc123def456..._100.webp\"\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:#005CC5;--shiki-dark:#79B8FF\">    \"width\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">512\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">    \"height\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">512\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">    \"size_bytes\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">38241\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">    \"deduplicated\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">false\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>variant_urls\u003C\u002Fcode> 提供 256 \u002F 100 像素的预生成缩略图，前端列表 \u002F 评论场景直接用 100，主页 \u002F 个人页用 256，原图（\u003Ccode>url\u003C\u002Fcode>）一般不需要展示。\u003Ccode>deduplicated=true\u003C\u002Fcode> 表示同 hash 文件以前传过，image_service 复用了已有对象，没有额外存储成本。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>错误响应\u003C\u002Fstrong>：\u003C\u002Fp>\n\u003Cdiv class=\"kun-table-wrap\">\u003Ctable>\u003Cthead>\n\u003Ctr>\n\u003Cth>HTTP\u003C\u002Fth>\n\u003Cth>code\u003C\u002Fth>\n\u003Cth>触发条件\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>400\u003C\u002Ftd>\n\u003Ctd>8\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>file\u003C\u002Fcode> 字段缺失或不是合法 multipart\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>401\u003C\u002Ftd>\n\u003Ctd>10001\u002F10002\u002F10003\u003C\u002Ftd>\n\u003Ctd>未提供 \u002F 无效 \u002F 过期 token\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>404\u003C\u002Ftd>\n\u003Ctd>10005\u003C\u002Ftd>\n\u003Ctd>用户记录不存在（一般 token 还有效就不会触发）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>500\u003C\u002Ftd>\n\u003Ctd>1\u003C\u002Ftd>\n\u003Ctd>image_service 不可达 \u002F 配额耗尽 \u002F 审核拒绝；详见 OAuth 服务端日志\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\u003C\u002Fdiv>\u003Cp>\u003Cstrong>和现有方式的关系\u003C\u002Fstrong>：\u003C\u002Fp>\n\u003Cdiv class=\"kun-table-wrap\">\u003Ctable>\u003Cthead>\n\u003Ctr>\n\u003Cth>方式\u003C\u002Fth>\n\u003Cth>谁调 image_service\u003C\u002Fth>\n\u003Cth>几次请求\u003C\u002Fth>\n\u003Cth>适合\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ccode>POST \u002Fauth\u002Fme\u002Favatar\u003C\u002Fcode>（\u003Cstrong>推荐\u003C\u002Fstrong>）\u003C\u002Ftd>\n\u003Ctd>OAuth 内部\u003C\u002Ftd>\n\u003Ctd>1 次\u003C\u002Ftd>\n\u003Ctd>标准 web \u002F 移动端&quot;用户改头像&quot;\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>PATCH \u002Fauth\u002Fme { avatar_image_hash }\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>下游自己\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>2 次（先上传到 image_service 拿 hash，再 PATCH）\u003C\u002Ftd>\n\u003Ctd>下游已有 image_service client（投稿 \u002F 截图等场景），头像和别的图片走同一上传管线\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\u003C\u002Fdiv>\u003Cp>两种方式可以并存，下游想用哪种都行。\u003Ccode>avatar\u003C\u002Fcode> 和 \u003Ccode>avatar_image_hash\u003C\u002Fcode> 仍是独立字段（参见 PATCH \u002Fauth\u002Fme 节）。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>配额归属\u003C\u002Fstrong>：图片走的是 OAuth 自己的 image_service client，配额从 OAuth 这一侧扣，\u003Cstrong>下游 kungal \u002F moyu 不需要为头像单独申请 image_service client\u003C\u002Fstrong>。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>CORS\u003C\u002Fstrong>：浏览器直传需要 OAuth 在 CORS 配置里允许下游 origin 上 \u003Ccode>POST\u003C\u002Fcode> + \u003Ccode>Authorization\u003C\u002Fcode> header，目前 \u003Ccode>*.kungal.com\u003C\u002Fcode> 已包含；新增子域接入前请确认。后端代理模式（kungal\u002Fmoyu 后端接 multipart 再转发）天然不受 CORS 影响。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>举例\u003C\u002Fstrong>：\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-bash\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">curl\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\"> -X\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\"> POST\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\"> https:\u002F\u002Foauth.kungal.com\u002Fapi\u002Fv1\u002Fauth\u002Fme\u002Favatar\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\"> \\\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">  -H\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\"> \"Authorization: Bearer &#x3C;access_token>\"\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\"> \\\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">  -F\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\"> \"file=@avatar.png\"\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>浏览器版本：\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-ts\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">const\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\"> fd\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> =\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> new\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> FormData\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">()\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">fd.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">append\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">'file'\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">, file)  \u003C\u002Fspan>\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\">\u002F\u002F &#x3C;input type=\"file\"> 的 File 对象\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">const\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\"> r\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> =\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> await\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> fetch\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">'https:\u002F\u002Foauth.kungal.com\u002Fapi\u002Fv1\u002Fauth\u002Fme\u002Favatar'\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">, {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">  method: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">'POST'\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">  headers: { Authorization: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">`Bearer ${\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">accessToken\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">}`\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> },\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">  body: fd  \u003C\u002Fspan>\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\">\u002F\u002F 注意：不要手动设 Content-Type，让浏览器自动带 boundary\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:#D73A49;--shiki-dark:#F97583\">const\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> { \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">data\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> } \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> await\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> r.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">json\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">()\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\">\u002F\u002F data.hash 已经被 OAuth 写入了用户记录，下一次 GET \u002Fauth\u002Fme 就能看到新头像\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Chr>\n\u003Ch1 id=\"身份层端点\" tabindex=\"-1\">身份层端点\u003C\u002Fh1>\n\u003Cblockquote>\n\u003Cp>下面三个端点\u003Cstrong>仅供 OAuth 自己的前端（apps\u002Fweb）使用\u003C\u002Fstrong>。kungal \u002F moyu \u002F wiki 等下游接入站\u003Cstrong>不应直接调用\u003C\u002Fstrong>，应该跳转到 OAuth profile 让用户在那里完成。原因和跳转示例见本文档开头的&quot;身份操作 vs 展示操作&quot;小节。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Ch2 id=\"post-auth-email-send-code\" tabindex=\"-1\">POST \u002Fauth\u002Femail\u002Fsend-code\u003C\u002Fh2>\n\u003Cp>发送邮箱变更验证码。\u003Cstrong>验证码寄到用户当前的旧邮箱\u003C\u002Fstrong>（不是新邮箱）—— 这是关键的安全设计：JWT 被窃后，攻击者也无法收到验证码完成劫持。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>请求头\u003C\u002Fstrong>：\u003Ccode>Authorization: Bearer &lt;access_token&gt;\u003C\u002Fcode>\u003C\u002Fp>\n\u003Cp>\u003Cstrong>请求体\u003C\u002Fstrong>：\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>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\"new_email\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"newaddress@example.com\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> }\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cdiv class=\"kun-table-wrap\">\u003Ctable>\u003Cthead>\n\u003Ctr>\n\u003Cth>字段\u003C\u002Fth>\n\u003Cth>类型\u003C\u002Fth>\n\u003Cth>约束\u003C\u002Fth>\n\u003Cth>说明\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>new_email\u003C\u002Ftd>\n\u003Ctd>string\u003C\u002Ftd>\n\u003Ctd>合法邮箱格式\u003C\u002Ftd>\n\u003Ctd>想换成的新邮箱\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\u003C\u002Fdiv>\u003Cp>\u003Cstrong>成功响应\u003C\u002Fstrong>：\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>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\"code\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">0\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\"message\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"验证码已发送到当前邮箱\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\"data\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">null\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> }\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>验证码 6 位数字，\u003Cstrong>默认有效期 15 分钟\u003C\u002Fstrong>（由 \u003Ccode>KUN_AUTH_VERIFICATION_CODE_TTL_MINUTES\u003C\u002Fcode> 配置；改邮箱 \u002F 注册 \u002F 任何走 Redis 6 位码的流程共用此值）。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>错误响应\u003C\u002Fstrong>：\u003C\u002Fp>\n\u003Cdiv class=\"kun-table-wrap\">\u003Ctable>\u003Cthead>\n\u003Ctr>\n\u003Cth>HTTP\u003C\u002Fth>\n\u003Cth>code\u003C\u002Fth>\n\u003Cth>触发条件\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>400\u003C\u002Ftd>\n\u003Ctd>1\u003C\u002Ftd>\n\u003Ctd>JSON 格式错误\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>400\u003C\u002Ftd>\n\u003Ctd>7\u003C\u002Ftd>\n\u003Ctd>new_email 不是合法邮箱格式\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>400\u003C\u002Ftd>\n\u003Ctd>10006\u003C\u002Ftd>\n\u003Ctd>新邮箱已被其他账号使用\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>400\u003C\u002Ftd>\n\u003Ctd>10012\u003C\u002Ftd>\n\u003Ctd>上一次发送距今不足限流间隔，请稍后重试\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>400\u003C\u002Ftd>\n\u003Ctd>10013\u003C\u002Ftd>\n\u003Ctd>新邮箱与当前邮箱相同\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>401\u003C\u002Ftd>\n\u003Ctd>10001\u002F10002\u002F10003\u003C\u002Ftd>\n\u003Ctd>未提供 \u002F 无效 \u002F 过期 token\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>404\u003C\u002Ftd>\n\u003Ctd>10005\u003C\u002Ftd>\n\u003Ctd>token 对应的用户不存在\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\u003C\u002Fdiv>\u003Chr>\n\u003Ch2 id=\"put-auth-email\" tabindex=\"-1\">PUT \u002Fauth\u002Femail\u003C\u002Fh2>\n\u003Cp>用旧邮箱收到的验证码确认换邮箱。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>请求头\u003C\u002Fstrong>：\u003Ccode>Authorization: Bearer &lt;access_token&gt;\u003C\u002Fcode>\u003C\u002Fp>\n\u003Cp>\u003Cstrong>请求体\u003C\u002Fstrong>：\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\">  \"code\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"123456\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">  \"new_email\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"newaddress@example.com\"\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">}\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cdiv class=\"kun-table-wrap\">\u003Ctable>\u003Cthead>\n\u003Ctr>\n\u003Cth>字段\u003C\u002Fth>\n\u003Cth>类型\u003C\u002Fth>\n\u003Cth>约束\u003C\u002Fth>\n\u003Cth>说明\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>code\u003C\u002Ftd>\n\u003Ctd>string\u003C\u002Ftd>\n\u003Ctd>长度必须为 6\u003C\u002Ftd>\n\u003Ctd>旧邮箱收到的验证码\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>new_email\u003C\u002Ftd>\n\u003Ctd>string\u003C\u002Ftd>\n\u003Ctd>合法邮箱格式；必须与 send-code 时提交的一致\u003C\u002Ftd>\n\u003Ctd>新邮箱\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\u003C\u002Fdiv>\u003Cp>\u003Cstrong>成功响应\u003C\u002Fstrong>：返回更新后的完整 \u003Ccode>UserResponse\u003C\u002Fcode>（同 GET \u002Fauth\u002Fme）。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>错误响应\u003C\u002Fstrong>：\u003C\u002Fp>\n\u003Cdiv class=\"kun-table-wrap\">\u003Ctable>\u003Cthead>\n\u003Ctr>\n\u003Cth>HTTP\u003C\u002Fth>\n\u003Cth>code\u003C\u002Fth>\n\u003Cth>触发条件\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>400\u003C\u002Ftd>\n\u003Ctd>1\u003C\u002Ftd>\n\u003Ctd>JSON 格式错误\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>400\u003C\u002Ftd>\n\u003Ctd>7\u003C\u002Ftd>\n\u003Ctd>code 长度不对 \u002F new_email 不是合法邮箱格式\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>400\u003C\u002Ftd>\n\u003Ctd>10006\u003C\u002Ftd>\n\u003Ctd>新邮箱已被其他账号使用（极少：并发竞争）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>400\u003C\u002Ftd>\n\u003Ctd>10010\u003C\u002Ftd>\n\u003Ctd>验证码错误\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>400\u003C\u002Ftd>\n\u003Ctd>10011\u003C\u002Ftd>\n\u003Ctd>验证码已过期（默认 15 分钟 TTL，由 \u003Ccode>KUN_AUTH_VERIFICATION_CODE_TTL_MINUTES\u003C\u002Fcode> 控制）或从未请求\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>401\u003C\u002Ftd>\n\u003Ctd>10001\u002F10002\u002F10003\u003C\u002Ftd>\n\u003Ctd>未提供 \u002F 无效 \u002F 过期 token\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\u003C\u002Fdiv>\u003Cblockquote>\n\u003Cp>\u003Cstrong>不一致检测\u003C\u002Fstrong>：如果用户在 send-code 时填的 new_email 是 \u003Ccode>A@example.com\u003C\u002Fcode>，但 PUT \u002Fauth\u002Femail 提交 \u003Ccode>B@example.com\u003C\u002Fcode>，会按 10010（验证码错误）拒绝——验证码绑定的是 send-code 当时的新邮箱。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Chr>\n\u003Ch2 id=\"put-auth-password\" tabindex=\"-1\">PUT \u002Fauth\u002Fpassword\u003C\u002Fh2>\n\u003Cp>改密码。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>请求头\u003C\u002Fstrong>：\u003Ccode>Authorization: Bearer &lt;access_token&gt;\u003C\u002Fcode>\u003C\u002Fp>\n\u003Cp>\u003Cstrong>请求体\u003C\u002Fstrong>：\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\">  \"old_password\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"oldpass123\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">  \"new_password\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"newpass456\"\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">}\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cdiv class=\"kun-table-wrap\">\u003Ctable>\u003Cthead>\n\u003Ctr>\n\u003Cth>字段\u003C\u002Fth>\n\u003Cth>类型\u003C\u002Fth>\n\u003Cth>约束\u003C\u002Fth>\n\u003Cth>说明\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>old_password\u003C\u002Ftd>\n\u003Ctd>string\u003C\u002Ftd>\n\u003Ctd>—\u003C\u002Ftd>\n\u003Ctd>当前密码。\u003Cstrong>仅当账号已设密码时必填\u003C\u002Fstrong>（从其他平台迁移过来还没设密码的账号可留空）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>new_password\u003C\u002Ftd>\n\u003Ctd>string\u003C\u002Ftd>\n\u003Ctd>6..100 字符\u003C\u002Ftd>\n\u003Ctd>新密码\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\u003C\u002Fdiv>\u003Cp>\u003Cstrong>成功响应\u003C\u002Fstrong>：\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>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\"code\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">0\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\"message\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"密码修改成功\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\"data\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">null\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> }\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cblockquote>\n\u003Cp>\u003Cstrong>不会自动登出其他设备\u003C\u002Fstrong>——其他 access_token 直到自然过期（15 分钟）才失效，refresh_token 直到自然过期（7 天）才失效。要立即下线所有设备需要走&quot;撤销 refresh_token&quot;流程（未来加），或 admin 手动 \u003Ccode>DELETE \u002Fadmin\u002Fusers\u002F:uuid\u002Fsessions\u003C\u002Fcode>。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Cp>\u003Cstrong>错误响应\u003C\u002Fstrong>：\u003C\u002Fp>\n\u003Cdiv class=\"kun-table-wrap\">\u003Ctable>\u003Cthead>\n\u003Ctr>\n\u003Cth>HTTP\u003C\u002Fth>\n\u003Cth>code\u003C\u002Fth>\n\u003Cth>触发条件\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>400\u003C\u002Ftd>\n\u003Ctd>1\u003C\u002Ftd>\n\u003Ctd>JSON 格式错误\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>400\u003C\u002Ftd>\n\u003Ctd>7\u003C\u002Ftd>\n\u003Ctd>new_password 长度不在 6..100\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>400\u003C\u002Ftd>\n\u003Ctd>10004\u003C\u002Ftd>\n\u003Ctd>旧密码错误（&quot;邮箱或密码错误&quot;——code 复用）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>401\u003C\u002Ftd>\n\u003Ctd>10001\u002F10002\u002F10003\u003C\u002Ftd>\n\u003Ctd>未提供 \u002F 无效 \u002F 过期 token\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>404\u003C\u002Ftd>\n\u003Ctd>10005\u003C\u002Ftd>\n\u003Ctd>用户不存在\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\u003C\u002Fdiv>\u003Chr>\n\u003Cp>完整错误码表见 \u003Ca href=\"\u002Fcontracts\u002Foauth\u002F04-tokens-and-errors#%E9%94%99%E8%AF%AF%E7%A0%81%E9%80%9F%E6%9F%A5\">04-tokens-and-errors.md\u003C\u002Fa>。\u003C\u002Fp>\n","kun-galgame-infra\u002Fdocs\u002Fintegration\u002Foauth\u002F02-user-profile.md",1781708341787]