mirror of
https://github.com/LukeHagar/better-auth.git
synced 2025-12-10 12:27:44 +00:00
docs: add Convex integration (#4785)
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> Co-authored-by: Bereket Engida <86073083+Bekacru@users.noreply.github.com> Co-authored-by: TheUntraceable <73362400+TheUntraceable@users.noreply.github.com>
This commit is contained in:
@@ -561,4 +561,27 @@ export const Icons = {
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
),
|
),
|
||||||
|
convex: (props?: SVGProps<any>) => (
|
||||||
|
<svg
|
||||||
|
width="1.2em"
|
||||||
|
height="1.2em"
|
||||||
|
viewBox="15 15 160 160"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M108.092 130.021C126.258 128.003 143.385 118.323 152.815 102.167C148.349 142.128 104.653 167.385 68.9858 151.878C65.6992 150.453 62.8702 148.082 60.9288 145.034C52.9134 132.448 50.2786 116.433 54.0644 101.899C64.881 120.567 86.8748 132.01 108.092 130.021Z"
|
||||||
|
fill="currentColor"
|
||||||
|
></path>
|
||||||
|
<path
|
||||||
|
d="M53.4012 90.1735C46.0375 107.19 45.7186 127.114 54.7463 143.51C22.9759 119.608 23.3226 68.4578 54.358 44.7949C57.2286 42.6078 60.64 41.3096 64.2178 41.1121C78.9312 40.336 93.8804 46.0225 104.364 56.6193C83.0637 56.8309 62.318 70.4756 53.4012 90.1735Z"
|
||||||
|
fill="currentColor"
|
||||||
|
></path>
|
||||||
|
<path
|
||||||
|
d="M114.637 61.8552C103.89 46.8701 87.0686 36.6684 68.6387 36.358C104.264 20.1876 148.085 46.4045 152.856 85.1654C153.3 88.7635 152.717 92.4322 151.122 95.6775C144.466 109.195 132.124 119.679 117.702 123.559C128.269 103.96 126.965 80.0151 114.637 61.8552Z"
|
||||||
|
fill="currentColor"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1391,6 +1391,11 @@ C0.7,239.6,62.1,0.5,62.2,0.4c0,0,54,13.8,119.9,30.8S302.1,62,302.2,62c0.2,0,0.2,
|
|||||||
icon: Icons.nestJS,
|
icon: Icons.nestJS,
|
||||||
href: "/docs/integrations/nestjs",
|
href: "/docs/integrations/nestjs",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "Convex",
|
||||||
|
icon: Icons.convex,
|
||||||
|
href: "/docs/integrations/convex",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
group: true,
|
group: true,
|
||||||
title: "Mobile & Desktop",
|
title: "Mobile & Desktop",
|
||||||
|
|||||||
347
docs/content/docs/integrations/convex.mdx
Normal file
347
docs/content/docs/integrations/convex.mdx
Normal file
@@ -0,0 +1,347 @@
|
|||||||
|
---
|
||||||
|
title: Convex Integration
|
||||||
|
description: Integrate Better Auth with Convex.
|
||||||
|
---
|
||||||
|
|
||||||
|
<Callout>
|
||||||
|
This documentation comes from the [Convex documentation](https://convex-better-auth.netlify.app/),
|
||||||
|
for more information, please refer to their documentation.
|
||||||
|
</Callout>
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
<Steps>
|
||||||
|
<Step>
|
||||||
|
### Create a Convex project
|
||||||
|
|
||||||
|
To use Convex + Better Auth, you'll first need a [Convex](https://www.convex.dev/) project.
|
||||||
|
If you don't have one, run the following command to get started.
|
||||||
|
```package-install
|
||||||
|
npm create convex@latest
|
||||||
|
```
|
||||||
|
|
||||||
|
Check out the [Convex docs](https://docs.convex.dev/home) to learn more about Convex.
|
||||||
|
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step>
|
||||||
|
|
||||||
|
### Run `convex dev`
|
||||||
|
|
||||||
|
Running the CLI during setup will initialize your Convex deployment
|
||||||
|
if it doesn't already exist, and keeps generated types current through the process. Keep it running.
|
||||||
|
|
||||||
|
```package-install
|
||||||
|
npx convex dev
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
</Steps>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Installation of Convex + Better Auth
|
||||||
|
|
||||||
|
The following documentation assumes you're using Next.js.
|
||||||
|
|
||||||
|
If you're not using Next.js, please refer to the [installation guide by Convex](https://convex-better-auth.netlify.app/#select-your-framework).
|
||||||
|
|
||||||
|
<Callout>
|
||||||
|
For a complete example, check out Convex + Better Auth example with Next.js in this [Github repository](https://github.com/get-convex/better-auth/tree/main/examples/next).
|
||||||
|
</Callout>
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
<Steps>
|
||||||
|
<Step>
|
||||||
|
#### Install packages
|
||||||
|
|
||||||
|
Install the component, a pinned version of Better Auth, and ensure the latest version of Convex.
|
||||||
|
|
||||||
|
|
||||||
|
<Callout>
|
||||||
|
This component requires Convex `1.25.0` or later.
|
||||||
|
</Callout>
|
||||||
|
|
||||||
|
```package-install
|
||||||
|
npm install better-auth@1.3.8 --save-exact
|
||||||
|
npm install convex@latest @convex-dev/better-auth
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
<Step>
|
||||||
|
#### Register the component
|
||||||
|
|
||||||
|
Register the Better Auth component in your Convex project.
|
||||||
|
|
||||||
|
```ts title="convex/convex.config.ts"
|
||||||
|
import { defineApp } from "convex/server";
|
||||||
|
import betterAuth from "@convex-dev/better-auth/convex.config";
|
||||||
|
|
||||||
|
const app = defineApp();
|
||||||
|
app.use(betterAuth);
|
||||||
|
|
||||||
|
export default app;
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
<Step>
|
||||||
|
#### Add Convex auth config
|
||||||
|
|
||||||
|
Add a `convex/auth.config.ts` file to configure Better Auth as an authentication provider.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```ts title="convex/auth.config.ts"
|
||||||
|
export default {
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
domain: process.env.CONVEX_SITE_URL,
|
||||||
|
applicationID: "convex",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
<Step>
|
||||||
|
#### Set environment variables
|
||||||
|
|
||||||
|
Generate a secret for encryption and generating hashes. Use the command below if you have openssl installed,
|
||||||
|
or use the button to generate a random value instead. Or generate your own however you like.
|
||||||
|
|
||||||
|
```package-install
|
||||||
|
npx convex env set BETTER_AUTH_SECRET=$(openssl rand -base64 32)
|
||||||
|
```
|
||||||
|
|
||||||
|
Add your site URL to your Convex deployment.
|
||||||
|
|
||||||
|
```package-install
|
||||||
|
npx convex env set SITE_URL http://localhost:3000
|
||||||
|
```
|
||||||
|
|
||||||
|
Add environment variables to the `.env.local` file created by `npx convex dev`.
|
||||||
|
It will be picked up by your framework dev server.
|
||||||
|
|
||||||
|
```shell title=".env.local" tab="Cloud"
|
||||||
|
# Deployment used by \`npx convex dev\`
|
||||||
|
CONVEX_DEPLOYMENT=dev:adjective-animal-123 # team: team-name, project: project-name
|
||||||
|
|
||||||
|
NEXT_PUBLIC_CONVEX_URL=https://adjective-animal-123.convex.cloud
|
||||||
|
|
||||||
|
# Same as NEXT_PUBLIC_CONVEX_URL but ends in .site // [!code ++]
|
||||||
|
NEXT_PUBLIC_CONVEX_SITE_URL=https://adjective-animal-123.convex.site # [!code ++]
|
||||||
|
|
||||||
|
# Your local site URL // [!code ++]
|
||||||
|
SITE_URL=http://localhost:3000 # [!code ++]
|
||||||
|
```
|
||||||
|
|
||||||
|
```shell title=".env.local" tab="Self hosted"
|
||||||
|
# Deployment used by \`npx convex dev\`
|
||||||
|
CONVEX_DEPLOYMENT=dev:adjective-animal-123 # team: team-name, project: project-name
|
||||||
|
|
||||||
|
NEXT_PUBLIC_CONVEX_URL=http://127.0.0.1:3210
|
||||||
|
|
||||||
|
# Will generally be one number higher than NEXT_PUBLIC_CONVEX_URL,
|
||||||
|
# so if your convex url is :3212, your site url will be :3213
|
||||||
|
NEXT_PUBLIC_CONVEX_SITE_URL=http://127.0.0.1:3211 # [!code ++]
|
||||||
|
|
||||||
|
# Your local site URL // [!code ++]
|
||||||
|
SITE_URL=http://localhost:3000 # [!code ++]
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
<Step>
|
||||||
|
### Create a Better Auth instance
|
||||||
|
Create a Better Auth instance and initialize the component.
|
||||||
|
|
||||||
|
<Callout>Some TypeScript errors will show until you save the file.</Callout>
|
||||||
|
|
||||||
|
```ts title="convex/auth.ts"
|
||||||
|
import { createClient, type GenericCtx } from "@convex-dev/better-auth";
|
||||||
|
import { convex } from "@convex-dev/better-auth/plugins";
|
||||||
|
import { components } from "./_generated/api";
|
||||||
|
import { DataModel } from "./_generated/dataModel";
|
||||||
|
import { query } from "./_generated/server";
|
||||||
|
import { betterAuth } from "better-auth";
|
||||||
|
|
||||||
|
const siteUrl = process.env.SITE_URL!;
|
||||||
|
|
||||||
|
// The component client has methods needed for integrating Convex with Better Auth,
|
||||||
|
// as well as helper methods for general use.
|
||||||
|
export const authComponent = createClient<DataModel>(components.betterAuth);
|
||||||
|
|
||||||
|
export const createAuth = (
|
||||||
|
ctx: GenericCtx<DataModel>,
|
||||||
|
{ optionsOnly } = { optionsOnly: false },
|
||||||
|
) => {
|
||||||
|
return betterAuth({
|
||||||
|
// disable logging when createAuth is called just to generate options.
|
||||||
|
// this is not required, but there's a lot of noise in logs without it.
|
||||||
|
logger: {
|
||||||
|
disabled: optionsOnly,
|
||||||
|
},
|
||||||
|
baseURL: siteUrl,
|
||||||
|
database: authComponent.adapter(ctx),
|
||||||
|
// Configure simple, non-verified email/password to get started
|
||||||
|
emailAndPassword: {
|
||||||
|
enabled: true,
|
||||||
|
requireEmailVerification: false,
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
// The Convex plugin is required for Convex compatibility
|
||||||
|
convex(),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Example function for getting the current user
|
||||||
|
// Feel free to edit, omit, etc.
|
||||||
|
export const getCurrentUser = query({
|
||||||
|
args: {},
|
||||||
|
handler: async (ctx) => {
|
||||||
|
return authComponent.getAuthUser(ctx);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
<Step>
|
||||||
|
### Create a Better Auth client instance
|
||||||
|
|
||||||
|
Create a Better Auth client instance for interacting with the Better Auth server from your client.
|
||||||
|
|
||||||
|
```ts title="src/lib/auth-client.ts"
|
||||||
|
import { createAuthClient } from "better-auth/react";
|
||||||
|
import { convexClient } from "@convex-dev/better-auth/client/plugins";
|
||||||
|
|
||||||
|
export const authClient = createAuthClient({
|
||||||
|
plugins: [convexClient()],
|
||||||
|
});
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
<Step>
|
||||||
|
### Mount handlers
|
||||||
|
|
||||||
|
Register Better Auth route handlers on your Convex deployment.
|
||||||
|
|
||||||
|
```ts title="convex/http.ts"
|
||||||
|
import { httpRouter } from "convex/server";
|
||||||
|
import { authComponent, createAuth } from "./auth";
|
||||||
|
|
||||||
|
const http = httpRouter();
|
||||||
|
|
||||||
|
authComponent.registerRoutes(http, createAuth);
|
||||||
|
|
||||||
|
export default http;
|
||||||
|
```
|
||||||
|
|
||||||
|
Set up route handlers to proxy auth requests from your framework server to your Convex deployment.
|
||||||
|
|
||||||
|
```ts title="app/api/auth/[...all]/route.ts"
|
||||||
|
import { nextJsHandler } from "@convex-dev/better-auth/nextjs";
|
||||||
|
|
||||||
|
export const { GET, POST } = nextJsHandler();
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
<Step>
|
||||||
|
### Set up Convex client provider
|
||||||
|
|
||||||
|
Wrap your app with the `ConvexBetterAuthProvider` component.
|
||||||
|
|
||||||
|
```ts title="app/ConvexClientProvider.tsx"
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import { ReactNode } from "react";
|
||||||
|
import { ConvexReactClient } from "convex/react";
|
||||||
|
import { authClient } from "@/lib/auth-client"; // [!code ++]
|
||||||
|
import { ConvexBetterAuthProvider } from "@convex-dev/better-auth/react"; // [!code ++]
|
||||||
|
|
||||||
|
const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL!, {
|
||||||
|
// Optionally pause queries until the user is authenticated // [!code ++]
|
||||||
|
expectAuth: true, // [!code ++]
|
||||||
|
});
|
||||||
|
|
||||||
|
export function ConvexClientProvider({ children }: { children: ReactNode }) {
|
||||||
|
return (
|
||||||
|
<ConvexBetterAuthProvider client={convex} authClient={authClient}> // [!code ++]
|
||||||
|
{children}
|
||||||
|
</ConvexBetterAuthProvider> // [!code ++]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
</Steps>
|
||||||
|
|
||||||
|
|
||||||
|
### You're done!
|
||||||
|
|
||||||
|
You're now ready to start using Better Auth with Convex.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Using Better Auth from the server
|
||||||
|
|
||||||
|
To use Better Auth's [server
|
||||||
|
methods](https://www.better-auth.com/docs/concepts/api) in server rendering,
|
||||||
|
server functions, or any other Next.js server code, use Convex functions
|
||||||
|
and call the function from your server code.
|
||||||
|
|
||||||
|
First, a token helper for calling Convex functions from your server code.
|
||||||
|
|
||||||
|
```ts title="src/lib/auth-server.ts"
|
||||||
|
import { createAuth } from "convex/auth";
|
||||||
|
import { getToken as getTokenNextjs } from "@convex-dev/better-auth/nextjs";
|
||||||
|
|
||||||
|
export const getToken = () => {
|
||||||
|
return getTokenNextjs(createAuth);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Here's an example Convex function that uses Better Auth's server methods, and
|
||||||
|
a server action that calls the Convex function.
|
||||||
|
|
||||||
|
```ts title="convex/users.ts"
|
||||||
|
import { mutation } from "./_generated/server";
|
||||||
|
import { v } from "convex/values";
|
||||||
|
import { createAuth, authComponent } from "./auth";
|
||||||
|
|
||||||
|
export const updateUserPassword = mutation({
|
||||||
|
args: {
|
||||||
|
currentPassword: v.string(),
|
||||||
|
newPassword: v.string(),
|
||||||
|
},
|
||||||
|
handler: async (ctx, args) => {
|
||||||
|
await createAuth(ctx).api.changePassword({
|
||||||
|
body: {
|
||||||
|
currentPassword: args.currentPassword,
|
||||||
|
newPassword: args.newPassword,
|
||||||
|
},
|
||||||
|
headers: await authComponent.getHeaders(ctx),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
```ts title="app/actions.ts"
|
||||||
|
"use server";
|
||||||
|
|
||||||
|
import { fetchMutation } from "convex/nextjs";
|
||||||
|
import { api } from "../convex/_generated/api";
|
||||||
|
import { getToken } from "../lib/auth-server";
|
||||||
|
|
||||||
|
// Authenticated mutation via server function
|
||||||
|
export async function updatePassword({
|
||||||
|
currentPassword,
|
||||||
|
newPassword,
|
||||||
|
}: {
|
||||||
|
currentPassword: string;
|
||||||
|
newPassword: string;
|
||||||
|
}) {
|
||||||
|
const token = await getToken();
|
||||||
|
await fetchMutation(
|
||||||
|
api.users.updatePassword,
|
||||||
|
{ currentPassword, newPassword },
|
||||||
|
{ token }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
<Callout>
|
||||||
|
This documentation comes from the [Convex documentation](https://convex-better-auth.netlify.app/),
|
||||||
|
for more information, please refer to their documentation.
|
||||||
|
</Callout>
|
||||||
Reference in New Issue
Block a user