Skip to content

GitHub OIDC token exchange endpoint#1569

Merged
miles-kt-inkeep merged 15 commits intomainfrom
feat/github-oidc-token-exchange
Jan 26, 2026
Merged

GitHub OIDC token exchange endpoint#1569
miles-kt-inkeep merged 15 commits intomainfrom
feat/github-oidc-token-exchange

Conversation

@miles-kt-inkeep
Copy link
Copy Markdown
Contributor

No description provided.

@vercel
Copy link
Copy Markdown

vercel Bot commented Jan 23, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
agents-api Ready Ready Preview, Comment Jan 26, 2026 5:20pm
agents-docs Ready Ready Preview, Comment Jan 26, 2026 5:20pm
agents-manage-ui Ready Ready Preview, Comment Jan 26, 2026 5:20pm

Request Review

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Jan 23, 2026

🦋 Changeset detected

Latest commit: d1ef925

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 8 packages
Name Type
@inkeep/agents-api Patch
@inkeep/agents-manage-ui Patch
@inkeep/agents-cli Patch
@inkeep/agents-core Patch
@inkeep/agents-manage-mcp Patch
@inkeep/agents-sdk Patch
@inkeep/ai-sdk-provider Patch
@inkeep/create-agents Patch

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
Copy link
Copy Markdown
Contributor

claude Bot commented Jan 23, 2026

Claude finished @miles-kt-inkeep's task —— View job


🔍 PR Review: GitHub OIDC Token Exchange Endpoint

🎯 Overview

This 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 Elements

Excellent Security Architecture

  • Comprehensive JWT validation with proper signature, issuer, audience, and expiration checks using the jose library
  • JWKS caching with refresh logic - handles key rotation gracefully by refreshing cache when keys are not found
  • Proper error classification - malformed tokens (400) vs. auth failures (401) vs. permission issues (403)
  • No secrets in logs - structured logging without exposing sensitive data

Robust Error Handling

  • RFC 7807 Problem Details format for consistent API errors
  • Detailed error taxonomy covering all failure modes (malformed, expired, wrong_issuer, etc.)
  • Graceful degradation - service returns 500 with clear messaging when GitHub App credentials aren't configured

High-Quality Testing

  • Comprehensive test coverage for all HTTP status codes and error paths
  • Excellent test utilities (testJwt.ts) with JWT generation helpers
  • Proper mocking strategy using Vitest hoisted mocks

🔧 Architectural Feedback & Improvements

1. Environment Configuration Pattern

Priority: 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 env pattern with createApiError for consistency with other authentication endpoints.

2. Cache Management & Memory Leaks

Priority: 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:

  • Memory accumulation in long-running processes
  • No cleanup mechanism for expired cache entries
  • Testing isolation - cache persists between tests

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 codebase

3. API Design & Future Extensibility

Priority: 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:

  • Scoped permissions - GitHub App tokens can be scoped to specific repositories
  • Permission validation - ensuring the OIDC token has required repository access
  • Audit trail - tracking which workflow/user initiated the exchange

4. Rate Limiting & Abuse Prevention

Priority: Medium

The endpoint lacks rate limiting, which could be important for:

  • GitHub API quota management - avoid hitting GitHub's rate limits
  • Abuse prevention - prevent malicious actors from exhausting GitHub App installations
  • Cost control - if GitHub charges for API calls

Recommendation: Consider adding rate limiting middleware consistent with other API endpoints.


🔒 Security Review

Excellent Security Posture

  • Proper JWT validation with signature verification against GitHub's JWKS
  • Audience validation ensures tokens are intended for this service
  • Issuer validation prevents token substitution attacks
  • Expiration handling prevents replay attacks with old tokens
  • No credential exposure in logs or error messages

Architecture Security

  • Proper separation of concerns - JWT validation, GitHub API calls, and business logic are well-isolated
  • Error information disclosure is appropriately limited
  • GitHub App credentials are properly protected with environment variable patterns

🏗️ Code Quality & Patterns

Follows Repository Standards

  • TypeScript best practices with proper type safety
  • Consistent error handling patterns using discriminated unions
  • OpenAPI schema integration with proper documentation
  • Structured logging with appropriate log levels
  • Domain-driven architecture with clear module boundaries

Minor Code Quality Suggestions

  1. Type Refinement - Consider extracting common error response types:
// Could be shared across GitHub domain
type GitHubApiError = { errorType: 'api_error' | 'jwt_error'; message: string; };
  1. Constants Organization - Move magic strings to constants file:
// 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 Excellence

The testing approach is exemplary:

  • Comprehensive coverage of all error scenarios
  • Realistic test data with proper JWT generation
  • Clean test organization with clear describe/it structure
  • Proper mocking strategy that tests business logic without external dependencies

🚀 Integration & Deployment

Environment Setup

The .env.example additions are appropriate and well-documented. Consider adding:

  • Setup documentation for obtaining GitHub App credentials
  • Development vs. production configuration guidance

Backward Compatibility

  • No breaking changes - this is a new endpoint
  • Graceful degradation when credentials aren't configured
  • Optional feature doesn't affect existing functionality

📋 Missing Elements (Repository Requirements)

According to AGENTS.md, all new features require:

  • Unit Tests - Comprehensive test suite provided
  • Agent Builder UI Components - Not applicable for this backend API endpoint
  • Documentation - No user-facing documentation in /agents-docs/

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 Assessment

This is a high-quality implementation that demonstrates:

  • Strong security engineering practices
  • Excellent error handling and observability
  • Comprehensive testing strategy
  • Clean architectural patterns
  • Good TypeScript and API design

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


miles-kt-inkeep and others added 10 commits January 23, 2026 17:44
- 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>
@vercel vercel Bot temporarily deployed to Preview – agents-docs January 26, 2026 15:32 Inactive
@vercel vercel Bot temporarily deployed to Preview – agents-docs January 26, 2026 17:16 Inactive
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.

1 participant