Get your Google credentials
To use Google as a social provider, you need to get your Google credentials. You can get them by creating a new project in the Google Cloud Console.
In the Google Cloud Console > Credentials > Authorized redirect URIs, make sure to set the redirect URL to http://localhost:3000/api/auth/callback/google for local development. For production, make sure to set the redirect URL as your application domain, e.g. https://example.com/api/auth/callback/google. If you change the base path of the auth routes, you should update the redirect URL accordingly.
Creating Your Google OAuth Credentials
If you haven't created OAuth credentials yet, follow these step-by-step instructions:
- Open Google Cloud Console → APIs & Services → Credentials
- Click Create Credentials → OAuth client ID
- Choose Web application
- Add your redirect URIs:
http://localhost:3000/api/auth/callback/google(for local development)https://your-domain.com/api/auth/callback/google(for production)
- Copy the Client ID and Client Secret into your environment variables
These steps avoid common issues such as redirect_uri_mismatch.
Configure the provider
To configure the provider, you need to pass the client_id and client_secret to BetterAuth::SocialProviders.google in your auth configuration.
require "better_auth"
auth = BetterAuth.auth(
secret: ENV.fetch("BETTER_AUTH_SECRET"),
base_url: ENV.fetch("BETTER_AUTH_URL", "http://localhost:3000"),
social_providers: {
google: BetterAuth::SocialProviders.google(
client_id: ENV.fetch("GOOGLE_CLIENT_ID"),
client_secret: ENV.fetch("GOOGLE_CLIENT_SECRET")
)
}
)Important: Set Your Base URL
You must configure the base_url to avoid redirect_uri_mismatch errors. Better Auth uses this to construct the OAuth callback URL sent to Google.
Option 1: Environment Variable (Recommended)
Add to your .env file:
BETTER_AUTH_URL=https://your-domain.comOption 2: Explicit Configuration
Pass base_url directly in the auth config as shown above.
Without this, the callback URL may default to localhost, causing Google OAuth to fail in production.
Usage
Sign In with Google
To sign in with Google, call auth.api.sign_in_social on your Ruby auth instance. The endpoint body takes the following properties:
provider: The provider to use. It should be set togoogle.
response = auth.api.sign_in_social(
body: {
provider: "google",
callback_url: "/dashboard",
error_callback_url: "/login",
disable_redirect: true
}
)
redirect_url = response.fetch(:url)Sign In with Google With ID Token
To sign in with Google using the ID Token, you can use the auth.api.sign_in_social function to pass the ID Token.
This is useful when you have the ID Token from Google on your app and want to use it to sign in on the server.
If ID token is provided no redirection will happen, and the user will be signed in directly.
result = auth.api.sign_in_social(
body: {
provider: "google",
id_token: {
token: google_id_token,
access_token: google_access_token
}
}
)
user = result.fetch(:user)If you want to use google one tap, you can use the One Tap Plugin guide.
Cross-Platform Sign In (Web, iOS, Android)
Google issues a separate Client ID per platform in the same Google Cloud project. Pass an array to client_id to accept ID tokens from any of them. See client_id for the shared provider-option semantics.
require "better_auth"
auth = BetterAuth.auth(
secret: ENV.fetch("BETTER_AUTH_SECRET"),
base_url: ENV.fetch("BETTER_AUTH_URL", "http://localhost:3000"),
social_providers: {
google: BetterAuth::SocialProviders.google(
client_id: [
ENV.fetch("GOOGLE_WEB_CLIENT_ID"),
ENV.fetch("GOOGLE_IOS_CLIENT_ID"),
ENV.fetch("GOOGLE_ANDROID_CLIENT_ID")
],
client_secret: ENV.fetch("GOOGLE_CLIENT_SECRET")
)
}
)Your mobile app signs in with the native Google SDK and forwards the ID token:
result = auth.api.sign_in_social(
body: {
provider: "google",
id_token: {
token: google_id_token,
access_token: google_access_token
}
}
)
user = result.fetch(:user)The array only expands ID token audience verification. The authorization code flow still uses the first entry paired with the single client_secret and redirect_uri, so those cannot vary per platform within one provider block.
Always ask to select an account
If you want to always ask the user to select an account, you pass the prompt parameter to the provider, setting it to select_account.
require "better_auth"
auth = BetterAuth.auth(
secret: ENV.fetch("BETTER_AUTH_SECRET"),
base_url: ENV.fetch("BETTER_AUTH_URL", "http://localhost:3000"),
social_providers: {
google: BetterAuth::SocialProviders.google(
client_id: ENV.fetch("GOOGLE_CLIENT_ID"),
client_secret: ENV.fetch("GOOGLE_CLIENT_SECRET"),
prompt: "select_account"
)
}
)Requesting Additional Google Scopes
If your application needs additional Google scopes after the user has already signed up (e.g., for Google Drive, Gmail, or other Google services), you can request them using the auth.api.link_social method with the same Google provider.
response = auth.api.link_social(
headers: {
"cookie" => request.env.fetch("HTTP_COOKIE", "")
},
body: {
provider: "google",
callback_url: "/dashboard",
scopes: ["https://www.googleapis.com/auth/drive.file"]
}
)
redirect_url = response.fetch(:url)This will trigger a new OAuth flow that requests the additional scopes. After completion, your account will have the new scope in the database, and the access token will give you access to the requested Google APIs.
Ensure you're using Better Auth version 1.2.7 or later to avoid "Social account already linked" errors when requesting additional scopes from the same provider.
Always get refresh token
Google only issues a refresh token the first time a user consents to your app. If the user has already authorized your app, subsequent OAuth flows will only return an access token, not a refresh token.
To always get a refresh token, you can set the access_type to offline, and prompt to select_account consent in the provider options.
require "better_auth"
auth = BetterAuth.auth(
secret: ENV.fetch("BETTER_AUTH_SECRET"),
base_url: ENV.fetch("BETTER_AUTH_URL", "http://localhost:3000"),
social_providers: {
google: BetterAuth::SocialProviders.google(
client_id: ENV.fetch("GOOGLE_CLIENT_ID"),
client_secret: ENV.fetch("GOOGLE_CLIENT_SECRET"),
access_type: "offline",
prompt: "select_account consent"
)
}
)Revoking Access: If you want to get a new refresh token for a user who has already authorized your app, you must have them revoke your app's access in their Google account settings, then re-authorize.