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

These examples assume you've already defined AppAuthSchema. If you want a complete setup from entity definitions through Axum mounting, start with the Axum integration guide.

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

let auth = BetterAuth::<AppAuthSchema>::new(config)
    .store(store)
    .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)read:user, 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::<AppAuthSchema>::new(config)
    .store(store)
    .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
/account-infoGETRetrieve provider user info for a linked account
/list-accountsGETList all linked accounts
/unlink-accountPOSTUnlink a social account

Provider Account Info

Use GET /account-info to fetch provider-specific user info for a linked account. Pass the provider account id as the accountId query parameter.

GET /account-info?accountId=provider-account-id
Authorization: Bearer <token>

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