#Convex + WorkOS Authentication issues
4 messages · Page 1 of 1 (latest)
It appears you are experiencing a known issue where WorkOS successfully authenticates the user, but Convex fails to recognize that identity. Based on the knowledge sources, here are the most likely causes and solutions for this specific state:
1. Configuration of auth.config.ts
The most common cause for isAuthenticated remaining false even after a successful WorkOS login is a configuration mismatch in your Convex backend.
- Remove redundant
applicationID: If you have two providers in yourconvex/auth.config.ts(to handle both old and new WorkOS issuer formats), ensure you remove theapplicationIDfield from the second provider entry. Keeping it there can cause validation to fail for newer WorkOS accounts [WorkOS/Convex Authenticated component not working; content within <Unauthenticated /> display regardless of user status]. - Issuer URL: Verify the first issuer is exactly
<https://api.workos.com/> (without your client ID at the end) [WorkOS Authkit as Custom Auth Integration with Convex].
Example recommended config:
const clientId = process.env.WORKOS_CLIENT_ID;
export default {
providers: [
{
type: "customJwt",
issuer: "<https://api.workos.com/",>
algorithm: "RS256",
applicationID: clientId,
jwks: `<https://api.workos.com/sso/jwks/${clientId}`,>
},
{
type: "customJwt",
issuer: `<https://api.workos.com/user_management/${clientId}`,>
algorithm: "RS256",
jwks: `<https://api.workos.com/sso/jwks/${clientId}`,>
// applicationID removed here
},
],
};
2. Missing aud (Audience) Claim
Convex requires the aud claim in the JWT to match your applicationID. By default, WorkOS JWTs might not include this.
- Go to your WorkOS Dashboard.
- Check your JWT configuration to ensure the audience claim is explicitly set to your WorkOS Client ID [Convex & WorkOS AuthKit].
3. Handling Race Conditions in the Auth Hook
If you are using a custom useAuth hook, you might be hitting a race condition where Convex tries to authenticate before the token is available. A recommended pattern to stabilize the auth state is to ensure isLoading correctly reflects both the user status and the token availability:
function useWorkosConvexAuth() {
const { user, loading: isUserLoading } = useAuth();
const { accessToken, loading: isTokenLoading } = useAccessToken();
const isLoading = isUserLoading || isTokenLoading;
const isAuthenticated = !!user && !!accessToken; // Ensure both exist
const fetchAccessToken = useCallback(async () => {
return accessToken ?? null;
}, [accessToken]);
return { isLoading, isAuthenticated, fetchAccessToken };
}
Adding || !accessToken to your loading logic can prevent a "flash" of unauthenticated state during the initial handshake [WorkOS Authkit as Custom Auth Integration with Convex].
4. Verification Steps
- Run
npx convex dev: Ensure your latestauth.config.tschanges are actually synced to the Convex cloud [Convex & WorkOS AuthKit]. - Check Convex Logs: If
ctx.auth.getUserIdentity()returnsnullinside a function despite the client being "authenticated," look for "Missing aud claim" or "Invalid issuer" errors in the Convex Dashboard logs [WorkOS Authkit as Custom Auth Integration with Convex].