Single Sign-On (SSO 单点登录) 是在企业/大平台之间非常常用的技术,旨在为用户提供一个统一的体验在登陆各种服务,也不用劳烦大脑记住各种不同的帐号密码,然后封号也只封禁一个帐号就好了。
Solarpass(居然有人不知道 Solarpass 是 Solar Network 的帐号也是神了)也支持 OAuth,还支持 OpenID Connect(OIDC)。所以你可以在 Solsynth 提供的各种服务,比如说 src.solsynth.dev、authmc.solsynth.dev、还有 MaidKit 以及各种 Solar Network 的周边项目使用 Solarpass,同步你的个人资料,然后小羊也不用重复写各种封控,注册,等等,皆大欢喜。
How does it works?
好久没有见到 Devlog 里面有 “How does it works?” 环节了吧,之前都是太谐的没什么技术含量的东西,虽然本次也没有很多技术含量就是了w
一般 SSO 的很流行的协议有两种,OAuth 和 SAML;SAML 是基于 XAML 比较“复古”的协议了,所以 Solar Network 的 SSO 走的都是 OAuth,其是基于 JWT 的标准作为基础的。
Token 令牌(不是词元)
其实 JWT 和 XAML 本质上没有什么区别,只是 XML 变成了 JSON 而已。然后很常遇到 JWT 太大然后在 Authorization: Bearer <JWT> 传给服务器的时候拿到 Header Too Large 的错误。所以 Solar Network 用的是 JWT + 数据会话的设计。
{
"aud": "https://solian.app",
"epoch": "0",
"exp": 1786516966,
"iat": 1786257766,
"is_superuser": "1",
"iss": "solar-network",
"jti": "37bb1984-56c0-4fa2-bc89-5cf5066dd363",
"name": "littlesheep",
"nbf": 1786257766,
"nick": "ラムです",
"perk_level": "3",
"region": "CN",
"scope": "*",
"sid": "37bb1984-56c0-4fa2-bc89-5cf5066dd363",
"sub": "00000000-0000-0000-0000-000000000001",
"type": "user",
"ver": "3"
}
如上,是一个简单的 Solar Network Access Token 的 JWT Payload (Claims),看起来很多,但是实际上并不少,让我一一解释一下。
aud:这个 Token 签发给谁的,如果是 OAuth 给别的 App 会替换这个(audience)epoch:这个 Token 刷新了多少次,以免有人拿 Refresh Token 给我派生出来一堆 Access Token(非标)exp:过期时间(expires_at)iat:签发时间(issue_at)nbf:在什么时间之前不生效(not_before)iss:签发服务(issuer)sub:主题,在这里就是用户 ID(subject)scope:作用范围sid:Session ID(非标)jti:JWT ID(标准,这里和 SID 一样是为了兼容性)verPayload 版本(非标)type令牌类型(非标)可能为api_key、oidc、user等
其他都没有什么很重要的了,都是 SN 业务相关的。可以看到这个 Payload 没有权限这一载荷,一方面权限是会在服务器动态更新的,另一方面是如果把用户有的说有权限都放进来会让 Token 太大。
看到这个 perk_level 你可能会想问如果我把它改成 3 是不是就可以伪装 Stellar Program Supernova?答案是不行的,有两层原因,首先如果你改了 Payload JWT 的签名会失效,服务器为给你严肃 401。另一个原因是其实这个 perk_level 不怎么会用到,DysonNetwork 的授权检查都会走 gRPC Call 打电话给 Stargate 授权服务获取用户实时的 Stellar Program 订阅状态(虽然有缓存)。
这个也是我们 JWT + Session 架构的优势所在,虽然服务器 Setup 比较繁琐一点,而且 Stargate 验证服务和其他服务是强耦合的,如果身份服务挂掉了其他服务也会扑街。但是我们可以确保数据是实时准确的,而且如果用户发现自己帐号被盗了或者登出的话服务器清除 Session 也会让相关的 Token 全部 401,避免了其他纯 JWT 架构的情况下还要维护一个各个服务中的失效 JWT 列表的情况下,反而和我们的差不多。
授权
虽然 Solarpass 的令牌在 Solar Network 内部服务之间流转可以直接通用,不用走 OAuth。但是登陆到别的服务还是要使用 OAuth 来验证的。
OAuth 2.0 设计出来最开始是为了允许第三方以用户的身份访问自己服务上的资源的,但是后来 IETF 在上面发展出来了 OIDC (OpenID Connect),来支撑如 Sign in with Google 这样的 Social Login。此时的中心从你能不能从 IdP(Identity Provider,身份服务)读取什么资源到 IdP 说明你是你。
换句话说,OAuth 是 Authorization(授权)用来表明这个 App 能访问什么,而 OIDC 是 Authentication(认证)用来表明谁是这个用户。
啊看不明白也没关系,反正两者在调用上没有什么本质上的区别,OIDC 是 OAuth 2.0 的超集,本质上就是多加了点表明用户身份的 id_token、userinfo_endpoint 和自动发现 discovery document 而已。
关于什么时候需要认证,而什么时候需要授权我个人的理解是这样的:
- 认证:你的 App 拥有自己的登陆流程,帐号系统,只是用 IdP 帮助用户快速登录而已,不会和 IdP 做用户信息获取之外的其他操作。
- 授权:你的 App 需要经常和 IdP 的服务打交道,比如说 MaidKit 要经常去找 SolWatt Flywheel 同步数据之类的。
关于具体客户端和服务器怎么实现就不在这里提了,因为这种东西已经是行业标准了,AI 也很熟练,生态系统也很成熟,基本不是很复杂。
使用
其实用户使用起来非常简单,你可能在使用其他小羊的 App 的时候会发现 Sign in with Solarpass 的选项,点一下发现弹到了 solian.app,点一下登陆你就登陆了(前提是你登陆了 FloatLand,没有的话就是登陆一下也没有很复杂),这也是做 SSO 的最终目的,加速用户的登陆流程在自己的其他服务中。
但是你可能没想到一个简单的 Authorize 按钮地下隐藏了这么多标准和智斗吧(
岁月史书
小羊第一个做的项目就是 IdP,为什么呢?因为感觉在自己的服务之间用 SSO 很方便也很酷。然后光阴似箭,岁月如梭,就到现在了(跳过了太多东西了吧w)。不过不管从 v0,到 v1,还是 v2,v3 第一个实现的都是身份验证服务和 OAuth,事到如今也可以说 Solarpass 是 Battle-tested 的吧。
来到 v3.5 时代,Padlock 被蛋雕了,而 Passport 也不再是核心服务的部分了,羊把所有的核心都压在了 Stargate 这个新生的 Go 写的身份服务身上,其很轻,非常轻。所以你也可以轻松的部署一个自己来玩,也不依赖任何外部服务(你可能需要个 Blade 就是,但是直接接反向代理也可以的)
当然我也不知道你为什么需要 Stargate,如果就单纯作为 IdP 来说的话,Authentic 他们可能更复杂确实,但是更强大的说。不过如果你要部署其他 Solar Network Ecosystem 的服务来说的话这就是必不可少的了。
羊单独把 Padlock 梦想改造家改为 Stargate 有两个目的,一是为了提高 Solarpass 的稳定性,因为 Solarpass 被依赖的越来越多,不想成为 All in boom 的架构。二是帮助其他想折腾(自己部署 Solar Network 个别服务)的人可以更好的开始。也不用那么复杂的配置。
不管怎么样,代码在这里,AGPL-3.0:https://github.com/Solsynth/Stargate
