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:
| Provider | Constructor | Default Scopes |
|---|---|---|
OAuthProvider::google(client_id, client_secret) | openid, email, profile | |
| GitHub | OAuthProvider::github(client_id, client_secret) | read:user, user:email |
| Discord | OAuthProvider::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.
| Endpoint | Method | Description |
|---|---|---|
/sign-in/social | POST | Initiate OAuth sign-in flow |
/callback/{provider} | GET | OAuth callback handler |
/link-social | POST | Link a social account to an existing user |
/get-access-token | POST | Retrieve stored OAuth access token |
/refresh-token | POST | Refresh the OAuth access token |
/account-info | GET | Retrieve provider user info for a linked account |
/list-accounts | GET | List all linked accounts |
/unlink-account | POST | Unlink 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:
- A random
code_verifieris generated - A
code_challengeis derived using SHA-256 - The challenge is sent in the authorization URL
- The verifier is stored in a verification record
- 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
| Status | Condition |
|---|---|
| 400 | Unknown provider name |
| 400 | Missing code or state in callback |
| 400 | Provider mismatch between state and callback |
| 409 | Social account already linked to another user |
| 500 | Token exchange or user info fetch failed |