Integrations

Single sign-on

Readers signed in on your site are signed in to the comments too. No second account, no second login.

How it works

  1. While a reader is signed in to your site, your backend signs a short-lived token that says who they are.
  2. Your page passes that token to the comment widget in the sso-token attribute.
  3. Crowment checks the signature and signs the reader in to the comments under that identity.

The token is signed with a secret that only your server and Crowment know, so nobody can sign readers in as someone else. Single sign-on is available on every plan.

1. Turn it on

Open Site Console → your widget → Single Sign-On. Owners and admins can change the settings.

Create the signing secret

Only the widget owner can do this. The secret is shown once: store it in your backend's environment, for example as CROWMENT_SSO_SECRET. Never put it in browser code, an app bundle or a public repository.

Pick a mode

  • Off: readers sign in with Crowment only.
  • Optional: readers signed in on your site are signed in automatically; Crowment sign-in stays available.
  • Required: only your site's accounts can sign in. Comments and sessions that already exist stay.

Set your sign-in and sign-out pages (optional)

Both must be on your widget's domain. The widget's login button sends readers to your sign-in page with a return_to parameter holding the page they came from. Check that return_to points to your own site before redirecting to it.

2. Sign a token on your server

Sign a JWT with HS256 and your secret, on every page view where a reader is signed in.

ClaimRequiredValue
subyesThe reader's user ID on your site, as a string, at most 128 characters. Use an ID that never changes and is never reused; an email address is a poor choice.
nameyesThe name shown next to their comments, up to 100 characters. HTML is removed.
avatarnoAn https:// image URL. Anything else is ignored.
badgesnoKeys of the badges the reader holds, e.g. ["vip"], matching the SSO keys set under Badges. Leave it out to keep their badges as they are; [] removes the badges they got this way. Badges given in the dashboard stay.
audyesYour widget's app key, exactly as in the embed code.
iat, expyesIssued-at and expiry, in seconds. The token may live at most one hour. A minute of clock difference is tolerated.

An email claim is not needed and not used: Crowment never matches accounts by email.

// 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. Pass it to the widget

With server-rendered pages, put the token in the embed code:

<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>

Pass a token on every page where the reader is signed in. A page without one tells the widget the reader signed out, and it signs them out of the comments too. Visiting more pages does not sign them in again each time: the widget keeps its session and only exchanges a new token when the reader changes.

In a single-page app, set and remove the attribute as your login state changes, and listen to the widget's events:

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: the widget needed to sign the reader in, but the token had expired. Set a fresh one.
  • crow:login-required: Required mode, no sign-in page set, and the reader pressed the login button.

Readers who signed in through your site sign out on your site. Without a sign-out page the widget hides its own sign-out button, since leaving the token out on the next page signs them out anyway.

4. Account linking

A reader who already has a Crowment account can link it from the comment section, with the link icon next to their avatar. They sign in to Crowment in a popup and confirm on a Crowment page. From then on your site signs them in as their Crowment account, and the comments they wrote with their site account move to it.

Linking lets your site sign that reader in as their Crowment account on your own pages; the confirmation page tells them so. They can unlink at any time under Profile → Linked sites in the User Console; their comments stay with them. You can switch linking off with Account linking in the SSO settings.

Names and avatars from your token update the reader's comment profile on every sign-in. Linked Crowment accounts keep their own name and avatar.

Security

  • Keep the secret on your server. Anyone who has it can sign readers in as anybody on your site.
  • If it leaks, choose Replace secret. Tokens signed with the old secret stop working at once, so deploy the new one right away. Readers already signed in to the comments stay signed in.
  • Sign tokens only for the reader of the current request, and keep them short-lived.

Troubleshooting

SymptomLikely cause
invalid_sso_tokenWrong or replaced secret, aud is not the app key, an algorithm other than HS256, sub sent as a number, an empty name, or a lifetime over one hour.
sso_token_expiredThe token's exp has passed, or your server clock is off. Sign a new token per page view.
SSO is not enabled for this appThe mode is Off, or no secret was created.
Request origin does not matchThe page is not on your widget's domain or one of its subdomains.