Skip to content

feat: GitHub Enterprise Server (GHES) support#326

Open
raghavpillai wants to merge 5 commits intobetter-auth:mainfrom
raghavpillai:feat/ghes-support
Open

feat: GitHub Enterprise Server (GHES) support#326
raghavpillai wants to merge 5 commits intobetter-auth:mainfrom
raghavpillai:feat/ghes-support

Conversation

@raghavpillai
Copy link
Copy Markdown

Summary

Adds support for self-hosted GitHub Enterprise Server instances. When configured via environment variables, all GitHub API calls, OAuth flows, avatar loading, and UI links point at the GHES host instead of github.com. When no GHES env vars are set, behavior is identical to today.

Closes #325

Problem

Every GitHub URL in the codebase is hardcoded to github.com / api.github.com. This makes it impossible to use Better Hub with a GHES instance. GHES also has some quirks — /api/v3 and /api/graphql live on the same host, and private-mode instances require web session cookies (not API tokens) to fetch avatar images.

Changes

Centralized GitHub URL config (src/lib/github-config.ts)

  • New module that reads GITHUB_WEB_URL, GITHUB_API_URL, GITHUB_GRAPHQL_URL from env vars
  • Exports IS_GHES, GITHUB_HOSTNAME, and URL constants
  • Falls back to github.com defaults when env vars are unset

Generic OAuth (src/lib/auth.ts, src/lib/auth-client.ts, src/components/login-button.tsx)

  • Replaced socialProviders.github with genericOAuth plugin so authorization and token endpoints can be configured per-instance
  • getUserInfo fetches from the configurable API URL with response status validation
  • Login button updated to use signIn.oauth2({ providerId: "github" })

GHES avatar proxy (src/app/api/github-avatar/route.ts, src/components/shared/ghes-image.tsx)

  • New server-side route that authenticates with the GHES web UI, caches the session cookie (4h TTL), and proxies avatar images
  • Automatic session refresh on failed fetches — if the cached session expires, re-authenticates and retries once before falling back to a colored SVG placeholder
  • GhesImage component wraps next/image to transparently route GHES avatar URLs through the proxy
  • githubAvatarUrl() helper exported for raw <img> tags

UI updates (~60 component files)

  • All hardcoded https://github.com references replaced with configurable GITHUB_WEB_URL / NEXT_PUBLIC_GITHUB_WEB_URL
  • Covers links, breadcrumbs, "Open in GitHub" buttons, copy-link, command menu, security advisories, release download URLs, etc.
  • next.config.ts updated to add GHES hostname to images.remotePatterns
  • GitHub link interceptor and proxy rewrite updated to use configurable hostname

Other

  • Octokit instances in src/lib/github.ts now use configurable baseUrl and GraphQL endpoint
  • .env.example updated with GHES configuration variables
  • Redis cache in getOctokitUser fixed — removed double JSON serialization that was causing cache misses

Configuration

# All optional — defaults to github.com when unset
NEXT_PUBLIC_GITHUB_WEB_URL=https://ghes.example.com
GITHUB_WEB_URL=https://ghes.example.com
GITHUB_API_URL=https://ghes.example.com/api/v3
GITHUB_GRAPHQL_URL=https://ghes.example.com/api/graphql

# Only needed for GHES private-mode avatar proxying
GHES_AVATAR_USERNAME=bot-account
GHES_AVATAR_PASSWORD=password

Validation

  • Deployed and tested against a live GHES 3.x instance in private mode
  • OAuth sign-in flow works end to end (authorize → callback → session)
  • Avatar proxying works for private-mode instances with automatic session refresh
  • All UI links correctly point at the GHES host
  • Verified that no env vars = unchanged github.com behavior
  • bun run build passes cleanly

Notes

  • The genericOAuth plugin is a drop-in replacement for socialProviders.github — the OAuth flow is identical, just with configurable endpoints
  • The avatar proxy is only active when IS_GHES is true, so there's zero overhead for github.com users
  • Remaining github.com references in the diff are intentional defaults/fallbacks in github-config.ts and next.config.ts

- Introduce github-config to centralize GitHub/GHES endpoint configuration
- Replace hardcoded github.com URLs with configurable GITHUB_* constants
- Add GHES-aware image component (ghes-image) and avatar proxy endpoint (/api/github-avatar)
- Proxy/rewrote raw, API, GraphQL, and web endpoints to use config
- Wire GHES-aware avatar handling via github-avatar helper and githubAvatarUrl
- Use GITHUB_* URLs across many components, pages, and API routes
- Add generic OAuth provider integration and update auth plugin usage
- Add GHES session login flow to fetch avatar images requiring cookie auth
- Swap next/image imports to GHES-aware component where needed
- Small misc: caching, error logging, and auth client plugin adjustments
- Change mapProfileToUser return to an explicit Record<string, unknown>
- Wrap returned object in parentheses and add type assertion to satisfy typings
- No functional change, resolves TypeScript type inference issues
- Change API error responses to JSON for github-avatar route
- Retry GHES avatar fetch once when session may have expired and refresh cookies
- Remove redundant HTML-response session-clear and use retry logic instead
- Return JSON on fetch failure for consistent error format
- Fix redis caching call to store raw user data instead of stringified JSON
- Add error throw when GitHub /user response is not ok before parsing JSON
@vercel
Copy link
Copy Markdown

vercel Bot commented Mar 15, 2026

@raghavpillai is attempting to deploy a commit to the better-auth Team on Vercel.

A member of the Team first needs to authorize it.

- Remove deprecated ghes-image component
- Add new github-image component that proxies GHES avatar URLs when needed
- Update numerous imports across app to use github-image instead of ghes-image
- Preserve proxy behavior and Next/Image passthrough for non-GHES hosts
- Replace manual fetch calls with Octokit client for GitHub API
- Use octokit.users.getAuthenticated and listEmailsForAuthenticatedUser
- Determine primary verified email and fallback to available emails
- Preserve returned user shape (id, name, email, image, login)
@ping-maxwell
Copy link
Copy Markdown
Member

Hey this is awesome work!

I'm keeping your PR open for now until we decide where Better Hub will go in terms of GHES support.
I can see pretty much all you've changed is the URL to GH endpoints, but we dont use GHES so it's hard to test and maintain.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

GitHub Enterprise Server (GHES) support

2 participants