#ConvexHttpClient - accessing internal functions / authentication without tokens

5 messages · Page 1 of 1 (latest)

real smelt
#

@small osprey

When using the in a node.js environment, if I need to use queries and mutations on behalf of users (given specific document IDs or query params) where I don't have access to auth tokens - as this is performed as part of background async jobs. What's the recommended pattern for performing those queries and mutations?

Here's two patterns:

  1. Public queries and mutations, but instead of using ctx.auth.getUserIdentity() to validate users, it instead validates a shared environment variable. As recommended here: https://docs.convex.dev/auth.

  2. Internal queries and mutations which can be accessed via the ConvexHttpClient by setting admin auth.

Personal bias, I prefer option 2 and that's the method I'm attempting, because I prefer the fact that the admin/system queries and mutations are never exposed publicly. However, when using option 2, I noticed in the github repo, https://github.com/get-convex/convex-js/blob/main/src/browser/http_client.ts, setAdminAuth is marked as @internal - meaning accessing it requires some type gymnastics ("(client as any).setAdminAuth(process.env.CONVEX_DEPLOY!);" to get it working). Does that mean it's not the recommended approach? Along with that, client.query() expects a public query, so additional type gymnastics is required to get that working with internal queries - such as // @ts-ignore - which unfortunately removes the implicit type.

To recap, what is the recommended / intended design pattern for solving the problem stated above? Which is running queries and mutations on an external service, where no auth token is available - but there's an obvious need to perform authentication for the system (It's my servers attempting to perform these admin level queries and mutations).

Is design pattern 1, 2 or an alternative pattern recommended?

Thanks!

small ospreyBOT
#

Hi @real smelt. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:

#

The recommended and most widely supported pattern for running Convex queries and mutations from an external service (like a Node.js background job) where you do not have user auth tokens is:

Pattern 1: Public queries/mutations with a shared secret for authentication

  • You define your Convex functions (queries/mutations/actions) as public.
  • You add a required argument (e.g., secret) to these functions.
  • Your external service (e.g., a Node.js job) includes the shared secret (from an environment variable) in each request.
  • The Convex function checks that the provided secret matches the expected value (also from an environment variable in Convex).
  • If the secret does not match, the function throws an error and does not proceed.

This approach is explicitly recommended in the Convex documentation and by Convex support staff for service-to-service authentication, especially when you do not have a user context or JWT available. It is simple, secure (as long as you keep the secret safe), and does not require any internal or admin-only APIs. Example guidance:

To provide access to Convex queries, mutations, and actions to an external service you can write public functions accessible to the internet that check a shared secret, for example from an environment variable, before doing anything else.
Authentication

Yes, a shared secret is the way to do this. You can set this shared secret in your convex deployment environment and just verify it in your mutations/queries the CF environment calls.
Discord: Machine-to-machine auth

#

Add an argument to the queries, mutations, and actions that you want to protect, and on the first line of those functions check that the secret argument and the environment variable match. Throw if not.
Discord: fetchAction/fetchMutation auth

Pattern 2: Internal functions with admin authentication via ConvexHttpClient

  • Internal functions are designed to only be called from within Convex (other Convex functions, dashboard, or CLI), not from external clients.
  • The ConvexHttpClient does not officially support calling internal functions from external services. The setAdminAuth method is marked as @internal and not part of the public API, so using it requires type overrides and is not recommended or supported for production use.
  • The Convex team and documentation do not recommend this approach for external service-to-Convex communication.

(Contrast with internalMutation/internalQuery/internalAction, which can only be called by your own convex functions, or the convex dashboard or CLI)
Discord: Lambda auth

Summary / Recommendation

  • Pattern 1 (public functions with a shared secret) is the recommended and supported approach for external, non-user, machine-to-machine access to Convex.
  • Pattern 2 (using internal functions with admin auth from outside Convex) is not supported and not recommended, as internal functions are intentionally not exposed to external clients for security reasons.
  • If you want to restrict access to certain operations so that only your backend can perform them, use public functions with a strong shared secret and validate it in every function you want to protect.
#

If you need even more security, you can consider using a JWT issued by a backend-only auth provider, but the shared secret pattern is the most common and recommended for this use case.

If you have further questions or want to discuss advanced patterns, the Convex Discord is a good place for community and staff support.


TL;DR:
Use public queries/mutations with a shared secret argument for machine-to-machine authentication. Do not use internal functions from external services, as this is not supported by Convex's public API and requires unsupported workarounds.
Authentication | Discord: Machine-to-machine auth

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: