mirror of
https://github.com/LukeHagar/better-auth.git
synced 2025-12-10 12:27:44 +00:00
docs: basic usage imporvements
This commit is contained in:
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@@ -8,7 +8,7 @@
|
||||
"editor.defaultFormatter": "biomejs.biome"
|
||||
},
|
||||
"[svelte]": {
|
||||
"editor.defaultFormatter": "svelte.svelte-vscode"
|
||||
"editor.defaultFormatter": "biomejs.biome"
|
||||
},
|
||||
"[vue]": {
|
||||
"editor.defaultFormatter": "Vue.volar"
|
||||
|
||||
@@ -263,7 +263,45 @@ export const contents: Content[] = [
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Providers",
|
||||
title: "Email & Password",
|
||||
Icon: () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1.4em"
|
||||
height="1.4em"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
d="M3.172 5.172C2 6.343 2 8.229 2 12c0 3.771 0 5.657 1.172 6.828C4.343 20 6.229 20 10 20h4c3.771 0 5.657 0 6.828-1.172C22 17.657 22 15.771 22 12c0-3.771 0-5.657-1.172-6.828C19.657 4 17.771 4 14 4h-4C6.229 4 4.343 4 3.172 5.172M8 13a1 1 0 1 0 0-2a1 1 0 0 0 0 2m5-1a1 1 0 1 1-2 0a1 1 0 0 1 2 0m3 1a1 1 0 1 0 0-2a1 1 0 0 0 0 2"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
),
|
||||
href: "/docs/authentication/email-password",
|
||||
list: [
|
||||
{
|
||||
title: "Introduction",
|
||||
href: "/docs/authentication/email-password",
|
||||
icon: () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1.2em"
|
||||
height="1.2em"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M5 21q-.825 0-1.412-.587T3 19V5q0-.825.588-1.412T5 3h4.2q.35-.9 1.1-1.45T12 1t1.7.55T14.8 3H19q.825 0 1.413.588T21 5v14q0 .825-.587 1.413T19 21zm7-16.75q.325 0 .538-.213t.212-.537t-.213-.537T12 2.75t-.537.213t-.213.537t.213.538t.537.212M12 13q1.45 0 2.475-1.025T15.5 9.5t-1.025-2.475T12 6T9.525 7.025T8.5 9.5t1.025 2.475T12 13m-7 6h14v-1.15q-1.35-1.325-3.137-2.087T12 15t-3.863.763T5 17.85z"
|
||||
></path>
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Social Sign-On",
|
||||
Icon: () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@@ -280,35 +318,25 @@ export const contents: Content[] = [
|
||||
</svg>
|
||||
),
|
||||
list: [
|
||||
{
|
||||
title: "Email & Password",
|
||||
href: "/docs/authentication/email-password",
|
||||
icon: () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1.2em"
|
||||
height="1.2em"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
d="M3.172 5.172C2 6.343 2 8.229 2 12c0 3.771 0 5.657 1.172 6.828C4.343 20 6.229 20 10 20h4c3.771 0 5.657 0 6.828-1.172C22 17.657 22 15.771 22 12c0-3.771 0-5.657-1.172-6.828C19.657 4 17.771 4 14 4h-4C6.229 4 4.343 4 3.172 5.172M8 13a1 1 0 1 0 0-2a1 1 0 0 0 0 2m5-1a1 1 0 1 1-2 0a1 1 0 0 1 2 0m3 1a1 1 0 1 0 0-2a1 1 0 0 0 0 2"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Social Sign-On",
|
||||
group: true,
|
||||
href: "/docs/authentication/social-providers",
|
||||
icon: () => (
|
||||
<svg>
|
||||
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm4.59-12.42L10 14.17l-2.59-2.58L6 13l4 4 8-8z"></path>
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
// {
|
||||
// title: "Email & Password",
|
||||
// href: "/docs/authentication/email-password",
|
||||
// icon: () => (
|
||||
// <svg
|
||||
// xmlns="http://www.w3.org/2000/svg"
|
||||
// width="1.2em"
|
||||
// height="1.2em"
|
||||
// viewBox="0 0 24 24"
|
||||
// >
|
||||
// <path
|
||||
// fill="currentColor"
|
||||
// fillRule="evenodd"
|
||||
// d="M3.172 5.172C2 6.343 2 8.229 2 12c0 3.771 0 5.657 1.172 6.828C4.343 20 6.229 20 10 20h4c3.771 0 5.657 0 6.828-1.172C22 17.657 22 15.771 22 12c0-3.771 0-5.657-1.172-6.828C19.657 4 17.771 4 14 4h-4C6.229 4 4.343 4 3.172 5.172M8 13a1 1 0 1 0 0-2a1 1 0 0 0 0 2m5-1a1 1 0 1 1-2 0a1 1 0 0 1 2 0m3 1a1 1 0 1 0 0-2a1 1 0 0 0 0 2"
|
||||
// clipRule="evenodd"
|
||||
// />
|
||||
// </svg>
|
||||
// ),
|
||||
// },
|
||||
{
|
||||
title: "Apple",
|
||||
href: "/docs/authentication/apple",
|
||||
|
||||
@@ -3,16 +3,16 @@ title: Basic Usage
|
||||
description: Getting started with Better Auth
|
||||
---
|
||||
|
||||
## Authentication
|
||||
|
||||
Better Auth provides built-in authentication support for:
|
||||
|
||||
- **Email and password**
|
||||
- **Social provider (Google, Github, Apple, and more)**
|
||||
|
||||
<Callout>
|
||||
You can extend authentication options using plugins, such as: Username-based login, Passkeys, Email magic links, and more.
|
||||
</Callout>
|
||||
|
||||
### Email and Password Authentication
|
||||
## Email & Password
|
||||
|
||||
To enable email and password authentication:
|
||||
|
||||
@@ -29,74 +29,102 @@ export const auth = betterAuth({
|
||||
|
||||
### Signup
|
||||
|
||||
To signup a user, you can use the `signUp.email` function provided by the client. The `signUp` function takes an object with the following properties:
|
||||
Before a user can sign in, they need to sign up. To sign up a user using email and password, you need to call the client method `signUp.email` with the user's information.
|
||||
|
||||
```ts title="sign-up.ts" twoslash
|
||||
// @filename: client.ts
|
||||
import { createAuthClient } from "better-auth/client"
|
||||
|
||||
export const client = createAuthClient()
|
||||
**Example: Using React**
|
||||
|
||||
// ---cut---
|
||||
// @filename: signup.ts
|
||||
// ---cut---
|
||||
import { client } from "./client";
|
||||
```tsx title="SignUp.tsx"
|
||||
import { authClient } from '@/auth'; //import the auth client
|
||||
|
||||
const res = await client.signUp.email({
|
||||
email: "test@example.com", // The email address of the user.
|
||||
password: "password1234", // The password of the user.
|
||||
name: "test", // The name of the user.
|
||||
image: "https://example.com/image.png", // The image of the user. (optional)
|
||||
callbackURL: "/" // The url to redirect to after the user has signed up. (optional)
|
||||
})
|
||||
export default function SignUp() {
|
||||
const [email, setEmail] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [name, setName] = useState('');
|
||||
const [image, setImage] = useState<File | null>(null);
|
||||
|
||||
const signUp = async () => {
|
||||
const res = await authClient.signUp.email({
|
||||
email,
|
||||
password,
|
||||
name,
|
||||
image: image ? convertImageToBase64(image) : undefined,
|
||||
}, {
|
||||
onRequest: (ctx) => {
|
||||
//show loading
|
||||
},
|
||||
onsSuccess: (ctx) => {
|
||||
//redirect to the dashboard
|
||||
},
|
||||
onError: (ctx) => {
|
||||
alert(ctx.error.message);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<input type="name" value={email} onChange={(e) => setEmail(e.target.value)} />
|
||||
<input type="password" value={password} onChange={(e) => setPassword(e.target.value)} />
|
||||
<input type="email" value={email} onChange={(e) => setEmail(e.target.value)} />
|
||||
<input type="file" onChange={(e) => setImage(e.target.files?.[0])} />
|
||||
<button onClick={signUp}>Sign Up</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
The function returns a promise that resolves an object with `data` and `error` properties. The `data` property contains the user object that was created, and the `error` property contains any error that occurred during the signup process.
|
||||
|
||||
<Callout type="info">
|
||||
If you want to use username instead of email, you can use <Link href="/docs/plugins/username">username Plugin</Link>.
|
||||
</Callout>
|
||||
|
||||
### Signin
|
||||
|
||||
To signin a user, you can use the `signIn.email` function provided by the client. The `signIn` function takes an object with the following properties:
|
||||
|
||||
```ts title="sing-in.ts" twoslash
|
||||
// @filename: client.ts
|
||||
import { createAuthClient } from "better-auth/client"
|
||||
**Example: Using Svlete**
|
||||
|
||||
export const client = createAuthClient()
|
||||
```svelte title="signin.svelte"
|
||||
<script lang="ts">
|
||||
import { client } from "$lib/auth-client"; //import the auth client
|
||||
import { writable } from "svelte/store";
|
||||
|
||||
// ---cut---
|
||||
// @filename: signup.ts
|
||||
// ---cut---
|
||||
import { client } from "./client";
|
||||
const email = writable("");
|
||||
const password = writable("");
|
||||
|
||||
const data = await client.signIn.email({
|
||||
email: "test@example.com", // The email address of the user.
|
||||
password: "password1234", // The password of the user.
|
||||
callbackURL: "/" // The url to redirect to after the user has signed in. (optional)
|
||||
})
|
||||
const handleSignIn = async () => {
|
||||
await client.signIn.email({
|
||||
email: $email,
|
||||
password: $password
|
||||
}, {
|
||||
onRequest: () => {
|
||||
//show loading
|
||||
},
|
||||
onSuccess: () => {
|
||||
//redirect to dashboard
|
||||
},
|
||||
onError: (ctx) => {
|
||||
alert(ctx.error.message)
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<input type="email" bind:value={$email} />
|
||||
<input type="password" bind:value={$password} />
|
||||
<button on:click={handleSignIn}>
|
||||
Sign In
|
||||
</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
|
||||
### Authentication with Social Providers
|
||||
## Social Sign-On
|
||||
|
||||
Better Auth supports multiple social providers, including Google, Github, Apple, Discord, and more. To use a social provider, you need to configure the ones you need in the `socialProvider` option on your `auth` object.
|
||||
|
||||
### Configure Social Providers
|
||||
|
||||
To configure social providers, you need to import the provider you want to use and pass it to the `socialProvider` option. For example, to configure the Github provider, you can use the following code:
|
||||
|
||||
```ts title="auth.ts"
|
||||
import { betterAuth } from "better-auth"
|
||||
import { github } from "better-auth/social-providers"
|
||||
|
||||
export const auth = betterAuth({
|
||||
database: {
|
||||
provider: "sqlite",
|
||||
url: "./db.sqlite",
|
||||
},
|
||||
//...rest of the options
|
||||
socialProvider: { // [!code highlight]
|
||||
github: { // [!code highlight]
|
||||
clientId: GITHUB_CLIENT_ID, // [!code highlight]
|
||||
@@ -106,24 +134,36 @@ export const auth = betterAuth({
|
||||
})
|
||||
```
|
||||
|
||||
<Callout type="info">
|
||||
See the <Link href="/docs/providers">Provider</Link> section for more information on how to configure each provider.
|
||||
</Callout>
|
||||
|
||||
### Signin with social providers
|
||||
|
||||
```ts title="signin.ts"
|
||||
import { client } from "./client"
|
||||
**Example: Using Vue**
|
||||
|
||||
const signin = async () => {
|
||||
const data = await client.signIn.social({
|
||||
provider: "github"
|
||||
})
|
||||
}
|
||||
```vue title="signin.vue"
|
||||
<template>
|
||||
<div>
|
||||
<h2>Sign In</h2>
|
||||
<button @click="handleSignIn">Sign In with GitHub</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { client } from "@/auth-client"; //import the auth client
|
||||
|
||||
export default {
|
||||
name: "SignIn",
|
||||
methods: {
|
||||
async handleSignIn() {
|
||||
await client.signIn.social({
|
||||
provider: "github",
|
||||
callbackURL: "/dashboard", //redirect to dashboard after sign in
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
|
||||
## Session
|
||||
## Session Mangemnt
|
||||
|
||||
Once a user is signed in, you'll want to access their session. Better auth allows you easily to access the session data from the server and client side.
|
||||
|
||||
@@ -131,7 +171,12 @@ Once a user is signed in, you'll want to access their session. Better auth allow
|
||||
|
||||
Better Auth provides a `useSession` hook to easily access session data on the client side. This hook is implemented in a reactive way for each supported framework, ensuring that any changes to the session (such as signing out) are immediately reflected in your UI.
|
||||
|
||||
<Tabs items={["React", "Vue","Svelte", "Solid"]} defaultValue="React">
|
||||
It has the following properties:
|
||||
- **data**: the actual session data which includes `session` and `user` object.
|
||||
- **isPending**: a boolean that indicates whether the session is being loaded.
|
||||
- **error**: an error object that contains any errors that occurred while loading the session.
|
||||
|
||||
<Tabs items={["Vanilla","React", "Vue","Svelte", "Solid"]} defaultValue="React">
|
||||
<Tab value="React">
|
||||
```tsx title="user.tsx"
|
||||
//make sure you're using the react client
|
||||
@@ -175,7 +220,7 @@ Better Auth provides a `useSession` hook to easily access session data on the cl
|
||||
```svelte title="user.svelte"
|
||||
<script lang="ts">
|
||||
import { client } from "$lib/client";
|
||||
const session = client.useSsession;
|
||||
const session = client.useSsession();
|
||||
</script>
|
||||
|
||||
<div
|
||||
@@ -214,6 +259,15 @@ Better Auth provides a `useSession` hook to easily access session data on the cl
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab value="Vanilla">
|
||||
```ts title="user.svelte"
|
||||
import { client } from "~/lib/client";
|
||||
clinet.useSession.subscribe((value)=>{
|
||||
//do something with the session
|
||||
})
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab value="Solid">
|
||||
```tsx title="user.tsx"
|
||||
import { client } from "~/lib/client";
|
||||
@@ -234,28 +288,85 @@ Better Auth provides a `useSession` hook to easily access session data on the cl
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### Server Side
|
||||
The server provides a `session` object that you can use to access the session data.
|
||||
|
||||
```ts title="server.ts"
|
||||
// somewhere in your server code
|
||||
import { auth } from "./auth"
|
||||
async function addToCart(request: Request){
|
||||
const session = await auth.api.getSession({
|
||||
headers: request.headers, //it requies a header to be passed
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
<Callout type="info">
|
||||
For next js on RSC and server actions you can use import `headers` from `next/headers` and pass it to the `getSession` function.
|
||||
<Callout>
|
||||
For more details check [session-management](/docs/concepts/session-management) documentation.
|
||||
</Callout>
|
||||
|
||||
## Two Factor
|
||||
### Server Side
|
||||
|
||||
### Introduction to plugins
|
||||
The server provides a `session` object that you can use to access the session data. It requires request headers object to be passed to the `getSession` method.
|
||||
|
||||
One of the unique features of better auth is a plugins ecosystem. It allows you to add complex auth realted functionilty with small lines of code. Better auth come with many 1st party plugins, but you can also create your own plugins.
|
||||
**Example: Using some popular frameworks**
|
||||
|
||||
<Tabs items={["NextJs", "NuxtJs", "Svelte", "Astro", "Hono"]}>
|
||||
<Tab value="NextJs">
|
||||
```ts title="server.ts"
|
||||
import { auth } from "./auth";
|
||||
import { headers } from "next/headers";
|
||||
|
||||
const session = await auth.api.getSession({
|
||||
headers: headers() // you need to pass the headers object.
|
||||
})
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="Astro">
|
||||
```astro title="index.astro"
|
||||
---
|
||||
import { auth } from "./auth";
|
||||
|
||||
const session = await auth.api.getSession({
|
||||
headers: Astro.request.headers,
|
||||
});
|
||||
---
|
||||
<!-- Your Astro Template -->
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="Svelte">
|
||||
```ts title="+page.ts"
|
||||
import { auth } from "./auth";
|
||||
|
||||
export async function load({ request }) {
|
||||
const session = await auth.api.getSession({
|
||||
headers: request.headers
|
||||
})
|
||||
return {
|
||||
props: {
|
||||
session
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="Hono">
|
||||
```ts title="index.ts"
|
||||
import { auth } from "./auth";
|
||||
|
||||
const app = new Hono();
|
||||
|
||||
app.get("/path", async (c) => {
|
||||
const session = auth.api.getSession({
|
||||
headers: c.req.raw.headers
|
||||
})
|
||||
});
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab value="Nuxt">
|
||||
```ts title="server/session.ts"
|
||||
import { auth } from "~/utils/auth";
|
||||
|
||||
export default defineEventHandler((event) => {
|
||||
const session = await auth.api.getSession({
|
||||
headers: event.headers,
|
||||
})
|
||||
});
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Using Plugins
|
||||
|
||||
One of the unique features of better auth is a plugins ecosystem. It allows you to add complex auth realted functionilty with small lines of code.
|
||||
|
||||
Below is an example of how to add two factor authentication using two factor plugin.
|
||||
|
||||
@@ -271,15 +382,9 @@ import { betterAuth } from "better-auth"
|
||||
import { twoFactor } from "better-auth/plugins"
|
||||
|
||||
export const auth = betterAuth({
|
||||
database: {
|
||||
provider: "sqlite",
|
||||
url: "./db.sqlite",
|
||||
},
|
||||
//...rest of the options
|
||||
plugins: [ // [!code highlight]
|
||||
twoFactor({ // [!code highlight]
|
||||
issuer: "my-app" // [!code highlight]
|
||||
}) // [!code highlight]
|
||||
twoFactor() // [!code highlight]
|
||||
] // [!code highlight]
|
||||
})
|
||||
```
|
||||
@@ -317,26 +422,36 @@ const client = createAuthClient({
|
||||
now two factor related methods will be available on the client.
|
||||
|
||||
```ts title="profile.ts"
|
||||
// @filename: client.ts
|
||||
import { createAuthClient } from "better-auth/client";
|
||||
import { twoFactorClient } from "better-auth/client/plugins";
|
||||
|
||||
export const client = createAuthClient({
|
||||
plugins: [ // [!code highlight]
|
||||
twoFactorClient({ // [!code highlight]
|
||||
twoFactorPage: "/two-factor" // [!code highlight]
|
||||
}) // [!code highlight]
|
||||
] // [!code highlight]
|
||||
})
|
||||
|
||||
// ---cut---
|
||||
|
||||
// @filename: profile.ts
|
||||
// ---cut---
|
||||
import { client } from "./client"
|
||||
|
||||
const enableTwoFactor = async() => {
|
||||
const data = await client.twoFactor.enable() // this will enable two factor authentication for the signed in user
|
||||
const data = await client.twoFactor.enable({
|
||||
password // the user password is required
|
||||
}) // this will enable two factor
|
||||
}
|
||||
|
||||
const disableTwoFactor = async() => {
|
||||
const data = await client.twoFactor.disable({
|
||||
password // the user password is required
|
||||
}) // this will disable two factor
|
||||
}
|
||||
|
||||
const signInWith2Factor = async() => {
|
||||
const data = await client.signIn.email({
|
||||
//...
|
||||
})
|
||||
//if the user has two factor enabled, it will redirect to the two factor page
|
||||
}
|
||||
|
||||
const verifyTOTP = async() => {
|
||||
const data = await client.twoFactor.verifyTOTP({
|
||||
code: "123456", // the code entered by the user
|
||||
/**
|
||||
* If the device is trusted, the user won't
|
||||
* need to pass 2FA again on the same device
|
||||
*/
|
||||
trustDevice: true
|
||||
})
|
||||
}
|
||||
```
|
||||
</Step>
|
||||
|
||||
29
docs/content/docs/email-password/index.mdx
Normal file
29
docs/content/docs/email-password/index.mdx
Normal file
@@ -0,0 +1,29 @@
|
||||
---
|
||||
title: Email & Password
|
||||
decription: Learn how to use email and password authentication with Better Auth
|
||||
---
|
||||
|
||||
Email and password authentication is a common method used by many applications. Better Auth provides a robust and secure way to authenticate users using email and password with minimal effort.
|
||||
|
||||
<Callout>
|
||||
If you want to add username-based authentication, check out the username plugin. It extends the email and password authenticator with username support.
|
||||
</Callout>
|
||||
|
||||
## Getting Started
|
||||
|
||||
Email and password authentication is disabled by default. To enable it, you need to enable the email and password authenticator in the Better Auth configuration.
|
||||
|
||||
```ts title="auth.ts"
|
||||
import { betterAuth } from 'better-auth';
|
||||
|
||||
export const auth = betterAuth({
|
||||
//...other configurations
|
||||
emailAndPassword: {
|
||||
enabled: true,
|
||||
},
|
||||
})
|
||||
```
|
||||
<Callout>
|
||||
Unless you explicitly enable the email and password authenticator, it will not allow users to sign up or sign in using email and password.
|
||||
</Callout>
|
||||
|
||||
55
docs/content/docs/email-password/sign-in-and-sign-up.mdx
Normal file
55
docs/content/docs/email-password/sign-in-and-sign-up.mdx
Normal file
@@ -0,0 +1,55 @@
|
||||
---
|
||||
title: Sign In & Sign Up
|
||||
description: Learn how to sign in and sign up users using email and password with Better Auth
|
||||
---
|
||||
|
||||
Once you have enabled the email and password authenticator, you can start signing in and signing up users using email and password.
|
||||
|
||||
## Sign Up
|
||||
|
||||
Before a user can sign in, they need to sign up. To sign up a user using email and password, you need to call the client method `signUp.email` with the user's information.
|
||||
|
||||
**Example: Using React**
|
||||
|
||||
```tsx title="SignUp.tsx"
|
||||
import { authClient } from '@/auth'; //import the auth client
|
||||
|
||||
export default function SignUp() {
|
||||
const [email, setEmail] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [name, setName] = useState('');
|
||||
const [image, setImage] = useState<File | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const signUp = async () => {
|
||||
const res = await authClient.signUp.email({
|
||||
email,
|
||||
password,
|
||||
name,
|
||||
image: image ? convertImageToBase64(image) : undefined,
|
||||
}, {
|
||||
onRequest: (ctx) => {
|
||||
setLoading(true);
|
||||
},
|
||||
onsSuccess: (ctx) => {
|
||||
setLoading(false);
|
||||
//redirect to the dashboard
|
||||
},
|
||||
onError: (ctx) => {
|
||||
setLoading(false);
|
||||
alert(ctx.error.message);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<input type="name" value={email} onChange={(e) => setEmail(e.target.value)} />
|
||||
<input type="password" value={password} onChange={(e) => setPassword(e.target.value)} />
|
||||
<input type="email" value={email} onChange={(e) => setEmail(e.target.value)} />
|
||||
<input type="file" onChange={(e) => setImage(e.target.files?.[0])} />
|
||||
<button onClick={signUp}>Sign Up</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
@@ -108,6 +108,13 @@ export const auth = betterAuth({
|
||||
```bash title="Terminal"
|
||||
npx better-auth migrate
|
||||
```
|
||||
|
||||
Better Auth automatically sets up the following tables in your database if they don't already exist:
|
||||
- `user`
|
||||
- `session`
|
||||
- `account`
|
||||
|
||||
For more details head over to [schema](/docs/concepts/database#core-schema) section.
|
||||
</Step>
|
||||
|
||||
<Step>
|
||||
|
||||
@@ -51,3 +51,4 @@ import RootLayout from "@/layouts/root-layout.astro";
|
||||
});
|
||||
</script>
|
||||
</RootLayout>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "social-sign-in",
|
||||
"name": "@example/svelte-kit",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
@@ -24,7 +24,7 @@
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@internationalized/date": "^3.5.5",
|
||||
"better-auth": "workspace:0.0.10-beta.18",
|
||||
"better-auth": "workspace:*",
|
||||
"bits-ui": "^0.21.15",
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk-sv": "^0.0.18",
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<script lang="ts">
|
||||
import { client } from "$lib/auth-client";
|
||||
import { Button } from "$lib/components/ui/button/index.js";
|
||||
import * as Card from "$lib/components/ui/card/index.js";
|
||||
import { Input } from "$lib/components/ui/input/index.js";
|
||||
import { Label } from "$lib/components/ui/label/index.js";
|
||||
import { writable } from "svelte/store";
|
||||
import { client } from "$lib/auth-client";
|
||||
import { Button } from "$lib/components/ui/button/index.js";
|
||||
import * as Card from "$lib/components/ui/card/index.js";
|
||||
import { Input } from "$lib/components/ui/input/index.js";
|
||||
import { Label } from "$lib/components/ui/label/index.js";
|
||||
import { writable } from "svelte/store";
|
||||
|
||||
const email = writable("");
|
||||
const email = writable("");
|
||||
</script>
|
||||
|
||||
<Card.Root class="mx-auto max-w-sm">
|
||||
@@ -36,19 +36,21 @@ const email = writable("");
|
||||
alert("Please enter your email address");
|
||||
return;
|
||||
}
|
||||
await client.forgetPassword({
|
||||
email: $email,
|
||||
redirectTo: "/reset-password",
|
||||
fetchOptions: {
|
||||
onSuccess(context) {
|
||||
await client.forgetPassword(
|
||||
{
|
||||
email: $email,
|
||||
redirectTo: "/reset-password",
|
||||
},
|
||||
{
|
||||
onSuccess() {
|
||||
alert("Password reset link sent to your email");
|
||||
window.location.href = "/sign-in";
|
||||
},
|
||||
onError(context) {
|
||||
alert(context.error.message);
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
}}
|
||||
>
|
||||
Reset Password
|
||||
|
||||
@@ -10,16 +10,18 @@ const email = writable("");
|
||||
const password = writable("");
|
||||
|
||||
const handleSignIn = async () => {
|
||||
await signIn.email({
|
||||
email: $email,
|
||||
password: $password,
|
||||
callbackURL: "/",
|
||||
fetchOptions: {
|
||||
await signIn.email(
|
||||
{
|
||||
email: $email,
|
||||
password: $password,
|
||||
callbackURL: "/",
|
||||
},
|
||||
{
|
||||
onError(context) {
|
||||
alert(context.error.message);
|
||||
},
|
||||
},
|
||||
});
|
||||
);
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -73,3 +75,9 @@ const handleSignIn = async () => {
|
||||
</div>
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
"dependencies": {
|
||||
"@babel/preset-react": "^7.24.7",
|
||||
"@babel/preset-typescript": "^7.24.7",
|
||||
"@better-fetch/fetch": "^1.1.8",
|
||||
"@better-fetch/fetch": "^1.1.9",
|
||||
"@better-fetch/logger": "^1.1.3",
|
||||
"@nanostores/query": "^0.3.4",
|
||||
"@nanostores/react": "^0.7.3",
|
||||
|
||||
@@ -43,15 +43,17 @@ describe("rate-limiter", async () => {
|
||||
vi.useFakeTimers();
|
||||
vi.advanceTimersByTime(3000);
|
||||
let retryAfter = "";
|
||||
await client.signIn.email({
|
||||
email: testUser.email,
|
||||
password: testUser.password,
|
||||
fetchOptions: {
|
||||
await client.signIn.email(
|
||||
{
|
||||
email: testUser.email,
|
||||
password: testUser.password,
|
||||
},
|
||||
{
|
||||
onError(context) {
|
||||
retryAfter = context.response.headers.get("X-Retry-After") ?? "";
|
||||
},
|
||||
},
|
||||
});
|
||||
);
|
||||
expect(retryAfter).toBe("7");
|
||||
});
|
||||
|
||||
|
||||
@@ -23,14 +23,16 @@ describe("forget password", async (it) => {
|
||||
|
||||
it("should verify the token", async () => {
|
||||
const newPassword = "new-password";
|
||||
const res = await client.resetPassword({
|
||||
fetchOptions: {
|
||||
const res = await client.resetPassword(
|
||||
{
|
||||
newPassword,
|
||||
},
|
||||
{
|
||||
query: {
|
||||
currentURL: `http://localhost:3000/reset-password?token=${token}`,
|
||||
},
|
||||
},
|
||||
newPassword,
|
||||
});
|
||||
);
|
||||
expect(res.data).toMatchObject({
|
||||
status: true,
|
||||
});
|
||||
|
||||
@@ -7,10 +7,12 @@ describe("session", async () => {
|
||||
const { client, testUser, sessionSetter } = await getTestInstance();
|
||||
|
||||
it("should set cookies correctly on sign in", async () => {
|
||||
await client.signIn.email({
|
||||
email: testUser.email,
|
||||
password: testUser.password,
|
||||
fetchOptions: {
|
||||
await client.signIn.email(
|
||||
{
|
||||
email: testUser.email,
|
||||
password: testUser.password,
|
||||
},
|
||||
{
|
||||
onSuccess(context) {
|
||||
const header = context.response.headers.get("set-cookie");
|
||||
const cookies = parseSetCookieHeader(header || "");
|
||||
@@ -23,7 +25,7 @@ describe("session", async () => {
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
);
|
||||
});
|
||||
|
||||
it("should return null when not authenticated", async () => {
|
||||
@@ -33,10 +35,12 @@ describe("session", async () => {
|
||||
|
||||
it("should update session when close to expiry", async () => {
|
||||
let headers = new Headers();
|
||||
const res = await client.signIn.email({
|
||||
email: testUser.email,
|
||||
password: testUser.password,
|
||||
fetchOptions: {
|
||||
const res = await client.signIn.email(
|
||||
{
|
||||
email: testUser.email,
|
||||
password: testUser.password,
|
||||
},
|
||||
{
|
||||
onSuccess(context) {
|
||||
const header = context.response.headers.get("set-cookie");
|
||||
const cookies = parseSetCookieHeader(header || "");
|
||||
@@ -44,7 +48,7 @@ describe("session", async () => {
|
||||
headers.set("cookie", `better-auth.session_token=${signedCookie}`);
|
||||
},
|
||||
},
|
||||
});
|
||||
);
|
||||
|
||||
if (!res.data?.session) {
|
||||
throw new Error("No session found");
|
||||
@@ -74,11 +78,13 @@ describe("session", async () => {
|
||||
|
||||
it("should handle 'don't remember me' option", async () => {
|
||||
let headers = new Headers();
|
||||
const res = await client.signIn.email({
|
||||
email: testUser.email,
|
||||
password: testUser.password,
|
||||
dontRememberMe: true,
|
||||
fetchOptions: {
|
||||
const res = await client.signIn.email(
|
||||
{
|
||||
email: testUser.email,
|
||||
password: testUser.password,
|
||||
dontRememberMe: true,
|
||||
},
|
||||
{
|
||||
onSuccess(context) {
|
||||
const header = context.response.headers.get("set-cookie");
|
||||
const cookies = parseSetCookieHeader(header || "");
|
||||
@@ -92,7 +98,7 @@ describe("session", async () => {
|
||||
);
|
||||
},
|
||||
},
|
||||
});
|
||||
);
|
||||
if (!res.data?.session) {
|
||||
throw new Error("No session found");
|
||||
}
|
||||
@@ -117,10 +123,12 @@ describe("session", async () => {
|
||||
|
||||
it("should clear session on sign out", async () => {
|
||||
let headers = new Headers();
|
||||
const res = await client.signIn.email({
|
||||
email: testUser.email,
|
||||
password: testUser.password,
|
||||
fetchOptions: {
|
||||
const res = await client.signIn.email(
|
||||
{
|
||||
email: testUser.email,
|
||||
password: testUser.password,
|
||||
},
|
||||
{
|
||||
onSuccess(context) {
|
||||
const header = context.response.headers.get("set-cookie");
|
||||
const cookies = parseSetCookieHeader(header || "");
|
||||
@@ -128,7 +136,7 @@ describe("session", async () => {
|
||||
headers.set("cookie", `better-auth.session_token=${signedCookie}`);
|
||||
},
|
||||
},
|
||||
});
|
||||
);
|
||||
if (!res.data?.session) {
|
||||
throw new Error("No session found");
|
||||
}
|
||||
@@ -148,13 +156,15 @@ describe("session", async () => {
|
||||
|
||||
it("should list sessions", async () => {
|
||||
const headers = new Headers();
|
||||
await client.signIn.email({
|
||||
email: testUser.email,
|
||||
password: testUser.password,
|
||||
fetchOptions: {
|
||||
await client.signIn.email(
|
||||
{
|
||||
email: testUser.email,
|
||||
password: testUser.password,
|
||||
},
|
||||
{
|
||||
onSuccess: sessionSetter(headers),
|
||||
},
|
||||
});
|
||||
);
|
||||
|
||||
const response = await client.user.listSessions({
|
||||
fetchOptions: {
|
||||
|
||||
@@ -57,8 +57,14 @@ export type InferRoute<API> = API extends {
|
||||
? C extends Context<any, any>
|
||||
? (
|
||||
...data: HasRequiredKeys<InferCtx<C>> extends true
|
||||
? [Prettify<InferCtx<C>>]
|
||||
: [Prettify<InferCtx<C>>?]
|
||||
? [
|
||||
Prettify<InferCtx<C>>,
|
||||
BetterFetchOption<C["body"], C["query"], C["params"]>?,
|
||||
]
|
||||
: [
|
||||
Prettify<InferCtx<C>>?,
|
||||
BetterFetchOption<C["body"], C["query"], C["params"]>?,
|
||||
]
|
||||
) => Promise<BetterFetchResponse<Awaited<R>>>
|
||||
: never
|
||||
: never
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { BetterFetch } from "@better-fetch/fetch";
|
||||
import type { BetterFetch, BetterFetchOption } from "@better-fetch/fetch";
|
||||
import type { Atom, PreinitializedWritableAtom } from "nanostores";
|
||||
import type { ProxyRequest } from "./path-to-object";
|
||||
import type { BetterAuthClientPlugin } from "./types";
|
||||
@@ -61,8 +61,13 @@ export function createDynamicPathProxy<T extends Record<string, any>>(
|
||||
)
|
||||
.join("/");
|
||||
const arg = (args[0] || {}) as ProxyRequest;
|
||||
const fetchOptions = (arg[1] || {}) as BetterFetchOption;
|
||||
const method = getMethod(routePath, knownPathMethods, arg);
|
||||
const { query, fetchOptions: options, ...body } = arg;
|
||||
const { query, fetchOptions: argFetchOptions, ...body } = arg;
|
||||
const options = {
|
||||
...fetchOptions,
|
||||
...argFetchOptions,
|
||||
};
|
||||
|
||||
return await client(routePath, {
|
||||
...options,
|
||||
|
||||
@@ -242,7 +242,6 @@ describe("organization", async (it) => {
|
||||
headers,
|
||||
},
|
||||
});
|
||||
|
||||
expect(organization.data).toBe(orgId);
|
||||
});
|
||||
|
||||
|
||||
18
pnpm-lock.yaml
generated
18
pnpm-lock.yaml
generated
@@ -789,7 +789,7 @@ importers:
|
||||
specifier: ^3.5.5
|
||||
version: 3.5.5
|
||||
better-auth:
|
||||
specifier: workspace:0.0.10-beta.18
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/better-auth
|
||||
bits-ui:
|
||||
specifier: ^0.21.15
|
||||
@@ -874,8 +874,8 @@ importers:
|
||||
specifier: ^7.24.7
|
||||
version: 7.24.7(@babel/core@7.25.2)
|
||||
'@better-fetch/fetch':
|
||||
specifier: ^1.1.8
|
||||
version: 1.1.8
|
||||
specifier: ^1.1.9
|
||||
version: 1.1.9
|
||||
'@better-fetch/logger':
|
||||
specifier: ^1.1.3
|
||||
version: 1.1.3
|
||||
@@ -1700,8 +1700,8 @@ packages:
|
||||
'@babel/helper-validator-identifier': 7.24.7
|
||||
to-fast-properties: 2.0.0
|
||||
|
||||
/@better-fetch/fetch@1.1.8:
|
||||
resolution: {integrity: sha512-ymfeM8CaRR/yEPCL+7ghwNDamRrn1uXPFIUKbTfxrilBzbKgmAdSauU5QIfoJA0f14KkmauzM1PfOLRHSltJMw==}
|
||||
/@better-fetch/fetch@1.1.9:
|
||||
resolution: {integrity: sha512-eeWQ4X9eewQgn8RC26oI8oW5DO8WRfD85hFOvOCbtG5UABqEh+vAmBEY7S6r56BTXzOsK+fVH204JTlvacxTew==}
|
||||
dev: false
|
||||
|
||||
/@better-fetch/logger@1.1.3:
|
||||
@@ -11359,7 +11359,7 @@ packages:
|
||||
resolution: {integrity: sha512-PR1bR6zbgNRkSHZAPQQEqaHpHMyASXwBU2IbDY60gDWJO72d3wBSCiOKQcSGJRxJ55O9jitHVMHqHcAtC0++0A==}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
'@better-fetch/fetch': 1.1.8
|
||||
'@better-fetch/fetch': 1.1.9
|
||||
'@better-fetch/logger': 1.1.3
|
||||
'@chronark/access-policies': 0.0.2
|
||||
'@nanostores/query': 0.3.4(nanostores@0.11.3)
|
||||
@@ -11406,7 +11406,7 @@ packages:
|
||||
peerDependencies:
|
||||
typescript: ^5.6.0-beta
|
||||
dependencies:
|
||||
'@better-fetch/fetch': 1.1.8
|
||||
'@better-fetch/fetch': 1.1.9
|
||||
'@types/set-cookie-parser': 2.4.10
|
||||
rou3: 0.5.1
|
||||
set-cookie-parser: 2.7.0
|
||||
@@ -11416,7 +11416,7 @@ packages:
|
||||
/better-call@0.2.3-beta.10:
|
||||
resolution: {integrity: sha512-yIb0lL6/XNOFEEon28wLdjDy/4oMKThFFt/5Tzz7xcwqqh++FwiLFIDwgVFP9pZYT4ytn6GdqQGQlFMe6ugaVg==}
|
||||
dependencies:
|
||||
'@better-fetch/fetch': 1.1.8
|
||||
'@better-fetch/fetch': 1.1.9
|
||||
'@types/set-cookie-parser': 2.4.10
|
||||
rou3: 0.5.1
|
||||
set-cookie-parser: 2.7.0
|
||||
@@ -11426,7 +11426,7 @@ packages:
|
||||
/better-call@0.2.3-beta.2:
|
||||
resolution: {integrity: sha512-ybOtGcR4pOsHI2XE+urR9zcmK+s0YnhJSx8KDj6ul7MUEyYOiMEnq/bylyH62/7qXuYb9q8Oqkp9NF9vWOZ4Mg==}
|
||||
dependencies:
|
||||
'@better-fetch/fetch': 1.1.8
|
||||
'@better-fetch/fetch': 1.1.9
|
||||
'@types/set-cookie-parser': 2.4.10
|
||||
rou3: 0.5.1
|
||||
set-cookie-parser: 2.7.0
|
||||
|
||||
Reference in New Issue
Block a user