集成

单点登录(SSO)

已登录你网站的读者,在评论区也自动处于登录状态。无需第二个账号,也无需再次登录。

工作原理

  1. 读者登录你的网站期间,你的服务器会签发一个短期令牌,说明读者的身份。
  2. 页面通过 sso-token 属性把这个令牌传给评论组件。
  3. Crowment 校验签名,并以该身份让读者登录评论区。

令牌使用只有你的服务器和 Crowment 知道的密钥签名,因此没人能冒用他人身份让读者登录。所有套餐均可使用单点登录。

1. 开启单点登录

打开 Site Console → 你的组件 → Single Sign-On。所有者和管理员可以修改设置。

创建签名密钥

只有组件所有者可以执行此操作。密钥只显示一次:请把它保存在服务器的环境变量中,例如 CROWMENT_SSO_SECRET。切勿把它放进浏览器端代码、应用安装包或公开代码仓库。

选择模式

  • Off:读者只能用 Crowment 登录。
  • Optional:已登录你网站的读者会自动登录;Crowment 登录方式依然可用。
  • Required:只有你网站的账号可以登录。已有的评论和会话保持不变。

设置登录页和退出页(可选)

两者都必须位于组件的域名下。组件的登录按钮会把读者带到你的登录页,并附带 return_to 参数,记录读者来自哪个页面。重定向前请确认 return_to 指向你自己的网站。

2. 在服务器上签发令牌

读者处于登录状态时,每次页面浏览都要用 HS256 和你的密钥签发一个 JWT。

Claim必填值
sub是读者在你网站上的用户 ID,必须是字符串,最多 128 个字符。请使用永不改变、永不复用的 ID;邮箱地址不是好的选择。
name是显示在其评论旁的名称,最多 100 个字符。HTML 会被移除。
avatar否以 https:// 开头的图片 URL。其他值会被忽略。
badges否读者持有的徽章键,例如 ["vip"],需与 Badges 页面中设置的 SSO 键一致。省略时徽章保持不变;[] 会移除通过此方式获得的徽章。在控制台中授予的徽章会保留。
aud是组件的 app key,必须与嵌入代码中的完全一致。
iat、exp是签发时间和过期时间(秒)。令牌有效期最长一小时,允许一分钟的时钟误差。

不需要也不会使用 email claim:Crowment 从不通过邮箱匹配账号。

// npm install jsonwebtoken
import jwt from 'jsonwebtoken'

const ssoToken = jwt.sign(
  { sub: String(user.id), name: user.displayName, avatar: user.avatarUrl },
  process.env.CROWMENT_SSO_SECRET,
  { algorithm: 'HS256', audience: CROWMENT_APP_KEY, expiresIn: '1h' },
)

3. 把令牌传给组件

对于服务器端渲染的页面,把令牌写进嵌入代码:

<script defer src="https://embeds.crowment.com/comments.js"></script>

<!-- signed in: pass the token your backend signed for this page view -->
<crow-comments app-key="YOUR_APP_KEY" sso-token="SIGNED_TOKEN"></crow-comments>

<!-- signed out: leave sso-token out -->
<crow-comments app-key="YOUR_APP_KEY"></crow-comments>

读者处于登录状态的每一个页面都要传入令牌。没有令牌的页面会告诉组件读者已退出登录,组件也会让读者退出评论区。浏览更多页面不会每次都重新登录:组件会保持会话,只有当读者变化时才换用新令牌。

在单页应用中,请随登录状态的变化设置或移除该属性,并监听组件的事件:

const widget = document.querySelector('crow-comments')

// after your site signs the reader in
widget.setAttribute('sso-token', await fetchCrowmentToken())

// after your site signs the reader out
widget.removeAttribute('sso-token')

// the token expired while the page was open: hand over a fresh one
widget.addEventListener('crow:sso-expired', async () => {
  widget.setAttribute('sso-token', await fetchCrowmentToken())
})

// Required mode without a sign-in page: show your own login
widget.addEventListener('crow:login-required', () => openLoginDialog())
  • crow:sso-expired:组件需要为读者登录,但令牌已过期。请设置新令牌。
  • crow:login-required:处于 Required 模式、未设置登录页,且读者点击了登录按钮。

通过你的网站登录的读者,也通过你的网站退出。未设置退出页时,组件会隐藏自身的退出按钮,因为下一个页面不传令牌本身就会让读者退出。

4. 账号关联

已经拥有 Crowment 账号的读者,可以在评论区点击头像旁的链接图标进行关联。读者会在弹出窗口中登录 Crowment,并在 Crowment 页面上确认。此后,你的网站会以其 Crowment 账号为其登录,他用网站账号写过的评论也会转移到这个账号。

关联后,你的网站只能在你自己的页面上以其 Crowment 账号为其登录;确认页面会向读者说明这一点。读者可以随时在 User Console 的 Profile → Linked sites 中解除关联,评论仍归读者所有。你也可以在 SSO 设置中通过 Account linking 关闭关联功能。

令牌中的名称和头像会在每次登录时更新读者的评论资料。已关联的 Crowment 账号则保留自己的名称和头像。

安全

  • 把密钥保存在服务器上。拿到密钥的人可以在你的网站上以任何人的身份让读者登录。
  • 如果密钥泄露,请点击 Replace secret。用旧密钥签发的令牌会立即失效,因此请马上部署新密钥。已登录评论区的读者会保持登录。
  • 只为当前请求的读者签发令牌,并保持令牌短期有效。

故障排查

现象可能原因
invalid_sso_token密钥错误或已被更换、aud 不是 app key、使用了 HS256 以外的算法、sub 以数字形式发送、name 为空,或有效期超过一小时。
sso_token_expired令牌的 exp 已过,或服务器时钟不准。请在每次页面浏览时签发新令牌。
SSO is not enabled for this app模式为 Off,或尚未创建密钥。
Request origin does not match页面不在组件的域名或其子域名下。