fix(msa): 微软登录默认走设备码,浏览器流降级为可选加速路
官方产物上点「微软登录」必失败:GUI 只接了授权码流(随机端口回环回调),而 Azure 应用 注册当初是为 device-code 建的,重定向 URI 一项都没登记,于是授权端点当场回 `invalid_request: ... 'redirect_uri' is not valid`,浏览器里只剩一张 AAD 原始错误页, 启动器干等 5 分钟超时。设备码流本身是好的(CLI `accounts add msa` 一直能用)。 - core:新增 `preflight`(回环重定向 URI 注册预检)与 `login`(浏览器优先 + 自动回退设备码)。 预检拿「即将使用的 redirect_uri」(端口不参与匹配,微软官方文档 *Localhost exceptions* 原文已引进模块文档)先问一次授权端点;没登记 / 问不出来就地跳过,**一个监听口都不开**。 预检通过才真绑端口走授权码;授权码失败(超时/被拒/绑不上)自动回退设备码并带上结构化 原因码;用户取消(`Canceled`)原样上抛,不偷偷换流。设备码是默认,零监听口。 - presenter:`start_login` = 设备码(默认);`start_browser_login` = 浏览器优先 + 回退, 回退原因码写进 `DeviceLoginView.browser_note`(原文只进日志)。 - i18n:三条回退文案(含「怎么修:把 http://localhost/auth 加进重定向 URI,端口不参与匹配」)。 - app-iced:登录面板主按钮改成设备码、浏览器降级为次按钮;重登也走设备码;设备码面板与失败 面板都能画回退说明。倒计时文案改由视图模型持有(借用渲染),顺带修掉中文倒计时整行不显示。 - 守卫(都能变红):①授权端点回 invalid_request 时确实回退设备码且原因是结构化码; ②未登记回环 / 默认设备码路 `listeners_opened == 0`;③回环登记了仍能走授权码(不焊死加速路); ④用户取消不被偷偷换流;⑤client id 仍只能构建期注入(扫 `auth/microsoft/` 源码无 UUID 字面量)。
This commit is contained in:
1 parent
329c529bea
commit
f82bd395f8
23 files changed
+1373
-131
No files matched your search
@@ -115,6 +115,23 @@ macro_rules! account_entries {
|
||||
// ── 重新登录的引导
|
||||
AccountHintAuthlibReauth => ("Enter the password again for {server}.", "请输入 {server} 的密码。"),
|
||||
AccountHintOfflineNoRelogin => ("Local profiles have no server account to sign in again — create a new profile instead.", "本地档案没有可重新登录的服务器账户,请新建一个档案。"),
|
||||
// ── 浏览器登录回退到设备码的说明(M178)
|
||||
// 口径:这不是「登录失败」——设备码接着跑。第一句说清「已改用设备码」,
|
||||
// 需要改配置的那一条**必须给可照抄的下一步**(哪个门户、加什么值、端口不参与匹配),
|
||||
// 且**不许**出现 AAD 的原始报文(`invalid_request` / `AADSTS…` / JSON)——
|
||||
// 用户 2026-11-08 实测撞到的就是那张原始错误页。
|
||||
AccountBrowserFallbackNotRegistered => (
|
||||
"This build's app registration has no loopback redirect URI, so browser sign-in cannot work; switched to a device code. To fix: add http://localhost/auth under Redirect URIs in the Azure app registration (the port is ignored), then restart the launcher.",
|
||||
"这个构建的应用注册里没有登记回环重定向 URI,浏览器登录用不了,已自动改用设备码。怎么修:在 Azure 应用注册的「重定向 URI」里加上 http://localhost/auth(端口不参与匹配),然后重启启动器。"
|
||||
),
|
||||
AccountBrowserFallbackUnverified => (
|
||||
"Could not confirm that browser sign-in is usable, so it switched to a device code.",
|
||||
"没能确认浏览器登录是否可用(预检没成功),已自动改用设备码。"
|
||||
),
|
||||
AccountBrowserFallbackFailed => (
|
||||
"Browser sign-in did not finish, so it switched to a device code. If the browser page said the redirect URI is not valid, add http://localhost/auth under Redirect URIs of the app registration.",
|
||||
"浏览器登录没有完成,已自动改用设备码。如果浏览器里提示 redirect_uri 无效,请把 http://localhost/auth 加进应用注册的「重定向 URI」。"
|
||||
),
|
||||
} }
|
||||
};
|
||||
}
|
||||
@@ -125,6 +142,48 @@ pub(crate) use account_entries;
|
||||
mod tests {
|
||||
use crate::{Catalog, Key, Lang};
|
||||
|
||||
/// **M178**:浏览器登录回退到设备码时那一句,必须「说清为什么 + 怎么修」,且**不许**把
|
||||
/// AAD 的原始报文搬上屏(用户实测撞到的正是那张 `invalid_request ... redirect_uri` 错误页)。
|
||||
///
|
||||
/// 抓得住:文案退回「登录失败,完整原因在日志里」、删掉可照抄的重定向 URI、或有人把
|
||||
/// AAD 原始错误码/JSON 塞进词条。
|
||||
/// 抓不住:读起来顺不顺(那靠 review 与截图)。
|
||||
#[test]
|
||||
fn the_browser_fallback_copy_explains_itself_without_raw_aad_text() {
|
||||
for &lang in Lang::ALL {
|
||||
let catalog = Catalog::of(lang);
|
||||
let not_registered = catalog.text(Key::AccountBrowserFallbackNotRegistered);
|
||||
let unverified = catalog.text(Key::AccountBrowserFallbackUnverified);
|
||||
let failed = catalog.text(Key::AccountBrowserFallbackFailed);
|
||||
// 「已改用设备码」这条结论三句里都得有(用户要知道画面为什么变了)。
|
||||
for copy in [not_registered, unverified, failed] {
|
||||
assert!(
|
||||
copy.contains("device code") || copy.contains("设备码"),
|
||||
"{}: {copy}",
|
||||
lang.tag()
|
||||
);
|
||||
// 原文不上屏:这两条是 AAD 的机器码,出现即语言/脱敏纪律失效。
|
||||
assert!(!copy.contains("invalid_request"), "{}: {copy}", lang.tag());
|
||||
assert!(!copy.contains("AADSTS"), "{}: {copy}", lang.tag());
|
||||
assert!(!copy.contains('{'), "{}: {copy}", lang.tag());
|
||||
}
|
||||
// 「没登记回环」这一条必须给**可照抄**的下一步(门户 + 值 + 端口不参与匹配)。
|
||||
assert!(
|
||||
not_registered.contains("http://localhost/auth"),
|
||||
"{not_registered}"
|
||||
);
|
||||
assert!(
|
||||
not_registered.to_lowercase().contains("redirect uri")
|
||||
|| not_registered.contains("重定向 URI"),
|
||||
"{not_registered}"
|
||||
);
|
||||
assert!(
|
||||
not_registered.to_lowercase().contains("port") || not_registered.contains("端口"),
|
||||
"{not_registered}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// **M132**:缺 client id 这条是本页唯一「说清原因」的失败文案,它的纪律比别的正文更硬。
|
||||
///
|
||||
/// 抓得住:文案退回「登录失败 / 完整原因在日志里」、退化成「未支持」、或者只说缺 id 却
|
||||
|
||||
@@ -180,6 +180,9 @@ pub(super) const ROWS: &[(&str, &str, &str)] = &[
|
||||
("AccountNoticeTitleDone", "Account updated", "账户已更新"),
|
||||
("AccountHintAuthlibReauth", "Enter the password again for {server}.", "请输入 {server} 的密码。"),
|
||||
("AccountHintOfflineNoRelogin", "Local profiles have no server account to sign in again — create a new profile instead.", "本地档案没有可重新登录的服务器账户,请新建一个档案。"),
|
||||
("AccountBrowserFallbackNotRegistered", "This build's app registration has no loopback redirect URI, so browser sign-in cannot work; switched to a device code. To fix: add http://localhost/auth under Redirect URIs in the Azure app registration (the port is ignored), then restart the launcher.", "这个构建的应用注册里没有登记回环重定向 URI,浏览器登录用不了,已自动改用设备码。怎么修:在 Azure 应用注册的「重定向 URI」里加上 http://localhost/auth(端口不参与匹配),然后重启启动器。"),
|
||||
("AccountBrowserFallbackUnverified", "Could not confirm that browser sign-in is usable, so it switched to a device code.", "没能确认浏览器登录是否可用(预检没成功),已自动改用设备码。"),
|
||||
("AccountBrowserFallbackFailed", "Browser sign-in did not finish, so it switched to a device code. If the browser page said the redirect URI is not valid, add http://localhost/auth under Redirect URIs of the app registration.", "浏览器登录没有完成,已自动改用设备码。如果浏览器里提示 redirect_uri 无效,请把 http://localhost/auth 加进应用注册的「重定向 URI」。"),
|
||||
("SettingsGroupUpdate", "Updates", "更新"),
|
||||
("SettingsUpdateCurrent", "Current version", "当前版本"),
|
||||
("SettingsUpdateChannel", "Channel", "更新渠道"),
|
||||
|
||||
Reference in new issue
Block a user