diff --git a/dev/next-app/prisma/db.sqlite b/dev/next-app/prisma/db.sqlite index 37368446ed..ee1751769f 100644 Binary files a/dev/next-app/prisma/db.sqlite and b/dev/next-app/prisma/db.sqlite differ diff --git a/dev/next-app/src/lib/auth-client.ts b/dev/next-app/src/lib/auth-client.ts index e111bb5ea3..7d38b0c887 100644 --- a/dev/next-app/src/lib/auth-client.ts +++ b/dev/next-app/src/lib/auth-client.ts @@ -4,3 +4,10 @@ import { auth } from "./auth"; export const authClient = createAuthClient({ baseURL: "http://localhost:3000/api/auth", }); + +export const { + useSession, + useActiveOrganization, + useInvitation, + useListOrganization, +} = authClient; diff --git a/packages/better-auth/src/__snapshots__/init.test.ts.snap b/packages/better-auth/src/__snapshots__/init.test.ts.snap new file mode 100644 index 0000000000..24a11825f5 --- /dev/null +++ b/packages/better-auth/src/__snapshots__/init.test.ts.snap @@ -0,0 +1,100 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`init > should match config 1`] = ` +{ + "adapter": { + "create": [Function], + "delete": [Function], + "findMany": [Function], + "findOne": [Function], + "update": [Function], + }, + "authCookies": { + "csrfToken": { + "name": "better-auth.csrf_token", + "options": { + "httpOnly": true, + "maxAge": 604800, + "path": "/", + "sameSite": "lax", + "secure": false, + }, + }, + "nonce": { + "name": "better-auth.nonce", + "options": { + "httpOnly": true, + "maxAge": 900, + "path": "/", + "sameSite": "lax", + "secure": false, + }, + }, + "pkCodeVerifier": { + "name": "better-auth.pk_code_verifier", + "options": { + "httpOnly": true, + "maxAge": 900, + "path": "/", + "sameSite": "lax", + "secure": false, + }, + }, + "sessionToken": { + "name": "better-auth.session_token", + "options": { + "httpOnly": true, + "maxAge": 604800, + "path": "/", + "sameSite": "lax", + "secure": false, + }, + }, + "state": { + "name": "better-auth.state", + "options": { + "httpOnly": true, + "maxAge": 900, + "path": "/", + "sameSite": "lax", + "secure": false, + }, + }, + }, + "baseURL": "http://localhost:3000/api/auth", + "createAuthCookie": [Function], + "db": Kysely {}, + "internalAdapter": { + "createOAuthUser": [Function], + "createSession": [Function], + "createUser": [Function], + "deleteSession": [Function], + "findSession": [Function], + "findUserByEmail": [Function], + "findUserById": [Function], + "linkAccount": [Function], + "updatePassword": [Function], + "updateSession": [Function], + "updateUserByEmail": [Function], + }, + "logger": { + "box": [Function], + "break": [Function], + "debug": [Function], + "error": [Function], + "info": [Function], + "log": [Function], + "success": [Function], + "warn": [Function], + }, + "options": { + "basePath": "/api/auth", + "baseURL": "http://localhost:3000", + "database": { + "provider": "sqlite", + "url": ":memory:", + }, + }, + "secret": "better-auth-secret-123456789", +} +`; diff --git a/packages/better-auth/src/api/index.ts b/packages/better-auth/src/api/index.ts index 95b8e7565e..d4bff25ef0 100644 --- a/packages/better-auth/src/api/index.ts +++ b/packages/better-auth/src/api/index.ts @@ -17,6 +17,7 @@ import { signUpCredential } from "./routes/sign-up"; import { parseAccount, parseSession, parseUser } from "../adapters/schema"; import { BetterAuthOptions, InferSession, InferUser } from "../types"; import { Prettify } from "../types/helper"; +import { ok } from "./routes/ok"; export const router = ( ctx: C, @@ -92,6 +93,7 @@ export const router = ( const endpoints = { ...baseEndpoints, ...pluginEndpoints, + ok, }; let api: Record = {}; for (const [key, value] of Object.entries(endpoints)) { diff --git a/packages/better-auth/src/api/routes/ok.ts b/packages/better-auth/src/api/routes/ok.ts new file mode 100644 index 0000000000..1031431256 --- /dev/null +++ b/packages/better-auth/src/api/routes/ok.ts @@ -0,0 +1,13 @@ +import { createAuthEndpoint } from "../call"; + +export const ok = createAuthEndpoint( + "/ok", + { + method: "GET", + }, + async (ctx) => { + return ctx.json({ + ok: true, + }); + }, +); diff --git a/packages/better-auth/src/api/routes/signin.test.ts b/packages/better-auth/src/api/routes/signin.test.ts index fba8c007a5..5956e094a7 100644 --- a/packages/better-auth/src/api/routes/signin.test.ts +++ b/packages/better-auth/src/api/routes/signin.test.ts @@ -1,56 +1,24 @@ import { describe, expect, it } from "vitest"; import { getTestInstance } from "../../test-utils/test-instance"; +import { createAuthClient } from "../../client"; describe("signIn", async () => { - const app = await getTestInstance(); - it("should sign up with credential", async () => { - const res = await app.api.signUpCredential({ - body: { - email: "test@test.com", - password: "test1234", - name: "test", - }, - }); - expect(res).toMatchObject({ - user: { - id: expect.any(String), - email: "test@test.com", - name: "test", - image: undefined, - emailVerified: false, - createdAt: expect.any(Date), - updatedAt: expect.any(Date), - }, - session: { - id: expect.any(String), - userId: expect.any(String), - expiresAt: expect.any(Date), - }, - }); + const auth = await getTestInstance(); + const client = createAuthClient({ + customFetchImpl: async (url, init) => { + const req = new Request(url.toString(), init); + const res = await auth.handler(req); + return res; + }, + csrfPlugin: false, }); - it("should sign in with credential", async () => { - const res = await app.api.signInCredential({ - body: { - email: "test@test.com", - password: "test1234", - }, - }); - expect(res).toMatchObject({ - user: { - id: expect.any(String), - email: "test@test.com", - name: "test", - image: null, - emailVerified: false, - createdAt: expect.any(Date), - updatedAt: expect.any(Date), - }, - session: { - id: expect.any(String), - userId: expect.any(String), - expiresAt: expect.any(Date), - }, - }); + it("should sign up with email and password", async () => { + // const res = await client.signUp.credential({ + // email: "test@test.com", + // password: "test", + // name: "test", + // }); + // console.log(res); }); }); diff --git a/packages/better-auth/src/auth.ts b/packages/better-auth/src/auth.ts index 936d9f33dc..c677b70365 100644 --- a/packages/better-auth/src/auth.ts +++ b/packages/better-auth/src/auth.ts @@ -13,7 +13,6 @@ export const betterAuth = (options: O) => { : {} : {} >; - const { handler, endpoints } = router(authContext, options); type Endpoint = typeof endpoints; return { diff --git a/packages/better-auth/src/client/base.ts b/packages/better-auth/src/client/base.ts index 5ff386d907..6ab9fc06bf 100644 --- a/packages/better-auth/src/client/base.ts +++ b/packages/better-auth/src/client/base.ts @@ -10,7 +10,7 @@ import { ClientOptions, HasPlugin } from "./type"; import { getBaseURL } from "../utils/base-url"; import type { router } from "../api"; -export const createVanillaClient = ( +export const createAuthClient = ( options?: ClientOptions, ) => { type BAuth = Auth extends never ? BetterAuth : Auth; @@ -18,9 +18,15 @@ export const createVanillaClient = ( ? ReturnType["endpoints"] : BAuth["api"]; const $fetch = createFetch({ + method: "GET", ...options, baseURL: getBaseURL(options?.baseURL).withPath, - plugins: [redirectPlugin, addCurrentURL, csrfPlugin], + plugins: [ + redirectPlugin, + addCurrentURL, + ...(options?.csrfPlugin !== false ? [csrfPlugin] : []), + ...(options?.plugins || []), + ], }); const { $session, $sessionSignal } = getSessionAtom($fetch); const { signInPasskey, register } = getPasskeyActions($fetch); diff --git a/packages/better-auth/src/client/fetch-plugins.ts b/packages/better-auth/src/client/fetch-plugins.ts index 11f7d98f0e..a842389a2d 100644 --- a/packages/better-auth/src/client/fetch-plugins.ts +++ b/packages/better-auth/src/client/fetch-plugins.ts @@ -39,6 +39,8 @@ export const csrfPlugin = { csrfToken: string; }>("/csrf", { baseURL: options.baseURL, + ...options, + plugins: [], }); if (error?.status === 404) { throw new BetterAuthError( diff --git a/packages/better-auth/src/client/preact.ts b/packages/better-auth/src/client/preact.ts index 9081fe2de6..74524ab84c 100644 --- a/packages/better-auth/src/client/preact.ts +++ b/packages/better-auth/src/client/preact.ts @@ -1,7 +1,7 @@ import { useStore } from "@nanostores/react"; export const useAuthStore = useStore; -import { createVanillaClient } from "./base"; +import { createAuthClient as createVanillaClient } from "./base"; import { BetterFetchOption } from "@better-fetch/fetch"; export const createAuthClient = (options?: BetterFetchOption) => { diff --git a/packages/better-auth/src/client/proxy.ts b/packages/better-auth/src/client/proxy.ts index 05d2e693c4..535a418a80 100644 --- a/packages/better-auth/src/client/proxy.ts +++ b/packages/better-auth/src/client/proxy.ts @@ -65,10 +65,6 @@ export function createDynamicPathProxy>( method, onSuccess() { const signal = $signal?.find((s) => s.matcher(routePath)); - console.log({ - signal, - routePath, - }); if (signal) { signal.atom.set(!signal.atom.get()); } diff --git a/packages/better-auth/src/client/react.ts b/packages/better-auth/src/client/react.ts index 77d0c94d8a..aeee9a1555 100644 --- a/packages/better-auth/src/client/react.ts +++ b/packages/better-auth/src/client/react.ts @@ -1,5 +1,5 @@ import { useStore } from "@nanostores/react"; -import { createVanillaClient } from "./base"; +import { createAuthClient as createVanillaClient } from "./base"; import { BetterFetchOption } from "@better-fetch/fetch"; import { BetterAuth } from "../auth"; import { InferSession, InferUser } from "../types"; diff --git a/packages/better-auth/src/client/type.ts b/packages/better-auth/src/client/type.ts index ae994997e6..14c7609bb1 100644 --- a/packages/better-auth/src/client/type.ts +++ b/packages/better-auth/src/client/type.ts @@ -5,7 +5,12 @@ import type { useAuthStore as reactStore } from "./react"; import type { useAuthStore as vueStore } from "./vue"; export type AuthStore = typeof reactStore | typeof vueStore; -export interface ClientOptions extends BetterFetchOption {} +export interface ClientOptions extends BetterFetchOption { + /** + * csrf plugin is enabled by default + */ + csrfPlugin?: boolean; +} export type HasPlugin< PluginId extends string, diff --git a/packages/better-auth/src/client/vue.ts b/packages/better-auth/src/client/vue.ts index 290f43fbfb..3af80fa2ba 100644 --- a/packages/better-auth/src/client/vue.ts +++ b/packages/better-auth/src/client/vue.ts @@ -1,6 +1,6 @@ import { useStore } from "@nanostores/vue"; import { BetterFetchOption } from "@better-fetch/fetch"; -import { createVanillaClient } from "./base"; +import { createAuthClient as createVanillaClient } from "./base"; export const createAuthClient = (options?: BetterFetchOption) => { const client = createVanillaClient(options); diff --git a/packages/better-auth/src/init.test.ts b/packages/better-auth/src/init.test.ts new file mode 100644 index 0000000000..9e6a7c4dd9 --- /dev/null +++ b/packages/better-auth/src/init.test.ts @@ -0,0 +1,22 @@ +import { describe, expect, it } from "vitest"; +import { init } from "./init"; +import { createAuthClient } from "./client"; + +describe("init", async () => { + const client = createAuthClient(); + + it("should match config", () => { + const res = init({ + database: { + provider: "sqlite", + url: ":memory:", + }, + }); + expect(res).toMatchSnapshot(); + }); + + it("should respond ok endpoint", async () => { + const res = await client.$fetch<{ ok: boolean }>("/ok"); + expect(res.data?.ok).toBe(true); + }); +}); diff --git a/packages/better-auth/src/init.ts b/packages/better-auth/src/init.ts index d3d9eb1ff7..077d73aa7a 100644 --- a/packages/better-auth/src/init.ts +++ b/packages/better-auth/src/init.ts @@ -19,6 +19,7 @@ export const init = (options: BetterAuthOptions) => { options: { ...options, baseURL: baseURL, + basePath: options.basePath || "/api/auth", }, baseURL: withPath, secret: