#Can i setup manual `Auth in Functions`?

1 messages · Page 1 of 1 (latest)

native prairie
#

I had implemented my own auth flow using next-auth without using convex's adapter. But I wanna validate in convex function call. How can i manually setup Auth In Function?

orchid adderBOT
#

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

#

Yes, you can set up manual authentication for your Convex functions when using your own auth flow with next-auth. There are a few approaches you can take:

Using JWT with Convex's built-in auth system

The most integrated approach is to configure Convex to validate JWTs issued by your Next.js server:

  1. Generate a public/private key pair using the jose library:

    import { exportJWK, exportPKCS8, generateKeyPair } from "jose";
    
    const keys = await generateKeyPair("RS256");
    const privateKey = await exportPKCS8(keys.privateKey);
    const publicKey = await exportJWK(keys.publicKey);
    const jwks = JSON.stringify({ keys: [{ use: "sig", ...publicKey }] });
    
  2. Set up your Convex auth.config.ts to recognize your domain:

    export default {
      providers: [
        {
          domain: process.env.CONVEX_SITE_URL,
          applicationID: "convex",
        },
      ],
    };
    
  3. Expose the JWKS endpoint in your Convex http.ts:

    http.route({
      path: "/.well-known/jwks.json",
      method: "GET",
      handler: httpAction(async () => {
        return new Response(process.env.JWKS, {
          status: 200,
          headers: {
            "Content-Type": "application/json",
          },
        });
      }),
    });
    
  4. Create a token fetcher function for your client:

    const fetchToken = async ({ forceRefreshToken }) => {
      // Get token from your next-auth session
      const { data } = await axios.get(`/api/session/token?force=${forceRefreshToken}`);
      return data.token;
    };
    
    convex.setAuth(fetchToken);
    

This approach allows you to use ctx.auth.getUserIdentity() in your Convex functions.

Stack: Convex with Auth.js provides more details on this approach.

Alternative: API Key Authentication

#

If you prefer a simpler approach without JWT validation, you can use a shared secret:

  1. Generate a secret and store it as an environment variable in both Convex and your Next.js app.

  2. Pass this secret with your function calls:

    const result = await fetchMutation(api.myFunction.doSomething, 
      { param1: value1 }, 
      { apiKey: process.env.SHARED_SECRET }
    );
    
  3. In your Convex functions, validate the secret:

    export const doSomething = mutation({
      args: { /* your args */ },
      handler: async (ctx, args) => {
        // Check if the API key matches
        if (ctx.auth.apiKey !== process.env.SHARED_SECRET) {
          throw new Error("Unauthorized");
        }
        
        // Continue with authorized function
      }
    });
    

This approach is mentioned by a Convex support staff in one of the Discord threads as a viable alternative when you don't need the full JWT validation flow.

Choose the approach that best fits your security requirements and implementation preferences.

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

#

@native prairie Kapa.ai is still learning and improving, please let me know how I did by reacting below.

native prairie
#

@orchid adder i wanna know more about using JWT with convex's built-in auth system ... can you share me docs

orchid adderBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

I'd be happy to share more information about using JWT with Convex's built-in auth system. Here are the relevant documentation resources:

Custom Auth with JWT

For implementing custom authentication with JWT in Convex, you can follow the approach outlined in the custom auth documentation:

Convex Custom Auth

This documentation explains how to configure Convex to validate JWTs issued by your server.

NextAuth Integration Example

If you're specifically using NextAuth, there's a detailed guide on Stack Convex that walks through the entire process:

Convex Adapter for Auth.js Setup Guide

This guide covers:

  1. Generating key pairs
  2. Exposing the public key through Convex HTTP endpoints
  3. Configuring auth.config.ts
  4. Issuing JWTs from NextAuth

JWT Implementation Steps

The implementation involves these key steps:

  1. Generate a public/private key pair using the jose library
  2. Configure your Convex backend with the public key
  3. Set up the necessary OIDC endpoints in your Convex HTTP router
  4. Issue JWTs from your NextAuth session callback:
async session({ session }) {
  const privateKey = await importPKCS8(
    process.env.CONVEX_AUTH_PRIVATE_KEY!,
    "RS256",
  );
  const convexToken = await new SignJWT({
    sub: session.userId,
  })
    .setProtectedHeader({ alg: "RS256" })
    .setIssuedAt()
    .setIssuer(CONVEX_SITE_URL)
    .setAudience("convex")
    .setExpirationTime("1h")
    .sign(privateKey);
  return { ...session, convexToken };
}
  1. Provide the JWT to the Convex client:
#
function useAuth() {
  const { data: session, update } = useSession();
  
  const convexToken = convexTokenFromSession(session);
  return useMemo(
    () => ({
      isLoading: false,
      isAuthenticated: session !== null,
      fetchAccessToken: async ({
        forceRefreshToken,
      }) => {
        if (forceRefreshToken) {
          const session = await update();
          return convexTokenFromSession(session);
        }
        return convexToken;
      },
    }),
    [JSON.stringify(session?.user)],
  );
}

Server-Side Authentication

For server-side authentication in Next.js, you can use the convexAuthNextjsToken function:

Server-side authentication in Next.js

This allows you to make authenticated requests to Convex during server rendering:

import { convexAuthNextjsToken } from "@convex-dev/auth/nextjs/server";
import { preloadQuery } from "convex/nextjs";
import { api } from "@/convex/_generated/api";

export async function TasksWrapper() {
  const preloadedTasks = await preloadQuery(
    api.tasks.list,
    { list: "default" },
    { token: await convexAuthNextjsToken() },
  );
  return <Tasks preloadedTasks={preloadedTasks} />;
}

These resources should provide you with comprehensive guidance on implementing JWT authentication with Convex's built-in auth system.

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

#

@native prairie Kapa.ai is still learning and improving, please let me know how I did by reacting below.

native prairie
#

@orchid adder can i use without adapter?

orchid adderBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

Yes, you can use JWT authentication with Convex without using the NextAuth adapter. Based on the knowledge sources, there are several approaches to implement custom authentication without relying on the adapter.

Custom JWT Authentication

You can implement your own JWT authentication flow by:

  1. Setting up your own JWT generation and validation:
export default {
  providers: [
    {
      domain: process.env.CONVEX_SITE_URL,
      applicationID: "convex",
    },
  ],
};
  1. Exposing the necessary JWKS endpoints in your Convex HTTP router:
http.route({
  path: "/.well-known/openid-configuration",
  method: "GET",
  handler: httpAction(async () => {
    return new Response(
      JSON.stringify({
        issuer: process.env.CONVEX_SITE_URL,
        jwks_uri: process.env.CONVEX_SITE_URL + "/.well-known/jwks.json",
        authorization_endpoint: process.env.CONVEX_SITE_URL + "/oauth/authorize",
      }),
      {
        status: 200,
        headers: {
          "Content-Type": "application/json",
        },
      },
    );
  }),
});

http.route({
  path: "/.well-known/jwks.json",
  method: "GET",
  handler: httpAction(async () => {
    return new Response(process.env.JWKS, {
      status: 200,
      headers: {
        "Content-Type": "application/json",
      },
    });
  }),
});
  1. Creating a custom useAuth hook to provide tokens to the Convex client:
function useAuth() {
  const { data: session, update } = useSession();

  const fetchAccessToken = async ({ forceRefreshToken }) => {
    if (forceRefreshToken) {
      // Get a fresh token
      const newSession = await update();
      return newSession?.convexToken || null;
    }
    return session?.convexToken || null;
  };

  return {
    isLoading: false,
    isAuthenticated: session !== null,
    fetchAccessToken,
  };
}
#
  1. Using the custom auth hook with ConvexProviderWithAuth:
<ConvexProviderWithAuth client={convex} useAuth={useAuth}>
  {children}
</ConvexProviderWithAuth>

This approach allows you to generate and validate JWTs without using the Convex adapter for NextAuth, while still being able to use ctx.auth.getUserIdentity() in your Convex functions.

As mentioned in one of the Discord threads, you'll need to ensure your JWT has the proper format and fields that Convex expects, particularly the sub field which should identify your user.

Stack: Convex Adapter for Auth.js Setup Guide provides more details on the JWT requirements and implementation.

I hope this helps with your custom authentication implementation!

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