new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

Better Auth · Concepts · all subjects

api

57 notes, read out of this brain and free to use. Each one was extracted from a source and is re-checked against its exam.

Getting headers from API endpoint response

To get the `headers` from an API endpoint response on the server, pass the `returnHeaders` option set to `true`. This returns an object with `headers` and `response` properties. The `headers` is a `Headers` object which you can use to get cookies with `headers.getSetCookie()` or individual headers with `headers.get('header-name')`.

Getting Response object from API endpoint

To get the full `Response` object from an API endpoint call on the server, pass the `asResponse` option set to `true` to the endpoint.

API endpoint error handling on the server

When you call an API endpoint on the server, it will throw an error if the request fails. The error instance is an instance of `APIError`. You can check if an error is an `APIError` using the `isAPIError()` function imported from `better-auth/api`, and access properties like `error.message` and `error.status`.

Better Auth API built on better-call framework

Better Auth API endpoints are built on top of better-call, a tiny web framework that allows calling REST API endpoints as if they were regular functions and enables automatic client type inference from the server.

API object provides access to all Better Auth endpoints

When you create a new Better Auth instance, it provides an `api` object. This object exposes every endpoint that exists in your Better Auth instance, including endpoints from plugins and the core. You can use this to interact with Better Auth server-side.

Calling API endpoints on the server

To call an API endpoint on the server, import your `auth` instance and call the endpoint using the `api` object. Example: `await auth.api.getSession({ headers: await headers() })`

Server API parameter structure: body, headers, query

Unlike the client, the server needs values passed as an object with specific keys: use `body` for the request body, `headers` for HTTP headers, and `query` for query parameters. Example: `await auth.api.signInEmail({ body: { email: 'john@doe.com', password: 'password' }, headers: await headers() })`

Get OAuth access token with automatic refresh

To get the access token for a social provider, use getAccessToken(). When called, if the access token is expired, it will be refreshed automatically.

Server-side get access token

Server-side usage to get access token for a social provider: ```ts await auth.api.getAccessToken({ body: { providerId: "google", // or any other provider id accountId: "accountId", // optional, if you want to get the access token for a specific account userId: "userId", // optional, if you don't provide headers with authenticated token }, headers: await headers() // headers containing the user's session token }); ```

Request additional OAuth scopes using linkSocial

To request additional OAuth scopes after the user has already signed up, use the linkSocial method with the same provider and pass the desired scopes. This triggers a new OAuth flow that requests the additional scopes while maintaining the existing account connection.

Better Auth version requirement for additional scopes

Better Auth version 1.2.7 or later is required for requesting additional scopes. Earlier versions like 1.2.2 may show a "Social account already linked" error when trying to link with an existing provider for additional scopes.

Pass additional data through OAuth flow

Better Auth allows passing additional data through the OAuth flow without storing it in the database. This is useful for tracking referral codes, analytics sources, or other temporary data that should be processed during authentication but not persisted.

Server-side pass additional data with sign in

Server-side example passing additional data during OAuth sign-in: ```ts await auth.api.signInSocial({ body: { provider: "google", additionalData: { referralCode: "ABC123", source: "admin-panel", }, }, }); ```

Microsoft Entra ID provider specific notes: use profile.oid as identity anchor

For Microsoft Entra ID provider: because email is tenant-mutable and never verified, use profile.oid (immutable, stable within the tenant) as the identity anchor; treat email as a profile attribute only. Microsoft's claims validation guidance explicitly warns never to use email, preferred_username, or unique_name for authorization decisions.

Default OAuth state includes multiple fields

By default, OAuth state includes the following data: callbackURL (the callback URL for the OAuth flow), codeVerifier (the code verifier for the OAuth flow), errorURL (the error URL for the OAuth flow), newUserURL (the new user URL for the OAuth flow), link (the link for the OAuth flow, containing email and user id), requestSignUp (whether to request sign up for the OAuth flow), expiresAt (the expiration time of the OAuth state), and any additional data passed in the OAuth flow.

Synthesize placeholder email with mapProfileToUser for missing email

When a provider omits email, you can fall back to the provider's stable ID using mapProfileToUser to synthesize a placeholder email.

mapProfileToUser example with placeholder emails

Example of synthesizing placeholder emails for providers that may not return email: ```ts import { betterAuth } from "better-auth"; export const auth = betterAuth({ socialProviders: { discord: { clientId: process.env.DISCORD_CLIENT_ID!, clientSecret: process.env.DISCORD_CLIENT_SECRET!, mapProfileToUser: (profile) => ({ email: profile.email ?? `${profile.id}@discord.placeholder.local`, }), }, apple: { clientId: process.env.APPLE_CLIENT_ID!, clientSecret: process.env.APPLE_CLIENT_SECRET!, mapProfileToUser: (profile) => ({ email: profile.email ?? `${profile.sub}@apple.placeholder.local`, }), }, microsoft: { clientId: process.env.MICROSOFT_CLIENT_ID!, clientSecret: process.env.MICROSOFT_CLIENT_SECRET!, mapProfileToUser: (profile) => ({ email: profile.email ?? `${profile.oid}@entra.placeholder.local`, }), }, }, }); ```

Apple provider specific notes: persist email on first sign-in

For Apple provider: persist the email the first time you see it. Apple provides no user-info endpoint, so if you don't store it on first sign-in you cannot retrieve it later. Both email_verified and is_private_email are serialized as strings ("true" / "false"), not booleans.

GitHub provider specific notes: user:email scope and private emails

For GitHub provider: the user:email scope is requested by default. Private emails still return null on /user; the primary verified address is available at /user/emails.

Provider option: clientId

The OAuth 2.0 Client ID issued by the provider. For providers that verify ID tokens by audience (Google, Apple, Microsoft Entra, Facebook, Cognito), you can pass an array to accept tokens issued for any of the configured clients. The first entry is used when Better Auth drives the authorization code flow; all entries are accepted when verifying an ID token's aud claim. This enables cross-platform sign-in (Web, iOS, Android) with a single backend configuration, where each platform's native SDK issues tokens under its own Client ID. For providers that don't verify ID tokens by audience, only a single string is accepted.

Provider option: clientId array example

Example of configuring clientId as an array for cross-platform sign-in: ```ts import { betterAuth } from "better-auth"; export const auth = betterAuth({ socialProviders: { google: { clientId: [ process.env.GOOGLE_WEB_CLIENT_ID as string, process.env.GOOGLE_IOS_CLIENT_ID as string, process.env.GOOGLE_ANDROID_CLIENT_ID as string, ], clientSecret: process.env.GOOGLE_CLIENT_SECRET as string, }, }, }); ```

Provider option: scope

The scope of the access request. For example, email or profile.

Provider option: scope example

Example of configuring scope for a provider: ```ts import { betterAuth } from "better-auth"; export const auth = betterAuth({ socialProviders: { google: { clientId: "YOUR_GOOGLE_CLIENT_ID", clientSecret: "YOUR_GOOGLE_CLIENT_SECRET", scope: ["email", "profile"], }, }, }); ```

Provider option: redirectURI

Custom redirect URI for the provider. By default, it uses /api/auth/callback/${providerName}.

Provider option: redirectURI example

Example of configuring a custom redirect URI: ```ts import { betterAuth } from "better-auth"; export const auth = betterAuth({ socialProviders: { google: { clientId: "YOUR_GOOGLE_CLIENT_ID", clientSecret: "YOUR_GOOGLE_CLIENT_SECRET", redirectURI: "https://your-app.com/auth/callback", }, }, }); ```

Provider option: disableSignUp

Disables sign-up for new users when set for a provider.

Provider option: disableIdTokenSignIn

Disables the use of the ID token for sign-in. By default, it is enabled for some providers like Google and Apple.

Provider option: verifyIdToken

A custom function to verify the ID token. Receives the token, an optional nonce, and the request endpoint context so you can branch on headers or other request data. Providing verifyIdToken replaces the provider's built-in verification (signature, issuer, audience, and expiry). Your callback must perform those checks itself. Client-supplied headers such as x-platform are attacker-controlled — use them only to select which audience (or other claim) to verify against, not as proof of identity on their own.

Provider option: verifyIdToken example

Example of implementing a custom verifyIdToken function: ```ts import { betterAuth } from "better-auth"; import { createRemoteJWKSet, jwtVerify } from "jose"; const appleJwks = createRemoteJWKSet( new URL("https://appleid.apple.com/auth/keys"), ); export const auth = betterAuth({ socialProviders: { apple: { clientId: "YOUR_APPLE_CLIENT_ID", clientSecret: "YOUR_APPLE_CLIENT_SECRET", verifyIdToken: async (token, nonce, ctx) => { const audience = ctx?.headers?.get("x-platform") === "ios" ? process.env.APPLE_APP_BUNDLE_IDENTIFIER! : process.env.APPLE_CLIENT_ID!; try { const { payload } = await jwtVerify(token, appleJwks, { issuer: "https://appleid.apple.com", audience, maxTokenAge: "1h", }); if (nonce && payload.nonce !== nonce) { return false; } return true; } catch { return false; } }, }, }, }); ```

Provider option: overrideUserInfoOnSignIn

A boolean value that determines whether to override the user information in the database when signing in. By default, it is set to false, meaning that the user information will not be overridden during sign-in. If you want to update the user information every time they sign in, set this to true.

Provider option: mapProfileToUser

Use mapProfileToUser to change the default user mapping or populate additional user fields from the provider profile. Better Auth treats the function's return value as provider input, even though the function runs on your server. It applies the input rules from user.additionalFields during OAuth sign-up, sign-in profile override, and account-link profile sync. Mapped fields that allow input are parsed and stored, while mapped values for fields marked input: false are ignored.

Provider option: mapProfileToUser example

Example of using mapProfileToUser to map provider profile fields: ```ts import { betterAuth } from "better-auth"; export const auth = betterAuth({ socialProviders: { google: { clientId: "YOUR_GOOGLE_CLIENT_ID", clientSecret: "YOUR_GOOGLE_CLIENT_SECRET", mapProfileToUser: (profile) => { return { firstName: profile.given_name, lastName: profile.family_name, }; }, }, }, }); ```

Enforce authorization policies before OAuth sign-in completes

If a provider claim controls who may sign in, enforce the policy before Better Auth completes OAuth sign-in. Do not defer the check until after sign-in, because Better Auth may already have issued a valid session. Prefer a provider-specific option when one exists. For flows that invoke getUserInfo, a custom implementation can verify the provider response and return null when the policy fails. Configure equivalent enforcement for separate sign-in paths that do not invoke getUserInfo.

Provider option: refreshAccessToken

A custom function to refresh the token. This feature is only supported for built-in social providers (Google, Facebook, GitHub, etc.) and is not currently supported for custom OAuth providers configured through the Generic OAuth Plugin. For built-in providers, you can provide a custom function to refresh the token if needed.

Provider option: refreshAccessToken example

Example of implementing a custom refreshAccessToken function: ```ts import { betterAuth } from "better-auth"; export const auth = betterAuth({ socialProviders: { google: { clientId: "YOUR_GOOGLE_CLIENT_ID", clientSecret: "YOUR_GOOGLE_CLIENT_SECRET", refreshAccessToken: async (token) => { return { accessToken: "new-access-token", refreshToken: "new-refresh-token", }; }, }, }, }); ```

OAuth 2.0 and OpenID Connect support

Better Auth comes with built-in support for OAuth 2.0 and OpenID Connect. This allows authentication via popular OAuth providers like Google, Facebook, GitHub, and more.

Provider option: clientKey example for TikTok

Example of configuring TikTok social provider with clientKey: ```ts import { betterAuth } from "better-auth"; export const auth = betterAuth({ socialProviders: { tiktok: { clientKey: "YOUR_TIKTOK_CLIENT_KEY", clientSecret: "YOUR_TIKTOK_CLIENT_SECRET", }, }, }); ```

Provider option: getUserInfo

A custom function to get user info from the provider. This allows you to override the default user info retrieval process.

Provider option: getUserInfo example

Example of implementing a custom getUserInfo function: ```ts import { betterAuth } from "better-auth"; export const auth = betterAuth({ socialProviders: { google: { clientId: "YOUR_GOOGLE_CLIENT_ID", clientSecret: "YOUR_GOOGLE_CLIENT_SECRET", getUserInfo: async (token) => { const response = await fetch("https://www.googleapis.com/oauth2/v2/userinfo", { headers: { Authorization: `Bearer ${token.accessToken}`, }, }); const profile = await response.json(); return { user: { id: profile.id, name: profile.name, email: profile.email, image: profile.picture, emailVerified: profile.verified_email, }, data: profile, }; }, }, }, }); ```

Provider option: disableImplicitSignUp

Disables implicit sign up for new users. When set to true for the provider, sign-in needs to be called with requestSignUp as true to create new users.

Provider option: disableImplicitSignUp example

Example of disabling implicit sign up: ```ts import { betterAuth } from "better-auth"; export const auth = betterAuth({ socialProviders: { google: { clientId: "YOUR_GOOGLE_CLIENT_ID", clientSecret: "YOUR_GOOGLE_CLIENT_SECRET", disableImplicitSignUp: true, }, }, }); ```

Provider option: prompt

The prompt to use for the authorization code request. This controls the authentication flow behavior. Valid values include: select_account, consent, login, none, or select_account+consent.

Provider option: prompt example

Example of configuring the prompt option: ```ts import { betterAuth } from "better-auth"; export const auth = betterAuth({ socialProviders: { google: { clientId: "YOUR_GOOGLE_CLIENT_ID", clientSecret: "YOUR_GOOGLE_CLIENT_SECRET", prompt: "select_account", // or "consent", "login", "none", "select_account+consent" }, }, }); ```

Provider option: responseMode

The response mode to use for the authorization code request. This determines how the authorization response is returned. Valid values are query or form_post.

Provider option: responseMode example

Example of configuring the responseMode option: ```ts import { betterAuth } from "better-auth"; export const auth = betterAuth({ socialProviders: { google: { clientId: "YOUR_GOOGLE_CLIENT_ID", clientSecret: "YOUR_GOOGLE_CLIENT_SECRET", responseMode: "query", // or "form_post" }, }, }); ```

Provider option: disableDefaultScope

Removes the default scopes of the provider. By default, providers include certain scopes like email and profile. Set this to true to remove these default scopes and use only the scopes you specify.

Provider option: disableDefaultScope example

Example of disabling default scopes: ```ts import { betterAuth } from "better-auth"; export const auth = betterAuth({ socialProviders: { google: { clientId: "YOUR_GOOGLE_CLIENT_ID", clientSecret: "YOUR_GOOGLE_CLIENT_SECRET", disableDefaultScope: true, scope: ["https://www.googleapis.com/auth/userinfo.email"], // Only this scope will be used }, }, }); ```

Get provider account info with accountInfo

To get provider specific account info you can use the accountInfo function with the authClient or auth.api for server-side usage.

Server-side get provider account info

Server-side usage to get provider account info: ```ts await auth.api.accountInfo({ query: { accountId: "accountId", userId: "userId", // optional, if you don't provide headers with authenticated token }, headers: await headers() // headers containing the user's session token }); ```

Provider option: clientKey for TikTok

The client key of your application. This is used by TikTok Social Provider instead of clientId.

Generic OAuth Plugin for custom providers

If a desired provider is not directly supported, you can use the Generic OAuth Plugin for custom integrations.

Social provider configuration requires clientId and clientSecret

To enable a social provider, you must provide clientId and clientSecret for the provider.

Facebook provider specific notes: treat email as unverified

For Facebook provider: without a per-email verification flag, treat every Facebook email as unverified unless you run your own verification challenge.

Social provider configuration example

Example configuration of Google as a provider: ```ts import { betterAuth } from "better-auth"; export const auth = betterAuth({ socialProviders: { google: { clientId: "YOUR_GOOGLE_CLIENT_ID", clientSecret: "YOUR_GOOGLE_CLIENT_SECRET", }, }, }); ```

Server-side OAuth sign-in with auth.api

To sign in with a social provider on the server side, use auth.api.signInSocial() with the provider in the request body: ```ts await auth.api.signInSocial({ body: { provider: "google", // or any other provider id }, }); ```

Server-side OAuth account linking

To link an account to a social provider on the server side, use auth.api.linkSocialAccount() with the provider in the request body and headers containing the user's session token: ```ts await auth.api.linkSocialAccount({ body: { provider: "google", // or any other provider id }, headers: await headers() // headers containing the user's session token }); ```

changePassword API endpoint parameters

The POST /change-password endpoint requires a session and accepts newPassword (required, string), currentPassword (required, string), and revokeOtherSessions (optional boolean, default true). When revokeOtherSessions is true, all other active sessions for the user are invalidated.

Give your agent this brain