Social Login Providers
Configure Google, GitHub, and Microsoft social login for Atlas managed auth.
Self-Hosted Only
Configuring social login providers requires editing the betterAuth() call in your Atlas source code. This applies to self-hosted deployments only. On app.useatlas.dev, social login providers are managed by the Atlas platform — go to Admin > Authentication to enable them for your workspace.
Atlas's managed auth (Better Auth) supports social login — no plugin required. Adding social providers requires a small configuration change to the betterAuth() call in packages/api/src/lib/auth/server.ts. This guide walks through setting up each provider.
Prerequisites
- Managed auth enabled (
BETTER_AUTH_SECRET+DATABASE_URL) BETTER_AUTH_URLset to your API's public URL (e.g.https://api.example.com)- OAuth credentials from the provider you want to enable
How It Works
- User clicks a social login button (or your frontend calls
auth.signIn.social()) - Browser redirects to the provider's OAuth consent screen
- Provider redirects back to Atlas at
/api/auth/callback/{provider} - Better Auth creates or links the user account and starts a session
Social login users go through the same admin bootstrap and invitation logic as email/password signups — the first user gets admin, invitation matches auto-assign roles, and ATLAS_ADMIN_EMAIL is respected. This logic runs on first sign-in (account creation). If a social login links to an existing account, the user keeps their current role.
Callback URL
Each provider needs a callback URL during OAuth app setup. The format is:
{BETTER_AUTH_URL}/api/auth/callback/{provider}| Provider | Callback URL |
|---|---|
https://api.example.com/api/auth/callback/google | |
| GitHub | https://api.example.com/api/auth/callback/github |
| Microsoft | https://api.example.com/api/auth/callback/microsoft |
Replace https://api.example.com with your BETTER_AUTH_URL value. For local development, use http://localhost:3001.
Create OAuth credentials
- Go to the Google Cloud Console
- Create a project (or select an existing one)
- Go to APIs & Services > Credentials
- Click Create Credentials > OAuth client ID
- Select Web application as the application type
- Under Authorized redirect URIs, add your callback URL:
https://api.example.com/api/auth/callback/google - Copy the Client ID and Client Secret
If you haven't configured the OAuth consent screen yet, Google will prompt you. Internal restricts sign-in to users within your Google Workspace organization (no verification needed). External allows any Google account but starts in "testing" mode (limited to 100 test users) until you submit for verification.
Set environment variables
# .env
GOOGLE_CLIENT_ID=123456789.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=GOCSPX-...Configure Atlas
Add a socialProviders block to the betterAuth() call in packages/api/src/lib/auth/server.ts, after the emailAndPassword configuration:
const instance = betterAuth({
// ... existing config (database, secret, emailAndPassword) ...
socialProviders: {
google: {
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
},
},
// ... rest of config (session, plugins, databaseHooks) ...
});Test
- Start Atlas:
bun run dev - Navigate to the login page
- Click Sign in with Google
- Complete the Google consent screen
- Verify you're redirected back and signed in
GitHub
Create an OAuth App
- Go to GitHub Settings > Developer settings > OAuth Apps
- Click New OAuth App
- Fill in the form:
- Application name: Your app name (e.g. "Atlas")
- Homepage URL: Your app URL (e.g.
https://app.example.com) - Authorization callback URL:
https://api.example.com/api/auth/callback/github
- Click Register application
- Copy the Client ID
- Click Generate a new client secret and copy it
GitHub only shows the client secret once. Copy it immediately after generating.
Set environment variables
# .env
GITHUB_CLIENT_ID=Iv1.abc123def456
GITHUB_CLIENT_SECRET=your-github-client-secretConfigure Atlas
const instance = betterAuth({
// ... existing config ...
socialProviders: {
github: {
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
},
},
});Test
- Start Atlas:
bun run dev - Navigate to the login page
- Click Sign in with GitHub
- Authorize the OAuth app on GitHub
- Verify you're redirected back and signed in
Microsoft (Entra ID)
Register an application
- Go to the Azure Portal > App registrations
- Click New registration
- Fill in the form:
- Name: Your app name (e.g. "Atlas")
- Supported account types: Choose based on your needs:
- Single tenant — Only your organization
- Multitenant — Any Microsoft work/school account
- Multitenant + personal — Any Microsoft account (default)
- Redirect URI: Select Web and enter:
https://api.example.com/api/auth/callback/microsoft
- Click Register
- Copy the Application (client) ID from the overview page
- Copy the Directory (tenant) ID if you're restricting to your organization
Create a client secret
- In your app registration, go to Certificates & secrets
- Click New client secret
- Add a description and set an expiration
- Click Add
- Copy the secret Value (not the Secret ID)
The secret value is only shown once. Copy it immediately after creation. Set a calendar reminder before expiration to rotate the secret.
Set environment variables
# .env
MICROSOFT_CLIENT_ID=12345678-abcd-efgh-ijkl-1234567890ab
MICROSOFT_CLIENT_SECRET=your-microsoft-client-secret
# Optional: restrict to a specific tenant (default: "common" = any Microsoft account)
MICROSOFT_TENANT_ID=your-tenant-idConfigure Atlas
const instance = betterAuth({
// ... existing config ...
socialProviders: {
microsoft: {
clientId: process.env.MICROSOFT_CLIENT_ID!,
clientSecret: process.env.MICROSOFT_CLIENT_SECRET!,
tenantId: process.env.MICROSOFT_TENANT_ID || "common",
},
},
});Tenant ID values:
| Value | Who can sign in |
|---|---|
common | Any Microsoft account (personal + work/school) |
organizations | Any work/school account |
consumers | Personal Microsoft accounts only |
{tenant-id} | Only accounts in that specific directory |
Test
- Start Atlas:
bun run dev - Navigate to the login page
- Click Sign in with Microsoft
- Sign in with a Microsoft account
- Verify you're redirected back and signed in
Multiple Providers
Enable any combination of providers in a single configuration:
const instance = betterAuth({
// ... existing config ...
emailAndPassword: { enabled: true },
socialProviders: {
google: {
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
},
github: {
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
},
microsoft: {
clientId: process.env.MICROSOFT_CLIENT_ID!,
clientSecret: process.env.MICROSOFT_CLIENT_SECRET!,
tenantId: process.env.MICROSOFT_TENANT_ID || "common",
},
},
});Social providers work alongside email/password. Users who sign up via a social provider can later add a password, and vice versa. Better Auth links accounts by email address when the provider returns a verified email — if a user signs in with Google using the same email they previously used for email/password signup, the accounts are linked. Google and Microsoft return verified emails by default. For GitHub, the user's primary email must be verified on GitHub for automatic linking to work.
Frontend Integration
The Atlas web UI currently supports email/password sign-in. To add social login buttons, use the Better Auth client in your frontend:
import { createAuthClient } from "better-auth/react";
const auth = createAuthClient({
baseURL: "https://api.example.com",
});
// Redirect to provider's OAuth consent screen
await auth.signIn.social({ provider: "google" });
await auth.signIn.social({ provider: "github" });
await auth.signIn.social({ provider: "microsoft" });After successful authentication, the user is redirected back to your app with an active session.
Troubleshooting
Redirect URI mismatch
Symptom: OAuth provider returns an error like "redirect_uri_mismatch" or "Invalid redirect URI".
Fix: The callback URL registered with the provider must exactly match what Better Auth generates. Check:
- Protocol:
https://vshttp://(usehttp://only forlocalhost) - Trailing slashes: most providers are strict about this
- Port: include the port for non-standard ports (e.g.
http://localhost:3001/api/auth/callback/google) BETTER_AUTH_URLmust be set correctly — Better Auth uses it to construct callback URLs
"email_not_found" error with GitHub
Symptom: GitHub sign-in fails with an email-related error.
Fix: GitHub OAuth does not return the user's email if their email is set to private. Better Auth requests the user:email scope automatically, but the user must have at least one verified, primary email on GitHub. Ask the user to:
- Go to GitHub email settings
- Ensure at least one email is verified (has a green checkmark)
- Ensure a primary email is set
User lands on wrong role after social login
Symptom: A user signs in via social provider but gets analyst instead of admin.
Explanation: Social login goes through the same bootstrap logic as email/password:
- If
ATLAS_ADMIN_EMAILmatches the social account's email →admin - If no admin exists yet → first signup gets
admin - If a pending invitation matches the email → invited role is assigned
- Otherwise →
analyst(thedefaultRolefrom the admin plugin)
Check that ATLAS_ADMIN_EMAIL matches the email the provider returns (case-insensitive).
Microsoft: "AADSTS700016" application error
Symptom: Microsoft login fails with error code AADSTS700016.
Fix: The Application (client) ID is incorrect or the app registration is not in the expected tenant. Verify:
MICROSOFT_CLIENT_IDmatches the Application (client) ID (not the Object ID)MICROSOFT_TENANT_IDmatches the directory where the app is registered (or usecommonfor multi-tenant)
Microsoft: client secret expired
Symptom: Microsoft login suddenly stops working.
Fix: Entra ID client secrets have an expiration date. Generate a new secret in Certificates & secrets and update MICROSOFT_CLIENT_SECRET. Set a reminder to rotate before the next expiration.
Session cookie not sent (cross-origin)
Symptom: Social login completes but the user appears unauthenticated on the frontend.
Fix: If your API and frontend are on different origins, ensure cross-origin cookies are configured:
ATLAS_CORS_ORIGIN=https://app.example.com
BETTER_AUTH_TRUSTED_ORIGINS=https://app.example.com
BETTER_AUTH_URL=https://api.example.comSee Cross-origin deployment for details.
Account linking
When a user signs in with a social provider using the same verified email as an existing account, Better Auth links the accounts. The user can then sign in with either method. If you see duplicate accounts, check whether:
- The social provider is returning a different email than expected (e.g. a personal vs. work Microsoft account)
- The GitHub user's email is unverified (unverified emails are not linked automatically)
Related
- Authentication — full auth mode reference including managed auth setup
- Admin Console — manage users and roles
- Row-Level Security — tenant isolation with JWT claims
- Troubleshooting — general auth troubleshooting