GitHub OAuth Setup Guide
This guide walks you through creating a GitHub OAuth App and configuring it for izri.
1. Create a GitHub OAuth App
- Go to GitHub Developer Settings → OAuth Apps
- Click "New OAuth App"
- Fill in the form:
| Field | Value |
|---|---|
| Application name | izri (dev) (or any name) |
| Homepage URL | http://localhost:5173 |
| Authorization callback URL | http://localhost:4000/api/auth/callback/github |
- Click "Register application"
- Copy your Client ID
- Click "Generate a new client secret" and copy the Client Secret
⚠️ The client secret is only shown once. Store it safely.
2. Configure Environment Variables
In your .env.shared file (copy from .env.shared.example if it doesn't exist):
# GitHub OAuth
GITHUB_CLIENT_ID=your_client_id_here
GITHUB_CLIENT_SECRET=your_client_secret_here
Then regenerate app env files:
pnpm env:generate
3. Callback URL Reference
| Environment | Callback URL |
|---|---|
| Development | http://localhost:4000/api/auth/callback/github |
| Production | https://your-domain.com/api/auth/callback/github |
The path /api/auth/callback/github is automatically handled by Better Auth's GitHub social provider.
4. Required Scopes
Better Auth requests these scopes by default when using the GitHub provider:
| Scope | Purpose |
|---|---|
read:user |
Read user profile (name, avatar, email) |
user:email |
Read user's email address |
For GitHub repository integration (repo analysis features), additional scopes are needed:
| Scope | Purpose |
|---|---|
repo |
Read access to repositories |
read:org |
Read organization membership |
To request additional scopes, configure the GitHub provider in packages/auth/src/better-auth.ts:
socialProviders: {
github: {
clientId: env.GITHUB_CLIENT_ID,
clientSecret: env.GITHUB_CLIENT_SECRET,
scope: ['read:user', 'user:email', 'repo', 'read:org'],
},
},
5. How OAuth Flow Works
User clicks "Sign in with GitHub"
→ Frontend redirects to /api/auth/signin/github
→ Better Auth redirects to GitHub OAuth page
→ User authorizes
→ GitHub redirects to /api/auth/callback/github
→ Better Auth validates code, fetches user info
→ Creates/updates user in DB (users table)
→ Creates account record (accounts table) with accessToken
→ Creates session (sessions table)
→ Frontend receives session cookie
6. Token Storage
After successful OAuth, Better Auth stores:
accounttable:accessToken,accessTokenExpiresAt,scope,providerId = "github",accountId = github_user_idsessiontable: Session token and expiryusertable: User profile data (name, email, image)
To retrieve a user's GitHub access token:
import { db, accounts } from '@izri/database'
import { eq, and } from 'drizzle-orm'
const githubAccount = await db.query.accounts.findFirst({
where: and(
eq(accounts.userId, userId),
eq(accounts.providerId, 'github')
),
})
const githubToken = githubAccount?.accessToken
7. Testing the OAuth Flow Locally
- Start all services:
pnpm dev - Navigate to
http://localhost:5173/auth/login - Click "Sign in with GitHub"
- Authorize the app on GitHub
- You should be redirected back and logged in
8. Production Setup
For production, create a separate GitHub OAuth App with:
- Homepage URL:
https://your-domain.com - Callback URL:
https://your-domain.com/api/auth/callback/github
Update environment variables in your production environment accordingly.
Staging OAuth App
Create a separate GitHub OAuth App for the staging environment:
| Field | Value |
|---|---|
| Application name | izri (staging) |
| Homepage URL | https://<staging-web-public-domain> |
| Authorization callback URL | https://<staging-api-public-domain>/api/auth/callback/github |
Set GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET on the staging Railway services.
Troubleshooting
"redirect_uri_mismatch" error
The callback URL in your GitHub OAuth App settings doesn't match what Better Auth is sending. Verify API_URL in your .env.shared matches the registered callback URL host.
"Bad credentials" error
Your GITHUB_CLIENT_ID or GITHUB_CLIENT_SECRET is incorrect. Double-check the values in GitHub Developer Settings.
User not created in database
Check DATABASE_URL is correct and migrations have been run (pnpm db:migrate).