#Fully Custom Authentication System
11 messages · Page 1 of 1 (latest)
To use a fully custom authentication system with Convex—such as authenticating users through Steam (which does not support OpenID Connect) and issuing your own JWTs from a Cloudflare Worker—you should use Convex's Custom JWT Provider feature.
Here’s how you can set this up:
1. Configure Convex for Custom JWT
In your convex/auth.config.js (or .ts), add a provider with type: "customJwt". You must specify:
issuer: This must exactly match theissfield in your JWT.jwks: The URL where Convex can fetch your public key(s) in JWKS format to verify the JWT signature.algorithm: The signing algorithm (usually "RS256" or "ES256").applicationID(optional but recommended): If you include this, the JWT’saudclaim must match.
Example:
export default {
providers: [
{
type: "customJwt",
applicationID: "your-app-id", // Should match the `aud` claim in your JWT
issuer: "<https://your-auth-server.com",> // Must match the `iss` claim
jwks: "<https://your-auth-server.com/.well-known/jwks.json",> // JWKS endpoint
algorithm: "RS256",
},
],
};
Make sure your JWTs have the required claims (iss, sub, exp, and optionally aud) and that the values match your config exactly. Use a tool like jwt.io to inspect your tokens and confirm this match. If you want to include custom claims, they will be accessible in Convex functions as described in the docsCustom JWT Provider.
2. Expose a JWKS Endpoint
Your Cloudflare Worker (auth server) must expose a JWKS endpoint (e.g., /.well-known/jwks.json) that returns your public key(s) in the correct format. Convex will fetch this to verify JWT signatures.
If your platform does not return the correct Content-Type (application/json or application/jwk-set+json), you may need to proxy or reformat the response, as some users have done by creating a simple API route that fetches and returns the JWKS with the correct headersDiscord: WorkOS Authkit as Custom Auth Integration with Convex.
3. Pass the JWT to the Convex Client
On the client side, use the Convex client’s setAuth or ConvexProviderWithAuth to provide the JWT for each request. Your fetchToken function should retrieve the JWT from your auth server (e.g., from local storage or via an API call) and return it to the Convex clientCustom auth.
Example (React):
convex.setAuth(async () => {
// Fetch your JWT from wherever you store it
return localStorage.getItem("jwt");
});
4. Debugging
If you see errors like "No auth provider found matching the given token," double-check that the iss and aud fields in your JWT match your Convex config exactly, and that your JWKS endpoint is accessible and returns the correct content typeCustom JWT Provider.
Summary:
You can use your own JWTs with Convex by configuring a Custom JWT Provider, ensuring your JWTs and JWKS endpoint are set up correctly, and passing the JWT to the Convex client. This approach is fully supported for non-OIDC providers like Steam, as long as you handle the JWT issuance and verification endpoints yourself.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@oblique hollow Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Let me check the knowledge base for you again. One moment! :books:
The error "Invalid auth jwks response body" typically means that Convex tried to fetch your JWKS (JSON Web Key Set) endpoint, but the response was not in the expected format or had an incorrect content type.
Common causes and solutions:
-
Content-Type Header
- Convex expects the JWKS endpoint to return either
application/jsonorapplication/jwk-set+jsonas theContent-Typeheader. If your endpoint returns a different content type, Convex will reject it. Some users have worked around this by proxying the JWKS through their own API route to ensure the correct headers are set. For example, in a Next.js API route, you can fetch the JWKS and return it with the correct content type:
export async function GET(request) { const jwks = await fetch("<https://your-auth-server.com/.well-known/jwks.json">); const data = await jwks.json(); return new Response(JSON.stringify(data), { status: 200, headers: { "Content-Type": "application/json" }, }); }Then, point your Convex
jwksconfig to this endpointDiscord: WorkOS Authkit as Custom Auth Integration with Convex. - Convex expects the JWKS endpoint to return either
-
JWKS Format
- The JWKS response must be a valid JSON object with a
keysarray. For example:{ "keys": [ { "kty": "RSA", "kid": "your-key-id", "use": "sig", "alg": "RS256", "n": "...", "e": "..." } ] } - If the response is missing the
keysarray or is not valid JSON, Convex will throw this error.
- The JWKS response must be a valid JSON object with a
- Endpoint Accessibility
- Make sure your JWKS endpoint is publicly accessible from the Convex cloud. If you are running your auth server locally (e.g., on
localhost), Convex cloud cannot reach it. You need to expose it via a public URL (e.g., using a tunnel or deploying to a public server)Discord: No auth provider found.
- Make sure your JWKS endpoint is publicly accessible from the Convex cloud. If you are running your auth server locally (e.g., on
Summary:
Check that your JWKS endpoint:
- Returns a valid JWKS JSON object with a
keysarray. - Sets the
Content-Typeheader toapplication/jsonorapplication/jwk-set+json. - Is accessible from the public internet if using Convex cloud.
If you address these points and still see the error, please share the actual JWKS response (with sensitive info redacted) for further troubleshooting.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@oblique hollow Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Let me check the knowledge base for you again. One moment! :books:
The error "Could not decode token" when using a custom JWT provider with Convex usually indicates that the backend was unable to parse or validate the JWT you provided. Based on the knowledge sources, here are the most common causes and troubleshooting steps:
-
Issuer Format (
issclaim)- The
issuerfield in your Convexauth.config.tsmust exactly match theissclaim in your JWT. If yourissueris not a full HTTPS URL, Convex may prefix it with<https://,> which can cause mismatches. For example, if your JWT has"iss": "privy.io", but Convex expects"<https://privy.io",> decoding will fail. Always ensure these match exactly, including the protocol if present. You can check this by decoding your JWT at jwt.io and comparing theissvalue to your configDiscord: "Could not decode token" custom JWT auth.
- The
-
Algorithm Support
- Convex currently only supports
RS256andEd25519algorithms for custom JWTs. If your JWT is signed with a different algorithm (e.g.,ES384orRsaSsaPssSha256), Convex will not be able to decode itDiscord: "Could not decode token" custom JWT auth.
- Convex currently only supports
-
JWKS Endpoint and Key Format
- The JWKS endpoint must return a valid JSON Web Key Set with the correct content type and structure. If the JWKS is malformed or missing required fields, Convex will not be able to decode the token.
-
Token Structure
- The JWT must be a valid, well-formed JWT. If the token is corrupted, missing segments, or not properly base64-encoded, decoding will fail.
What to do next:
- Double-check that your JWT’s
issclaim and theissuerin your Convex config match exactly. - Ensure your JWT is signed with
RS256orEd25519. - Verify your JWKS endpoint is accessible and returns the correct structure and content type.
- Use jwt.io to inspect your JWT and confirm it is valid and well-formed.
If you continue to see this error after checking these points, please provide the decoded JWT header and payload (with sensitive info redacted) and your Convex auth.config.ts for more targeted help.
References: