Better Auth in Rust

OAuth / Social Login

Sign in with Google, GitHub, Discord, and other OAuth providers.

The OAuthPlugin enables social sign-in via OAuth 2.0 providers. It handles the full authorization code flow with PKCE, token exchange, user info fetching, and account linking.

Setup

use better_auth::plugins::OAuthPlugin;
use better_auth::plugins::oauth::OAuthProvider;

let auth = BetterAuth::new(config)
    .database(database)
    .plugin(
        OAuthPlugin::new()
            .add_provider("google", OAuthProvider::google(
                "GOOGLE_CLIENT_ID",
                "GOOGLE_CLIENT_SECRET",
            ))
            .add_provider("github", OAuthProvider::github(
                "GITHUB_CLIENT_ID",
                "GITHUB_CLIENT_SECRET",
            ))
    )
    .build()
    .await?;

Built-in Providers

Better Auth RS includes pre-configured providers with correct URLs and user info mapping:

ProviderConstructorDefault Scopes
GoogleOAuthProvider::google(client_id, client_secret)openid, email, profile
GitHubOAuthProvider::github(client_id, client_secret)user:email
DiscordOAuthProvider::discord(client_id, client_secret)identify, email

Custom Provider

You can register any OAuth 2.0 provider by constructing an OAuthProvider manually:

use better_auth::plugins::oauth::{OAuthProvider, OAuthUserInfo};

let custom_provider = OAuthProvider {
    client_id: "your-client-id".to_string(),
    client_secret: "your-client-secret".to_string(),
    auth_url: "https://provider.com/oauth/authorize".to_string(),
    token_url: "https://provider.com/oauth/token".to_string(),
    user_info_url: "https://provider.com/api/userinfo".to_string(),
    scopes: vec!["openid".to_string(), "email".to_string()],
    map_user_info: |v| {
        Ok(OAuthUserInfo {
            id: v["sub"].as_str().ok_or("missing sub")?.to_string(),
            email: v["email"].as_str().ok_or("missing email")?.to_string(),
            name: v["name"].as_str().map(String::from),
            image: v["avatar"].as_str().map(String::from),
            email_verified: v["email_verified"].as_bool().unwrap_or(false),
        })
    },
};

let auth = BetterAuth::new(config)
    .database(database)
    .plugin(
        OAuthPlugin::new()
            .add_provider("custom", custom_provider)
    )
    .build()
    .await?;

The map_user_info function transforms the provider's raw JSON user info response into a standardized OAuthUserInfo struct.

API Endpoints

The OAuth plugin exposes the following endpoints. For full request/response details, see the OpenAPI Reference.

EndpointMethodDescription
/sign-in/socialPOSTInitiate OAuth sign-in flow
/callback/{provider}GETOAuth callback handler
/link-socialPOSTLink a social account to an existing user
/get-access-tokenPOSTRetrieve stored OAuth access token
/refresh-tokenPOSTRefresh the OAuth access token
/list-accountsGETList all linked accounts
/unlink-accountPOSTUnlink a social account

How It Works

PKCE Flow

All OAuth flows use PKCE (Proof Key for Code Exchange) for enhanced security:

  1. A random code_verifier is generated
  2. A code_challenge is derived using SHA-256
  3. The challenge is sent in the authorization URL
  4. The verifier is stored in a verification record
  5. During token exchange, the verifier proves the request originated from the same client

Account Linking Behavior

When the OAuth callback completes:

  • Existing account for provider: Updates the stored tokens and creates a new session
  • New provider, existing email: Links the new provider to the existing user
  • New provider, new email: Creates a new user and links the provider account

State Management

OAuth state is stored as a verification record with a 10-minute expiration. The state includes:

  • Provider name
  • Callback URL
  • PKCE code verifier
  • Link user ID (if linking to existing user)
  • Scopes

Errors

StatusCondition
400Unknown provider name
400Missing code or state in callback
400Provider mismatch between state and callback
409Social account already linked to another user
500Token exchange or user info fetch failed

On this page