-
Notifications
You must be signed in to change notification settings - Fork 0
fix: enable private repo subscriptions via OAuth token #57
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Fixes the issue where users couldn't subscribe to private org repos because validateRepository() was called before checking GitHub App installation. Private repos require the app to be installed before the user's OAuth token can access them. Root cause: - validateRepository() with user OAuth token fails (404) for private repos - This happened before installation check, causing incorrect error flow - User couldn't complete subscription even after installing the app Solution - Remove OAuth scope field (GitHub Apps don't use scopes): - Remove scope column from github_user_tokens schema - Remove scope parameter from OAuth authorization (caused TypeScript errors) - Remove scope storage logic (was checking non-existent field) - Update docs: "OAuth app" → "GitHub App" - Generate migration with proper snapshots (0000, 0001, 0002) Solution - Fix installation URL for private repos: - Add getOwnerIdFromUsername() to fetch owner ID from public profile - Use real owner ID instead of hardcoded 0 in installation URLs - Fall back to /installations/new if lookup fails (manual account selection) - Reorder validation: check installation before user access - Improve error messages (remove duplication) Result: - Private org repo subscriptions now work correctly - Installation URLs no longer show 404 errors - Better error handling and user feedback - Proper GitHub App permission model implemented Database changes: - Migration 0002: DROP COLUMN scope from github_user_tokens - Retroactively created snapshots for migrations 0000 and 0001 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
WalkthroughThis PR removes the Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Possibly related PRs
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
src/services/github-oauth-service.ts (1)
45-63: Updated GitHub App token docs look good; minor wording nit around 403 loggingThe new docstring accurately describes the GitHub App user access vs installation tokens and how this service uses them, which is helpful given the mixed auth model.
One small follow‑up you might consider (non‑blocking):
validateToken’s 403 log still says “insufficient scope, rate limit, or lockout”, but scopes are no longer central here; renaming that to something like “insufficient permissions, rate limit, or lockout” would better match the GitHub App framing you describe above.src/api/user-oauth-client.ts (1)
103-132:getOwnerIdFromUsernamehelper is robust with a safe fallbackThe org‑first then user fallback strategy and outer try/catch give you a good chance of resolving the owner ID, while returning
undefinedon failure cleanly supports the “no target_id” install URL path. The console warning on failure should be enough for debugging without impacting user flow.If this ends up on a hot path, you might later consider caching owner IDs to avoid repeated lookups, but it’s not necessary for correctness.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
drizzle/0002_zippy_celestials.sql(1 hunks)drizzle/meta/0000_snapshot.json(1 hunks)drizzle/meta/0001_snapshot.json(1 hunks)drizzle/meta/0002_snapshot.json(1 hunks)drizzle/meta/_journal.json(1 hunks)src/api/user-oauth-client.ts(2 hunks)src/db/schema.ts(0 hunks)src/services/github-oauth-service.ts(1 hunks)src/services/subscription-service.ts(3 hunks)
💤 Files with no reviewable changes (1)
- src/db/schema.ts
🧰 Additional context used
🧬 Code graph analysis (1)
src/services/subscription-service.ts (1)
src/api/user-oauth-client.ts (3)
RepositoryInfo(6-14)validateRepository(32-76)getOwnerIdFromUsername(110-132)
🔇 Additional comments (8)
drizzle/0002_zippy_celestials.sql (1)
1-1: Migration droppinggithub_user_tokens.scopeis consistent with schema changesThe migration cleanly removes the
scopecolumn and aligns with the updated model and snapshots; no dependent indexes/constraints are present, so this looks safe.drizzle/meta/0002_snapshot.json (1)
245-333: Snapshot 0002 correctly reflects removal ofgithub_user_tokens.scopeThe 0002 snapshot’s
github_user_tokensdefinition matches the migration (noscopecolumn) and keeps existing PK/unique index structure intact, so the snapshot looks consistent.drizzle/meta/_journal.json (1)
18-24: Journal entry for0002_zippy_celestialsis well-formedThe added journal entry (idx 2 / tag
0002_zippy_celestials) is consistent with the new migration and snapshots and keeps the index sequence contiguous.drizzle/meta/0000_snapshot.json (1)
1-624: Baseline snapshot 0000 is consistent and self-containedThis snapshot cleanly captures the initial schema (including
github_user_tokens.scope) and matches the evolution implied by later snapshots and migrations.drizzle/meta/0001_snapshot.json (1)
1-655: Intermediate snapshot 0001 matches the expected schema evolutionThe 0001 snapshot accurately reflects the schema (including
scope) with the additional constraints/indexes and forms a coherent step between 0000 and 0002.src/api/user-oauth-client.ts (1)
54-72: Wrapped 404/403 errors withcauseimprove upstream diagnosticsUsing
new Error(message, { cause: error })for 404/403 along with more specific messages is a solid improvement and enables callers (like the subscription service) to inspectcause.statuswithout losing the original error.Just make sure your runtime/TS target supports
Erroroptions andcause(e.g., Node >= 16 and appropriatelibsettings), so this compiles and behaves as expected.src/services/subscription-service.ts (2)
136-183: Reordered install check and refined 404/403 handling correctly align with GitHub App behaviorChecking
installationIdbefore callingvalidateRepository, then usingerror.cause?.statuslets you distinguish “private repo without installation” (404 + no installation) from other failures and return arequiresInstallationresult with a targeted install URL viagetOwnerIdFromUsername. The additional 403/org detection based onrepoIdentifiervsgithubUser.loginalso reads well.One minor thing to keep in mind: for a genuinely non‑existent repo you’ll still surface a “might need installation” path on 404 + no installation, but that ambiguity already exists at the API level and your generic error message reflects it. Overall the flow looks sound for the private‑org‑repo case this PR is fixing.
457-467: OptionaltargetIdinstall URL generation matches new call sitesAllowing
generateInstallUrlto omittargetIdand fall back to the baseinstallations/newURL fits the new error path wheregetOwnerIdFromUsernamecan returnundefined. The existing call sites that pass a concrete owner ID still get the.../permissions?target_id=...variant, so behavior remains correct while adding a safe fallback.
Change 'insufficient scope' to 'insufficient permissions' since GitHub Apps don't use OAuth scopes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/services/github-oauth-service.ts (1)
46-63: Doc updates correctly reflect GitHub App model; consider clarifying the “does NOT read repository contents” guaranteeThe expanded comment nicely distinguishes user access vs installation tokens and aligns with the new permission model. To avoid suggesting a GitHub‑wide guarantee, you might want to phrase that bullet as an app‑level usage guarantee rather than a capability guarantee, e.g.:
- * - Does NOT read repository contents or receive webhook events + * - In this app, not used to read repository contents or receive webhook events
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/services/github-oauth-service.ts(2 hunks)
🔇 Additional comments (1)
src/services/github-oauth-service.ts (1)
364-369: 403 handling message now correctly avoids “scope” terminologyUpdating the 403 comment and log from “insufficient scope” to “insufficient permissions” keeps the behavior the same while matching the new GitHub App permission model and removal of scopes from storage. No further changes needed here.
Fixes the issue where users couldn't subscribe to private org repos because validateRepository() was called before checking GitHub App installation. Private repos require the app to be installed before the user's OAuth token can access them.
Root cause:
Solution - Remove OAuth scope field (GitHub Apps don't use scopes):
Solution - Fix installation URL for private repos:
Result:
Database changes:
🤖 Generated with Claude Code
Summary by CodeRabbit
Bug Fixes
Improvements
New Features
Documentation
✏️ Tip: You can customize this high-level summary in your review settings.