feat: add read-only GitHub App access alongside OAuth

This commit is contained in:
Thomas Durieux
2026-09-09 22:08:12 +02:00
parent e5c44845f0
commit a60ad6a921
49 changed files with 1754 additions and 139 deletions
+9
View File
@@ -1,4 +1,13 @@
<div class="anonymize-page h-100">
<div class="container py-3" v-if="githubConnections?.appEnabled">
<span v-if="isUpdate">Connection: {{ githubConnection === 'github-app' ? 'GitHub App (read-only)' : 'Legacy OAuth' }}. <a href="/connections">Change connection</a></span>
<span v-if="!isUpdate">Repository access:
<button class="btn" :disabled="githubConnection === 'github-app'" @click="chooseGitHubConnection('github-app')">Read-only GitHub App</button>
<button v-if="githubConnections?.oauthConnected" class="btn" :disabled="githubConnection === 'oauth'" @click="chooseGitHubConnection('oauth')">Use existing OAuth access</button>
</span>
<button class="btn btn-ink" @click="grantGitHubAccess()">{{ githubConnections?.appConnected ? 'Allow repository access on GitHub' : 'Connect read-only GitHub access' }}</button>
<p class="mb-0"><small>Return here after confirming access on GitHub. Anonymization publishes the configured content, including content from private repositories. Gists currently use OAuth.</small></p>
</div>
<!-- ===== STATE 1: No URL — centered input ===== -->
<div class="anonymize-landing" v-show="!(sourceUrl)">
<div class="anonymize-landing-inner">
+44
View File
@@ -0,0 +1,44 @@
<div class="container page paper-page">
<div class="paper-crumbs"><a href="/dashboard">My work</a> / GitHub connections</div>
<h1 class="paper-page-title">GitHub <em>connections</em></h1>
<p>Connect the GitHub account you use here. Your anonymizations and URLs stay the same.</p>
<p class="alert alert-danger" role="alert" v-if="connectionError">{{ connectionError }}</p>
<div v-if="connections">
<section class="paper-settings-section" v-if="connections.appEnabled">
<h2>Read-only GitHub App</h2>
<p v-if="connections.appError" role="alert">{{ connections.appError }}. Reconnect to restore access.</p>
<a class="btn btn-ink" target="_self" href="/github/app/login">{{ connections.appConnected ? 'Reconnect GitHub account' : 'Connect GitHub account' }}</a>
<a class="btn" v-if="connections.appConnected" target="_self" href="/github/app/install">Allow repositories on GitHub</a>
<p class="mt-2">GitHub opens directly to the access screen. Select repositories and confirm. Organization access may require an administrator's approval.</p>
<ul>
<li v-for="installation in connections.installations" :key="installation.id">
{{ installation.account }} {{ installation.suspended ? '(suspended)' : '' }} —
<a target="_self" :href="'/github/app/install?installationId=' + installation.id">Add or remove repository access</a>
</li>
</ul>
<button class="btn" :disabled="busy" @click="loadConnections()">Refresh access after approval</button>
</section>
<section class="paper-settings-section">
<h2>Legacy OAuth</h2>
<p>{{ connections.oauthConnected ? 'Connected' : 'Not connected' }}. Existing OAuth anonymizations continue to use this connection.</p>
<a v-if="connections.oauthEnabled" target="_self" href="/github/login" class="btn">Connect legacy OAuth</a>
<p v-if="connections.gistCount">{{ connections.gistCount }} gist(s) still require OAuth.</p>
<button v-if="connections.oauthConnected &amp;&amp; connections.appConnected" class="btn" :disabled="busy || connections.gistCount || connections.resources.some(r => r.connection === 'oauth')" @click="disconnectOAuth()">Revoke OAuth access</button>
<p>Migrate all dependent resources before revoking OAuth. Revocation removes GitHub's broad OAuth repository grant.</p>
</section>
<section class="paper-settings-section">
<h2>Repository connections</h2>
<p>Check read-only access, then switch each resource. Connecting the App alone does not change existing resources.</p>
<p>Removing GitHub access stops fetching new source content. Already published anonymized content follows its existing expiration and removal settings.</p>
<div v-for="resource in connections.resources" :key="resource.type + resource.id" class="mb-3">
<strong>{{ resource.id }}</strong> — {{ resource.name }} ({{ resource.type }})<br>
Connection: {{ resource.connection === 'github-app' ? 'GitHub App (read-only)' : 'Legacy OAuth' }}
<div v-if="resource.connection === 'oauth' &amp;&amp; connections.appEnabled &amp;&amp; connections.appConnected">
<button class="btn" :disabled="busy" @click="changeConnection(resource, 'github-app', true)">Check read-only access</button>
<button v-if="resource.eligible" class="btn btn-ink" :disabled="busy" @click="changeConnection(resource, 'github-app', false)">Switch to read-only access</button>
</div>
<button v-if="resource.connection === 'github-app' &amp;&amp; connections.oauthConnected" class="btn" :disabled="busy" @click="changeConnection(resource, 'oauth', false)">Switch to existing OAuth access</button>
</div>
</section>
</div>
</div>
+2
View File
@@ -12,6 +12,8 @@
</a>
</div>
<p class="mt-3"><a href="/connections">Manage GitHub connections and read-only access</a></p>
<!-- Quota -->
<div class="quota-row" v-if="quota">
<div class="quota-item" v-for="(q, index) in [
+2 -1
View File
@@ -64,7 +64,7 @@
</li>
<li class="nav-item" v-if="!user">
<a class="nav-link btn-signin" target="_self" href="/github/login" data-offset="30"><i class="fab fa-github mr-1"></i>
<a class="nav-link btn-signin" target="_self" href="/signin" data-offset="30"><i class="fab fa-github mr-1"></i>
Sign in
</a>
</li>
@@ -75,6 +75,7 @@
</a>
<div class="dropdown-menu dropdown-menu-right user-menu" aria-labelledby="navbarDropdownMenuLink">
<h6 class="dropdown-header">Signed in as @{{ user?.username }}</h6>
<a class="dropdown-item" href="/connections">GitHub connections</a>
<a class="dropdown-item" href="/profile">
<i class="fas fa-sliders-h" aria-hidden="true"></i>
<span>Settings<small>Defaults, quotas, account</small></span>
+1 -1
View File
@@ -14,7 +14,7 @@
</p>
<div class="paper-cta-row">
<a href="/github/login" target="_self" class="btn-hero" v-if="!user">
<a href="/signin" target="_self" class="btn-hero" v-if="!user">
<i class="fab fa-github mr-2" aria-hidden="true"></i>Sign in with GitHub
</a>
<a href="/anonymize" class="btn-hero" v-if="user">
+8
View File
@@ -0,0 +1,8 @@
<div class="container page paper-page">
<h1 class="paper-page-title">Connect to <em>GitHub</em></h1>
<p>Sign in to manage your anonymizations. Both connections use the same Anonymous GitHub account.</p>
<p v-if="site_options?.GITHUB_APP_ENABLED"><a class="btn btn-ink" target="_self" href="/github/app/login">Continue with read-only GitHub App</a></p>
<p v-if="site_options?.GITHUB_APP_ENABLED">Choose which repositories to allow after signing in. Private repositories are accessed with read-only permissions.</p>
<p v-if="site_options?.GITHUB_OAUTH_ENABLED"><a class="btn" target="_self" href="/github/login">Continue with legacy GitHub OAuth</a></p>
<p v-if="site_options?.GITHUB_OAUTH_ENABLED">OAuth supports existing connections and gists. GitHub's private repository OAuth scope includes write access.</p>
</div>