VoteFirst is a feedback board, and its single sign on is one signed token, not a protocol suite. Your own server signs a JSON Web Token with a shared secret, VoteFirst checks it against that secret, and the vote lands under your customer's own identity instead of a second account they had to make. It is a Pro feature, $39 a month, and it is JWT only.
How does a customer end up signed in on the board
Your server signs a token and hands it to the board, in one of two places.
On the web, the board reads a vf_token value from the query string or from a posted form field, verifies it, and resolves it to a voter before it reads or writes anything else for that request. In the iOS app, the same token goes to VoteFirst.signIn(ssoToken:), called on launch the way you restore your own session. Either way, nothing about your customer travels except what you put in the token, and the token is useless outside the one project it was signed for.
A token with no matching secret, an expired token, or a token signed by anything other than that project's own secret is refused. The customer falls back to the anonymous voter their browser or device already had, so a bad token leaves them able to take part rather than stranded.
What the token has to carry
| Claim | Required | What it holds |
|---|---|---|
sub |
Yes | Your own id for this person, as a string or a whole number. VoteFirst turns it into the voter id sso_{project}_{sub}, so the same sub always lands on the same voter |
exp |
Yes | The token's expiry, as a Unix timestamp. A token with no exp is refused outright |
name |
No | What their comments and votes are signed with |
email |
No | Kept on their voter profile. Never shown on the public board |
A numeric sub has to be a whole number no larger than a JSON number holds exactly, and not zero. Send an id that large as a string instead.
Where the secret comes from, and how to replace it
On the project's Share page, as the project owner. The value is shown once, the same way an API key is, and the page afterward shows only a masked version of it.
Pressing the button again replaces the secret rather than adding a second one. Every token already signed with the old secret stops working the moment the new one is saved, because verification checks against whichever secret is stored now. Treat a new secret the way you would treat rotating any other signing key, by deploying your server's copy of it before anyone hits a board page with a token signed under the old one.
What signing in does and does not do to a voter's history
Signing in resolves to one voter id built from your sub, and every vote and comment from that token lands under it from then on. Calling it again with the same sub reaches the same voter and the same history, on any device.
It does not merge with whatever that person did anonymously before you added sign in. The device's own anonymous voter and the token's sso_ voter are two different ids, and nothing on VoteFirst's side joins them. A customer who voted anonymously last week and signs in today starts a new voting history under their signed in identity.
A signed in voter is also the one a tier and a revenue figure can be attached to, through the Stripe, Paddle or CSV import on the voters screen. The token itself carries no money. Weighting is the page for what that tier does to a vote, and the voter list is where the tier gets assigned.
Signing a token on your own server
Node, using the jsonwebtoken package, and the secret from the Share page.
const jwt = require("jsonwebtoken");
const token = jwt.sign(
{
sub: user.id,
name: user.name,
email: user.email,
},
process.env.VOTEFIRST_SSO_SECRET,
{ algorithm: "HS256", expiresIn: "1h" }
);
Go, using golang-jwt/jwt, the same library VoteFirst itself verifies against.
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"sub": user.ID,
"name": user.Name,
"email": user.Email,
"exp": time.Now().Add(time.Hour).Unix(),
})
signed, err := token.SignedString([]byte(os.Getenv("VOTEFIRST_SSO_SECRET")))
Both sign with HMAC SHA-256, the only algorithm VoteFirst's verification accepts. A token signed with anything else, including an asymmetric algorithm, is refused before its claims are even read.
What this is next to the other feedback tools
Read on the vendor's own site, 2026-10-02.
| VoteFirst | Canny | Featurebase | Upvoty | Frill | |
|---|---|---|---|---|---|
| Protocol | JWT only | JWT only | JWT only | JWT, plus SAML and OIDC | JWT only |
| Signing | HMAC SHA-256 | HMAC SHA-256 | HMAC SHA-256 | Not stated for JWT | Not stated |
| Required claims | sub, exp |
id, email |
Not stated as required | Not stated | email |
| Plan it needs | Pro, $39 a month | Not stated on the SSO page itself | Not stated on the SSO page itself | SAML and OIDC named for enterprise plans; JWT plan not stated | Growth plan or higher |
Upvoty is the only one of the four offering more than a signed token, with SAML and OIDC alongside JWT for enterprise customers. VoteFirst does not have either of those, on any plan.
What this does not do
No SAML. No OIDC. JWT is the only protocol, on every plan that carries SSO at all, which is Pro.
No directory sync. A token is read once, when it arrives, and nothing calls back to your identity provider for a list of users or a list of groups.
No single sign on into the VoteFirst dashboard itself. This token signs a visitor into one project's board. Your own team still signs into VoteFirst with VoteFirst's own login.
No merge of anonymous history into a signed in voter, as covered above.
A few questions this raises
Does the Lite plan get any of this? No. Only a project owner on Pro can generate the secret, and without a secret there is nothing to sign a token with.
Can a token be signed for a user with no email yet? Yes. email is optional, and VoteFirst never needs an address to accept a token, only a sub and an exp.
What happens to a banned voter who still signs in? The same thing that happens to a banned anonymous voter. Reading, voting and liking keep working. Commenting and suggesting are refused.
Can a customer view a different customer's votes by changing the token? Not without your signing secret. The secret lives on your server and never ships to a browser or an app, so forging a sub means forging a valid signature, which needs the secret itself.
Does this work from a mobile app, not just a web page? Yes, through the same call the Swift package exposes, VoteFirst.signIn(ssoToken:). What that package covers on iOS is its own page.
What this costs
Pro is $39 a month or $390 a year, and SSO is on it. There is no Lite path to this feature. The trial runs 14 days with a card at signup, and SSO works during a Pro trial.
The API is the other half of talking to a board from your own server, and the voter list is where a signed in voter's tier and history show up.
14 days, a card at signup, then $9 or $39 a month.