fix(github): improve GitHub App setup and installation flow

- resolve the GitHub App by a stable identifier during installation
  callbacks so installing and re-installing keeps working over the
  full lifetime of the App
- verify the installation id received from the callback against the
  GitHub API before persisting it
- support re-installing an already configured GitHub App instead of
  blocking it
- require an authenticated session and rate limit the setup callback
  routes
- extend manifest setup state validity to match GitHub's manifest
  code lifetime

Adds feature coverage for the GitHub App setup and installation
callbacks.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Andras BacsaiandClaude Opus 4.7 committed 2026-05-22 16:34:36 +02:00
1 parent fcd63f40eb
commit 5a7408a919
5 files changed
+413 -45

No files matched your search

+23
View File
@@ -7,7 +7,9 @@ use App\Models\GithubApp;
use App\Models\PrivateKey;
use App\Rules\SafeExternalUrl;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Str;
use Lcobucci\JWT\Configuration;
use Lcobucci\JWT\Signer\Key\InMemory;
use Lcobucci\JWT\Signer\Rsa\Sha256;
@@ -72,6 +74,8 @@ class Change extends Component
public $privateKeys;
public string $manifestState = '';
protected function rules(): array
{
return [
@@ -147,6 +151,24 @@ class Change extends Component
}
}
private function githubAppSetupStateCacheKey(string $state): string
{
return 'github-app-setup-state:'.hash('sha256', $state);
}
private function createGithubAppSetupState(string $action): string
{
$state = Str::random(64);
Cache::put($this->githubAppSetupStateCacheKey($state), [
'action' => $action,
'github_app_id' => $this->github_app->id,
'team_id' => $this->github_app->team_id,
], now()->addMinutes(60));
return $state;
}
public function checkPermissions()
{
try {
@@ -211,6 +233,7 @@ class Change extends Component
// Override name with kebab case for display
$this->name = str($this->github_app->name)->kebab();
$this->fqdn = $settings->fqdn;
$this->manifestState = $this->createGithubAppSetupState('manifest');
if ($settings->public_ipv4) {
$this->ipv4 = 'http://'.$settings->public_ipv4.':'.config('app.port');