From 5ca615a3e44e809d06ee25c5d4b7cd7af7cc41f2 Mon Sep 17 00:00:00 2001 From: Bereket Engida Date: Sat, 21 Sep 2024 11:04:44 +0300 Subject: [PATCH] feat: docs example trial --- dev/express/package.json | 3 +- docs/.gitignore | 3 +- docs/app/changelogs/_logs/2024-08-09.tsx | 39 - docs/app/changelogs/_logs/2024-09-28.tsx | 33 + docs/app/changelogs/_logs/index.ts | 19 +- docs/app/changelogs/layout.tsx | 161 +- docs/app/changelogs/page.tsx | 45 +- docs/components/features.tsx | 414 +- docs/components/side-bar.tsx | 140 +- docs/components/sidebar-content.tsx | 801 +- docs/content/docs/examples/next-js.mdx | 20 + docs/content/docs/integrations/hono.mdx | 73 + .../docs/integrations/nuxt.mdx | 2 +- .../content/docs/integrations/solid-start.mdx | 64 + docs/content/docs/integrations/svelte-kit.mdx | 148 + docs/content/docs/plugins/2fa.mdx | 387 + docs/content/docs/plugins/organization.mdx | 4 + docs/content/docs/plugins/passkey.mdx | 158 + docs/content/docs/plugins/rate-limit.mdx | 4 + docs/content/docs/plugins/username.mdx | 119 + docs/mdx-components.tsx | 43 +- pnpm-lock.yaml | 34808 +++++++--------- rough-doc.md | 52 - 23 files changed, 17972 insertions(+), 19568 deletions(-) delete mode 100644 docs/app/changelogs/_logs/2024-08-09.tsx create mode 100644 docs/app/changelogs/_logs/2024-09-28.tsx create mode 100644 docs/content/docs/examples/next-js.mdx create mode 100644 docs/content/docs/integrations/hono.mdx rename docs/{_content => content}/docs/integrations/nuxt.mdx (99%) create mode 100644 docs/content/docs/integrations/solid-start.mdx create mode 100644 docs/content/docs/integrations/svelte-kit.mdx create mode 100644 docs/content/docs/plugins/2fa.mdx create mode 100644 docs/content/docs/plugins/organization.mdx create mode 100644 docs/content/docs/plugins/passkey.mdx create mode 100644 docs/content/docs/plugins/rate-limit.mdx create mode 100644 docs/content/docs/plugins/username.mdx delete mode 100644 rough-doc.md diff --git a/dev/express/package.json b/dev/express/package.json index ce9211d9..6b680421 100644 --- a/dev/express/package.json +++ b/dev/express/package.json @@ -1,5 +1,6 @@ { - "name": "express", + "name": "@better-auth/dev-express", + "private": true, "module": "index.ts", "type": "module", "devDependencies": { diff --git a/docs/.gitignore b/docs/.gitignore index 51c0e6b5..b563c330 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -25,4 +25,5 @@ yarn-error.log* # others .env*.local .vercel -next-env.d.ts \ No newline at end of file +next-env.d.ts +certificates \ No newline at end of file diff --git a/docs/app/changelogs/_logs/2024-08-09.tsx b/docs/app/changelogs/_logs/2024-08-09.tsx deleted file mode 100644 index 4fa49402..00000000 --- a/docs/app/changelogs/_logs/2024-08-09.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { AnimatePresence } from "@/components/ui/fade-in" - -const ChangelogOne = () => { - return ( - -
- -
-

- Commit message suggestions -

-
-

- In the latest release, I've added support for commit message and description suggestions via an integration with OpenAI. Commit looks at all of your changes, and feeds that into the machine with a bit of prompt-tuning to get back a commit message that does a surprisingly good job at describing the intent of your changes. - It's also been a pretty helpful way to remind myself what the hell I was working on at the end of the day yesterday when I get back to my computer and realize I didn't commit any of my work. -

-
-

Improvement

-
-
    -
  • - Added commit message and description suggestions powered by OpenAI -
  • -
  • - Started commit message and description suggestions powered by OpenAI -
  • - -
  • - Restored message and description suggestions powered by OpenAI -
  • -
  • - Added commit message and description suggestions powered by OpenAI -
  • -
-
-
- ) -} -export default ChangelogOne \ No newline at end of file diff --git a/docs/app/changelogs/_logs/2024-09-28.tsx b/docs/app/changelogs/_logs/2024-09-28.tsx new file mode 100644 index 00000000..697878ad --- /dev/null +++ b/docs/app/changelogs/_logs/2024-09-28.tsx @@ -0,0 +1,33 @@ +import { AnimatePresence } from "@/components/ui/fade-in"; + +const listOfFeatures = ["The first better-auth public beta release"]; + +const bugFixes = [""]; + +const ChangelogOne = () => { + return ( + +
+
+

+ Public Beta Release +

+

+ The first public beta release of better-auth is now available. This + release includes a lot of new features and improvements. +

+
+

+
+

Features

+
+
    + {listOfFeatures.map((change, i) => ( +
  • {change}
  • + ))} +
+
+
+ ); +}; +export default ChangelogOne; diff --git a/docs/app/changelogs/_logs/index.ts b/docs/app/changelogs/_logs/index.ts index ee8218cd..665706a4 100644 --- a/docs/app/changelogs/_logs/index.ts +++ b/docs/app/changelogs/_logs/index.ts @@ -1,23 +1,8 @@ -import ChangelogOne from "./2024-08-09"; +import ChangelogOne from "./2024-09-28"; export const Logs = [ { name: "Changelog 1", - date: "2024-09-09", - component: ChangelogOne, - }, - { - name: "Changelog 1", - date: "2024-09-09", - component: ChangelogOne, - }, - { - name: "Changelog 1", - date: "2024-09-09", - component: ChangelogOne, - }, - { - name: "Changelog 1", - date: "2024-09-09", + date: "2024-09-28", component: ChangelogOne, }, ]; diff --git a/docs/app/changelogs/layout.tsx b/docs/app/changelogs/layout.tsx index 1be6d4d9..83d65a82 100644 --- a/docs/app/changelogs/layout.tsx +++ b/docs/app/changelogs/layout.tsx @@ -1,83 +1,112 @@ -import { Layout } from './_components/_layout' -import { useId } from 'react' +import { Layout } from "./_components/_layout"; +import { useId } from "react"; -import { Intro, IntroFooter } from './_components/changelog-layout' -import { StarField } from './_components/stat-field' +import { Intro, IntroFooter } from "./_components/changelog-layout"; +import { StarField } from "./_components/stat-field"; function Timeline() { - let id = useId() + let id = useId(); - return ( -
- -
- ) + return ( +
+ +
+ ); } function Glow() { - let id = useId() + let id = useId(); - return ( -
- -
-
- ) + return ( +
+ +
+
+ ); } -function FixedSidebar({ main, footer }: { main: React.ReactNode; footer: React.ReactNode }) { - return ( -
- -
-
-
-
- - {main} -
-
-
{footer}
-
+function FixedSidebar({ + main, + footer, +}: { + main: React.ReactNode; + footer: React.ReactNode; +}) { + return ( +
+ +
+
+
+
+ + {main}
+
+
+ {footer} +
- ) +
+
+ ); } const ChangeLogLayout = ({ children }: { children: React.ReactNode }) => { - return ( -
- - } footer={} /> -
-
- -
{children}
-
+ return ( +
+ } footer={} /> +
+
+ +
+ {children}
- ) -} +
+
+ ); +}; -export default ChangeLogLayout +export default ChangeLogLayout; diff --git a/docs/app/changelogs/page.tsx b/docs/app/changelogs/page.tsx index 72a98727..69ef5f02 100644 --- a/docs/app/changelogs/page.tsx +++ b/docs/app/changelogs/page.tsx @@ -1,24 +1,25 @@ -import { AnimatePresence } from "@/components/ui/fade-in" -import { Logs } from "./_logs" -import { FormattedDate } from "./_components/fmt-dates" +import { AnimatePresence } from "@/components/ui/fade-in"; +import { Logs } from "./_logs"; +import { FormattedDate } from "./_components/fmt-dates"; const ChangelogPage = () => { - return ( -
-
- {Logs.map((log) => { - return ( -
-
- -
- - -
- ) - })} + return ( +
+
+ {Logs.map((log) => { + return ( +
+
+ +
+
- -
- ) -} -export default ChangelogPage + ); + })} +
+
+ ); +}; +export default ChangelogPage; diff --git a/docs/components/features.tsx b/docs/components/features.tsx index 0d15373e..a12a7c49 100644 --- a/docs/components/features.tsx +++ b/docs/components/features.tsx @@ -1,231 +1,209 @@ "use client"; import { - CheckIcon, - Globe2Icon, - PlugIcon, - PlugZap2Icon, - Plus, - RabbitIcon, - ShieldCheckIcon, - Webhook, - XIcon, + CheckIcon, + Globe2Icon, + PlugIcon, + PlugZap2Icon, + Plus, + RabbitIcon, + ShieldCheckIcon, + Webhook, + XIcon, } from "lucide-react"; -import { - LockClosedIcon, -} from "@radix-ui/react-icons"; +import { LockClosedIcon } from "@radix-ui/react-icons"; import Link from "next/link"; import { Button } from "./ui/button"; import React, { useState } from "react"; import { TechStackDisplay } from "./display-techstack"; import { Ripple } from "./ripple"; - export default function Features() { + return ( +
+ +
+
+ - return ( -
- -
-
- - -
- -

Framework Agnostic

-
-
-
-
-

- Supports popular frameworks -

-
-
-

- Supports your favorite frontend, backend and meta frameworks, including React, Vue, Svelte, Solid, Next.js, Nuxt.js, Hono, and more{" "} - - Learn more - -

-
-
-
- - -
- -

Authentication

-
-
-
-
-

- Email & Password Authentication -

-
-
-

- Builtin support for email and password authentication, with secure password hashing and account management features{" "} - - Learn more - -

-
-
-
- - -
- -

Social Sign-on

-
-
-
-
-

- Support multiple OAuth providers. -

-
-
-

- Allow users to sign in with their accounts, including Github, Google, Discord, Twitter, and more.{" "} - - Learn more - -

-
-
-
-
- -

Two Factor

-
-
-
-
-

- Two Factor Authentication -

-
-
-

- With our built-in two factor authentication plugin, you can add an extra layer of security to your account. - {" "} - - Learn more - -

- -
-
-
-
- -

Organization & Access Control

-
-
-
-
-

- Gain and manage access. -

-
-
-

- Manage users and their access to resources within your application.{" "} - - Learn more - -

-
-
-
- -
- -

Plugin Ecosystem

-
-
-
-

- Pluggable with custom. -

-
-
-
-

- Enhance your application with our official plugins and those created by the community.{" "} - - Learn more - -

-
-
-
- - -
-
-
- -

- Own your auth -

-
-

- - Roll your own auth with confidence in minutes! - -

-
- -
- - - - -
-
- - -
+
+ +

+ Framework Agnostic{" "} +

+
+
+
+
+

+ Supports popular frameworks +

+
+

+ Supports your favorite frontend, backend and meta frameworks, + including React, Vue, Svelte, Solid, Next.js, Nuxt.js, Hono, and + more{" "} + + Learn more + +

+
- ); -} \ No newline at end of file +
+ + +
+ +

Authentication

+
+
+
+
+

+ Email & Password Authentication +

+
+
+

+ Builtin support for email and password authentication, with secure + password hashing and account management features{" "} + + Learn more + +

+
+
+
+ + +
+ +

Social Sign-on

+
+
+
+
+

+ Support multiple OAuth providers. +

+
+
+

+ Allow users to sign in with their accounts, including Github, + Google, Discord, Twitter, and more.{" "} + + Learn more + +

+
+
+
+
+ +

Two Factor

+
+
+
+
+

+ Two Factor Authentication +

+
+
+

+ With our built-in two factor authentication plugin, you can add an + extra layer of security to your account.{" "} + + Learn more + +

+
+
+
+
+ +

+ Organization & Access Control{" "} +

+
+
+
+
+

+ Gain and manage access. +

+
+
+

+ Manage users and their access to resources within your + application.{" "} + + Learn more + +

+
+
+
+ +
+ +

+ Plugin Ecosystem{" "} +

+
+
+
+

+ Pluggable with custom. +

+
+
+
+

+ Enhance your application with our official plugins and those + created by the community.{" "} + + Learn more + +

+
+
+
+ + +
+
+
+ +

+ Own your auth +

+
+

+ Roll your own auth with confidence in minutes! +

+
+ +
+ + + + +
+
+
+
+
+ ); +} diff --git a/docs/components/side-bar.tsx b/docs/components/side-bar.tsx index 097b7e2c..d0c4e8ca 100644 --- a/docs/components/side-bar.tsx +++ b/docs/components/side-bar.tsx @@ -1,9 +1,9 @@ "use client"; import { - Accordion, - AccordionContent, - AccordionItem, - AccordionTrigger, + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, } from "@/components/ui/accordion"; import { AsideLink } from "@/components/ui/aside-link"; import { FadeIn, FadeInStagger } from "@/components/ui/fade-in"; @@ -13,70 +13,70 @@ import { usePathname } from "next/navigation"; import { contents } from "./sidebar-content"; import { Search } from "lucide-react"; export default function ArticleLayout() { - const { setOpenSearch } = useSearchContext(); - const pathname = usePathname(); - function getDefaultValue() { - const defaultValue = contents.findIndex((item) => - item.list.some((listItem) => listItem.href === pathname), - ); - return defaultValue === -1 ? 0 : defaultValue; - } - return ( - - ); + const { setOpenSearch } = useSearchContext(); + const pathname = usePathname(); + function getDefaultValue() { + const defaultValue = contents.findIndex((item) => + item.list.some((listItem) => listItem.href === pathname) + ); + return defaultValue === -1 ? 0 : defaultValue; + } + return ( + + ); } diff --git a/docs/components/sidebar-content.tsx b/docs/components/sidebar-content.tsx index 53a2221a..4ddf9406 100644 --- a/docs/components/sidebar-content.tsx +++ b/docs/components/sidebar-content.tsx @@ -1,402 +1,421 @@ import { - Key, - LucideAArrowDown, - LucideIcon, - ScanFace, - Users2, - UserSquare2, + AppWindow, + Clock, + Key, + LucideAArrowDown, + LucideIcon, + ScanFace, + Users2, + UserSquare2, } from "lucide-react"; import { ReactNode, SVGProps } from "react"; import { Icons } from "./icons"; interface Content { - title: string; - href?: string; - Icon: ((props?: SVGProps) => ReactNode) | LucideIcon; - list: { - title: string; - href: string; - icon: ((props?: SVGProps) => ReactNode) | LucideIcon; - group?: boolean; - }[]; + title: string; + href?: string; + Icon: ((props?: SVGProps) => ReactNode) | LucideIcon; + list: { + title: string; + href: string; + icon: ((props?: SVGProps) => ReactNode) | LucideIcon; + group?: boolean; + }[]; } export const contents: Content[] = [ - { - title: "Get Started", - Icon: () => ( - - - - ), - list: [ - { - title: "Introduction", - href: "/docs/introduction", - icon: () => ( - - - - ), - }, - { - title: "Installation", - href: "/docs/installation", - icon: () => ( - - - - ), - }, - { - title: "Basic Usage", - href: "/docs/basic-usage", - icon: () => ( - - - - ), - }, - ], - }, - { - title: "Authentication", - Icon: () => ( - - - - ), - list: [ - { - title: "Email & Password", - href: "/docs/authentication/email-password", - icon: () => ( - - - - ), - }, - { - title: "Social Sign-On", - group: true, - href: "/docs/authentication/social-providers", - icon: () => ( - - - - ), - }, - { - title: "Apple", - href: "/docs/authentication/apple", - icon: () => ( - - - - ), - }, + { + title: "Get Started", + Icon: () => ( + + + + ), + list: [ + { + title: "Introduction", + href: "/docs/introduction", + icon: () => ( + + + + ), + }, + { + title: "Installation", + href: "/docs/installation", + icon: () => ( + + + + ), + }, + { + title: "Basic Usage", + href: "/docs/basic-usage", + icon: () => ( + + + + ), + }, + ], + }, + { + title: "Authentication", + Icon: () => ( + + + + ), + list: [ + { + title: "Email & Password", + href: "/docs/authentication/email-password", + icon: () => ( + + + + ), + }, + { + title: "Social Sign-On", + group: true, + href: "/docs/authentication/social-providers", + icon: () => ( + + + + ), + }, + { + title: "Apple", + href: "/docs/authentication/apple", + icon: () => ( + + + + ), + }, - { - title: "Discord", - href: "/docs/authentication/discord", - icon: () => ( - - - - ), - }, - { - title: "Facebook", - href: "/docs/authentication/facebook", - icon: () => ( - - - - - - - ), - }, - { - title: "Github", - href: "/docs/authentication/github", - icon: () => ( - - - - ), - }, - { - title: "Google", - href: "/docs/authentication/google", - icon: () => ( - - - - ), - }, - { - title: "Twitch", - href: "/docs/authentication/twitch", - icon: () => ( - - - - ), - }, - { - title: "X (Twitter)", - href: "/docs/authentication/twitter", - icon: () => ( - - - - ), - }, - ], - }, - { - title: "Integrations", - Icon: () => ( - - - - ), - list: [ - { - group: true, - title: "Frameworks", - href: "/docs/integrations", - icon: LucideAArrowDown, - }, - { - title: "Next", - icon: Icons.nextJS, - href: "/docs/integrations/next", - }, - { - title: "Nuxt", - icon: Icons.nuxt, - href: "/docs/integrations/nuxt", - }, - { - title: "Svelte Kit", - icon: Icons.svelteKit, - href: "/docs/integrations/svelte-kit", - }, - { - title: "Hono", - icon: Icons.hono, - href: "/docs/integrations/hono", - }, - { - title: "Solid Start", - icon: Icons.solidStart, - href: "/docs/integrations/solid-start", - }, - ], - }, - { - title: "Plugins", - Icon: () => ( - - - - - - - ), - list: [ - { - title: "Authentication", - group: true, - href: "/docs/plugins/1st-party-plugins", - icon: LucideAArrowDown, - }, - { - title: "Passkey", - href: "/docs/plugins/passkey", - icon: () => ( - - - - ), - }, - { - title: "Two Factor", - icon: ScanFace, - href: "/docs/plugins/2fa", - }, - { - title: "Username", - icon: UserSquare2, - href: "/docs/plugins/username", - }, - { - title: "Authorization", - group: true, - href: "/docs/plugins/1st-party-plugins", - icon: LucideAArrowDown, - }, - { - title: "Organization", - icon: Users2, - href: "/docs/plugins/organization", - }, - { - title: "Utility", - group: true, - href: "/docs/plugins/1st-party-plugins", - icon: LucideAArrowDown, - }, - { - title: "Bearer", - icon: Key, - href: "/docs/plugins/bearer", - }, - ], - }, + { + title: "Discord", + href: "/docs/authentication/discord", + icon: () => ( + + + + ), + }, + { + title: "Facebook", + href: "/docs/authentication/facebook", + icon: () => ( + + + + + + + ), + }, + { + title: "Github", + href: "/docs/authentication/github", + icon: () => ( + + + + ), + }, + { + title: "Google", + href: "/docs/authentication/google", + icon: () => ( + + + + ), + }, + { + title: "Twitch", + href: "/docs/authentication/twitch", + icon: () => ( + + + + ), + }, + { + title: "X (Twitter)", + href: "/docs/authentication/twitter", + icon: () => ( + + + + ), + }, + ], + }, + { + title: "Integrations", + Icon: () => ( + + + + ), + list: [ + { + group: true, + title: "Frameworks", + href: "/docs/integrations", + icon: LucideAArrowDown, + }, + { + title: "Next", + icon: Icons.nextJS, + href: "/docs/integrations/next", + }, + { + title: "Nuxt", + icon: Icons.nuxt, + href: "/docs/integrations/nuxt", + }, + { + title: "Svelte Kit", + icon: Icons.svelteKit, + href: "/docs/integrations/svelte-kit", + }, + { + title: "Hono", + icon: Icons.hono, + href: "/docs/integrations/hono", + }, + { + title: "Solid Start", + icon: Icons.solidStart, + href: "/docs/integrations/solid-start", + }, + ], + }, + { + title: "Plugins", + Icon: () => ( + + + + + + + ), + list: [ + { + title: "Authentication", + group: true, + href: "/docs/plugins/1st-party-plugins", + icon: LucideAArrowDown, + }, + + { + title: "Two Factor", + icon: ScanFace, + href: "/docs/plugins/2fa", + }, + { + title: "Username", + icon: UserSquare2, + href: "/docs/plugins/username", + }, + { + title: "Passkey", + href: "/docs/plugins/passkey", + icon: () => ( + + + + ), + }, + { + title: "Authorization", + group: true, + href: "/docs/plugins/1st-party-plugins", + icon: LucideAArrowDown, + }, + { + title: "Organization", + icon: Users2, + href: "/docs/plugins/organization", + }, + { + title: "Utility", + group: true, + href: "/docs/plugins/1st-party-plugins", + icon: LucideAArrowDown, + }, + { + title: "Rate Limit", + icon: Clock, + href: "/docs/plugins/rate-limit", + }, + { + title: "Bearer", + icon: Key, + href: "/docs/plugins/bearer", + }, + ], + }, + { + title: "Examples", + list: [ + { + title: "Next JS", + href: "/docs/examples/next-js", + icon: Icons.nextJS, + }, + ], + Icon: AppWindow, + }, ]; diff --git a/docs/content/docs/examples/next-js.mdx b/docs/content/docs/examples/next-js.mdx new file mode 100644 index 00000000..b50426c6 --- /dev/null +++ b/docs/content/docs/examples/next-js.mdx @@ -0,0 +1,20 @@ +--- +title: Next js example +descirption: Better auth next js example +--- + + diff --git a/docs/content/docs/integrations/hono.mdx b/docs/content/docs/integrations/hono.mdx new file mode 100644 index 00000000..f973babd --- /dev/null +++ b/docs/content/docs/integrations/hono.mdx @@ -0,0 +1,73 @@ +--- +title: Hono Integration +description: Hono Integration Guide +--- + +This integration guide is assuming you are using Hono with node server. + +## Installation + +First, install Better Auth + +```package-install +npm install better-auth +``` + +## Set Environment Variables + +Create a `.env` file in the root of your project and add the following environment variables: + +**Set Base URL** +```txt title=".env" +BETTER_AUTH_URL=http://localhost:3000 # Base URL of your Next.js app +``` + +**Set Secret** + +Random value used by the library for encryption and generating hashes. You can generate one using the button below or you can use something like openssl. +```txt title=".env" +BETTER_AUTH_SECRET= +``` + + +## Configure Server + +### Create Better Auth instance + +We recommend to create `auth.ts` file inside your `lib/` directory. This file will contain your Better Auth instance. + +```ts twoslash title="auth.ts" +import { betterAuth } from "better-auth" + +export const auth = betterAuth({ + database: { + provider: "sqlite", //change this to your database provider + url: "./db.sqlite", // path to your database or connection string + } + // Refer to the api documentation for more configuration options +}) +``` + + +Better Auth currently supports only SQLite, MySQL, and PostgreSQL. It uses Kysely under the hood, so you can also pass any Kysely dialect directly to the database object. + + +### Mount the handler + +We need to mount the handler to hono endpoint. + +```ts +import { Hono } from "hono"; +import { auth } from "./auth"; +import { serve } from "@hono/node-server"; +import { cors } from "hono/cors"; + +const app = new Hono(); + +app.on(["POST", "GET"], "/api/auth/**", (c) => { + return auth.handler(c.req.raw); +}); + +serve(app); +``` + diff --git a/docs/_content/docs/integrations/nuxt.mdx b/docs/content/docs/integrations/nuxt.mdx similarity index 99% rename from docs/_content/docs/integrations/nuxt.mdx rename to docs/content/docs/integrations/nuxt.mdx index be43ac30..4d77bf27 100644 --- a/docs/_content/docs/integrations/nuxt.mdx +++ b/docs/content/docs/integrations/nuxt.mdx @@ -1,5 +1,5 @@ --- -title: Nuxt.js +title: Nuxt.js Integration description: Learn how to integrate Better Auth with Nuxt.js --- diff --git a/docs/content/docs/integrations/solid-start.mdx b/docs/content/docs/integrations/solid-start.mdx new file mode 100644 index 00000000..21afbe52 --- /dev/null +++ b/docs/content/docs/integrations/solid-start.mdx @@ -0,0 +1,64 @@ +--- +title: Solid Start Integration +description: Solid Start integratons guide +--- + + +## Installation + +First, install Better Auth + +```package-install +npm install better-auth +``` + +## Set Environment Variables + +Create a `.env` file in the root of your project and add the following environment variables: + +**Set Base URL** +```txt title=".env" +BETTER_AUTH_URL=http://localhost:3000 # Base URL of your Next.js app +``` + +**Set Secret** + +Random value used by the library for encryption and generating hashes. You can generate one using the button below or you can use something like openssl. +```txt title=".env" +BETTER_AUTH_SECRET= +``` + + + +## Configure Server + +### Create Better Auth instance + +We recommend to create `auth.ts` file inside your `lib/` directory. This file will contain your Better Auth instance. + +```ts twoslash title="auth.ts" +import { betterAuth } from "better-auth" + +export const auth = betterAuth({ + database: { + provider: "sqlite", //change this to your database provider + url: "./db.sqlite", // path to your database or connection string + } + // Refer to the api documentation for more configuration options +}) +``` + + +Better Auth currently supports only SQLite, MySQL, and PostgreSQL. It uses Kysely under the hood, so you can also pass any Kysely dialect directly to the database object. + + +### Mount the handler + +We need to mount the handler to Solid Start server. Put the following code in your `*auth.ts` file inside `/routes/api/auth` folder. + +```ts title="*auth.ts" +import { auth } from "~/lib/auth"; +import { toSolidStartHandler } from "better-auth/solid-start"; + +export const { GET, POST } = toSolidStartHandler(auth); +``` \ No newline at end of file diff --git a/docs/content/docs/integrations/svelte-kit.mdx b/docs/content/docs/integrations/svelte-kit.mdx new file mode 100644 index 00000000..dcc49f5b --- /dev/null +++ b/docs/content/docs/integrations/svelte-kit.mdx @@ -0,0 +1,148 @@ +--- +title: Svelte Kit Integration +description: Learn how to integrate Better Auth with Svelte Kit +--- + +Better Auth has first class support for Svelte Kit. It provides utilities to make it easier to use Better Auth with Svelte Kit. + +## Installation + +First, install Better Auth + +```package-install +npm install better-auth +``` + +## Set Environment Variables + +Create a `.env` file in the root of your project and add the following environment variables: + +**Set Base URL** +```txt title=".env" +BETTER_AUTH_URL=http://localhost:3000 # Base URL of your Next.js app +``` + +**Set Secret** + +Random value used by the library for encryption and generating hashes. You can generate one using the button below or you can use something like openssl. +```txt title=".env" +BETTER_AUTH_SECRET= +``` + + + +## Configure Server + +### Create Better Auth instance + +We recommend to create `auth.ts` file inside your `lib/` directory. This file will contain your Better Auth instance. + +```ts twoslash title="auth.ts" +import { betterAuth } from "better-auth" + +export const auth = betterAuth({ + database: { + provider: "sqlite", //change this to your database provider + url: "./db.sqlite", // path to your database or connection string + } + // Refer to the api documentation for more configuration options +}) +``` + + +Better Auth currently supports only SQLite, MySQL, and PostgreSQL. It uses Kysely under the hood, so you can also pass any Kysely dialect directly to the database object. + + +### Mount the handler + +We need to mount the handler to svelte kit server hook. + +```ts +import { auth } from "$lib/auth"; +import { svelteKitHandler } from "better-auth/svelte-kit"; + +export async function handle({ event, resolve }) { + return svelteKitHandler({ event, resolve, auth }); +} +``` + +### Migrate the database +Run the following command to create the necessary tables in your database: + +```bash +npx better-auth migrate +``` + + +## Create a client + +Create a client instance. You can name the file anything you want. Here we are creating `client.ts` file inside the `lib/` directory. + +```ts twoslash title="client.ts" +import { createAuthClient } from "better-auth/svelte" // make sure to import from better-auth/svlete + +export const client = createAuthClient({ + //you can pass client configuration here +}) +``` + +Once you have created the client, you can use it to sign up, sign in, and perform other actions. +Some of the actinos are reactive. The client use [nano-store](https://github.com/nanostores/nanostores) to store the state and refelct changes when there is a change like a user signing in or out affecting the session state. + +### Example usage +```svelte + +
+ {#if $session} +
+

+ {$session?.user.name} +

+ +
+ {:else} + + {/if} +
+``` + +### Example: Getting Session on a loader + +```ts title="+page.ts" +import { auth } from "$lib/auth"; + +export async function load(request: Request) { + const session = await auth.api.getSession({ + headers: request.headers, + }); + if (!session) { + return { + status: 401, + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + error: "Unauthorized", + }), + }; + } + return session; +} +``` \ No newline at end of file diff --git a/docs/content/docs/plugins/2fa.mdx b/docs/content/docs/plugins/2fa.mdx new file mode 100644 index 00000000..2c284433 --- /dev/null +++ b/docs/content/docs/plugins/2fa.mdx @@ -0,0 +1,387 @@ +--- +title: Two-Factor Authentication (2FA) +description: Enhance your app's security with two-factor authentication +--- + +`OTP` `TOTP` `Backup Codes` `Trusted Devices` + +## What is Two-Factor Authentication? + +Two-Factor Authentication (2FA) adds an extra security step when users log in. Instead of just using a password, they'll need to provide a second form of verification. This makes it much harder for unauthorized people to access accounts, even if they've somehow gotten the password. + +This plugin offers two main methods of 2FA: + +1. **OTP (One-Time Password)**: A temporary code sent to the user's email or phone. +2. **TOTP (Time-based One-Time Password)**: A code generated by an app on the user's device. + +**Additional features include:** +- Generating backup codes for account recovery +- Enabling/disabling 2FA +- Managing trusted devices + + +## Initial Setup + +To get started with Two-Factor Authentication, follow these steps: + + + + ### Add the plugin to your auth config: + + ```ts title="auth.ts" + import { betterAuth } from "better-auth" + import { twoFactor } from "better-auth/plugins" + + export const auth = await betterAuth({ + // ... other config options + plugins: [ + twoFactor({ + issuer: "my-app" + }) + ] + }) + ``` + + + + ### Migrate your database: + + ```bash + npx better-auth migrate + ``` + + + + ### Add the client plugin: + + ```ts title="client.ts" + import { createAuthClient } from "better-auth/client" + import { twoFactorClient } from "better-auth/client/plugins" + + const client = createAuthClient({ + plugins: [ + twoFactorClient({ + twoFactorPage: "/two-factor" //redirect for two factor verification if enabled // [!code highlight] + }) + ] + }) + ``` + + + +## Usage + +### Enabling 2FA + +To enable two-factor authentication for a user: + +```ts title="two-factor.ts" +const enableTwoFactor = async() => { + const { data, error } = await client.twoFactor.enable() + if (data) { + // 2FA has been enabled successfully + } +} +``` + + +### Sign In with 2FA + +When a user with 2FA enabled tries to sign in, you'll need to verify their 2FA code. If they have 2FA enabled, they'll be redirected to the `twoFactorPage` where they can enter their 2FA code. + +```ts +const signin = async () => { + const { data, error } = await client.signIn.email({ + email: "user@example.com", + password: "password123", + }) +} +``` + +By default, the user will be redirected to the `twoFactorPage` if they have 2FA enabled. If you want to handle the 2FA verification in place, you can use the `options` parameter. + +```ts +const signin = async () => { + const { data, error } = await client.signIn.email({ + email: "user@example.com", + password: "password123", + options: { + async onSuccess(context) { + if (context.data.twoFactorRedirect) { + // Handle the 2FA verification in place + } + } + } + }) +} +``` + + +### TOTP + +TOTP is a time-based one-time password algorithm that generates a code based on the current time. It's a more secure method than OTP because it takes into account the time it takes to generate the code. + +#### Getting TOTP URI + +After enabling 2FA, you can get the TOTP URI to display to the user. + +```ts +const { data, error } = await client.twoFactor.getTotpUri() +if (data) { + // Use data.totpURI to generate a QR code or display to the user +} +``` + +#### Generating a QR Code + +You can use a library like `qrcode` to generate a QR code from the TOTP URI. + +```ts +import { toCanvas } from "qrcode" + +toCanvas(document.getElementById('canvas'), data.totpURI) +``` + +#### Verifying TOTP + +After the user has entered their 2FA code, you can verify it + +```ts +const verifyTotp = async (code: string) => { + const { data, error } = await client.twoFactor.verifyTotp({ code }) +} +``` + +### OTP + +OTP is a one-time password algorithm that generates a code based on the current time. It's a more secure method than TOTP because it takes into account the time it takes to generate the code. + + +Before using OTP, you need to setup `sendOTP` function. + +```ts title="auth.ts" twoslash +import { betterAuth } from "better-auth" +import { twoFactor } from "better-auth/plugins" + +export const auth = await betterAuth({ + database: { + provider: "sqlite", + url: "./db.sqlite", + }, + plugins: [ + twoFactor({ + otpOptions: { + async sendOTP(user, otp) { + console.log({ user, otp }); + }, + }, + }) + ] +}) +``` + +#### Sending OTP + +sending otp is done by calling `sendOtp` function. This functino will call your `sendOTP` function that you provide in the `otpOptions` with the otp code and the user. + +```ts +const { data, error } = await client.twoFactor.sendOtp() +if (data) { + // Show the OTP to the user +} +``` + +#### Verifying OTP + +After the user has entered their OTP code, you can verify it + +```ts +const verifyOtp = async (code: string) => { + const { data, error } = await client.twoFactor.verifyOtp({ code }) +} +``` + +### Backup Codes + + +Backup codes are generated and stored in the database when the user enabled two factor authentication. This can be used to recover access to the account if the user loses access to their phone or email. + +#### Generating Backup Codes +Generate backup codes for account recovery: + +```ts +const { data, error } = await client.twoFactor.generateBackupCodes() +if (data) { + // Show the backup codes to the user +} +``` + +#### Using Backup Codes + +Backup codes can be used to recover access to the account if the user loses access to their phone or email. + +```ts +const { data, error } = await client.twoFactor.verifyBackupCode({code: ""}) +if (data) { + // 2FA verified and account recovered +} +``` + +### Trusted Devices + +You can mark a device as trusted by passing `trustDevice` to `verifyTotp` or `verifyOtp`. + +```ts +const verify2FA = async (code: string) => { + const { data, error } = await client.twoFactor.verifyTotp({ + code, + callbackURL: "/dashboard", + trustDevice: true // Mark this device as trusted + }) + if (data) { + // 2FA verified and device trusted + } +} +``` + +When `trustDevice` is set to `true`, the current device will be remembered for 60 days. During this period, the user won't be prompted for 2FA on subsequent sign-ins from this device. The trust period is refreshed each time the user signs in successfully. + + + Trusted devices enhance user convenience but should be used carefully, as they slightly reduce security. Encourage users to only trust personal, secure devices. + + +## Configuration + +### Server + +To configure the two factor plugin, you need to add the following code to your better auth instance. + +```ts title="auth.ts" twoslash +import { betterAuth } from "better-auth" +import { twoFactor } from "better-auth/plugins" + +export const auth = await betterAuth({ + database: { + provider: "sqlite", + url: "./db.sqlite", + }, + plugins: [ // [!code highlight] + twoFactor({ // [!code highlight] + issuer: "my-app" // [!code highlight] + }) // [!code highlight] + ] // [!code highlight] +}) +``` + +**Issuer**: The issuer is the name of your application. It's used to generate totp codes. It'll be displayed in the authenticator apps. + +**TOTP options** + +these are options for TOTP. + + + +**OTP options** + +these are options for OTP. + + + +**Backup Code Options** + +backup codes are generated and stored in the database when the user enabled two factor authentication. This can be used to recover access to the account if the user loses access to their phone or email. + + + +### Client + +To use the two factor plugin in the client, you need to add it on your plugins list. + +```ts title="client.ts" twoslash +import { createAuthClient } from "better-auth/client" +import { twoFactorClient } from "better-auth/client/plugins" + +const client = createAuthClient({ + plugins: [ + twoFactorClient({ // [!code highlight] + twoFactorPage: "/two-factor" // [!code highlight] + }) // [!code highlight] + ] // [!code highlight] +}) +``` + +**Options** + +`twoFactorPage`: The page to redirect the user to after they have enabled 2-Factor. This is the page where the user will be redirected to verify their 2-Factor code. + +`redirect`: If set to `false`, the user will not be redirected to the `twoFactorPage` after they have enabled 2-Factor. + + +## Database Schema + +Two factores requires additional fields on the user table. If you use better auth's migration system, it will automatically create this table for you. + +```ts +const schema = { + user: { + fields: { + twoFactorEnabled: { + type: "boolean", + required: false, + defaultValue: false, + }, + twoFactorSecret: { + type: "string", + required: false, + }, + twoFactorBackupCodes: { + type: "string", + required: false, + returned: false, + }, + }, + }, +} +``` \ No newline at end of file diff --git a/docs/content/docs/plugins/organization.mdx b/docs/content/docs/plugins/organization.mdx new file mode 100644 index 00000000..16315734 --- /dev/null +++ b/docs/content/docs/plugins/organization.mdx @@ -0,0 +1,4 @@ +--- +title: Organization +descirption: The organization plugin allows you to manage your organization's members and teams. +--- \ No newline at end of file diff --git a/docs/content/docs/plugins/passkey.mdx b/docs/content/docs/plugins/passkey.mdx new file mode 100644 index 00000000..a9f56632 --- /dev/null +++ b/docs/content/docs/plugins/passkey.mdx @@ -0,0 +1,158 @@ +--- +title: Passkey +description: Passkey +--- + +Passkeys are a secure, passwordless authentication method using cryptographic key pairs, supported by WebAuthn and FIDO2 standards in web browsers. They replace passwords with unique key pairs: a private key stored on the user’s device and a public key shared with the website. Users can log in using biometrics, PINs, or security keys, providing strong, phishing-resistant authentication without traditional passwords. + +The passkey plugin implementation is powered by [simple-web-authn](https://simplewebauthn.dev/) behind the scenes. + +## Quick setup + + + + ### Add the plugin to your auth config + To add the passkey plugin to your auth config, you need to import the plugin and pass it to the `plugins` option of the auth instance. + + **Options** + + `rpID`: A unique identifier for your website. 'localhost' is okay for local dev + + `rpName`: Human-readable title for your website + + `origin`: The URL at which registrations and authentications should occur. 'http://localhost' and 'http://localhost:PORT' are also valid.Do NOT include any trailing / + + ```ts title="auth.ts" twoslash + import { betterAuth } from "better-auth" + import { passkey } from "better-auth/plugins" + + export const auth = await betterAuth({ + database: { + provider: "sqlite", + url: "./db.sqlite", + }, + plugins: [ // [!code highlight] + passkey({ // [!code highlight] + rpID: "localhost", // [!code highlight] + rpName: "BetterAuth", // [!code highlight] + origin: "http://localhost:3000", // [!code highlight] + }), // [!code highlight] + ], // [!code highlight] + }) + ``` + + + ### Add the client plugin + + ```ts title="client.ts" twoslash + import { createAuthClient } from "better-auth/client" + import { passkeyClient } from "better-auth/client/plugins" + + const client = createAuthClient({ + plugins: [ // [!code highlight] + passkeyClient() // [!code highlight] + ] // [!code highlight] + }) + ``` + + + + ### Add a passkey + + To add a passkey make sure a user is authenticated and then call the `passkey.addPasskey` function provided by the client. + + ```ts title="client.ts" twoslash + import { createAuthClient } from "better-auth/client" + import { passkeyClient } from "better-auth/client/plugins" + + const client = createAuthClient({ + plugins: [ // [!code highlight] + passkeyClient() // [!code highlight] + ] // [!code highlight] + }) + // ---cut--- + const data = await client.passkey.addPasskey() + ``` + This will prompt the user to register a passkey. And it'll add the passkey to the user's account. + + + + ### Signin with a passkey + + To signin with a passkey you can use the passkeySignIn method. This will prompt the user to sign in with their passkey. + + Signin method accepts: + + `autoFill`: Browser autofill, a.k.a. Conditional UI. [read more](https://simplewebauthn.dev/docs/packages/browser#browser-autofill-aka-conditional-ui) + + `callbackURL`: The URL to redirect to after the user has signed in. (optional) + + ```ts title="client.ts" twoslash + import { createAuthClient } from "better-auth/client" + import { passkeyClient } from "better-auth/client/plugins" + + const client = createAuthClient({ + plugins: [ // [!code highlight] + passkeyClient() // [!code highlight] + ] // [!code highlight] + }) + // ---cut--- + const data = await client.signIn.passkey() + ``` + + + + + +## Passkey Configuration + +**rpID**: A unique identifier for your website. 'localhost' is okay for local dev. + +**rpName**: Human-readable title for your website. + +**origin**: The URL at which registrations and authentications should occur. 'http://localhost' and 'http://localhost:PORT' are also valid. Do NOT include any trailing /. + + +## Database Schema + +Passkey requires a database table called `passkey` with the following fields. If you use better auth's migration system, it will automatically create this table for you. + +```ts +const schema = { + passkey: { + fields: { + publicKey: { + type: "string", + }, + userId: { + type: "string", + references: { + model: "user", + field: "id", + }, + }, + webauthnUserID: { + type: "string", + }, + counter: { + type: "number", + }, + deviceType: { + type: "string", + }, + backedUp: { + type: "boolean", + }, + transports: { + type: "string", + required: false, + }, + createdAt: { + type: "date", + defaultValue: new Date(), + required: false, + }, + }, + }, +} +``` \ No newline at end of file diff --git a/docs/content/docs/plugins/rate-limit.mdx b/docs/content/docs/plugins/rate-limit.mdx new file mode 100644 index 00000000..4b4b42ec --- /dev/null +++ b/docs/content/docs/plugins/rate-limit.mdx @@ -0,0 +1,4 @@ +--- +title: Rate Limit +description: The rate limit plugin allows you to limit the number of requests a user can make to your auth server. +--- diff --git a/docs/content/docs/plugins/username.mdx b/docs/content/docs/plugins/username.mdx new file mode 100644 index 00000000..e3adbdb5 --- /dev/null +++ b/docs/content/docs/plugins/username.mdx @@ -0,0 +1,119 @@ +--- +title: Username +description: Username plugin +--- + +The username plugin wraps the email and password authenticator and adds username support. This allows users to sign in and sign up with their username instead of their email. + +## Qiuck setup + + + + ### Add Plugin to the server + + ```ts title="auth.ts" twoslash + import { betterAuth } from "better-auth" + import { username } from "better-auth/plugins" + + const auth = await betterAuth({ + database: { + provider: "sqlite", + url: "./db.sqlite", + }, + plugins: [ // [!code highlight] + username() // [!code highlight] + ] // [!code highlight] + }) + ``` + + + ### Add the client plugin + + ```ts title="client.ts" twoslash + import { createAuthClient } from "better-auth/client" + import { usernameClient } from "better-auth/client/plugins" + + const client = createAuthClient({ + plugins: [ // [!code highlight] + usernameClient() // [!code highlight] + ] // [!code highlight] + }) + ``` + + + ### Signup with username + + To signup a user with username, you can use the `signUp.username` function provided by the client. The `signUp` function takes an object with the following properties: + + - `username`: The username of the user. + - `email`: The email address of the user. + - `password`: The password of the user. It should be at least 8 characters long and max 32 by default. + - `name`: The name of the user. + - `image`: The image of the user. (optional) + - `callbackURL`: The url to redirect to after the user has signed up. (optional) + + ```ts title="client.ts" twoslash + import { createAuthClient } from "better-auth/client" + import { usernameClient } from "better-auth/client/plugins" + const client = createAuthClient({ + plugins: [ // [!code highlight] + usernameClient() // [!code highlight] + ] // [!code highlight] + }) + // ---cut--- + + const data = await client.signUp.username({ + username: "test", + email: "test@email.com", + password: "password1234", + name: "test", + image: "https://example.com/image.png", + }) + ``` + + + ### Signin with username + + To signin a user with username, you can use the `signIn.username` function provided by the client. The `signIn` function takes an object with the following properties: + + - `username`: The username of the user. + - `password`: The password of the user. + - `callbackURL`: The url to redirect to after the user has signed in. (optional) + + ```ts title="client.ts" twoslash + import { createAuthClient } from "better-auth/client" + import { usernameClient } from "better-auth/client/plugins" + const client = createAuthClient({ + plugins: [ // [!code highlight] + usernameClient() // [!code highlight] + ] // [!code highlight] + }) + // ---cut--- + + const data = await client.signIn.username({ + username: "test", + password: "password1234", + }) + ``` + + + +## Configuration + +The username plugin doesn't require any configuration. It just needs to be added to the server and client. + +## Database Schema + +The username plugin requires a `username` field in the user table. If you're using better auth migration tool it will automatically add the `username` field to the user table. If not you can add it manually. + +```ts +const shcmea = { + user: { + username: { + type: "string", + unique: true, + required: false + } + } +} +``` diff --git a/docs/mdx-components.tsx b/docs/mdx-components.tsx index 264955a4..feb2e4d4 100644 --- a/docs/mdx-components.tsx +++ b/docs/mdx-components.tsx @@ -11,25 +11,26 @@ import { TypeTable } from "fumadocs-ui/components/type-table"; import { Features } from "./components/blocks/features"; export function useMDXComponents(components: MDXComponents): MDXComponents { - return { - ...defaultComponents, - ...components, - Link: ({ className, ...props }: React.ComponentProps) => ( - - ), - Step, - Steps, - Tab, - Tabs, - GenerateSecret, - Popup, - PopupTrigger, - PopupContent, - AnimatePresence, - TypeTable, - Features, - }; + return { + ...defaultComponents, + ...components, + Link: ({ className, ...props }: React.ComponentProps) => ( + + ), + Step, + Steps, + Tab, + Tabs, + GenerateSecret, + Popup, + PopupTrigger, + PopupContent, + AnimatePresence, + TypeTable, + Features, + iframe: (props) =>