diff --git a/docs/content/docs/plugins/2fa.mdx b/docs/content/docs/plugins/2fa.mdx index 3e1676bbc7..b03c655644 100644 --- a/docs/content/docs/plugins/2fa.mdx +++ b/docs/content/docs/plugins/2fa.mdx @@ -159,10 +159,15 @@ const authClient = createAuthClient({ When you call `auth.api.signInEmail` on the server, and the user has 2FA enabled, it will return an object where `twoFactorRedirect` is set to `true`. This behavior isn’t inferred in TypeScript, which can be misleading. You can check using `in` instead to check if `twoFactorRedirect` is set to `true`. +`authClient.twoFactor.*` handles cookies automatically in the browser. If you continue the 2FA flow with `auth.api.*` on the server, you must pass the relevant headers so Better Auth can read the current 2FA state and set the resulting 2FA/session cookies. The generated examples below use `await headers()` for this, but in other frameworks you should pass the equivalent incoming request headers. + +If you chain multiple `auth.api.*` calls in the same server flow, make sure you forward the cookies from the previous auth response into the next call. + ```ts title="sign-in.ts" import { auth } from "@/lib/auth" -const response = await auth.api.signInEmail({ +const { headers: responseHeaders, response } = await auth.api.signInEmail({ + returnHeaders: true, body: { email: "test@test.com", password: "test", @@ -170,6 +175,7 @@ const response = await auth.api.signInEmail({ }); if ("twoFactorRedirect" in response) { + // Forward the cookies from responseHeaders into the next auth.api 2FA call. // Handle the 2FA verification in place } ``` @@ -252,7 +258,12 @@ By default the issuer for TOTP is set to the app name provided in the auth confi After the user has entered their 2FA code, you can verify it using `twoFactor.verifyTotp` method. `Better Auth` follows standard practice by accepting TOTP codes from one period before and one after the current code, ensuring users can authenticate even with minor time delays on their end. - + ```ts type verifyTOTP = { /** @@ -292,11 +303,16 @@ export const auth = betterAuth({ #### Sending OTP -Sending an OTP is done by calling the `twoFactor.sendOtp` function. This function will trigger your sendOTP implementation that you provided in the Better Auth configuration. +Sending an OTP is done by calling the `authClient.twoFactor.sendOtp` function on the client or `auth.api.sendTwoFactorOTP` on the server. This function will trigger your sendOTP implementation that you provided in the Better Auth configuration. - + ```ts -type send2FaOTP = { +type sendTwoFactorOTP = { /** * If true, the device will be trusted for 30 days. It'll be refreshed on every sign in request within this time. */ @@ -311,11 +327,16 @@ if (data) { #### Verifying OTP -After the user has entered their OTP code, you can verify it +After the user has entered their OTP code, you can verify it using `authClient.twoFactor.verifyOtp` on the client or `auth.api.verifyTwoFactorOTP` on the server. - + ```ts -type verifyOTP = { +type verifyTwoFactorOTP = { /** * The otp code to verify. */ @@ -364,7 +385,12 @@ When you generate backup codes, the old backup codes will be deleted and new one You can now allow users to provide a backup code as an account recovery method. - + ```ts type verifyBackupCode = { /** diff --git a/landing/components/api-method.tsx b/landing/components/api-method.tsx index fa2961d5b3..aafee659eb 100644 --- a/landing/components/api-method.tsx +++ b/landing/components/api-method.tsx @@ -48,7 +48,9 @@ export const APIMethod = ({ children, noResult, requireSession, + requireHeaders, requireBearerToken, + headersComment, note, clientOnlyNote, serverOnlyNote, @@ -73,6 +75,16 @@ export const APIMethod = ({ * @default false */ requireBearerToken?: boolean; + /** + * If enabled, we will add `headers` to the fetch options without assuming the request is backed by a session. + * + * @default false + */ + requireHeaders?: boolean; + /** + * A custom comment to display above the generated `headers` line when `requireHeaders` is enabled. + */ + headersComment?: string; /** * The HTTP method to the endpoint * @@ -152,7 +164,9 @@ export const APIMethod = ({ props, method: method ?? "GET", requireSession: requireSession ?? false, + requireHeaders: requireHeaders ?? false, requireBearerToken: requireBearerToken ?? false, + headersComment, forceAsQuery, forceAsParam, forceAsBody, @@ -768,7 +782,9 @@ function shouldServerUseQueryParams( function createServerBody({ props, requireSession, + requireHeaders, requireBearerToken, + headersComment, method, forceAsBody, forceAsParam, @@ -776,7 +792,9 @@ function createServerBody({ }: { props: Property[]; requireSession: boolean; + requireHeaders: boolean; requireBearerToken: boolean; + headersComment: string | undefined; method: string; forceAsQuery: boolean | undefined; forceAsParam: boolean | undefined; @@ -840,6 +858,11 @@ function createServerBody({ if (requireSession) { fetchOptions += "\n // This endpoint requires session cookies.\n headers: await headers(),"; + } else if (requireHeaders) { + fetchOptions += `\n // ${ + headersComment || + "Pass the current request headers so Better Auth can read and set cookies." + }\n headers: await headers(),`; } if (requireBearerToken) { diff --git a/packages/better-auth/src/plugins/two-factor/otp/index.ts b/packages/better-auth/src/plugins/two-factor/otp/index.ts index 332370f382..2d394c5a6d 100644 --- a/packages/better-auth/src/plugins/two-factor/otp/index.ts +++ b/packages/better-auth/src/plugins/two-factor/otp/index.ts @@ -395,7 +395,7 @@ export const otp2fa = (options?: OTPOptions | undefined) => { * ### API Methods * * **server:** - * `auth.api.send2FaOTP` + * `auth.api.sendTwoFactorOTP` * * **client:** * `authClient.twoFactor.sendOtp` @@ -411,7 +411,7 @@ export const otp2fa = (options?: OTPOptions | undefined) => { * ### API Methods * * **server:** - * `auth.api.verifyOTP` + * `auth.api.verifyTwoFactorOTP` * * **client:** * `authClient.twoFactor.verifyOtp`