BTST
PluginsQuickstartDocs
Live Blog

From research to product evaluation

Evaluating a publishing workflow for an app you already own?

See what the BTST Blog plugin adds to an existing React or Next.js app
BTST

Open-source TypeScript features for the React application, data, and deployment you already own.

Released plugins

  • Blog
  • AI Chat
  • CMS
  • Form Builder
  • UI Builder
  • Kanban
  • Comments
  • Media
  • Route Docs
  • OpenAPI
  • Better Auth UI

Resources

  • Quickstart
  • Documentation
  • All plugins
  • Live Blog
  • GitHub (opens in a new tab)
© 2026 BTST. Open source under the MIT License.
AI Chat
September 16, 2026ReactBetter Auth UI

Add Google and GitHub Sign-In to BTST Better Auth UI

Align provider callbacks, server credentials, and BTST buttons while preserving registration, account-linking, and authorization policies.

Add Google and GitHub Sign-In to BTST Better Auth UI

Social sign-in requires agreement between the identity provider, the Better Auth server, and the buttons BTST renders. A visible Google or GitHub button only establishes the last of those three pieces.

This guide adds social providers to an existing @btst/better-auth-ui@2.0.0 application using Better Auth 1.6.16. Keep the working auth integration and its existing client, database, session refresh, and authorization adapter.

Keep three destinations separate#

For a production site at https://app.example.com, an auth API mounted at /api/auth, and BTST pages mounted at /p, the paths have different roles:

DestinationExamplePurpose
Sign-in page/p/auth/sign-inDisplays BTST's provider buttons
Provider callbackhttps://app.example.com/api/auth/callback/googleReturns Google's authorization response to Better Auth
Application destination/p/account/settingsOpens after authentication completes

GitHub's callback ends in /callback/github. Register each exact callback with its provider, using the real public origin and configured auth base path. Register development callbacks separately. A post-login page is not the provider callback, and a BTST page mount does not move the auth API.

The Google setup documentation covers its OAuth credentials and callback configuration. The GitHub setup documentation distinguishes OAuth Apps from GitHub Apps; a GitHub App needs permission to read email addresses. Follow the setup for the application type you actually created.

Configure the Better Auth server#

This server-only helper validates the required configuration and returns the two provider entries:

TS
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
import type { BetterAuthOptions } from "better-auth";

function required(name: string): string {
  const value = process.env[name];
  if (!value) throw new Error(`Missing ${name}`);
  return value;
}

export function socialProviders() {
  return {
    google: {
      clientId: required("GOOGLE_CLIENT_ID"),
      clientSecret: required("GOOGLE_CLIENT_SECRET"),
    },
    github: {
      clientId: required("GITHUB_CLIENT_ID"),
      clientSecret: required("GITHUB_CLIENT_SECRET"),
    },
  } satisfies BetterAuthOptions["socialProviders"];
}

Merge the returned object into betterAuth({ socialProviders: ... }), retaining existing providers and any provider-specific policy. Call the helper only from server configuration. Client secrets must not use a public environment-variable prefix or appear in client provider overrides.

Preserve Better Auth's state, cookie, origin, and redirect validation. A callback mismatch is a reason to compare configuration with the request's actual destination, not to disable those checks. If the application sits behind a proxy, confirm that Better Auth is using the intended public origin.

Enable the matching buttons#

Merge these fields into your existing StackProvider auth overrides:

TS
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
import type { AuthPluginOverrides } from "@btst/better-auth-ui/client";

export const socialButtons = {
  social: { providers: ["google", "github"] },
  redirectTo: "/p/account/settings",
} satisfies Partial<AuthPluginOverrides>;

Keep authClient and onSessionChange in the surrounding configuration. The provider list contains identifiers, not credentials. Use your own existing application destination if account pages have a different mount or are not enabled.

The released provider button normally calls authClient.signIn.social. It also handles the companion's callback path when client persistence is configured. Leave that built-in behavior intact while establishing the basic flow. Google and GitHub's built-in Better Auth providers do not require a separate generic OAuth client plugin.

Removing a button does not disable the server provider. Conversely, listing a provider in the UI does not enable its server configuration. Keep both lists intentional.

Verify registration and account linking separately#

A first social sign-in can create an account, depending on server policy. It may also encounter an existing email associated with another authentication method. Decide how your application should handle those cases before offering the button. Hiding BTST's sign-up link is not a server registration restriction.

Account linking is a separate operation from sign-in. Review the configured linking policy and the provider's email-verification guarantees; do not broaden trusted providers merely to make an error disappear. Test with controlled identities instead of modifying real users to force a successful demonstration.

Diagnose the failed step#

SymptomFirst checks
Button is absentoverrides.auth.social.providers and the rendered auth view
Provider rejects the redirectExact scheme, hostname, API base path, and provider callback
GitHub cannot supply an emailApp type, email permission, and provider response
Callback succeeds but UI looks signed outSession cookie, client API origin, and framework refresh callback
Sign-in succeeds but data is forbiddenBackend authorization for that user and resource

Test both providers, cancellation at the provider, an existing account, a new account under the intended registration policy, and a protected request after returning. A successful session should grant only the permissions your backend assigns.

Continue with BTST auth documentation, compatible dependency versions, and session management. Configuration and type checks cannot substitute for exercising your registered provider applications.

In This Post

Keep three destinations separateConfigure the Better Auth serverEnable the matching buttonsVerify registration and account linking separatelyDiagnose the failed step