Skip to main content
An API connector set to As the signed-in user sends your API a token on every call: a JSON Web Token, signed by Upsolve with your project’s private key, naming the user the call is for. Your API verifies it and then treats the request as that user. This page is the whole contract, written so you, or a coding agent, can follow it from top to bottom.

Your values

Open the connector in Upsolve (Project settings → API connectors). It shows three values, which you use as constants: The issuer also publishes an OpenID configuration at <issuer>/.well-known/openid-configuration, which auth providers read to accept tokens from another issuer. There is no secret. Upsolve keeps the private key; the JWKS URL publishes only public keys.

The token

It travels in Authorization: Bearer <token> unless the connector names another header and prefix.
The connector can add up to ten claims of your choosing: a property of the user or their organization (the properties you send to embed-token), or a fixed text such as role: "authenticated". Upsolve sets iss aud sub org act iat exp nbf jti itself. The header carries alg: "ES256", typ: "JWT" and a kid naming the key.

Verification rules

Verify every request that carries an Upsolve token:
  1. Signature: ES256 only. Find the key by the header’s kid in the JWKS, which you cache and fetch again when a kid is not in it (Upsolve rotates keys; an old key stays published for 24 hours). Reject a token without a kid.
  2. iss equals your issuer exactly.
  3. aud equals your audience exactly.
  4. exp is in the future. Allow at most 60 seconds of clock skew.
Then load the user by sub (and their organization by org, when present) and authorize the request exactly as if that user had signed in. Answer 401 for a missing or invalid token, and for a valid token naming nobody you know. Answer 403 for a user who may not do what was asked. Keep your existing authentication beside it: requests without an Upsolve token work as before. Use act.source if you want to limit what Upsolve-made calls may do, for example no writes from tile.

Snippets

Each one verifies all four rules. Replace the values with yours.

Using your auth provider

Supabase

  1. In the Supabase Dashboard, open Authentication → Sign In / Providers → Third-Party Auth.
  2. Click Add provider, choose Custom OIDC and enter your issuer. Supabase reads the keys through the OpenID configuration.
  3. On the connector, click Supabase compatible. It adds the claim role: "authenticated", without which requests run as anon.
  4. In your RLS policies, read the user as auth.jwt()->>'sub'. auth.uid() casts sub to a UUID, so it only works when the externalId you send to Upsolve is the user’s Supabase id.
  5. If you also use Supabase Auth, add a restrictive policy that checks auth.jwt()->>'iss', so ids from the two issuers cannot collide.

Auth0

Auth0 issues your users’ tokens; Upsolve’s tokens are checked by your API next to them. If your API uses express-oauth2-jwt-bearer, add a second auth({ issuerBaseURL: <issuer>, audience: <audience>, tokenSigningAlg: "ES256" }) and accept a request that either one accepts. With any other middleware, add Upsolve as a second trusted issuer the same way, or use a snippet above.

Clerk

Clerk verifies the sessions it issues, not Upsolve’s tokens. Keep Clerk’s middleware for your app’s own requests, and put a snippet above in front of it for requests that carry an Upsolve token. Look the user up by sub, the externalId you send to Upsolve (usually the Clerk user id).

Check it

  1. On the connector, click Copy a test token. You get a ready curl line with a token that lasts five minutes (act.source is test). Run it against your local server and confirm it answers 200 as that user.
  2. Change the audience you expect, or wait five minutes, and confirm the same call answers 401.
  3. Deploy, then run Check in Upsolve. It calls the endpoint you pick three times: with a valid token (it expects 2xx), with a token for another audience, and with an expired token (both properly signed; it expects 401 or 403). Each failure comes with what to fix. Upsolve never shows your API’s response bodies.

Keys and rotation

Each Upsolve project has one signing key at a time. Rotate signing key on the connector makes a new key sign every token from then on; the old key stays in the JWKS for 24 hours, so a token it signed a moment ago still verifies, and is then removed. A verifier that fetches the JWKS again on an unknown kid needs no change.