GitHub OIDC token exchange endpoint#1569
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
🦋 Changeset detectedLatest commit: d1ef925 The changes in this PR will be included in the next version bump. This PR includes changesets to release 8 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
Claude finished @miles-kt-inkeep's task —— View job 🔍 PR Review: GitHub OIDC Token Exchange Endpoint🎯 OverviewThis PR implements a secure GitHub OIDC token exchange endpoint that allows GitHub Actions to obtain installation access tokens through the GitHub App authentication flow. The implementation is well-architected and follows security best practices. ✅ Strengths & Well-Designed ElementsExcellent Security Architecture
Robust Error Handling
High-Quality Testing
🔧 Architectural Feedback & Improvements1. Environment Configuration PatternPriority: Low The configuration pattern is inconsistent with existing auth patterns in the codebase: // Current approach - throws on missing config
export function getGitHubAppConfig(): GitHubAppConfig {
// ... throws Error if not configured
}
// Existing pattern from playgroundToken.ts
if (!env.INKEEP_AGENTS_TEMP_JWT_PRIVATE_KEY) {
throw createApiError({ code: 'internal_server_error', ... });
}Recommendation: Consider using the existing 2. Cache Management & Memory LeaksPriority: Medium The current JWKS cache implementation is module-scoped and persistent: // agents-api/src/domains/github/jwks.ts:16
let jwksCache: JwksCache | null = null;Concerns:
Recommendations: // Add periodic cleanup or size limits
const MAX_CACHE_SIZE = 10;
const cleanupExpiredEntries = () => { /* cleanup logic */ };
// Or consider using the existing caching patterns from the codebase3. API Design & Future ExtensibilityPriority: Low-Medium Current request/response schema is minimal but may limit future extensibility: // Current
const TokenExchangeRequestSchema = z.object({
oidc_token: z.string().describe('GitHub Actions OIDC token to exchange'),
});Considerations for future:
4. Rate Limiting & Abuse PreventionPriority: Medium The endpoint lacks rate limiting, which could be important for:
Recommendation: Consider adding rate limiting middleware consistent with other API endpoints. 🔒 Security ReviewExcellent Security Posture
Architecture Security
🏗️ Code Quality & PatternsFollows Repository Standards
Minor Code Quality Suggestions
// Could be shared across GitHub domain
type GitHubApiError = { errorType: 'api_error' | 'jwt_error'; message: string; };
// agents-api/src/domains/github/constants.ts
export const GITHUB_API_BASE = 'https://api.github.com';
export const GITHUB_OIDC_ISSUER = 'https://token.actions.githubusercontent.com';🧪 Testing ExcellenceThe testing approach is exemplary:
🚀 Integration & DeploymentEnvironment SetupThe
Backward Compatibility
📋 Missing Elements (Repository Requirements)According to
Note: This appears to be a backend API feature that may not require UI components, but user-facing documentation for the GitHub App setup and token exchange process would be valuable. 🎯 Final AssessmentThis is a high-quality implementation that demonstrates:
The code is ready for production with only minor architectural considerations for future enhancement. The security implementation is particularly commendable and follows industry best practices for OIDC token validation and GitHub App authentication. Recommendation: ✅ Approve with minor suggestions for future iteration |
- Add POST endpoint at /api/github/token-exchange - Accept JSON body with oidc_token field - Return JSON response with token, expires_at, repository, installation_id - Route is unauthenticated (OIDC token is the authentication) - Add RFC 7807 problem details schema for error responses Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Create config module to load GITHUB_APP_ID and GITHUB_APP_PRIVATE_KEY - Add startup validation with warning log if credentials not configured - Update token exchange route to return 500 if credentials missing - Document environment variables in .env.example - Private key is never logged or exposed in error messages Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implement JWKS fetch and caching module for GitHub OIDC token verification: - Fetch JWKS from https://token.actions.githubusercontent.com/.well-known/jwks - Cache JWKS with 1 hour TTL to avoid fetching on every request - Handle JWKS fetch failures gracefully with clear error messages - Refresh cache when key ID (kid) doesn't match cached keys - Add jose dependency for JWT/JWK operations Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add oidcToken.ts module that validates GitHub Actions OIDC tokens: - Verify JWT signature using GitHub's JWKS (RS256) - Validate issuer claim equals https://token.actions.githubusercontent.com - Validate audience claim equals inkeep-agents-action - Validate token expiration - Extract claims: repository, repository_owner, repository_id, workflow, actor, ref - Return 401 Unauthorized with clear message for invalid/expired tokens Update tokenExchange route to use validation and return proper error responses. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add installation lookup module that:
- Creates RS256-signed JWT for GitHub App authentication
- Calls GitHub API /repos/{owner}/{repo}/installation endpoint
- Returns 403 Forbidden if App not installed on repository
- Returns 500 on API errors or JWT creation failures
- Exports types for installation info and result types
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add generateInstallationAccessToken function that creates short-lived
installation tokens for GitHub Actions to access repository data.
- Create RS256-signed JWT for GitHub App authentication (10 min expiry)
- Call POST /app/installations/{id}/access_tokens GitHub API endpoint
- Return token and expiration timestamp on success
- Return 500 error with RFC 7807 format on API or JWT failures
- Update tokenExchange route to generate real tokens instead of placeholder
- Export new types from github domain index
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Added defaultHook to tokenExchange route for RFC 7807 formatted validation errors with 'error' field as required by OpenAPI schema - Return 400 Bad Request for malformed oidc_token (JWT format issues) - Return 401 Unauthorized for authentication failures (invalid signature, expired, wrong issuer, wrong audience) - All error responses now include 'error' field with human-readable message - All error responses use application/problem+json content type Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add testJwt.ts module providing helpers for testing the GitHub OIDC token exchange endpoint. Includes RS256 key pair generation with caching, configurable JWT creation with custom claims, helpers for expired tokens and wrong issuer/audience, malformed token generators, and JWKS mock response helpers. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Comprehensive tests for the GitHub OIDC token exchange endpoint covering success case (200), validation errors (400), authentication failures (401), forbidden errors (403), and internal server errors (500). Uses vi.hoisted pattern for mocking GitHub JWKS fetch and App API calls. Utilizes test JWT utilities from US-008 for generating valid, expired, wrong-issuer, and malformed tokens. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
d32d45c to
6098eb9
Compare
No description provided.