Identity
SSO quickstart
Have your own signed in users vote and comment under their real names, in three steps. Nobody creates a VoteFirst account, and nothing about your sign in changes.
The shape of it: your server signs a short lived token saying who this person is, your page hands that token to the script, and the board takes them at their word because only your server could have signed it.
This page assumes the embed script is already running on your page. If it is not, start with the embed quickstart, which is three lines and takes a minute.
-
Generate the signing secret
In the dashboard, under Share & Embed, find SSO secret and press Generate. It is shown in full once, so take it then and put it wherever your server keeps its secrets.
This secret mints identities. Anyone holding it can sign a token claiming to be any of your users on this board. Keep it on a server you control and never put it in a page.
-
Sign a token on your server
The token is a JSON Web Token with four claims, two of them required:
sub, your own id for this person, andexp, when the token stops working.const jwt = require('jsonwebtoken'); const token = jwt.sign( { sub: String(user.id), email: user.email, name: user.name }, process.env.VOTEFIRST_SSO_SECRET, { algorithm: 'HS256', expiresIn: '1h' } );import os, time, jwt token = jwt.encode( { 'sub': str(user.id), 'email': user.email, 'name': user.name, 'exp': int(time.time()) + 3600, }, os.environ['VOTEFIRST_SSO_SECRET'], algorithm='HS256', )import ( "os" "strconv" "time" "github.com/golang-jwt/jwt/v5" ) claims := jwt.MapClaims{ "sub": strconv.Itoa(user.ID), "email": user.Email, "name": user.Name, "exp": time.Now().Add(time.Hour).Unix(), } token, err := jwt.NewWithClaims(jwt.SigningMethodHS256, claims). SignedString([]byte(os.Getenv("VOTEFIRST_SSO_SECRET")))use Firebase\JWT\JWT; $token = JWT::encode( [ 'sub' => (string) $user->id, 'email' => $user->email, 'name' => $user->name, 'exp' => time() + 3600, ], getenv('VOTEFIRST_SSO_SECRET'), 'HS256' );require 'jwt' token = JWT.encode( { sub: user.id.to_s, email: user.email, name: user.name, exp: Time.now.to_i + 3600 }, ENV.fetch('VOTEFIRST_SSO_SECRET'), 'HS256' )Hand the token to your page the way you hand it anything else about the signed in user: rendered into the template, or fetched from an endpoint of your own.
-
Hand it to the script
In your pageVoteFirst.init({ project: 'your-project-slug', key: 'your-embed-key' }); VoteFirst.client.signIn(TOKEN_FROM_YOUR_SERVER) .catch(function (error) { console.error(error.message); }); VoteFirst.roadmap('#roadmap');initcomes first. After that the sign in and the widget calls may be in either order, because a widget already on the page reloads itself when a visitor signs in, and again whenVoteFirst.client.signOut()is called.The sign in is held in memory, so your page calls it on every load. Votes cast before signing in stay with the anonymous identity that cast them.
Check that it worked
Two ways, and it is worth doing both the first time.
In the dashboard, under Share & Embed, paste what your server produced into Test a token. The check records nothing, spends none of your project's budget, and names the reason a token is refused instead of answering every refusal with the same sentence.
On the page, vote on something while signed in and open Voters in the dashboard. The vote is against the person, with the name and email the token carried, rather than against a generated handle.