Sign in with Upvera — integration

What you get

  • Sign-in closed to bot farms: every sign-in carries the person's proof-of-personhood tier (browser key · device key · device attestation).
  • Verified people who sign in to your product become Traction proof on Upvera — the count is observed at our provider.
  • The person's email isn't shared by default; if you ask, it comes only when they allow it.

Four steps

  1. 1Verify your domain on Upvera (DNS TXT or a .well-known file).
  2. 2In your startup panel, Sign in with Upvera › Add client: redirect URIs must sit under your verified domain.
  3. 3Give your library the discovery URL; authorization code flow + PKCE S256 + nonce are required.
  4. 4Place the button; verify the ID token against the JWKS and check iss.

Button

The proof button says what it gives. Use the dark version on light backgrounds and the light version on dark ones; don't change its text or shape.

Sign in with UpveraVerified human · an identity just for this site
DarkDownload SVG
Sign in with UpveraVerified human · an identity just for this site
LightDownload SVG

Endpoints

Issuer
https://id.upvera.co
Discovery
https://id.upvera.co/.well-known/openid-configuration
authorize
https://id.upvera.co/authorize
token
https://id.upvera.co/token
userinfo
https://id.upvera.co/userinfo
jwks
https://id.upvera.co/.well-known/jwks.json

Rules

  • Authorization code flow only (response_type=code). No implicit flow.
  • PKCE S256 is required; plain isn't accepted.
  • nonce and state are required. Check iss in the response (RFC 9207).
  • redirect_uri must match the registered one exactly; no query, no #.
  • ID tokens are ES256 with a kid; keys rotate — cache the JWKS, but refresh when you see an unknown kid.
  • A code lives 60 seconds and works once; a second use also revokes the tokens issued from it.
  • If you use an identity broker (Supabase Auth · Clerk · Auth0), connect your own domain for the redirect URI.

Claims

sub
A stable identity just for this site (22 characters). Web and mobile clients of the same startup on the same domain see the same value; another startup sees the same person under a different value.
upvera:human
The proof-of-personhood tier and the month it was reached. Weigh the tier by your own rule: unsigned < web_key < device_key < device_attested.
name · picture
profile scope: name, picture — if the person allows.
email
email scope: email, email_verified — if the person turns it on; off by default.
upvera:founder
upvera:founder scope: true if the person is a verified founder and allows it.

Example (Node, openid-client)

import * as client from 'openid-client';

// 1) discovery + client authentication (registered method: client_secret_basic)
const config = await client.discovery(
  new URL('https://id.upvera.co'),
  process.env.UPVERA_CLIENT_ID,      // upv_…
  undefined,
  client.ClientSecretBasic(process.env.UPVERA_CLIENT_SECRET), // upvs_…
);

// 2) sign-in: PKCE S256 + nonce + state (keep them in the session)
const code_verifier = client.randomPKCECodeVerifier();
const nonce = client.randomNonce();
const state = client.randomState();
const url = client.buildAuthorizationUrl(config, {
  redirect_uri: 'https://lumen.app/auth/upvera/callback',
  scope: 'openid profile',
  code_challenge: await client.calculatePKCECodeChallenge(code_verifier),
  code_challenge_method: 'S256',
  nonce,
  state,
});

// 3) callback: iss (RFC 9207) + state + nonce are checked, the id_token is verified against the JWKS
const tokens = await client.authorizationCodeGrant(config, new URL(req.url, 'https://lumen.app'), {
  pkceCodeVerifier: code_verifier,
  expectedNonce: nonce,
  expectedState: state,
});
const claims = tokens.claims();
// claims.sub                 → an identity just for this site
// claims['upvera:human']     → { tier: 'web_key' | 'device_key' | 'device_attested' | 'unsigned', since: '2026-05' }

Privacy

Upvera keeps which products a person signs in to only as a day bucket and a hash, for 400 days; the person can disconnect anytime. Public counts under 5 people aren't shown.

Questions and security reports: destek@upvera.co