← Features

JWT single sign on, so a vote carries your customer's real name

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.

The voter list, one row per person, with their tier and the revenue behind their votes
A signed in voter's row, with the tier and revenue a CSV or Stripe import attached to their account.

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.

Related reading

The feature request API, and every endpoint it has Customer feedback segmentation, and the voter list behind every count Feature voting inside the app your iOS users already have open A feature request board that needs no account to vote