Skip to content

Commit bc937aa

Browse files
Merge pull request #3 from thyuhtooaung-dev/feat/auth
feat: integrate Better Auth with Convex for user authentication and session management
2 parents 5d2fadb + b524915 commit bc937aa

18 files changed

Lines changed: 563 additions & 17 deletions

File tree

.github/workflows/ci.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,7 @@ jobs:
4343

4444
- name: Build Project
4545
run: pnpm run build
46+
env:
47+
NEXT_PUBLIC_CONVEX_URL: "https://insightful-pheasant-237.convex.cloud"
48+
NEXT_PUBLIC_CONVEX_SITE_URL: "https://insightful-pheasant-237.convex.site"
49+
NEXT_PUBLIC_SITE_URL: "http://localhost:3000"

app/(shared-layout)/test/page.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
"use client";
2+
3+
import { api } from "@/convex/_generated/api";
4+
import { useQuery } from "convex/react";
5+
6+
export default function Page() {
7+
const tasks = useQuery(api.tasks.get);
8+
return (
9+
<main className="flex min-h-screen flex-col items-center justify-between p-24">
10+
{tasks?.map(({ _id, text }) => (
11+
<div key={_id}>{text}</div>
12+
))}
13+
</main>
14+
);
15+
}

app/api/auth/[...all]/route.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { handler } from "@/lib/auth-server";
2+
3+
export const { GET, POST } = handler;

app/auth/sign-up/page.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
FieldLabel,
2020
} from "@/components/ui/field";
2121
import { Button } from "@/components/ui/button";
22+
import { authClient } from "@/lib/auth-client";
2223

2324
export default function SignupPage() {
2425
const form = useForm<z.infer<typeof signUpSchema>>({
@@ -31,8 +32,12 @@ export default function SignupPage() {
3132
},
3233
});
3334

34-
const onSubmit = () => {
35-
console.log("yoo");
35+
const onSubmit = async (data: z.infer<typeof signUpSchema>) => {
36+
await authClient.signUp.email({
37+
email: data.email,
38+
name: data.name,
39+
password: data.password,
40+
});
3641
};
3742

3843
return (

app/layout.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import React from "react";
33
import { Outfit, Geist_Mono } from "next/font/google";
44
import "./globals.css";
55
import { ThemeProvider } from "@/components/ui/theme-provider";
6+
import { ConvexClientProvider } from "../components/web/ConvexClientProvider";
67

78
const outfit = Outfit({
89
variable: "--font-sans",
@@ -35,7 +36,7 @@ export default function RootLayout({
3536
disableTransitionOnChange
3637
>
3738
<main className={"w-full max-auto px-4 md:px-6 lg:px-8"}>
38-
{children}
39+
<ConvexClientProvider>{children}</ConvexClientProvider>
3940
</main>
4041
</ThemeProvider>
4142
</body>
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
"use client";
2+
3+
import { ReactNode } from "react";
4+
import { ConvexReactClient } from "convex/react";
5+
import { authClient } from "@/lib/auth-client";
6+
import { ConvexBetterAuthProvider } from "@convex-dev/better-auth/react";
7+
8+
const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL!);
9+
10+
export function ConvexClientProvider({
11+
children,
12+
initialToken,
13+
}: {
14+
children: ReactNode;
15+
initialToken?: string | null;
16+
}) {
17+
return (
18+
<ConvexBetterAuthProvider
19+
client={convex}
20+
authClient={authClient}
21+
initialToken={initialToken}
22+
>
23+
{children}
24+
</ConvexBetterAuthProvider>
25+
);
26+
}

components/web/navbar.tsx

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1+
"use client";
2+
13
import Link from "next/link";
2-
import { buttonVariants } from "@/components/ui/button";
4+
import { Button, buttonVariants } from "@/components/ui/button";
35
import { ThemeToggle } from "@/components/ui/theme-toggle";
6+
import { useConvexAuth } from "convex/react";
7+
import { authClient } from "@/lib/auth-client";
48

59
export default function Navbar() {
10+
const { isAuthenticated, isLoading } = useConvexAuth();
611
return (
712
<nav className={"w-full py-5 flex items-center justify-between"}>
813
<div className={"flex items-center gap-8"}>
@@ -30,15 +35,21 @@ export default function Navbar() {
3035
</div>
3136
</div>
3237
<div className={"flex items-center gap-2"}>
33-
<Link href={"/auth/sign-up"} className={buttonVariants()}>
34-
Sign up
35-
</Link>
36-
<Link
37-
href={"/auth/login"}
38-
className={buttonVariants({ variant: "secondary" })}
39-
>
40-
Login
41-
</Link>
38+
{isLoading ? null : isAuthenticated ? (
39+
<Button onClick={() => authClient.signOut({})}>LogOut</Button>
40+
) : (
41+
<>
42+
<Link href={"/auth/sign-up"} className={buttonVariants()}>
43+
Sign up
44+
</Link>
45+
<Link
46+
href={"/auth/login"}
47+
className={buttonVariants({ variant: "secondary" })}
48+
>
49+
Login
50+
</Link>
51+
</>
52+
)}
4253
<ThemeToggle />
4354
</div>
4455
</nav>

convex/_generated/api.d.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,21 @@
88
* @module
99
*/
1010

11+
import type * as auth from "../auth.js";
12+
import type * as http from "../http.js";
13+
import type * as tasks from "../tasks.js";
14+
1115
import type {
1216
ApiFromModules,
1317
FilterApi,
1418
FunctionReference,
1519
} from "convex/server";
1620

17-
declare const fullApi: ApiFromModules<{}>;
21+
declare const fullApi: ApiFromModules<{
22+
auth: typeof auth;
23+
http: typeof http;
24+
tasks: typeof tasks;
25+
}>;
1826

1927
/**
2028
* A utility for referencing Convex functions in your app's public API.
@@ -42,4 +50,6 @@ export declare const internal: FilterApi<
4250
FunctionReference<any, "internal">
4351
>;
4452

45-
export declare const components: {};
53+
export declare const components: {
54+
betterAuth: import("@convex-dev/better-auth/_generated/component.js").ComponentApi<"betterAuth">;
55+
};

convex/auth.config.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { getAuthConfigProvider } from "@convex-dev/better-auth/auth-config";
2+
import type { AuthConfig } from "convex/server";
3+
4+
export default {
5+
providers: [getAuthConfigProvider()],
6+
} satisfies AuthConfig;

convex/auth.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { createClient, type GenericCtx } from "@convex-dev/better-auth";
2+
import { convex } from "@convex-dev/better-auth/plugins";
3+
import { components } from "./_generated/api";
4+
import { DataModel } from "./_generated/dataModel";
5+
import { query } from "./_generated/server";
6+
import { betterAuth } from "better-auth/minimal";
7+
import authConfig from "./auth.config";
8+
9+
const siteUrl = process.env.SITE_URL!;
10+
11+
// The component client has methods needed for integrating Convex with Better Auth,
12+
// as well as helper methods for general use.
13+
export const authComponent = createClient<DataModel>(components.betterAuth);
14+
15+
export const createAuth = (ctx: GenericCtx<DataModel>) => {
16+
return betterAuth({
17+
baseURL: siteUrl,
18+
database: authComponent.adapter(ctx),
19+
// Configure simple, non-verified email/password to get started
20+
emailAndPassword: {
21+
enabled: true,
22+
requireEmailVerification: false,
23+
},
24+
plugins: [
25+
// The Convex plugin is required for Convex compatibility
26+
convex({ authConfig }),
27+
],
28+
});
29+
};
30+
31+
// Example function for getting the current user
32+
// Feel free to edit, omit, etc.
33+
export const getCurrentUser = query({
34+
args: {},
35+
handler: async (ctx) => {
36+
return authComponent.getAuthUser(ctx);
37+
},
38+
});

0 commit comments

Comments
 (0)