diff --git a/demo/nextjs/.env.exmaple b/demo/nextjs/.env.exmaple new file mode 100644 index 00000000..cc4427a2 --- /dev/null +++ b/demo/nextjs/.env.exmaple @@ -0,0 +1,14 @@ +GOOGLE_CLIENT_SECRET= +GOOGLE_CLIENT_ID= +BETTER_AUTH_URL="http://localhost:3000" +BETTER_AUTH_SECRET= +TURSO_DATABASE_URL= +TURSO_AUTH_TOKEN= +GITHUB_CLIENT_ID= +GITHUB_CLIENT_SECRET= +RESEND_API_KEY= +TEST_EMAIL= +DISCORD_CLIENT_ID= +DISCORD_CLIENT_SECRET= +MICROSOFT_CLIENT_ID= +MICROSOFT_CLIENT_SECRET= \ No newline at end of file diff --git a/demo/nextjs/.gitignore b/demo/nextjs/.gitignore index b6cd43c6..2fd56990 100644 --- a/demo/nextjs/.gitignore +++ b/demo/nextjs/.gitignore @@ -30,7 +30,7 @@ yarn-debug.log* yarn-error.log* # env files (can opt-in for commiting if needed) -.env* +.env # vercel .vercel diff --git a/dev/bc-fe/hono/.gitignore b/dev/bc-fe/hono/.gitignore deleted file mode 100644 index 506e4c37..00000000 --- a/dev/bc-fe/hono/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# deps -node_modules/ diff --git a/dev/bc-fe/hono/README.md b/dev/bc-fe/hono/README.md deleted file mode 100644 index 6dd13e7c..00000000 --- a/dev/bc-fe/hono/README.md +++ /dev/null @@ -1,11 +0,0 @@ -To install dependencies: -```sh -bun install -``` - -To run: -```sh -bun run dev -``` - -open http://localhost:3000 diff --git a/dev/bc-fe/hono/auth.d.ts b/dev/bc-fe/hono/auth.d.ts deleted file mode 100644 index 0c231982..00000000 --- a/dev/bc-fe/hono/auth.d.ts +++ /dev/null @@ -1,23 +0,0 @@ -export type Auth = { - baseURL: "http://localhost:3000"; - basePath: "/auth"; - database: { - provider: "sqlite"; - url: "./db.sqlite"; - }; - socialProviders: [ - { - id: "github"; - }, - ]; - plugins: [ - { - id: "two-factor"; - endpoints: {}; - }, - { - id: "organization"; - endpoints: {}; - }, - ]; -}; diff --git a/dev/bc-fe/hono/db.sqlite b/dev/bc-fe/hono/db.sqlite deleted file mode 100644 index 6474cce8..00000000 Binary files a/dev/bc-fe/hono/db.sqlite and /dev/null differ diff --git a/dev/bc-fe/hono/package.json b/dev/bc-fe/hono/package.json deleted file mode 100644 index 7ce2d283..00000000 --- a/dev/bc-fe/hono/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "@dev/hono", - "scripts": { - "dev": "tsx -r dotenv/config src/index.ts", - "auth": "pnpm better-auth" - }, - "dependencies": { - "@hono/node-server": "^1.12.2", - "better-auth": "workspace:*", - "@types/better-sqlite3": "^7.6.11", - "better-sqlite3": "^11.3.0", - "dotenv": "^16.4.5", - "hono": "^4.5.9", - "tsx": "^4.19.0" - }, - "devDependencies": { - "@types/bun": "latest" - } -} \ No newline at end of file diff --git a/dev/bc-fe/hono/src/auth.ts b/dev/bc-fe/hono/src/auth.ts deleted file mode 100644 index 0ccd5cd2..00000000 --- a/dev/bc-fe/hono/src/auth.ts +++ /dev/null @@ -1,25 +0,0 @@ -import Database from "better-sqlite3"; -import { betterAuth } from "better-auth"; -import { organization, twoFactor, username } from "better-auth/plugins"; - -export const auth = betterAuth({ - baseURL: "http://localhost:3000", - basePath: "/auth", - database: new Database("./db.sqlite"), - socialProviders: { - github: { - clientId: process.env.GITHUB_CLIENT_ID || "", - clientSecret: process.env.GITHUB_CLIENT_SECRET || "", - }, - }, - plugins: [ - twoFactor({ - issuer: "BetterAuth", - }), - organization(), - username(), - ], - emailAndPassword: { - enabled: true, - }, -}); diff --git a/dev/bc-fe/hono/src/index.ts b/dev/bc-fe/hono/src/index.ts deleted file mode 100644 index 94938f49..00000000 --- a/dev/bc-fe/hono/src/index.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Hono } from "hono"; -import { auth } from "./auth"; -import { serve } from "@hono/node-server"; -import { cors } from "hono/cors"; - -const app = new Hono(); - -app.use( - "/api/auth/**", - cors({ - origin: "http://localhost:5173", - allowHeaders: ["Content-Type", "Authorization"], - allowMethods: ["POST", "GET", "OPTIONS"], - exposeHeaders: ["Content-Length"], - maxAge: 600, - credentials: true, - }), -); - -app.on(["POST", "GET"], "/api/auth/**", (c) => { - return auth.handler(c.req.raw); -}); - -serve(app); diff --git a/dev/bc-fe/hono/tsconfig.json b/dev/bc-fe/hono/tsconfig.json deleted file mode 100644 index 4a655351..00000000 --- a/dev/bc-fe/hono/tsconfig.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "compilerOptions": { - "strict": true, - "jsx": "react-jsx", - "jsxImportSource": "hono/jsx", - "module": "Preserve" - } -} diff --git a/dev/bc-fe/react/.gitignore b/dev/bc-fe/react/.gitignore deleted file mode 100644 index a547bf36..00000000 --- a/dev/bc-fe/react/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* -lerna-debug.log* - -node_modules -dist -dist-ssr -*.local - -# Editor directories and files -.vscode/* -!.vscode/extensions.json -.idea -.DS_Store -*.suo -*.ntvs* -*.njsproj -*.sln -*.sw? diff --git a/dev/bc-fe/react/README.md b/dev/bc-fe/react/README.md deleted file mode 100644 index 74872fd4..00000000 --- a/dev/bc-fe/react/README.md +++ /dev/null @@ -1,50 +0,0 @@ -# React + TypeScript + Vite - -This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. - -Currently, two official plugins are available: - -- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh -- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh - -## Expanding the ESLint configuration - -If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: - -- Configure the top-level `parserOptions` property like this: - -```js -export default tseslint.config({ - languageOptions: { - // other options... - parserOptions: { - project: ['./tsconfig.node.json', './tsconfig.app.json'], - tsconfigRootDir: import.meta.dirname, - }, - }, -}) -``` - -- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked` -- Optionally add `...tseslint.configs.stylisticTypeChecked` -- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config: - -```js -// eslint.config.js -import react from 'eslint-plugin-react' - -export default tseslint.config({ - // Set the react version - settings: { react: { version: '18.3' } }, - plugins: { - // Add the react plugin - react, - }, - rules: { - // other rules... - // Enable its recommended rules - ...react.configs.recommended.rules, - ...react.configs['jsx-runtime'].rules, - }, -}) -``` diff --git a/dev/bc-fe/react/eslint.config.js b/dev/bc-fe/react/eslint.config.js deleted file mode 100644 index 34a820fe..00000000 --- a/dev/bc-fe/react/eslint.config.js +++ /dev/null @@ -1,28 +0,0 @@ -import js from "@eslint/js"; -import reactHooks from "eslint-plugin-react-hooks"; -import reactRefresh from "eslint-plugin-react-refresh"; -import globals from "globals"; -import tseslint from "typescript-eslint"; - -export default tseslint.config( - { ignores: ["dist"] }, - { - extends: [js.configs.recommended, ...tseslint.configs.recommended], - files: ["**/*.{ts,tsx}"], - languageOptions: { - ecmaVersion: 2020, - globals: globals.browser, - }, - plugins: { - "react-hooks": reactHooks, - "react-refresh": reactRefresh, - }, - rules: { - ...reactHooks.configs.recommended.rules, - "react-refresh/only-export-components": [ - "warn", - { allowConstantExport: true }, - ], - }, - }, -); diff --git a/dev/bc-fe/react/index.html b/dev/bc-fe/react/index.html deleted file mode 100644 index e4b78eae..00000000 --- a/dev/bc-fe/react/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - Vite + React + TS - - -
- - - diff --git a/dev/bc-fe/react/package.json b/dev/bc-fe/react/package.json deleted file mode 100644 index cd0d6609..00000000 --- a/dev/bc-fe/react/package.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "@dev/react", - "private": true, - "version": "0.0.0", - "type": "module", - "scripts": { - "dev": "vite", - "build": "tsc -b && vite build", - "lint": "eslint .", - "preview": "vite preview" - }, - "dependencies": { - "better-auth": "workspace:^", - "react": "^18.3.1", - "react-dom": "^18.3.1" - }, - "devDependencies": { - "@eslint/js": "^9.9.0", - "@types/react": "^18.3.3", - "@types/react-dom": "^18.3.0", - "@vitejs/plugin-react": "^4.3.1", - "eslint": "^9.9.0", - "eslint-plugin-react-hooks": "^5.1.0-rc.0", - "eslint-plugin-react-refresh": "^0.4.9", - "globals": "^15.9.0", - "typescript": "^5.5.3", - "typescript-eslint": "^8.0.1", - "vite": "^5.4.1" - } -} \ No newline at end of file diff --git a/dev/bc-fe/react/public/vite.svg b/dev/bc-fe/react/public/vite.svg deleted file mode 100644 index e7b8dfb1..00000000 --- a/dev/bc-fe/react/public/vite.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/dev/bc-fe/react/src/App.css b/dev/bc-fe/react/src/App.css deleted file mode 100644 index b9d355df..00000000 --- a/dev/bc-fe/react/src/App.css +++ /dev/null @@ -1,42 +0,0 @@ -#root { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; -} - -.logo { - height: 6em; - padding: 1.5em; - will-change: filter; - transition: filter 300ms; -} -.logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); -} -.logo.react:hover { - filter: drop-shadow(0 0 2em #61dafbaa); -} - -@keyframes logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - -@media (prefers-reduced-motion: no-preference) { - a:nth-of-type(2) .logo { - animation: logo-spin infinite 20s linear; - } -} - -.card { - padding: 2em; -} - -.read-the-docs { - color: #888; -} diff --git a/dev/bc-fe/react/src/App.tsx b/dev/bc-fe/react/src/App.tsx deleted file mode 100644 index 6a1020c5..00000000 --- a/dev/bc-fe/react/src/App.tsx +++ /dev/null @@ -1,204 +0,0 @@ -import { useState } from "react"; -import "./App.css"; -import { auth } from "./lib/auth"; - -function App() { - const session = auth.useSession(); - return ( - <> -

Better Auth

-
- {session ? ( -
-

{session.user.name}

-

{session.user.username}

-

{session.user.email}

-
- {session.user.twoFactorEnabled ? ( - - ) : ( - - )} - - -
-
- ) : ( -
- - - -
- )} -
- - ); -} - -export default App; - -function SignUp() { - const [email, setEmail] = useState(""); - const [name, setName] = useState(""); - const [username, setUsername] = useState(""); - const [password, setPassword] = useState(""); - return ( -
- setEmail(e.target.value)} - /> - setName(e.target.value)} - /> - setUsername(e.target.value)} - /> - setPassword(e.target.value)} - /> - -
- ); -} - -function SignIn() { - const [email, setEmail] = useState(""); - const [password, setPassword] = useState(""); - return ( -
- setEmail(e.target.value)} - /> - - setPassword(e.target.value)} - /> - -
- ); -} diff --git a/dev/bc-fe/react/src/assets/react.svg b/dev/bc-fe/react/src/assets/react.svg deleted file mode 100644 index 6c87de9b..00000000 --- a/dev/bc-fe/react/src/assets/react.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/dev/bc-fe/react/src/index.css b/dev/bc-fe/react/src/index.css deleted file mode 100644 index 6119ad9a..00000000 --- a/dev/bc-fe/react/src/index.css +++ /dev/null @@ -1,68 +0,0 @@ -:root { - font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; - line-height: 1.5; - font-weight: 400; - - color-scheme: light dark; - color: rgba(255, 255, 255, 0.87); - background-color: #242424; - - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; -} -a:hover { - color: #535bf2; -} - -body { - margin: 0; - display: flex; - place-items: center; - min-width: 320px; - min-height: 100vh; -} - -h1 { - font-size: 3.2em; - line-height: 1.1; -} - -button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - background-color: #1a1a1a; - cursor: pointer; - transition: border-color 0.25s; -} -button:hover { - border-color: #646cff; -} -button:focus, -button:focus-visible { - outline: 4px auto -webkit-focus-ring-color; -} - -@media (prefers-color-scheme: light) { - :root { - color: #213547; - background-color: #ffffff; - } - a:hover { - color: #747bff; - } - button { - background-color: #f9f9f9; - } -} diff --git a/dev/bc-fe/react/src/lib/auth.ts b/dev/bc-fe/react/src/lib/auth.ts deleted file mode 100644 index e89bb140..00000000 --- a/dev/bc-fe/react/src/lib/auth.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { createAuthClient } from "better-auth/react"; -import { twoFactorClient, usernameClient } from "better-auth/client/plugins"; - -export const auth = createAuthClient({ - baseURL: "http://localhost:3000/api/auth", - plugins: [ - twoFactorClient({ - twoFactorPage: "/two-factor", - }), - usernameClient(), - ], -}); diff --git a/dev/bc-fe/react/src/main.tsx b/dev/bc-fe/react/src/main.tsx deleted file mode 100644 index f84bc47d..00000000 --- a/dev/bc-fe/react/src/main.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { StrictMode } from "react"; -import { createRoot } from "react-dom/client"; -import App from "./App.tsx"; -import "./index.css"; - -createRoot(document.getElementById("root")!).render( - - - , -); diff --git a/dev/bc-fe/react/src/vite-env.d.ts b/dev/bc-fe/react/src/vite-env.d.ts deleted file mode 100644 index 11f02fe2..00000000 --- a/dev/bc-fe/react/src/vite-env.d.ts +++ /dev/null @@ -1 +0,0 @@ -/// diff --git a/dev/bc-fe/react/tsconfig.app.json b/dev/bc-fe/react/tsconfig.app.json deleted file mode 100644 index b896f924..00000000 --- a/dev/bc-fe/react/tsconfig.app.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2020", - "useDefineForClassFields": true, - "lib": ["ES2020", "DOM", "DOM.Iterable"], - "module": "ESNext", - "skipLibCheck": true, - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "isolatedModules": true, - "moduleDetection": "force", - "noEmit": true, - "jsx": "react-jsx", - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true - }, - "include": ["src"] -} diff --git a/dev/bc-fe/react/tsconfig.json b/dev/bc-fe/react/tsconfig.json deleted file mode 100644 index fb124188..00000000 --- a/dev/bc-fe/react/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "files": [], - "references": [ - { "path": "./tsconfig.app.json" }, - { "path": "./tsconfig.node.json" } - ] -} diff --git a/dev/bc-fe/react/tsconfig.node.json b/dev/bc-fe/react/tsconfig.node.json deleted file mode 100644 index da16d82d..00000000 --- a/dev/bc-fe/react/tsconfig.node.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "lib": ["ES2023"], - "module": "ESNext", - "skipLibCheck": true, - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "isolatedModules": true, - "moduleDetection": "force", - "noEmit": true, - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true - }, - "include": ["vite.config.ts"] -} diff --git a/dev/bc-fe/react/vite.config.ts b/dev/bc-fe/react/vite.config.ts deleted file mode 100644 index e29a791f..00000000 --- a/dev/bc-fe/react/vite.config.ts +++ /dev/null @@ -1,7 +0,0 @@ -import react from "@vitejs/plugin-react"; -import { defineConfig } from "vite"; - -// https://vitejs.dev/config/ -export default defineConfig({ - plugins: [react()], -}); diff --git a/dev/express/.gitignore b/dev/express/.gitignore deleted file mode 100644 index 9b1ee42e..00000000 --- a/dev/express/.gitignore +++ /dev/null @@ -1,175 +0,0 @@ -# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore - -# Logs - -logs -_.log -npm-debug.log_ -yarn-debug.log* -yarn-error.log* -lerna-debug.log* -.pnpm-debug.log* - -# Caches - -.cache - -# Diagnostic reports (https://nodejs.org/api/report.html) - -report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json - -# Runtime data - -pids -_.pid -_.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover - -lib-cov - -# Coverage directory used by tools like istanbul - -coverage -*.lcov - -# nyc test coverage - -.nyc_output - -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) - -.grunt - -# Bower dependency directory (https://bower.io/) - -bower_components - -# node-waf configuration - -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) - -build/Release - -# Dependency directories - -node_modules/ -jspm_packages/ - -# Snowpack dependency directory (https://snowpack.dev/) - -web_modules/ - -# TypeScript cache - -*.tsbuildinfo - -# Optional npm cache directory - -.npm - -# Optional eslint cache - -.eslintcache - -# Optional stylelint cache - -.stylelintcache - -# Microbundle cache - -.rpt2_cache/ -.rts2_cache_cjs/ -.rts2_cache_es/ -.rts2_cache_umd/ - -# Optional REPL history - -.node_repl_history - -# Output of 'npm pack' - -*.tgz - -# Yarn Integrity file - -.yarn-integrity - -# dotenv environment variable files - -.env -.env.development.local -.env.test.local -.env.production.local -.env.local - -# parcel-bundler cache (https://parceljs.org/) - -.parcel-cache - -# Next.js build output - -.next -out - -# Nuxt.js build / generate output - -.nuxt -dist - -# Gatsby files - -# Comment in the public line in if your project uses Gatsby and not Next.js - -# https://nextjs.org/blog/next-9-1#public-directory-support - -# public - -# vuepress build output - -.vuepress/dist - -# vuepress v2.x temp and cache directory - -.temp - -# Docusaurus cache and generated files - -.docusaurus - -# Serverless directories - -.serverless/ - -# FuseBox cache - -.fusebox/ - -# DynamoDB Local files - -.dynamodb/ - -# TernJS port file - -.tern-port - -# Stores VSCode versions used for testing VSCode extensions - -.vscode-test - -# yarn v2 - -.yarn/cache -.yarn/unplugged -.yarn/build-state.yml -.yarn/install-state.gz -.pnp.* - -# IntelliJ based IDEs -.idea - -# Finder (MacOS) folder config -.DS_Store diff --git a/dev/express/README.md b/dev/express/README.md deleted file mode 100644 index bbb379a8..00000000 --- a/dev/express/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# express - -To install dependencies: - -```bash -bun install -``` - -To run: - -```bash -bun run index.ts -``` - -This project was created using `bun init` in bun v1.1.27. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime. diff --git a/dev/express/auth.ts b/dev/express/auth.ts deleted file mode 100644 index 5f70fe73..00000000 --- a/dev/express/auth.ts +++ /dev/null @@ -1,6 +0,0 @@ -import Database from "better-sqlite3"; -import { betterAuth } from "better-auth"; - -export const auth = betterAuth({ - database: new Database("better_auth.db"), -}); diff --git a/dev/express/index.ts b/dev/express/index.ts deleted file mode 100644 index 6bbf6fcb..00000000 --- a/dev/express/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -import express from "express"; -import { toNodeHandler } from "better-auth/node"; -import { auth } from "./auth"; - -const app = express(); -const port = 3005; - -app.get("/api/auth/*", toNodeHandler(auth)); - -app.listen(port, () => { - console.log(`Example app listening on port ${port}`); -}); diff --git a/dev/express/package.json b/dev/express/package.json deleted file mode 100644 index ecaaec3c..00000000 --- a/dev/express/package.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "@better-auth/dev-express", - "private": true, - "module": "index.ts", - "type": "module", - "devDependencies": { - "@types/bun": "latest", - "@types/express": "^4.17.21" - }, - "peerDependencies": { - "typescript": "^5.0.0", - "@types/better-sqlite3": "^7.6.11", - "better-sqlite3": "^11.3.0" - }, - "dependencies": { - "better-auth": "workspace:*", - "express": "^4.21.0", - "tsx": "^4.19.0" - } -} \ No newline at end of file diff --git a/dev/express/tsconfig.json b/dev/express/tsconfig.json deleted file mode 100644 index e0abf845..00000000 --- a/dev/express/tsconfig.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "compilerOptions": { - "esModuleInterop": true, - "skipLibCheck": true, - "target": "es2022", - "allowJs": true, - "resolveJsonModule": true, - "moduleDetection": "force", - "isolatedModules": true, - "verbatimModuleSyntax": true, - "strict": true, - "moduleResolution": "Bundler", - "outDir": "dist", - "sourceMap": true, - "lib": ["es2022"] - } -} diff --git a/dev/schema-gen/auth.ts b/dev/schema-gen/auth.ts deleted file mode 100644 index f2ab4bda..00000000 --- a/dev/schema-gen/auth.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { betterAuth } from "better-auth"; -import { prismaAdapter } from "better-auth/adapters/prisma"; -import { PrismaClient } from "@prisma/client"; - -const prisma = new PrismaClient(); - -export const auth = betterAuth({ - database: prismaAdapter(prisma, { - provider: "sqlite", - }), - provider: "sqlite", -}); diff --git a/dev/schema-gen/drizzle-auth.ts b/dev/schema-gen/drizzle-auth.ts deleted file mode 100644 index c85b729d..00000000 --- a/dev/schema-gen/drizzle-auth.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { betterAuth } from "better-auth"; -import { drizzleAdapter } from "better-auth/adapters/drizzle"; -import { db } from "./drizzle"; - -export const auth = betterAuth({ - database: drizzleAdapter(db, { - provider: "pg", - }), -}); diff --git a/dev/schema-gen/drizzle.ts b/dev/schema-gen/drizzle.ts deleted file mode 100644 index a770e1fd..00000000 --- a/dev/schema-gen/drizzle.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { drizzle } from "drizzle-orm/postgres-js"; -import { - pgTable, - serial, - varchar, - text, - timestamp, - integer, - boolean, -} from "drizzle-orm/pg-core"; -import postgres from "postgres"; - -const table = pgTable("test", { - id: text("id").primaryKey(), -}); - -const schema = { - table, -}; - -export const client = postgres(process.env.POSTGRES_URL || ""); -export const db = drizzle(client, { schema }); diff --git a/dev/schema-gen/index.ts b/dev/schema-gen/index.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/dev/schema-gen/package.json b/dev/schema-gen/package.json deleted file mode 100644 index b35ad940..00000000 --- a/dev/schema-gen/package.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "name": "prisma-dev", - "dependencies": { - "@prisma/client": "^5.19.1", - "better-auth": "workspace:*", - "drizzle-orm": "^0.33.0", - "postgres": "^3.4.4", - "prisma": "^5.19.1" - } -} \ No newline at end of file diff --git a/dev/schema-gen/prisma/schema.prisma b/dev/schema-gen/prisma/schema.prisma deleted file mode 100644 index 197dc594..00000000 --- a/dev/schema-gen/prisma/schema.prisma +++ /dev/null @@ -1,49 +0,0 @@ - -generator client { - provider = "prisma-client-js" -} - -datasource db { - provider = "sqlite" - url = "file:.db/dev.db" -} - -model Test { - id String @id @default(cuid()) -} - -model user { - id String @id - name String - email String - emailVerified Boolean - image String? - createdAt DateTime - updatedAt DateTime - session session[] - account account[] - - @@unique([email]) -} - -model session { - id String @id - expiresAt DateTime - ipAddress String? - userAgent String? - userId String - User user @relation(fields: [userId], references: [id], onDelete: Cascade) -} - -model account { - id String @id - accountId String - providerId String - userId String - User user @relation(fields: [userId], references: [id], onDelete: Cascade) - accessToken String? - refreshToken String? - idToken String? - expiresAt DateTime? - password String? -} diff --git a/dev/schema-gen/schema.ts b/dev/schema-gen/schema.ts deleted file mode 100644 index f02132f8..00000000 --- a/dev/schema-gen/schema.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { - pgTable, - text, - integer, - timestamp, - boolean, -} from "drizzle-orm/pg-core"; - -export const user = pgTable("user", { - id: text("id").primaryKey(), - name: text("name").notNull(), - email: text("email").notNull().unique(), - emailVerified: boolean("emailVerified").notNull(), - image: text("image"), - createdAt: timestamp("createdAt").notNull(), - updatedAt: timestamp("updatedAt").notNull(), -}); - -export const session = pgTable("session", { - id: text("id").primaryKey(), - expiresAt: timestamp("expiresAt").notNull(), - ipAddress: text("ipAddress"), - userAgent: text("userAgent"), - userId: text("userId") - .notNull() - .references(() => user.id), -}); - -export const account = pgTable("account", { - id: text("id").primaryKey(), - accountId: text("accountId").notNull(), - providerId: text("providerId").notNull(), - userId: text("userId") - .notNull() - .references(() => user.id), - accessToken: text("accessToken"), - refreshToken: text("refreshToken"), - idToken: text("idToken"), - expiresAt: timestamp("expiresAt"), - password: text("password"), -}); diff --git a/examples/astro-example/.env.example b/examples/astro-example/.env.example new file mode 100644 index 00000000..9abb10ae --- /dev/null +++ b/examples/astro-example/.env.example @@ -0,0 +1,3 @@ +GOOGLE_CLIENT_SECRET= +GOOGLE_CLIENT_ID= +RESEND_API_KEY= \ No newline at end of file diff --git a/examples/nextjs-example/.env.exmaple b/examples/nextjs-example/.env.exmaple new file mode 100644 index 00000000..cc4427a2 --- /dev/null +++ b/examples/nextjs-example/.env.exmaple @@ -0,0 +1,14 @@ +GOOGLE_CLIENT_SECRET= +GOOGLE_CLIENT_ID= +BETTER_AUTH_URL="http://localhost:3000" +BETTER_AUTH_SECRET= +TURSO_DATABASE_URL= +TURSO_AUTH_TOKEN= +GITHUB_CLIENT_ID= +GITHUB_CLIENT_SECRET= +RESEND_API_KEY= +TEST_EMAIL= +DISCORD_CLIENT_ID= +DISCORD_CLIENT_SECRET= +MICROSOFT_CLIENT_ID= +MICROSOFT_CLIENT_SECRET= \ No newline at end of file diff --git a/examples/nextjs-example/.gitignore b/examples/nextjs-example/.gitignore index 26b002aa..2fd56990 100644 --- a/examples/nextjs-example/.gitignore +++ b/examples/nextjs-example/.gitignore @@ -30,7 +30,7 @@ yarn-debug.log* yarn-error.log* # env files (can opt-in for commiting if needed) -.env* +.env # vercel .vercel @@ -38,3 +38,5 @@ yarn-error.log* # typescript *.tsbuildinfo next-env.d.ts + +certificates \ No newline at end of file diff --git a/examples/nextjs-example/README.md b/examples/nextjs-example/README.md index e88e5d16..e215bc4c 100644 --- a/examples/nextjs-example/README.md +++ b/examples/nextjs-example/README.md @@ -1,19 +1,36 @@ -# Better Auth Next js example +This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). -This is an example of how to use Better Auth with Next. +## Getting Started -**Implements the following features:** -Email & Password . Social Sign-in . Passkeys . Email Verification . Password Reset . Two Factor Authentication . Profile Update . Session Management . Organization, Members and Roles +First, run the development server: +```bash +npm run dev +# or +yarn dev +# or +pnpm dev +# or +bun dev +``` +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. -## How to run +You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. -1. Clone the code sandbox (or the repo) and open it in your code editor -2. Move .env.example to .env and provide necessary variables -3. Run the following commands - ```bash - pnpm install - pnpm dev - ``` -4. Open the browser and navigate to `http://localhost:3000` \ No newline at end of file +This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. diff --git a/examples/nextjs-example/app/(auth)/forget-password/page.tsx b/examples/nextjs-example/app/(auth)/forget-password/page.tsx index 06c91dfe..d16a6f5c 100644 --- a/examples/nextjs-example/app/(auth)/forget-password/page.tsx +++ b/examples/nextjs-example/app/(auth)/forget-password/page.tsx @@ -28,13 +28,11 @@ export default function Component() { setIsSubmitting(true); setError(""); - // Simulate API call try { const res = await client.forgetPassword({ email, redirectTo: "/reset-password", }); - // If the API call is successful, set isSubmitted to true setIsSubmitted(true); } catch (err) { setError("An error occurred. Please try again."); diff --git a/examples/nextjs-example/app/(auth)/reset-password/page.tsx b/examples/nextjs-example/app/(auth)/reset-password/page.tsx index 365e8545..7a8751ab 100644 --- a/examples/nextjs-example/app/(auth)/reset-password/page.tsx +++ b/examples/nextjs-example/app/(auth)/reset-password/page.tsx @@ -18,16 +18,11 @@ import { useRouter } from "next/navigation"; import { useState } from "react"; import { toast } from "sonner"; -export default function ResetPassword({ - params, -}: { - params: { token: string }; -}) { +export default function ResetPassword() { const [password, setPassword] = useState(""); const [confirmPassword, setConfirmPassword] = useState(""); const [isSubmitting, setIsSubmitting] = useState(false); const [error, setError] = useState(""); - const token = params.token; const router = useRouter(); async function handleSubmit(e: React.FormEvent) { e.preventDefault(); diff --git a/examples/nextjs-example/app/(auth)/two-factor/page.tsx b/examples/nextjs-example/app/(auth)/two-factor/page.tsx index fbb3ac42..130e1157 100644 --- a/examples/nextjs-example/app/(auth)/two-factor/page.tsx +++ b/examples/nextjs-example/app/(auth)/two-factor/page.tsx @@ -32,7 +32,7 @@ export default function Component() { code: totpCode, }) .then((res) => { - if (res.data?.status) { + if (res.data?.session) { setSuccess(true); setError(""); } else { diff --git a/examples/nextjs-example/app/accept-invitation/[id]/page.tsx b/examples/nextjs-example/app/accept-invitation/[id]/page.tsx index b2a01478..c98f0ade 100644 --- a/examples/nextjs-example/app/accept-invitation/[id]/page.tsx +++ b/examples/nextjs-example/app/accept-invitation/[id]/page.tsx @@ -11,19 +11,15 @@ import { } from "@/components/ui/card"; import { CheckIcon, XIcon } from "lucide-react"; import { useEffect, useState } from "react"; -import { useRouter } from "next/navigation"; +import { useParams, useRouter } from "next/navigation"; import { Skeleton } from "@/components/ui/skeleton"; import { client, organization } from "@/lib/auth-client"; import { InvitationError } from "./invitation-error"; -import { Invitation } from "@/lib/auth-types"; -export default function InvitationPage({ - params, -}: { - params: { +export default function InvitationPage() { + const params = useParams<{ id: string; - }; -}) { + }>(); const router = useRouter(); const [invitationStatus, setInvitationStatus] = useState< "pending" | "accepted" | "rejected" diff --git a/examples/nextjs-example/app/admin/page.tsx b/examples/nextjs-example/app/admin/page.tsx new file mode 100644 index 00000000..d8b645df --- /dev/null +++ b/examples/nextjs-example/app/admin/page.tsx @@ -0,0 +1,460 @@ +"use client"; + +import { useState } from "react"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; +import { toast, Toaster } from "sonner"; +import { client } from "@/lib/auth-client"; +import { useQuery, useQueryClient } from "@tanstack/react-query"; +import { useRouter } from "next/navigation"; +import { + Loader2, + Plus, + Trash, + RefreshCw, + UserCircle, + Calendar as CalendarIcon, +} from "lucide-react"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Calendar } from "@/components/ui/calendar"; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover"; +import { format } from "date-fns"; +import { cn } from "@/lib/utils"; +import { Badge } from "@/components/ui/badge"; + +type User = { + id: string; + email: string; + name: string; + role: "admin" | "user"; +}; + +export default function AdminDashboard() { + const queryClient = useQueryClient(); + const router = useRouter(); + const [isDialogOpen, setIsDialogOpen] = useState(false); + const [newUser, setNewUser] = useState({ + email: "", + password: "", + name: "", + role: "user" as const, + }); + const [isLoading, setIsLoading] = useState(); + const [isBanDialogOpen, setIsBanDialogOpen] = useState(false); + const [banForm, setBanForm] = useState({ + userId: "", + reason: "", + expirationDate: undefined as Date | undefined, + }); + + const { data: users, isLoading: isUsersLoading } = useQuery({ + queryKey: ["users"], + queryFn: () => + client.admin + .listUsers({ + query: { + limit: 10, + sortBy: "createdAt", + sortDirection: "desc", + }, + }) + .then((res) => res.data?.users ?? []), + }); + + const handleCreateUser = async (e: React.FormEvent) => { + e.preventDefault(); + setIsLoading("create"); + try { + await client.admin.createUser({ + email: newUser.email, + password: newUser.password, + name: newUser.name, + role: newUser.role, + }); + toast.success("User created successfully"); + setNewUser({ email: "", password: "", name: "", role: "user" }); + setIsDialogOpen(false); + queryClient.invalidateQueries({ + queryKey: ["users"], + }); + } catch (error: any) { + toast.error(error.message || "Failed to create user"); + } finally { + setIsLoading(undefined); + } + }; + + const handleDeleteUser = async (id: string) => { + setIsLoading(`delete-${id}`); + try { + await client.admin.removeUser({ userId: id }); + toast.success("User deleted successfully"); + queryClient.invalidateQueries({ + queryKey: ["users"], + }); + } catch (error: any) { + toast.error(error.message || "Failed to delete user"); + } finally { + setIsLoading(undefined); + } + }; + + const handleRevokeSessions = async (id: string) => { + setIsLoading(`revoke-${id}`); + try { + await client.admin.revokeUserSessions({ userId: id }); + toast.success("Sessions revoked for user"); + } catch (error: any) { + toast.error(error.message || "Failed to revoke sessions"); + } finally { + setIsLoading(undefined); + } + }; + + const handleImpersonateUser = async (id: string) => { + setIsLoading(`impersonate-${id}`); + try { + await client.admin.impersonateUser({ userId: id }); + toast.success("Impersonated user"); + router.push("/dashboard"); + } catch (error: any) { + toast.error(error.message || "Failed to impersonate user"); + } finally { + setIsLoading(undefined); + } + }; + + const handleBanUser = async (e: React.FormEvent) => { + e.preventDefault(); + setIsLoading(`ban-${banForm.userId}`); + try { + if (!banForm.expirationDate) { + throw new Error("Expiration date is required"); + } + await client.admin.banUser({ + userId: banForm.userId, + banReason: banForm.reason, + banExpiresIn: banForm.expirationDate.getTime() - new Date().getTime(), + }); + toast.success("User banned successfully"); + setIsBanDialogOpen(false); + queryClient.invalidateQueries({ + queryKey: ["users"], + }); + } catch (error: any) { + toast.error(error.message || "Failed to ban user"); + } finally { + setIsLoading(undefined); + } + }; + + return ( +
+ + + + Admin Dashboard + + + + + + + Create New User + +
+
+ + + setNewUser({ ...newUser, email: e.target.value }) + } + required + /> +
+
+ + + setNewUser({ ...newUser, password: e.target.value }) + } + required + /> +
+
+ + + setNewUser({ ...newUser, name: e.target.value }) + } + required + /> +
+
+ + +
+ +
+
+
+ + + + Ban User + +
+
+ + + setBanForm({ ...banForm, reason: e.target.value }) + } + required + /> +
+
+ + + + + + + + setBanForm({ ...banForm, expirationDate: date }) + } + initialFocus + /> + + +
+ +
+
+
+
+ + {isUsersLoading ? ( +
+ +
+ ) : ( + + + + Email + Name + Role + Banned + Actions + + + + {users?.map((user) => ( + + {user.email} + {user.name} + {user.role || "user"} + + {user.banned ? ( + Yes + ) : ( + No + )} + + +
+ + + + +
+
+
+ ))} +
+
+ )} +
+
+
+ ); +} diff --git a/examples/nextjs-example/app/dashboard/page.tsx b/examples/nextjs-example/app/dashboard/page.tsx index 317741be..d94dbb5a 100644 --- a/examples/nextjs-example/app/dashboard/page.tsx +++ b/examples/nextjs-example/app/dashboard/page.tsx @@ -7,10 +7,10 @@ import { OrganizationCard } from "./organization-card"; export default async function DashboardPage() { const [session, activeSessions] = await Promise.all([ auth.api.getSession({ - headers: headers(), + headers: await headers(), }), auth.api.listSessions({ - headers: headers(), + headers: await headers(), }), ]).catch((e) => { throw redirect("/sign-in"); diff --git a/examples/nextjs-example/app/dashboard/user-card.tsx b/examples/nextjs-example/app/dashboard/user-card.tsx index 8dab1f14..a61aa8e9 100644 --- a/examples/nextjs-example/app/dashboard/user-card.tsx +++ b/examples/nextjs-example/app/dashboard/user-card.tsx @@ -62,7 +62,7 @@ export default function UserCard(props: { activeSessions: Session["session"][]; }) { const router = useRouter(); - const { data, isPending, error } = useSession(props.session); + const { data, isPending, error } = useSession(); const [ua, setUa] = useState(); const session = data || props.session; @@ -216,7 +216,7 @@ export default function UserCard(props: {

Two Factor

- {session?.user.twoFactorEnabled && ( + {!!session?.user.twoFactorEnabled && ( - - + + + + + +
- - + }); + }} + > + + + + + +
diff --git a/examples/nextjs-example/lib/auth-client.ts b/examples/nextjs-example/lib/auth-client.ts index 5a87c355..391e03f1 100644 --- a/examples/nextjs-example/lib/auth-client.ts +++ b/examples/nextjs-example/lib/auth-client.ts @@ -3,6 +3,7 @@ import { organizationClient, passkeyClient, twoFactorClient, + adminClient, } from "better-auth/client/plugins"; import { toast } from "sonner"; @@ -10,9 +11,11 @@ export const client = createAuthClient({ plugins: [ organizationClient(), twoFactorClient({ + redirect: true, twoFactorPage: "/two-factor", }), passkeyClient(), + adminClient(), ], fetchOptions: { onError(e) { diff --git a/examples/nextjs-example/lib/auth.ts b/examples/nextjs-example/lib/auth.ts index 81fc1c19..b9754a59 100644 --- a/examples/nextjs-example/lib/auth.ts +++ b/examples/nextjs-example/lib/auth.ts @@ -1,37 +1,45 @@ import { betterAuth } from "better-auth"; -import { organization, passkey, twoFactor } from "better-auth/plugins"; +import { + bearer, + organization, + passkey, + twoFactor, + admin, +} from "better-auth/plugins"; import { reactInvitationEmail } from "./email/invitation"; +import { LibsqlDialect } from "@libsql/kysely-libsql"; import { reactResetPasswordEmail } from "./email/rest-password"; import { resend } from "./email/resend"; const from = process.env.BETTER_AUTH_EMAIL || "delivered@resend.dev"; const to = process.env.TEST_EMAIL || ""; + +const libsql = new LibsqlDialect({ + url: process.env.TURSO_DATABASE_URL || "", + authToken: process.env.TURSO_AUTH_TOKEN || "", +}); + export const auth = betterAuth({ database: { - provider: "sqlite", - url: "./db.sqlite", + dialect: libsql, + type: "sqlite", }, emailAndPassword: { enabled: true, - async sendResetPassword(token, user) { - const res = await resend.emails.send({ + async sendResetPassword(url, user) { + await resend.emails.send({ from, to: user.email, subject: "Reset your password", react: reactResetPasswordEmail({ username: user.email, - resetLink: `${ - process.env.NODE_ENV === "development" - ? "http://localhost:3000" - : process.env.NEXT_PUBLIC_APP_URL || - process.env.VERCEL_URL || - process.env.BETTER_AUTH_URL - }/reset-password/${token}`, + resetLink: url, }), }); }, sendEmailVerificationOnSignUp: true, async sendVerificationEmail(email, url) { + console.log("Sending verification email to", email); const res = await resend.emails.send({ from, to: to || email, @@ -68,12 +76,19 @@ export const auth = betterAuth({ }), twoFactor({ otpOptions: { - sendOTP(user, otp) { - console.log({ otp }); + async sendOTP(user, otp) { + await resend.emails.send({ + from, + to: user.email, + subject: "Your OTP", + html: `Your OTP is ${otp}`, + }); }, }, }), passkey(), + bearer(), + admin(), ], socialProviders: { github: { @@ -84,5 +99,13 @@ export const auth = betterAuth({ clientId: process.env.GOOGLE_CLIENT_ID || "", clientSecret: process.env.GOOGLE_CLIENT_SECRET || "", }, + discord: { + clientId: process.env.DISCORD_CLIENT_ID || "", + clientSecret: process.env.DISCORD_CLIENT_SECRET || "", + }, + microsoft: { + clientId: process.env.MICROSOFT_CLIENT_ID || "", + clientSecret: process.env.MICROSOFT_CLIENT_SECRET || "", + }, }, }); diff --git a/examples/nextjs-example/package.json b/examples/nextjs-example/package.json index 396a49bd..dc06d2c6 100644 --- a/examples/nextjs-example/package.json +++ b/examples/nextjs-example/package.json @@ -1,10 +1,11 @@ { - "name": "@better-auth/next", + "name": "@better-auth/demo", "version": "0.1.0", "private": true, "scripts": { - "dev": "pnpm migrate && next dev", - "migrate": "better-auth migrate", + "dev": "next dev", + "dev:secure": "next dev --experimental-https", + "typecheck": "tsc --noEmit", "build": "next build", "start": "next start", "lint": "next lint" @@ -46,13 +47,14 @@ "@react-email/components": "^0.0.25", "@react-three/fiber": "^8.17.7", "@tanstack/react-query": "^5.56.2", - "better-auth": "workspace:*", "@types/better-sqlite3": "^7.6.11", - "better-sqlite3": "^11.3.0", + "better-auth": "workspace:*", "better-call": "0.2.3-beta.2", + "better-sqlite3": "^11.3.0", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "cmdk": "1.0.0", + "consola": "^3.2.3", "date-fns": "^3.6.0", "embla-carousel-react": "^8.2.1", "framer-motion": "^11.5.4", @@ -61,7 +63,7 @@ "kysely": "^0.27.4", "lucide-react": "^0.439.0", "mini-svg-data-uri": "^1.4.4", - "next": "15.0.0-canary.157", + "next": "15.0.0-canary.185", "next-themes": "^0.3.0", "prisma": "^5.19.1", "react": "19.0.0-rc-7771d3a7-20240827", diff --git a/examples/nextjs-example/public/favicon/android-chrome-192x192.png b/examples/nextjs-example/public/favicon/android-chrome-192x192.png index 45b1a90a..c2b1bbff 100644 Binary files a/examples/nextjs-example/public/favicon/android-chrome-192x192.png and b/examples/nextjs-example/public/favicon/android-chrome-192x192.png differ diff --git a/examples/nextjs-example/public/favicon/android-chrome-512x512.png b/examples/nextjs-example/public/favicon/android-chrome-512x512.png index 6d2d953a..5c7449d6 100644 Binary files a/examples/nextjs-example/public/favicon/android-chrome-512x512.png and b/examples/nextjs-example/public/favicon/android-chrome-512x512.png differ diff --git a/examples/nextjs-example/public/favicon/apple-touch-icon.png b/examples/nextjs-example/public/favicon/apple-touch-icon.png index 1000536e..25bf85b5 100644 Binary files a/examples/nextjs-example/public/favicon/apple-touch-icon.png and b/examples/nextjs-example/public/favicon/apple-touch-icon.png differ diff --git a/examples/nextjs-example/public/favicon/favicon-16x16.png b/examples/nextjs-example/public/favicon/favicon-16x16.png index 13f3ec81..c63e12ef 100644 Binary files a/examples/nextjs-example/public/favicon/favicon-16x16.png and b/examples/nextjs-example/public/favicon/favicon-16x16.png differ diff --git a/examples/nextjs-example/public/favicon/favicon-32x32.png b/examples/nextjs-example/public/favicon/favicon-32x32.png index 8e99d26a..fe912c8f 100644 Binary files a/examples/nextjs-example/public/favicon/favicon-32x32.png and b/examples/nextjs-example/public/favicon/favicon-32x32.png differ diff --git a/examples/nextjs-example/public/favicon/favicon.ico b/examples/nextjs-example/public/favicon/favicon.ico index 4ad7a8e6..9edb1b94 100644 Binary files a/examples/nextjs-example/public/favicon/favicon.ico and b/examples/nextjs-example/public/favicon/favicon.ico differ diff --git a/examples/nextjs-example/public/favicon/light/android-chrome-192x192.png b/examples/nextjs-example/public/favicon/light/android-chrome-192x192.png new file mode 100644 index 00000000..2dff0986 Binary files /dev/null and b/examples/nextjs-example/public/favicon/light/android-chrome-192x192.png differ diff --git a/examples/nextjs-example/public/favicon/light/android-chrome-512x512.png b/examples/nextjs-example/public/favicon/light/android-chrome-512x512.png new file mode 100644 index 00000000..f42fa86e Binary files /dev/null and b/examples/nextjs-example/public/favicon/light/android-chrome-512x512.png differ diff --git a/examples/nextjs-example/public/favicon/light/apple-touch-icon.png b/examples/nextjs-example/public/favicon/light/apple-touch-icon.png new file mode 100644 index 00000000..4649b141 Binary files /dev/null and b/examples/nextjs-example/public/favicon/light/apple-touch-icon.png differ diff --git a/examples/nextjs-example/public/favicon/light/favicon-16x16.png b/examples/nextjs-example/public/favicon/light/favicon-16x16.png new file mode 100644 index 00000000..2b18ff2c Binary files /dev/null and b/examples/nextjs-example/public/favicon/light/favicon-16x16.png differ diff --git a/examples/nextjs-example/public/favicon/light/favicon-32x32.png b/examples/nextjs-example/public/favicon/light/favicon-32x32.png new file mode 100644 index 00000000..dff2b87d Binary files /dev/null and b/examples/nextjs-example/public/favicon/light/favicon-32x32.png differ diff --git a/examples/nextjs-example/public/favicon/light/favicon.ico b/examples/nextjs-example/public/favicon/light/favicon.ico new file mode 100644 index 00000000..d3a2eb4c Binary files /dev/null and b/examples/nextjs-example/public/favicon/light/favicon.ico differ diff --git a/examples/nextjs-example/public/favicon/light/site.webmanifest b/examples/nextjs-example/public/favicon/light/site.webmanifest new file mode 100644 index 00000000..95911504 --- /dev/null +++ b/examples/nextjs-example/public/favicon/light/site.webmanifest @@ -0,0 +1,19 @@ +{ + "name": "", + "short_name": "", + "icons": [ + { + "src": "/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} diff --git a/examples/nuxt-example/.env.example b/examples/nuxt-example/.env.example new file mode 100644 index 00000000..9abb10ae --- /dev/null +++ b/examples/nuxt-example/.env.example @@ -0,0 +1,3 @@ +GOOGLE_CLIENT_SECRET= +GOOGLE_CLIENT_ID= +RESEND_API_KEY= \ No newline at end of file diff --git a/packages/better-auth/package.json b/packages/better-auth/package.json index 53066183..b80b209d 100644 --- a/packages/better-auth/package.json +++ b/packages/better-auth/package.json @@ -90,6 +90,7 @@ "@nanostores/solid": "^0.4.2", "@nanostores/vue": "^0.10.0", "@noble/ciphers": "^0.6.0", + "@noble/hashes": "^1.5.0", "@simplewebauthn/browser": "^10.0.0", "@simplewebauthn/server": "^10.0.1", "better-call": "0.2.6", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a3ef9694..b7b7d732 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1456,6 +1456,9 @@ importers: '@noble/ciphers': specifier: ^0.6.0 version: 0.6.0 + '@noble/hashes': + specifier: ^1.5.0 + version: 1.5.0 '@simplewebauthn/browser': specifier: ^10.0.0 version: 10.0.0 @@ -3630,6 +3633,10 @@ packages: '@noble/ciphers@0.6.0': resolution: {integrity: sha512-mIbq/R9QXk5/cTfESb1OKtyFnk7oc1Om/8onA1158K9/OZUQFDEVy55jVTato+xmp3XX6F6Qh0zz0Nc1AxAlRQ==} + '@noble/hashes@1.5.0': + resolution: {integrity: sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA==} + engines: {node: ^14.21.3 || >=16} + '@node-rs/argon2-android-arm-eabi@1.7.0': resolution: {integrity: sha512-udDqkr5P9E+wYX1SZwAVPdyfYvaF4ry9Tm+R9LkfSHbzWH0uhU6zjIwNRp7m+n4gx691rk+lqqDAIP8RLKwbhg==} engines: {node: '>= 10'} @@ -16299,6 +16306,8 @@ snapshots: '@noble/ciphers@0.6.0': {} + '@noble/hashes@1.5.0': {} + '@node-rs/argon2-android-arm-eabi@1.7.0': optional: true