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 · all subjects

authentication methods/social

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

Configure social provider authentication

To use a social provider, configure it in the socialProviders option. For example, GitHub requires clientId and clientSecret from environment variables.

Social provider sign-in method and options

Call authClient.signIn.social with an object containing: provider (required, string like 'github', 'google', 'apple'), callbackURL (optional, defaults to '/'), errorCallbackURL (optional), newUserCallbackURL (optional), and disableRedirect (optional boolean, defaults to false to disable automatic redirect to provider).

Auth0 migration social providers configuration

Configure social providers in betterAuth by adding socialProviders object with provider keys (e.g., google, github). Each provider requires clientId and clientSecret from environment variables. Example: socialProviders: { google: { clientId: process.env.GOOGLE_CLIENT_ID, clientSecret: process.env.GOOGLE_CLIENT_SECRET } }

Discovery providers verify identity tokens in 1.7

A generic OAuth provider configured with a discovery URL now verifies the provider identity token. A token that fails verification is rejected. If a discovery provider returned a token that could not be verified and your app trusted it anyway, the login is now rejected. Confirm the provider's published keys, issuer, and audience.

Generic OAuth rebuilds on social-provider path

The generic OAuth plugin is rebuilt to work like built-in social providers. Replace `signIn.oauth2({ providerId })` with `signIn.social({ provider })`. Replace `oauth2.link()` with `linkSocial()`. Update the provider callback URL from `/api/auth/oauth2/callback/:id` to `/api/auth/callback/:id`. Remove `genericOAuthClient()` from client plugins and use standard social client APIs. PKCE now defaults to on; set `pkce: false` only for providers that reject it. Remove `issuer` and `requireIssuerValidation` because issuer validation is now automatic. `authorizationUrlParams` and `tokenUrlParams` now accept only plain string maps. The `mapProfileToUser` profile argument is now typed as `GenericOAuthUserInfo` instead of `Record<string, any>`; coerce or cast non-standard provider fields when assigning to typed user fields.

Identity token verification centralized to one verifier

Each OAuth provider used to verify its own identity tokens. In 1.7, there is one verifier, and each provider declares an `idToken` config containing keys, issuer, and audience. A custom provider should replace its `verifyIdToken` method with an `idToken` config. PayPal no longer accepts identity-token login and uses its access token instead; switch PayPal identity-token login to the redirect flow. Built-in provider options are unchanged.

Electron PKCE and origin validation changes

The Electron login flow now requires S256 PKCE, no longer trusts a custom origin header, and matches custom URL schemes more safely. Upgrade the `@better-auth/electron` client and server together. Make sure the app URL scheme is in `trustedOrigins`. Remove the old `disableOriginOverride` option. Review trusted entries with a host, such as `myapp://callback`, because they no longer match lookalike hosts. A host-bearing entry now matches by exact authority, while a host-less entry like `myapp://` or `exp://` still trusts every host of that scheme. The default Expo `exp://` config is unaffected.

`generateState()` signature changed to options object

The public `generateState()` helper now takes an options object instead of positional arguments. Replace `generateState(c, link, additionalData)` with the options form `generateState(c, options)`.

OAuth callback error code `email_doesn't_match` renamed

The OAuth callback redirect error value `email_doesn't_match` is renamed `email_does_not_match`. Update any code that reads this error code from the callback redirect.

Google One Tap token validation stricter in 1.7

One Tap now rejects a malformed identity token, one with a missing or non-string `email` or `sub`, with a `400` instead of a soft error. With `requireEmailVerification` enabled for Google, an unverified email returns `403 EMAIL_NOT_VERIFIED`. Handle the `403` if you opt into `requireEmailVerification`.

Scopes kept across logins in 1.7

Granted scopes used to overwrite each other; a permission granted earlier could disappear after a later login that asked for less access. Better Auth 1.7 keeps the scopes already on the account across re-login and token refresh using the existing `account.scope` field. This is a behavior fix with no schema change, no backfill needed, and no action for most projects. An early beta moved scopes into a `grantedScopes` array, but a later beta reverted it; the shipped 1.7 keeps the original `account.scope` string. If you adopted the `grantedScopes` column from an early beta, drop it and restore `scope` on every schema you changed.

Signed-assertion login validated at startup

A signed-assertion login setup, also called private-key JWT, is now checked when it is created. An unsupported algorithm, a key with no material, or a mismatch between the declared algorithm and the key now fails immediately instead of silently doing the wrong thing. Fix any signing setup whose declared algorithm disagrees with the key.

OAuth2 token-request builders are now async

The low-level OAuth2 request builders are renamed and return promises. Replace `createAuthorizationCodeRequest`, `createRefreshAccessTokenRequest`, and `createClientCredentialsTokenRequest` with the async `authorizationCodeRequest`, `refreshAccessTokenRequest`, and `clientCredentialsTokenRequest`, and `await` them. This affects any code that builds token requests by hand, including custom providers and SSO integrations.

Anonymous account linking works in mobile and in-app browsers

Linking an anonymous account after a social login now works in Expo and other in-app browsers, where the callback returns without the usual cookie. A new `addOAuthServerContext` API carries trusted data across the login that a client cannot forge. If you carried anonymous-link state across the OAuth redirect yourself, move it onto `addOAuthServerContext`.

Client-side sign in with social provider

Client-side sign in with Better Auth uses authClient.signIn.social() method with a provider parameter. For example, to sign in with GitHub: await authClient.signIn.social({ provider: 'github' }). The method returns an object with data and error properties.

Server-side sign in with social provider

Server-side sign in with Better Auth uses auth.api.signInSocial() method with a body containing the provider. For example: await auth.api.signInSocial({ body: { provider: 'github' } }). The method returns an object with redirect and url properties.

SAML definition and role in Better Auth

SAML (Security Assertion Markup Language) is an XML-based standard for exchanging authentication and authorization data between an Identity Provider (IdP) such as Okta, Azure AD, or OneLogin and a Service Provider (SP). In Better Auth, the IdP authenticates users and sends assertions about their identity, while Better Auth validates assertions and logs the user in.

Okta SAML application configuration requirements

When creating a SAML 2.0 application in Okta, configure: Single Sign-on URL as the Better Auth callback endpoint (e.g., http://localhost:3000/api/auth/sso/saml2/callback/{providerId}), Audience URI (SP Entity ID) as the Better Auth metadata URL (e.g., http://localhost:3000/api/auth/sso/saml2/sp/metadata), and Name ID format as Email Address or another choice. Download the IdP metadata XML file and certificate after creation.

Better Auth SAML configuration structure

The defaultSSO configuration takes an array of provider objects. Each object contains: domain (required), providerId (required), and samlConfig (required). The samlConfig includes: issuer (SP entity ID), entryPoint (IdP SSO URL), callbackUrl (SAML ACS endpoint, defaults to {baseURL}/sso/saml2/sp/acs/{providerId} if omitted), idpMetadata object with entityID and singleSignOnService array, cert (IdP certificate as PEM), and spMetadata object with metadata XML.

Multiple SAML providers by domain

Better Auth supports configuring multiple SAML providers in defaultSSO array for different domains. The system can match providers either explicitly by providerId, or by domain fallback matching the user's email domain against configured domain values (e.g., user@company.com matches the provider with domain 'company.com').

Three methods to initiate SAML sign-in

SSO sign-in can be initiated three ways: (1) Explicitly by providerId using authClient.signIn.sso({ providerId: "provider-id", callbackURL: "/path" }), (2) By email domain matching using authClient.signIn.sso({ email: "user@domain.com", callbackURL: "/path" }), or (3) By specifying domain using authClient.signIn.sso({ domain: "domain.com", callbackURL: "/path" }). The providerId method is recommended.

IdP-initiated and SP-initiated SAML flows

Better Auth automatically handles both SP-initiated and IdP-initiated SAML flows. For IdP-initiated SSO where users access the app from the Okta dashboard, ensure the Single Sign-on URL points to the callback endpoint (/api/auth/sso/saml2/callback/{providerId}).

SAML configuration example for Okta

Example SAML configuration for Okta development setup: ```typescript const ssoConfig = { defaultSSO: [{ domain: "localhost:3000", providerId: "sso", samlConfig: { issuer: "http://localhost:3000/api/auth/sso/saml2/sp/metadata", entryPoint: "https://trial-1076874.okta.com/app/trial-1076874_samltest_1/exktofb0a62hqLAUL697/sso/saml", callbackUrl: "http://localhost:3000/api/auth/sso/saml2/sp/acs/sso", idpMetadata: { entityID: "https://trial-1076874.okta.com/app/exktofb0a62hqLAUL697/sso/saml/metadata", singleSignOnService: [{ Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", Location: "https://trial-1076874.okta.com/app/trial-1076874_samltest_1/exktofb0a62hqLAUL697/sso/saml" }], }, cert: `-----BEGIN CERTIFICATE-----...-----END CERTIFICATE-----`, spMetadata: { metadata: `<md:EntityDescriptor ...>...</md:EntityDescriptor>` } } }] } ```

Dynamic SAML provider registration API

SAML providers can be dynamically registered using authClient.sso.register() with parameters: providerId (required), issuer (required), domain (required), and samlConfig object. Example: await authClient.sso.register({ providerId: "okta-prod", issuer: "https://your-domain.com", domain: "your-domain.com", samlConfig: { /* configuration */ } }). See SSO Plugin Documentation for detailed registration instructions.

SAML callbackUrl endpoint behavior

The callbackUrl in samlConfig is the SAML ACS (Assertion Consumer Service) endpoint URL. If omitted, it defaults to {baseURL}/sso/saml2/sp/acs/{providerId}. The post-login destination is controlled separately by the callbackURL parameter passed to signIn.sso().

SAML configuration security notes

DummyIDP should only be used for development and testing, never in production. Never use test certificates in production environments. Always use proper IdP providers like Okta, Azure AD, or OneLogin for production. Example uses localhost:3000 - adjust all URLs for your actual environment.

phoneNumber plugin SMS integration

When using the phoneNumber plugin with Better Auth, SMS messages are automatically sent for phone number verification, phone-based two-factor authentication, and phone OTP sign-in flows. The sendOTP callback can be customized if needed.

GitHub social provider configuration

To enable GitHub as a social provider, add the following to the betterAuth configuration: `socialProviders: { github: { clientId: process.env.GITHUB_CLIENT_ID as string, clientSecret: process.env.GITHUB_CLIENT_SECRET as string } }`

Give your agent this brain