忘机山人
懒猫 SSO(懒猫微服自带的 OIDC 身份服务)并不是懒猫微服给你贴的“登录按钮”,
而是一套 在你自己的家庭服务器这种“半可信环境”中,为所有应用建立统一身份信任的工程流程。如果你只记住懒猫帮你注入了哪几个环境变量,却不理解每一步在防什么,
这套 SSO 会“看起来能跑”,但迟早会在回调地址、token 校验、多用户等边界条件上出问题。
这篇文章从端到端流程出发,基于懒猫微服实际暴露的懒猫 SSO 端点,完整梳理:
lzc-manifest.yml 注入的那几个环境变量到底对应 OIDC 的哪些字段下面所有例子都围绕同一个真实应用展开——懒猫 ENV 查看器(xu.deploy.env),部署在一个叫 alice 的盒子上,对外域名是 alice.heiyu.space,回调路径是 /callback。
在任何基于懒猫 SSO 的应用里,都至少有这三类角色:
client_secret 由懒猫注入到容器环境变量)CLIENT_SECRET 也注入进来了,在实操中介于两者之间(后面会专门讲这件事的边界)https://<盒子名>.heiyu.space/sys/oauth几组必须分清的概念:
offline_access scope 才会下发openid email groups profile offline_access)iss sub aud iat exp email email_verified locale name preferred_username at_hash)RS256)一句话记忆:
懒猫 SSO = OAuth 2.0 + 身份层(ID Token)+ 懒猫替你注入的 Client 凭证
懒猫 SSO discovery 里明确写了 "response_types_supported": ["code"],
也就是说——在懒猫上,除了授权码模式,别无选择。
这也是现在最推荐、最安全的一种模式,适用于 Web、SPA、家庭内网应用。
我们按真实时序走一遍。
容器启动时,或应用首次处理登录时,会访问:
GET https://alice.heiyu.space/sys/oauth/.well-known/openid-configuration
得到一份元数据,核心字段是这些(这段是实际返回,不是示意):
{
"issuer": "https://alice.heiyu.space/sys/oauth",
"authorization_endpoint": "https://alice.heiyu.space/sys/oauth/auth",
"token_endpoint": "https://alice.heiyu.space/sys/oauth/token",
"jwks_uri": "https://alice.heiyu.space/sys/oauth/keys",
"userinfo_endpoint": "https://alice.heiyu.space/sys/oauth/userinfo",
"device_authorization_endpoint": "https://alice.heiyu.space/sys/oauth/device/code",
"introspection_endpoint": "https://alice.heiyu.space/sys/oauth/token/introspect",
"grant_types_supported": [
"authorization_code",
"refresh_token",
"urn:ietf:params:oauth:grant-type:device_code",
"urn:ietf:params:oauth:grant-type:token-exchange"
],
"response_types_supported": ["code"],
"subject_types_supported": ["public"],
"id_token_signing_alg_values_supported": ["RS256"],
"code_challenge_methods_supported": ["S256", "plain"],
"scopes_supported": [
"openid", "email", "groups", "profile", "offline_access"
],
"token_endpoint_auth_methods_supported": [
"client_secret_basic", "client_secret_post"
],
"claims_supported": [
"iss", "sub", "aud", "iat", "exp",
"email", "email_verified", "locale",
"name", "preferred_username", "at_hash"
]
}
非常重要的一点:
后续所有校验,都会以这里返回的
issuer为“信任锚点”。
在懒猫里,issuer 的规则是:
https://<盒子名>.heiyu.space/sys/oauth
常见坑:
issuer 配成 https://alice.heiyu.space/sys/oauth/(尾部多斜杠)→ 后面 iss 校验直接失败issuer 写成 https://alice.heiyu.space/(漏掉 /sys/oauth)→ discovery 拿不到LAZYCAT_AUTH_OIDC_ISSUER_URI这是用户“看到懒猫授权页面”的那一步。
一个典型的跳转 URL(由 Client 拼出来,让浏览器跳过去):
GET https://alice.heiyu.space/sys/oauth/auth?
response_type=code
&client_id=xu.deploy.env
&redirect_uri=https%3A%2F%2Fenv.alice.heiyu.space%2Fcallback
&scope=openid%20email%20profile
&state=...
&nonce=...
&code_challenge=...
&code_challenge_method=S256
关键点说明:
client_id
xu.deploy.env。容器里从 LAZYCAT_AUTH_OIDC_CLIENT_ID 读redirect_uri
lzc-manifest.yml 里的 application.oidc_redirect_path 决定,懒猫会把它拼成 https://<subdomain>.<盒子>.heiyu.space/<path>openid scope
state / nonce
code_challenge
这一跳之后,浏览器会被懒猫 SSO 接管,用户看到的是懒猫那套统一的“登录 / 授权确认”页面(Grant Access)。
常见报错:
invalid_redirect_uri
redirect_uri 不在白名单 —— 在懒猫里,白名单是用 oidc_redirect_path 隐式注册的,你没在 manifest 里写,就不会被允许invalid_scope
scopes_supported 之外的 scope(比如自己脑补的 admin)unauthorized_client
oidc_redirect_path)Grant Access 完成后,浏览器被重定向回:
https://env.alice.heiyu.space/callback?code=abc123&state=xyz
Client 必须做的第一件事:
state(对上第 2 步生成的那个,对不上直接拒绝,不要进入 /token)常见情况:
access_denied
login_required / interaction_required
这是 Client 容器与懒猫 SSO 的直连请求(从用户浏览器看不到)。
POST https://alice.heiyu.space/sys/oauth/token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic base64(client_id:client_secret) # client_secret_basic
grant_type=authorization_code
&code=abc123
&redirect_uri=https%3A%2F%2Fenv.alice.heiyu.space%2Fcallback
&code_verifier=zzz
其中:
client_id = LAZYCAT_AUTH_OIDC_CLIENT_IDclient_secret = LAZYCAT_AUTH_OIDC_CLIENT_SECRETclient_secret_basic 和 client_secret_post 两种方式,任选PKCE 校验发生在这里:
SHA256(code_verifier) == bound_challenge ?
返回体典型形态:
{
"access_token": "eyJ...",
"id_token": "eyJ...",
"refresh_token": "...", // 只有带了 offline_access 才会有
"token_type": "bearer",
"expires_in": 86400
}
最常见报错(真实世界第一名):
invalid_grant
code_verifier 不匹配👉 90% 的懒猫 SSO 排障时间,都花在这里
拿到 token ≠ 可以直接 jwt.decode(id_token)["email"] 然后登录用户。
ID Token 是 JWS,结构是:
header.payload.signature
验签流程是固定的:
alg(懒猫固定 RS256)、kidjwks_uri(https://alice.heiyu.space/sys/oauth/keys)拉 JWKSkid 找到公钥必须注意的反模式:
jku / x5u(永远从 discovery 绑定的 jwks_uri 去拿)alg=noneLAZYCAT_AUTH_OIDC_ISSUER_URI 以外的地方“凑”出 jwks 地址这是懒猫 SSO 最核心、也最容易被省略的一步。
必须校验的 claims:
iss
https://alice.heiyu.space/sys/oauth(即 LAZYCAT_AUTH_OIDC_ISSUER_URI)aud
LAZYCAT_AUTH_OIDC_CLIENT_ID(也就是你的 App 包名,如 xu.deploy.env)exp
nonce
azp
一句非常重要的话:
签名验证通过,只能说明 token 是懒猫 SSO 发的,
不能说明 token 现在可以被你用。
Scope 经常被误解成“权限”。在懒猫里,它真正的语义是:
Client 向懒猫 SSO 声明的授权意图范围
懒猫 scopes_supported 就 5 个:
openid
email
email、email_verifiedprofile
name、preferred_username、localegroups
offline_access
refresh_token重要澄清:
正确做法是:
Scope → claims(特别是 groups / preferred_username) → 你自己的业务权限判断
在懒猫里这张表其实很短,因为懒猫 SSO 已经替你做了决定:
| 模式 | 在懒猫里能不能用 | 用途 |
|---|---|---|
| Authorization Code + PKCE | ✅ 默认 | Web / SPA / 内置应用 |
| Client Credentials | ❌ 懒猫 SSO 没在 grant_types_supported 里开 | — |
| Device Code | ✅ 有 device_authorization_endpoint | CLI / 智慧屏这类无浏览器场景 |
| Refresh Token | ✅ 需要 offline_access | 会话续期 |
| Token Exchange | ✅ 高级用法 | 跨应用换 token |
| Implicit | ❌ | 已过时 |
经验法则:
在懒猫上,默认就选 Authorization Code + PKCE。其他模式有特殊需求再上。
invalid_grantredirect_uri 是否和第 2 步字符级一致(尤其是端口、尾部斜杠、大小写)invalid_clientLAZYCAT_AUTH_OIDC_CLIENT_SECRET 没读对(经常是容器没拿到环境变量)client_secret_basic 和 client_secret_post,别用 noneinvalid_scopescopes_supported 之外的 scopeunauthorized_clientlzc-manifest.yml 里根本没写 application.oidc_redirect_path,懒猫没有为你这个包名注册 clientlogin_required / interaction_required/sys/oauth/auth如果你一路走完,会发现:
iss / aud / nonce 防的是 token 被误用lzc-manifest.yml 里那一行 oidc_redirect_path,懒猫 SSO 的安全性,不在某一个字段,
而在一整条不可跳过的验证链。
懒猫 SSO 从来没有承诺“帮你搞定登录”,
它只是把懒猫 SSO 封装成了“不用你填 client 表单”的形态,
给了你一套:
在家庭服务器这种半可信环境里,
逐步建立有限信任的工程方法。
只要你能回答:
“我为什么在这里相信这个懒猫账号?”
你就已经在正确地使用懒猫 SSO了。
后面九章,会把这张流程图拆开:
/sys/oauth/keys 到底在干什么groups 不是你的权限附录 A、B:Scope 在流程中的位置;state / nonce 对照。

评论
0暂无评论