Known 服务端可裁剪出一套双鉴权:人(浏览器)走 Cookie 会话、机器(CLI / 第三方)走 Bearer Token。 两条通道同一份身份后端,但受众边界、CORS、scope 完全不同。
接入拓扑
一份身份后端对两类受众暴露两条通道,两路互不越界:
┌─ /api/* 内部 BFF 面 · Cookie / SameSite=Lax ← 浏览器(人)
浏览器 / 客户端 ──HTTPS──▶
└─ /api/v1/* 公开机器面 · Bearer / scope 白名单 ← CLI / 第三方(机器)
- 作为接入方你不必关心身份后端跑在哪个进程:在同一 apex 域(
*.known.org.cn)下,认证 Cookie 走Domain=.known.org.cn,天然跨子域共享登录态(SSO),无需独立 auth 子域。 - 机器面(
/api/v1)只认 Bearer 令牌,绝不共享浏览器 Cookie;反之浏览器 Cookie 也进不了机器面白名单外的端点。
Cookie 会话(人)
IAuthProvider 抽象:
[WebApi]
public interface IAuthProvider
{
Task<Result<UserInfo>> CurrentAsync();
Task<Result> SignInAsync(string email, string password, bool remember);
Task<Result> SignOutAsync();
}
浏览器侧由 ASP.NET Core Cookie 中间件管理。登录成功后签发 Set-Cookie 带:
Domain=.known.org.cn; HttpOnly; SameSite=Lax; Secure; Max-Age=...
同域内(app 与自家 CLI 之外的所有 Known 子域)都自动带上;跨到 api.known.org.cn
时 SameSite=Lax 只在顶层导航发送、XHR 不带——所以机器面必须走 Bearer。
Bearer Token(机器)
/api/v1/* 只接 Bearer,三类令牌:
- Personal Access Token (PAT) — 用户签发、给 CLI 用;带 scope(如
read/write/admin); 落库仅存 SHA-256 哈希,明文只在签发那一刻可见一次。 - Client Credentials — 机器对机器,走标准 OAuth2 授权服务器;无交互。
- Authorization Code (OIDC) — 第三方网页应用登录,宿主应用充当 OIDC Provider;consent 屏列 scope。
C# 客户端调用示例:
var http = new HttpClient();
http.DefaultRequestHeaders.Authorization = new("Bearer", patFromConfig);
var res = await http.GetFromJsonAsync<Result<PagedResult<Project>>>
("https://api.known.org.cn/api/v1/projects");
服务端实现要点
Web 宿主(如 KnownWeb)层:
- 两条鉴权管道并行注册(
AddAuthentication().AddCookie().AddJwtBearer()), 路由前缀决定用哪条:/api/*走 Cookie、/api/v1/*走 Bearer。 - 默认全暴露 是个易踩的缺口——
Config.AddModule扫到的[WebApi]会同时挂到两条管道。 发布前必须显式白名单:机器面接口带[MachineEndpoint]特性、只有这些挂到/api/v1。 - scope 中间件在 Bearer 认证之后跑,校验令牌 scope 与 endpoint 需求匹配,不足返 403。
前端集成
LocalService 或 UserInfoService 提供当前用户:
@inject IAuthProvider Auth
@implements IDisposable
@if (_user is null) { <Spin /> }
else if (!_user.IsSignedIn) { <SignInPrompt /> }
else { <p>欢迎 @_user.Name</p> }
@code {
private UserInfo? _user;
protected override async Task OnInitializedAsync()
{
var res = await Auth.CurrentAsync();
if (res.Success) _user = res.Data;
Auth.UserChanged += OnUserChanged;
}
private void OnUserChanged(UserInfo u) => InvokeAsync(() => { _user = u; StateHasChanged(); });
public void Dispose() => Auth.UserChanged -= OnUserChanged;
}
常见坑
- 为不同子域各造一套登录/身份 — 不要。全站共享同一份身份后端与
.known.org.cnCookie 会话,SSO 靠域与共享 DataProtection 实现,别在一个 apex 下裂出多套账户体系。 - CLI 用 Cookie 登录 — 不要。CLI 走 PAT/Bearer,Cookie 只给浏览器。
- 在
/api/v1里返回Set-Cookie— 不要。机器面无会话;返回 401 后 CLI 自己带新令牌重试。 - PAT 明文入库 — 绝对不要。只存哈希、只在签发时一次性可见。
相关
- 完整决策与拓扑变更见
docs/deploy-topology.md§3 与Website/docs/docs-site-design.md§1。 - 双鉴权的完整落地(令牌签发、scope 治理、授权服务器)依具体产品形态而定;本指南只描述开源侧可见的对外契约。
扫码关注公众号
扫码加入 QQ 群