diff --git a/frameworks/react-cra/add-ons/better-auth/README.md b/frameworks/react-cra/add-ons/better-auth/README.md new file mode 100644 index 00000000..3676c31a --- /dev/null +++ b/frameworks/react-cra/add-ons/better-auth/README.md @@ -0,0 +1,32 @@ +## Setting up Better Auth + +1. Generate and set the `BETTER_AUTH_SECRET` environment variable in your `.env.local`: + + ```bash + npx @better-auth/cli secret + ``` + +2. Visit the [Better Auth documentation](https://www.better-auth.com) to unlock the full potential of authentication in your app. + +### Adding a Database (Optional) + +Better Auth can work in stateless mode, but to persist user data, add a database: + +```typescript +// src/lib/auth.ts +import { betterAuth } from "better-auth"; +import { Pool } from "pg"; + +export const auth = betterAuth({ + database: new Pool({ + connectionString: process.env.DATABASE_URL, + }), + // ... rest of config +}); +``` + +Then run migrations: + +```bash +npx @better-auth/cli migrate +``` diff --git a/frameworks/react-cra/add-ons/better-auth/assets/_dot_env.local.append b/frameworks/react-cra/add-ons/better-auth/assets/_dot_env.local.append new file mode 100644 index 00000000..408cfc28 --- /dev/null +++ b/frameworks/react-cra/add-ons/better-auth/assets/_dot_env.local.append @@ -0,0 +1,3 @@ +# Better Auth configuration +BETTER_AUTH_URL=http://localhost:3000 +BETTER_AUTH_SECRET= # Generate a secret key: `npx @better-auth/cli secret` diff --git a/frameworks/react-cra/add-ons/better-auth/assets/src/integrations/better-auth/header-user.tsx b/frameworks/react-cra/add-ons/better-auth/assets/src/integrations/better-auth/header-user.tsx new file mode 100644 index 00000000..e6f9556f --- /dev/null +++ b/frameworks/react-cra/add-ons/better-auth/assets/src/integrations/better-auth/header-user.tsx @@ -0,0 +1,43 @@ +import { authClient } from "@/lib/auth-client"; +import { Link } from "@tanstack/react-router"; + +export default function BetterAuthHeader() { + const { data: session, isPending } = authClient.useSession(); + + if (isPending) { + return ( +
+ ); + } + + if (session?.user) { + return ( +
+ {session.user.image ? ( + + ) : ( +
+ + {session.user.name?.charAt(0).toUpperCase() || "U"} + +
+ )} + +
+ ); + } + + return ( + + Sign in + + ); +} diff --git a/frameworks/react-cra/add-ons/better-auth/assets/src/lib/auth-client.ts b/frameworks/react-cra/add-ons/better-auth/assets/src/lib/auth-client.ts new file mode 100644 index 00000000..fc6c795b --- /dev/null +++ b/frameworks/react-cra/add-ons/better-auth/assets/src/lib/auth-client.ts @@ -0,0 +1,3 @@ +import { createAuthClient } from 'better-auth/react' + +export const authClient = createAuthClient() diff --git a/frameworks/react-cra/add-ons/better-auth/assets/src/lib/auth.ts b/frameworks/react-cra/add-ons/better-auth/assets/src/lib/auth.ts new file mode 100644 index 00000000..9bda4a75 --- /dev/null +++ b/frameworks/react-cra/add-ons/better-auth/assets/src/lib/auth.ts @@ -0,0 +1,7 @@ +import { betterAuth } from 'better-auth' + +export const auth = betterAuth({ + emailAndPassword: { + enabled: true, + }, +}) diff --git a/frameworks/react-cra/add-ons/better-auth/assets/src/routes/api/auth/$.ts b/frameworks/react-cra/add-ons/better-auth/assets/src/routes/api/auth/$.ts new file mode 100644 index 00000000..8e228fb6 --- /dev/null +++ b/frameworks/react-cra/add-ons/better-auth/assets/src/routes/api/auth/$.ts @@ -0,0 +1,11 @@ +import { createFileRoute } from '@tanstack/react-router' +import { auth } from '@/lib/auth' + +export const Route = createFileRoute('/api/auth/$')({ + server: { + handlers: { + GET: ({ request }) => auth.handler(request), + POST: ({ request }) => auth.handler(request), + }, + }, +}) diff --git a/frameworks/react-cra/add-ons/better-auth/assets/src/routes/demo/better-auth.tsx b/frameworks/react-cra/add-ons/better-auth/assets/src/routes/demo/better-auth.tsx new file mode 100644 index 00000000..a2325a85 --- /dev/null +++ b/frameworks/react-cra/add-ons/better-auth/assets/src/routes/demo/better-auth.tsx @@ -0,0 +1,232 @@ +import { createFileRoute } from "@tanstack/react-router"; +import { useState } from "react"; +import { authClient } from "@/lib/auth-client"; + +export const Route = createFileRoute("/demo/better-auth")({ + component: BetterAuthDemo, +}); + +function BetterAuthDemo() { + const { data: session, isPending } = authClient.useSession(); + const [isSignUp, setIsSignUp] = useState(false); + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const [name, setName] = useState(""); + const [error, setError] = useState(""); + const [loading, setLoading] = useState(false); + + if (isPending) { + return ( +
+
+
+ ); + } + + if (session?.user) { + return ( +
+
+
+

+ Welcome back +

+

+ You're signed in as {session.user.email} +

+
+ +
+ {session.user.image ? ( + + ) : ( +
+ + {session.user.name?.charAt(0).toUpperCase() || "U"} + +
+ )} +
+

+ {session.user.name} +

+

+ {session.user.email} +

+
+
+ + + +

+ Built with{" "} + + BETTER-AUTH + + . +

+
+
+ ); + } + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setError(""); + setLoading(true); + + try { + if (isSignUp) { + const result = await authClient.signUp.email({ + email, + password, + name, + }); + if (result.error) { + setError(result.error.message || "Sign up failed"); + } + } else { + const result = await authClient.signIn.email({ + email, + password, + }); + if (result.error) { + setError(result.error.message || "Sign in failed"); + } + } + } catch (err) { + setError("An unexpected error occurred"); + } finally { + setLoading(false); + } + }; + + return ( +
+
+

+ {isSignUp ? "Create an account" : "Sign in"} +

+

+ {isSignUp + ? "Enter your information to create an account" + : "Enter your email below to login to your account"} +

+ +
+ {isSignUp && ( +
+ + setName(e.target.value)} + className="flex h-9 w-full border border-neutral-300 dark:border-neutral-700 bg-transparent px-3 text-sm focus:outline-none focus:border-neutral-900 dark:focus:border-neutral-100 disabled:cursor-not-allowed disabled:opacity-50" + required + /> +
+ )} + +
+ + setEmail(e.target.value)} + className="flex h-9 w-full border border-neutral-300 dark:border-neutral-700 bg-transparent px-3 text-sm focus:outline-none focus:border-neutral-900 dark:focus:border-neutral-100 disabled:cursor-not-allowed disabled:opacity-50" + required + /> +
+ +
+ + setPassword(e.target.value)} + className="flex h-9 w-full border border-neutral-300 dark:border-neutral-700 bg-transparent px-3 text-sm focus:outline-none focus:border-neutral-900 dark:focus:border-neutral-100 disabled:cursor-not-allowed disabled:opacity-50" + required + minLength={8} + /> +
+ + {error && ( +
+

{error}

+
+ )} + + +
+ +
+ +
+ +

+ Built with{" "} + + BETTER-AUTH + + . +

+
+
+ ); +} diff --git a/frameworks/react-cra/add-ons/better-auth/info.json b/frameworks/react-cra/add-ons/better-auth/info.json new file mode 100644 index 00000000..4c3f8f67 --- /dev/null +++ b/frameworks/react-cra/add-ons/better-auth/info.json @@ -0,0 +1,26 @@ +{ + "name": "Better Auth", + "description": "Add Better Auth authentication to your application.", + "phase": "add-on", + "type": "add-on", + "priority": 26, + "link": "https://www.better-auth.com", + "modes": ["file-router"], + "tailwind": true, + "dependsOn": ["start"], + "routes": [ + { + "url": "/demo/better-auth", + "name": "Better Auth", + "path": "src/routes/demo/better-auth.tsx", + "jsName": "BetterAuthDemo" + } + ], + "integrations": [ + { + "type": "header-user", + "jsName": "BetterAuthHeader", + "path": "src/integrations/better-auth/header-user.tsx" + } + ] +} diff --git a/frameworks/react-cra/add-ons/better-auth/logo.svg b/frameworks/react-cra/add-ons/better-auth/logo.svg new file mode 100644 index 00000000..62a7d1c6 --- /dev/null +++ b/frameworks/react-cra/add-ons/better-auth/logo.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frameworks/react-cra/add-ons/better-auth/package.json b/frameworks/react-cra/add-ons/better-auth/package.json new file mode 100644 index 00000000..7a879444 --- /dev/null +++ b/frameworks/react-cra/add-ons/better-auth/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "better-auth": "^1.4.12" + } +} diff --git a/frameworks/react-cra/add-ons/better-auth/small-logo.svg b/frameworks/react-cra/add-ons/better-auth/small-logo.svg new file mode 100644 index 00000000..be7bf8c1 --- /dev/null +++ b/frameworks/react-cra/add-ons/better-auth/small-logo.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/frameworks/solid/add-ons/better-auth/README.md b/frameworks/solid/add-ons/better-auth/README.md new file mode 100644 index 00000000..3676c31a --- /dev/null +++ b/frameworks/solid/add-ons/better-auth/README.md @@ -0,0 +1,32 @@ +## Setting up Better Auth + +1. Generate and set the `BETTER_AUTH_SECRET` environment variable in your `.env.local`: + + ```bash + npx @better-auth/cli secret + ``` + +2. Visit the [Better Auth documentation](https://www.better-auth.com) to unlock the full potential of authentication in your app. + +### Adding a Database (Optional) + +Better Auth can work in stateless mode, but to persist user data, add a database: + +```typescript +// src/lib/auth.ts +import { betterAuth } from "better-auth"; +import { Pool } from "pg"; + +export const auth = betterAuth({ + database: new Pool({ + connectionString: process.env.DATABASE_URL, + }), + // ... rest of config +}); +``` + +Then run migrations: + +```bash +npx @better-auth/cli migrate +``` diff --git a/frameworks/solid/add-ons/better-auth/assets/_dot_env.local.append b/frameworks/solid/add-ons/better-auth/assets/_dot_env.local.append new file mode 100644 index 00000000..408cfc28 --- /dev/null +++ b/frameworks/solid/add-ons/better-auth/assets/_dot_env.local.append @@ -0,0 +1,3 @@ +# Better Auth configuration +BETTER_AUTH_URL=http://localhost:3000 +BETTER_AUTH_SECRET= # Generate a secret key: `npx @better-auth/cli secret` diff --git a/frameworks/solid/add-ons/better-auth/assets/src/integrations/better-auth/header-user.tsx b/frameworks/solid/add-ons/better-auth/assets/src/integrations/better-auth/header-user.tsx new file mode 100644 index 00000000..2893a122 --- /dev/null +++ b/frameworks/solid/add-ons/better-auth/assets/src/integrations/better-auth/header-user.tsx @@ -0,0 +1,51 @@ +import { Show } from "solid-js"; +import { Link } from "@tanstack/solid-router"; +import { authClient } from "../../lib/auth-client"; + +export default function BetterAuthHeader() { + const session = authClient.useSession(); + + return ( + + } + > + + Sign in + + } + > + {(user) => ( +
+ + + {user().name?.charAt(0).toUpperCase() || "U"} + +
+ } + > + {(image) => } +
+ +
+ )} + + + ); +} diff --git a/frameworks/solid/add-ons/better-auth/assets/src/lib/auth-client.ts b/frameworks/solid/add-ons/better-auth/assets/src/lib/auth-client.ts new file mode 100644 index 00000000..492f8f53 --- /dev/null +++ b/frameworks/solid/add-ons/better-auth/assets/src/lib/auth-client.ts @@ -0,0 +1,3 @@ +import { createAuthClient } from 'better-auth/solid' + +export const authClient = createAuthClient() diff --git a/frameworks/solid/add-ons/better-auth/assets/src/lib/auth.ts b/frameworks/solid/add-ons/better-auth/assets/src/lib/auth.ts new file mode 100644 index 00000000..9bda4a75 --- /dev/null +++ b/frameworks/solid/add-ons/better-auth/assets/src/lib/auth.ts @@ -0,0 +1,7 @@ +import { betterAuth } from 'better-auth' + +export const auth = betterAuth({ + emailAndPassword: { + enabled: true, + }, +}) diff --git a/frameworks/solid/add-ons/better-auth/assets/src/routes/api/auth/$.ts b/frameworks/solid/add-ons/better-auth/assets/src/routes/api/auth/$.ts new file mode 100644 index 00000000..17c74003 --- /dev/null +++ b/frameworks/solid/add-ons/better-auth/assets/src/routes/api/auth/$.ts @@ -0,0 +1,11 @@ +import { createFileRoute } from '@tanstack/solid-router' +import { auth } from '../../../lib/auth' + +export const Route = createFileRoute('/api/auth/$')({ + server: { + handlers: { + GET: ({ request }) => auth.handler(request), + POST: ({ request }) => auth.handler(request), + }, + }, +}) diff --git a/frameworks/solid/add-ons/better-auth/assets/src/routes/demo.better-auth.tsx b/frameworks/solid/add-ons/better-auth/assets/src/routes/demo.better-auth.tsx new file mode 100644 index 00000000..b54ec435 --- /dev/null +++ b/frameworks/solid/add-ons/better-auth/assets/src/routes/demo.better-auth.tsx @@ -0,0 +1,237 @@ +import { createFileRoute } from "@tanstack/solid-router"; +import { createSignal, Show } from "solid-js"; +import { authClient } from "../lib/auth-client"; + +export const Route = createFileRoute("/demo/better-auth")({ + component: BetterAuthDemo, +}); + +function BetterAuthDemo() { + const session = authClient.useSession(); + const [isSignUp, setIsSignUp] = createSignal(false); + const [email, setEmail] = createSignal(""); + const [password, setPassword] = createSignal(""); + const [name, setName] = createSignal(""); + const [error, setError] = createSignal(""); + const [loading, setLoading] = createSignal(false); + + const handleSubmit = async (e: Event) => { + e.preventDefault(); + setError(""); + setLoading(true); + + try { + if (isSignUp()) { + const result = await authClient.signUp.email({ + email: email(), + password: password(), + name: name(), + }); + if (result.error) { + setError(result.error.message || "Sign up failed"); + } + } else { + const result = await authClient.signIn.email({ + email: email(), + password: password(), + }); + if (result.error) { + setError(result.error.message || "Sign in failed"); + } + } + } catch (err) { + setError("An unexpected error occurred"); + } finally { + setLoading(false); + } + }; + + return ( + +
+
+ } + > + +
+

+ {isSignUp() ? "Create an account" : "Sign in"} +

+

+ {isSignUp() + ? "Enter your information to create an account" + : "Enter your email below to login to your account"} +

+ +
+ +
+ + setName(e.currentTarget.value)} + class="flex h-9 w-full border border-neutral-300 dark:border-neutral-700 bg-transparent px-3 text-sm focus:outline-none focus:border-neutral-900 dark:focus:border-neutral-100 disabled:cursor-not-allowed disabled:opacity-50" + required + /> +
+
+ +
+ + setEmail(e.currentTarget.value)} + class="flex h-9 w-full border border-neutral-300 dark:border-neutral-700 bg-transparent px-3 text-sm focus:outline-none focus:border-neutral-900 dark:focus:border-neutral-100 disabled:cursor-not-allowed disabled:opacity-50" + required + /> +
+ +
+ + setPassword(e.currentTarget.value)} + class="flex h-9 w-full border border-neutral-300 dark:border-neutral-700 bg-transparent px-3 text-sm focus:outline-none focus:border-neutral-900 dark:focus:border-neutral-100 disabled:cursor-not-allowed disabled:opacity-50" + required + minLength={8} + /> +
+ + +
+

+ {error()} +

+
+
+ + +
+ +
+ +
+ +

+ Built with{" "} + + BETTER-AUTH + + . +

+
+
+ } + > + {(user) => ( +
+
+
+

+ Welcome back +

+

+ You're signed in as {user().email} +

+
+ +
+ + + {user().name?.charAt(0).toUpperCase() || "U"} + +
+ } + > + {(image) => } + +
+

{user().name}

+

+ {user().email} +

+
+
+ + + +

+ Built with{" "} + + BETTER-AUTH + + . +

+
+ + )} + + + ); +} diff --git a/frameworks/solid/add-ons/better-auth/info.json b/frameworks/solid/add-ons/better-auth/info.json new file mode 100644 index 00000000..be41d737 --- /dev/null +++ b/frameworks/solid/add-ons/better-auth/info.json @@ -0,0 +1,24 @@ +{ + "name": "Better Auth", + "description": "Add Better Auth authentication to your application.", + "phase": "add-on", + "type": "add-on", + "link": "https://www.better-auth.com/", + "modes": ["file-router"], + "dependsOn": ["start"], + "routes": [ + { + "url": "/demo/better-auth", + "name": "Better Auth", + "path": "src/routes/demo.better-auth.tsx", + "jsName": "BetterAuthDemo" + } + ], + "integrations": [ + { + "type": "header-user", + "jsName": "BetterAuthHeader", + "path": "src/integrations/better-auth/header-user.tsx" + } + ] +} diff --git a/frameworks/solid/add-ons/better-auth/logo.svg b/frameworks/solid/add-ons/better-auth/logo.svg new file mode 100644 index 00000000..62a7d1c6 --- /dev/null +++ b/frameworks/solid/add-ons/better-auth/logo.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frameworks/solid/add-ons/better-auth/package.json b/frameworks/solid/add-ons/better-auth/package.json new file mode 100644 index 00000000..7a879444 --- /dev/null +++ b/frameworks/solid/add-ons/better-auth/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "better-auth": "^1.4.12" + } +} diff --git a/frameworks/solid/add-ons/better-auth/small-logo.svg b/frameworks/solid/add-ons/better-auth/small-logo.svg new file mode 100644 index 00000000..be7bf8c1 --- /dev/null +++ b/frameworks/solid/add-ons/better-auth/small-logo.svg @@ -0,0 +1,9 @@ + + + + + + + + +