docs: basic usage imporvements

This commit is contained in:
Bereket Engida
2024-09-26 11:06:11 +03:00
parent d4f4b94386
commit b18a691635
19 changed files with 477 additions and 208 deletions

View File

@@ -8,7 +8,7 @@
"editor.defaultFormatter": "biomejs.biome"
},
"[svelte]": {
"editor.defaultFormatter": "svelte.svelte-vscode"
"editor.defaultFormatter": "biomejs.biome"
},
"[vue]": {
"editor.defaultFormatter": "Vue.volar"

View File

@@ -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",

View File

@@ -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>

View 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>

View 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>
);
}
```

View File

@@ -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>

View File

@@ -51,3 +51,4 @@ import RootLayout from "@/layouts/root-layout.astro";
});
</script>
</RootLayout>

View File

@@ -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",

View File

@@ -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

View File

@@ -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>

View File

@@ -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",

View File

@@ -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");
});

View File

@@ -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,
});

View File

@@ -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: {

View File

@@ -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

View File

@@ -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,

View File

@@ -242,7 +242,6 @@ describe("organization", async (it) => {
headers,
},
});
expect(organization.data).toBe(orgId);
});

18
pnpm-lock.yaml generated
View File

@@ -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