OAuth
Better Auth Ruby supports built-in social providers and generic OAuth through plugins.
If a provider is not built in, use BetterAuth::Plugins.generic_oauth.
Configuring Social Providers
auth = BetterAuth.auth(
base_url: "https://app.example.com/api/auth",
secret: ENV.fetch("BETTER_AUTH_SECRET"),
social_providers: {
github: BetterAuth::SocialProviders.github(
client_id: ENV.fetch("GITHUB_CLIENT_ID"),
client_secret: ENV.fetch("GITHUB_CLIENT_SECRET")
),
google: BetterAuth::SocialProviders.google(
client_id: ENV.fetch("GOOGLE_CLIENT_ID"),
client_secret: ENV.fetch("GOOGLE_CLIENT_SECRET")
)
}
)Usage
Sign In
Start an OAuth flow:
result = auth.api.sign_in_social(
body: {
provider: "github",
callbackURL: "/dashboard",
errorCallbackURL: "/login",
disableRedirect: true
}
)
redirect_to result[:url]Browsers can call /sign-in/social and follow the returned provider URL.
Link Account
Link a social account to the current user:
result = auth.api.link_social(
headers: {"cookie" => cookie},
body: {
provider: "github",
callbackURL: "/settings/accounts",
scopes: ["repo"]
}
)
redirect_to result[:url]Ruby also supports native or ID-token linking for providers that expose verify_id_token.
Get Access Token
token = auth.api.get_access_token(
headers: {"cookie" => cookie},
body: {
providerId: "github"
}
)If the stored token is expired and the provider has a refresh callback, Better Auth refreshes and persists it.
Get Account Info Provided By The Provider
info = auth.api.account_info(
headers: {"cookie" => cookie},
query: {
accountId: "provider-account-id"
}
)Requesting Additional Scopes
Use link_social with the same provider and additional scopes:
auth.api.link_social(
headers: {"cookie" => cookie},
body: {
provider: "google",
scopes: ["https://www.googleapis.com/auth/drive.readonly"],
callbackURL: "/settings/integrations"
}
)Provider defaults can also include extra scopes:
BetterAuth::SocialProviders.github(
client_id: ENV.fetch("GITHUB_CLIENT_ID"),
client_secret: ENV.fetch("GITHUB_CLIENT_SECRET"),
scopes: ["user:email", "read:user", "repo"]
)Passing Additional Data Through OAuth Flow
Pass a hash in additionalData. Reserved OAuth state keys are filtered out.
auth.api.sign_in_social(
body: {
provider: "github",
callbackURL: "/dashboard",
additionalData: {
source: "pricing-page",
plan: "pro"
}
}
)Accessing Additional Data In Hooks
Hooks can inspect request body and callback state depending on which route they run around. Validate any additional data before using it.
hooks: {
after: [
{
matcher: ->(ctx) { ctx.path == "/callback/github" },
handler: ->(ctx) { Rails.logger.info(ctx.query["state"]) }
}
]
}Handling Providers Without Email
OAuth providers that do not return email cannot create or link a user directly. Use provider mapping to synthesize a placeholder email only when your app can safely treat it as non-contact identity.
Synthesize A Placeholder Email With map_profile_to_user
BetterAuth::SocialProviders.discord(
client_id: ENV.fetch("DISCORD_CLIENT_ID"),
client_secret: ENV.fetch("DISCORD_CLIENT_SECRET"),
map_profile_to_user: lambda do |profile|
id = profile["id"]
{
"email" => "discord-#{id}@users.example.invalid",
"emailVerified" => false
}
end
)Use a domain you control or a reserved suffix such as .invalid.
Provider-Specific Notes
Some providers return email only on first consent, only with specific scopes, or only for verified accounts. Check the provider class and scopes for your provider before relying on email.
Provider Options
clientId
Use Ruby client_id; camelCase provider options are normalized where applicable.
BetterAuth::SocialProviders.github(client_id: "...", client_secret: "...")scope
Use scope or scopes.
BetterAuth::SocialProviders.github(
client_id: "...",
client_secret: "...",
scope: ["read:user"]
)redirectURI
Override the redirect URI when required by the provider:
BetterAuth::SocialProviders.github(
client_id: "...",
client_secret: "...",
redirect_uri: "https://app.example.com/api/auth/callback/github"
)disableSignUp
BetterAuth::SocialProviders.github(
client_id: "...",
client_secret: "...",
disable_sign_up: true
)disableIdTokenSignIn
BetterAuth::SocialProviders.google(
client_id: "...",
client_secret: "...",
disable_id_token_sign_in: true
)verifyIdToken
Provide custom ID token verification:
BetterAuth::SocialProviders.google(
client_id: "...",
client_secret: "...",
verify_id_token: ->(token, nonce = nil) { MyVerifier.valid?(token, nonce) }
)overrideUserInfoOnSignIn
Update profile fields from provider data when an existing user signs in:
BetterAuth::SocialProviders.github(
client_id: "...",
client_secret: "...",
override_user_info_on_sign_in: true
)mapProfileToUser
BetterAuth::SocialProviders.github(
client_id: "...",
client_secret: "...",
map_profile_to_user: ->(profile) { {"name" => profile["login"]} }
)refreshAccessToken
Provide a custom refresh callback:
BetterAuth::SocialProviders.github(
client_id: "...",
client_secret: "...",
refresh_access_token: lambda do |refresh_token|
MyOAuth.refresh_github_token(refresh_token)
end
)clientKey
Some providers require a client key in addition to client ID and secret. Pass client_key when the provider supports it.
getUserInfo
Override user-info fetching:
BetterAuth::SocialProviders.github(
client_id: "...",
client_secret: "...",
get_user_info: lambda do |tokens|
profile = GitHubClient.new(tokens["accessToken"]).user
{
user: {
"id" => profile.id,
"email" => profile.email,
"name" => profile.name,
"emailVerified" => true
},
data: profile
}
end
)disableImplicitSignUp
BetterAuth::SocialProviders.github(
client_id: "...",
client_secret: "...",
disable_implicit_sign_up: true
)Users must explicitly request sign-up for that provider.
prompt
BetterAuth::SocialProviders.google(
client_id: "...",
client_secret: "...",
prompt: "select_account"
)responseMode
Use provider-specific options when a provider class accepts response mode settings.
disableDefaultScope
BetterAuth::SocialProviders.github(
client_id: "...",
client_secret: "...",
disable_default_scope: true,
scopes: ["read:user"]
)Other Provider Configurations
Provider classes normalize common upstream option names to Ruby snake_case. Inspect the specific provider class for provider-specific settings such as tenant IDs, app bundle identifiers, profile photo settings, and endpoint overrides.