Skip to content

Conversation

@mattgodbolt
Copy link
Member

Summary

Replace personal access token authentication with a GitHub App so "This is now live" notifications appear from a bot identity (compiler-explorer-bot[bot]) rather than an individual user.

Changes

  • Add github_app.py module for JWT generation and installation token exchange
  • Update notify.py to use Bearer authentication (required for GitHub App tokens)
  • Update blue_green.py to use get_github_app_token() instead of the old PAT
  • Add PyJWT[crypto] dependency for JWT signing

Testing

GitHub App and SSM parameters have been configured. Verified:

  1. Token retrieval - Successfully generates JWT and exchanges for installation token:

    Success! Got token: ghs_GqTNUQ2P8YL0WTgS...
    
  2. API access - Token works for GitHub API calls:

    PR #8304 title: [bot] Update browsers list
    API call with GitHub App token: SUCCESS
    
  3. Full notification flow - Dry-run shows correct behavior:

    Checking for live notifications from 26da446c... to 64a2ed40...
    [DRY RUN] Would notify PR #8304
    [DRY RUN] Would add 'live' label to compiler-explorer/compiler-explorer#8304
    [DRY RUN] Would comment 'This is now live' on compiler-explorer/compiler-explorer#8304
    
  4. Unit tests - All 458 tests pass, with new tests for the GitHub App module

Configuration

The following SSM parameters have been set up:

  • /compiler-explorer/github-app-id - App ID
  • /compiler-explorer/github-app-private-key - Private key (SecureString)

🤖 Generated with Claude Code

mattgodbolt and others added 2 commits November 30, 2025 19:26
Replace personal access token authentication with GitHub App so
notifications appear from a bot identity rather than an individual user.

Changes:
- Add github_app.py module for JWT generation and token exchange
- Update notify.py to use Bearer authentication
- Update blue_green.py to use get_github_app_token()
- Add PyJWT dependency for JWT signing

Manual setup (already done):
- Created GitHub App with Issues/PRs read/write permissions
- Stored App ID and private key in SSM parameters

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Convert from unittest.TestCase to plain functions
- Use pytest.raises instead of try/except

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@mattgodbolt
Copy link
Member Author

How JWT Authentication Works Here

GitHub Apps use a two-step authentication flow. This PR implements that flow.

Why JWT?

GitHub needs to verify that requests actually come from our app, not an impersonator. The app's private key (stored in SSM) is the proof of identity - only we have it.

The Authentication Flow

sequenceDiagram
    participant CE as CE Deploy Script
    participant SSM as AWS SSM
    participant GH as GitHub API

    CE->>SSM: Get app ID and private key
    SSM-->>CE: Credentials
    
    Note over CE: Generate JWT<br/>(signed with private key)
    
    CE->>GH: GET /app/installations<br/>Authorization: Bearer <jwt>
    Note over GH: Verify JWT signature<br/>using app's public key
    GH-->>CE: List of installations
    
    Note over CE: Find compiler-explorer<br/>installation ID
    
    CE->>GH: POST /app/installations/{id}/access_tokens<br/>Authorization: Bearer <jwt>
    GH-->>CE: Installation token (1 hour validity)
    
    CE->>GH: POST /repos/.../issues/.../comments<br/>Authorization: Bearer <token>
    Note over GH: Posts as<br/>compiler-explorer-bot[bot]
Loading

JWT Structure

A JWT is three base64-encoded parts separated by dots: header.payload.signature

  • Header: {"alg": "RS256", "typ": "JWT"} - says "this is signed with RSA"
  • Payload: {"iat": 1234567890, "exp": 1234568490, "iss": "12345"} - the claims
  • Signature: RSA signature of header.payload using our private key

GitHub has our public key (uploaded when the app was created). When we send a JWT, GitHub:

  1. Decodes the header and payload
  2. Verifies the signature using our public key
  3. Checks the claims (not expired, app ID exists, etc.)

If verification passes, GitHub knows the request is legitimately from our app.

Why RS256?

RS256 is asymmetric encryption - different keys for signing vs verifying. We keep the private key secret; GitHub has the public key. This means GitHub can verify our requests without ever having our secret.

Copilot finished reviewing on behalf of mattgodbolt December 1, 2025 01:54
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR replaces personal access token (PAT) authentication with GitHub App authentication for "This is now live" notifications. This allows notifications to appear from a bot identity (compiler-explorer-bot[bot]) rather than an individual user, improving the professionalism and maintainability of automated notifications.

Key changes:

  • New GitHub App authentication module with JWT generation and installation token exchange
  • Updated GitHub API authentication from token to Bearer format (required for GitHub Apps)
  • SSM parameter-based credential management for App ID and private key

Reviewed changes

Copilot reviewed 1 out of 5 changed files in this pull request and generated no comments.

Show a summary per file
File Description
pyproject.toml Adds PyJWT[crypto] dependency for RSA-based JWT signing
bin/lib/github_app.py New module implementing GitHub App authentication flow (JWT generation, installation lookup, token exchange)
bin/test/github_app_test.py Comprehensive test coverage for the new GitHub App module with unit tests for all major functions
bin/lib/notify.py Updates Authorization header format from token to Bearer for GitHub App compatibility
bin/lib/cli/blue_green.py Replaces PAT retrieval with GitHub App token; removes old error handling and fallback logic

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

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