|
| 1 | +--- |
| 2 | +title: Basic Usage |
| 3 | +description: Using Better Auth with Convex |
| 4 | +--- |
| 5 | + |
| 6 | +## Better Auth guide |
| 7 | + |
| 8 | +Better Auth's [basic usage guide](https://www.better-auth.com/docs/basic-usage) |
| 9 | +applies to Convex as well. It covers signing in and out, social providers, |
| 10 | +plugins, and more. You will be using Better Auth directly in your project, so |
| 11 | +their guides are a primary reference. |
| 12 | + |
| 13 | +### Exceptions |
| 14 | + |
| 15 | +There are a few areas in the Better Auth basic usage guide that work differently |
| 16 | +in Convex. |
| 17 | + |
| 18 | +- #### Server side authentication |
| 19 | + |
| 20 | + Better Auth supports signing users in and out through server side functions. |
| 21 | + Because Convex functions run over websockets and don't return HTTP responses |
| 22 | + or set cookies, signing up/in/out must be done from the client via |
| 23 | + `authClient.signIn.*` methods. |
| 24 | + |
| 25 | +- #### Schemas and migrations |
| 26 | + |
| 27 | + The basic usage guide includes information on database schema generation and |
| 28 | + migrations via the Better Auth CLI. This only applies for |
| 29 | + [local installs](/local-install), which support generating schemas. For |
| 30 | + projects not using local install, the default schema provided with the Better |
| 31 | + Auth component (preconfigured with the |
| 32 | + [supported plugins](/supported-plugins)) is used, and cannot be altered. |
| 33 | + |
| 34 | +## Using server methods with `auth.api` |
| 35 | + |
| 36 | +Better Auth's server side `auth.api` methods can be used with your `createAuth` |
| 37 | +function and the component `headers` method. Here's an example implementing the |
| 38 | +[`changePassword` server method](https://www.better-auth.com/docs/concepts/users-accounts#api-method-change-password). |
| 39 | + |
| 40 | +```ts |
| 41 | +export const updateUserPassword = mutation({ |
| 42 | + args: { |
| 43 | + currentPassword: v.string(), |
| 44 | + newPassword: v.string(), |
| 45 | + }, |
| 46 | + handler: async (ctx, args) => { |
| 47 | + // Many Better Auth server methods require a currently authenticated |
| 48 | + // user, so request headers have to be passed in so session cookies |
| 49 | + // can be parsed and validated. The `getAuth` method provides both the |
| 50 | + // auth object and headers for convenience. |
| 51 | + const { auth, headers } = await authComponent.getAuth(createAuth, ctx); |
| 52 | + await auth.api.changePassword({ |
| 53 | + body: { |
| 54 | + currentPassword: args.currentPassword, |
| 55 | + newPassword: args.newPassword, |
| 56 | + }, |
| 57 | + headers, |
| 58 | + }); |
| 59 | + }, |
| 60 | +}); |
| 61 | +``` |
| 62 | + |
| 63 | +## Using Convex ctx in Better Auth config |
| 64 | + |
| 65 | +The `ctx` param passed in to the `createAuth` function is the Convex context |
| 66 | +object. This can be used to access the Convex database or Convex functions in |
| 67 | +your Better Auth config. It can be a |
| 68 | +[query](https://docs.convex.dev/functions/query-functions#query-context), |
| 69 | +[mutation](https://docs.convex.dev/functions/mutation-functions#mutation-context), |
| 70 | +or [action](https://docs.convex.dev/functions/actions#action-context) context. |
| 71 | + |
| 72 | +A common use case is sending emails for verification or password resets with the |
| 73 | +[Resend component](https://www.convex.dev/components/resend). `resend.sendEmail` |
| 74 | +will produce a type error because the ctx object could be a query ctx. The |
| 75 | +component provides type guards for this. |
| 76 | + |
| 77 | +```ts |
| 78 | +import { requireActionCtx } from "@convex-dev/better-auth/utils"; |
| 79 | +import { type GenericCtx } from "@convex-dev/better-auth"; |
| 80 | +import { Resend } from "@convex-dev/resend"; |
| 81 | +import { components } from "./_generated/api"; |
| 82 | +import { type DataModel } from "./_generated/dataModel"; |
| 83 | + |
| 84 | +export const resend = new Resend(components.resend); |
| 85 | + |
| 86 | +export const createAuthOptions = (ctx: GenericCtx<DataModel>) => ({ |
| 87 | + baseURL: siteUrl, |
| 88 | + sendVerificationEmail: async ({ user, url }) => { |
| 89 | + // This function only requires a `runMutation` property on the ctx object, |
| 90 | + // but we'll make sure we have an action ctx because we know a network |
| 91 | + // request is being made, which requires an action ctx. |
| 92 | + await resend.sendEmail(requireActionCtx(ctx), { |
| 93 | + to: user.email, |
| 94 | + subject: "Verify your email", |
| 95 | + html: `<p>Click <a href="${url}">here</a> to verify your email</p>`, |
| 96 | + }); |
| 97 | + }, |
| 98 | +}); |
| 99 | +``` |
0 commit comments