Skip to content

feat: add private repo support via GitHub App#23

Merged
KerwinTsaiii merged 12 commits intodevelopfrom
feature/private-repo-support
Feb 27, 2026
Merged

feat: add private repo support via GitHub App#23
KerwinTsaiii merged 12 commits intodevelopfrom
feature/private-repo-support

Conversation

@MioYuuIH
Copy link
Contributor

Summary

  • Add private repository cloning support through GitHub App integration
  • Users can browse and select repos from their GitHub App installations directly in the spawn UI
  • Automatic OAuth token refresh for GitHub App user-to-server tokens (8h expiry)
  • Prevent username prefix spoofing in multi-auth mode

Changes

Private Repo Support

  • Add GitHub App OAuth callback handler with graceful setup_action redirect
  • Add /hub/api/github/repos API to list accessible repos from all GitHub App installations
  • Add repo picker dropdown in spawn UI (searchable, grouped by owner)
  • Inject GitHub access token into git-clone init container for private repo access
  • Support custom.gitClone.defaultToken for org-level default tokens
  • Add secret-git-token.yaml Helm template for token storage

OAuth Token Refresh

  • Store expires_at timestamp in auth_state during authentication
  • Add refresh_user() to CustomGitHubOAuthenticator with proactive refresh (10min before expiry)
  • Add refresh_user() delegation in CustomMultiAuthenticator to route refresh calls to the correct sub-authenticator
  • Set auth_refresh_age=3600 to check token freshness every hour
  • Compatible with GitHub Apps that have token expiration disabled (no refresh_token → skip entirely)

Username Prefix Spoofing Prevention

  • Add validate_username() to CustomMultiAuthenticator to reject unknown prefixes via Admin API
  • Block : in local account usernames in FirstUseAuthenticator to prevent prefix collision

UI Improvements

  • Repo picker dropdown floats above parent containers (z-index, overflow fixes)
  • Searchable repo list with GitHub App install link always visible

Documentation

  • Update GitHub App setup guide with new configuration options
  • Update multi-node example config with private repo and token settings

Files Changed

Backend

  • runtime/hub/core/authenticators/github_oauth.py — OAuth callback handler, token refresh
  • runtime/hub/core/authenticators/multi.pyrefresh_user delegation, validate_username
  • runtime/hub/core/authenticators/firstuse.py — block : in local usernames
  • runtime/hub/core/handlers.py — GitHub repos API endpoint
  • runtime/hub/core/spawner/kubernetes.py — inject access token into git-clone
  • runtime/hub/core/config.pydefaultToken config field
  • runtime/hub/core/setup.pyauth_refresh_age setting
  • runtime/hub/core/scripts/git-clone.sh — token-based auth for private repos

Frontend

  • runtime/hub/frontend/apps/spawn/ — repo picker UI
  • runtime/hub/frontend/packages/shared/src/api/git.ts — GitHub repos API client
  • runtime/hub/frontend/packages/shared/src/utils/user.ts — user helper utilities

Configuration

  • runtime/values.yaml — GitHub App and token settings
  • runtime/values-multi-nodes.yaml.example — updated example config
  • runtime/chart/templates/hub/secret-git-token.yaml — new Helm template
  • runtime/chart/values.schema.yaml — schema updates

Documentation

  • docs/jupyterhub/How_to_Setup_GitHub_OAuth.md — GitHub App setup guide
  • docs/jupyterhub/README.md — updated configuration reference

Testing

  • GitHub App OAuth login and repo listing
  • Private repo cloning via spawn UI
  • Token refresh with token expiration enabled (verified token auto-renewal)
  • Token refresh skipped when expiration disabled (NV20 compatible)
  • Username prefix spoofing blocked for local accounts
  • Repo picker dropdown overflow and positioning
  • Tested on local Kubernetes cluster

Checklist

  • Code follows project style guidelines (ruff format)
  • Changes are backward compatible
  • Tested on local Kubernetes cluster
  • Documentation updated

…t token

- Support user-provided PAT for cloning private repos via access token input
- GitHub App integration: OAuth token auto-used for private repo access when
  githubAppName is configured, with repo picker dropdown for installed apps
- Default access token via values.yaml (Helm auto-creates K8s Secret)
- Token priority: user PAT > OAuth token > defaultAccessToken
- Multi-strategy repo validation: GitHub REST API > dulwich with token > dulwich bare
- git-clone.sh uses dynamic insteadOf URL rewriting for credential injection
- Unified isGitHubUser utility in shared package for consistent auth checks
- Full dark mode support for repo picker, token input, and app install prompt
- GitHub App install prompt only shown to GitHub OAuth users
- Remove access token input field from spawn UI (security concern)
- Token sources now: OAuth token (GitHub App) > defaultAccessToken only
- Users needing other private repos can use git directly in notebook
- Handle GitHub App post-install redirect (setup_action=install without
  state) by redirecting to spawn page instead of 400 error
- Simplify GitHub App install prompt with GitHub logo
- Rewrite OAuth setup docs to use GitHub App instead of legacy OAuth App
- Add migration guide from OAuth App to GitHub App with comparison table
- Add image placeholders for GitHub App setup screenshots
- Reorder gitClone config in values.yaml (private repo settings first)
- Add comprehensive inline documentation for gitClone options
Add gitClone, accelerators, metadata, quota, localAccounts sections.
Update OAuth config to GitHub App style with scope: [].
Remove obsolete extraConfig template code.
GitHub App user-to-server tokens expire after 8 hours. Add proactive
token refresh that automatically renews tokens before expiry, so users
don't need to re-login.

- Store expires_at timestamp in auth_state during authentication
- Add refresh_user() to CustomGitHubOAuthenticator with proactive
  refresh when token is within 10 minutes of expiry
- Add refresh_user() delegation in CustomMultiAuthenticator to route
  refresh calls to the correct sub-authenticator
- Set auth_refresh_age=3600 to check token freshness every hour
- Compatible with GitHub Apps that have token expiration disabled
  (no refresh_token → skip refresh entirely)
Block local accounts from using usernames containing ':' to prevent
prefix collision with OAuth authenticators. Two layers of defense:
- MultiAuthenticator.validate_username rejects unknown prefixes via API
- FirstUseAuthenticator.authenticate rejects all ':' in local logins
@KerwinTsaiii KerwinTsaiii merged commit c6fd990 into develop Feb 27, 2026
4 checks passed
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.

2 participants