id_token is validated by Convex itself. For a custom backend (Hono, Express, Elysia, etc.) backed by Postgres or SQLite, you validate the JWT on each request and map sub (Ave identity UUID) to your users table.
Verify id_token on the server
Use verifyAveIdToken from @ave-id/sdk/server — it wraps JWKS verification and checks aud against your registered clientId.
Shortcut — parse the header and verify in one step:
verifyAveIdToken on the raw JWT string.
Pass the same id_token your SPA would send in the Authorization header. Do not accept access_token_jwt as a substitute for identity unless you intentionally validate the API JWT audience — for app users, id_token is the right signal.
Drizzle upsert (Postgres or SQLite)
Example: one row per Ave identity, keyed byave_identity_id.
onConflictDoNothing then findFirst by aveIdentityId, or try/catch on unique constraint and findFirst on retry.
Use a unique index on ave_identity_id. If you need a stable ID across Ave identities for one human user, consider the user_id scope and separate columns — see Convex custom auth for how sub vs user_id differ.
Scopes
Requestopenid profile email offline_access for SPAs that refresh tokens; the server only needs the Authorization: Bearer value carrying the id_token (or your own session cookie after establishing trust once).