Skip to content

Commit caad379

Browse files
GitHub OIDC token exchange endpoint (#1569)
* init * [US-001] Create token exchange route structure - 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 <[email protected]> * [US-002] Configure GitHub App credentials from environment - 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 <[email protected]> * [US-003] Fetch and cache GitHub OIDC JWKS 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 <[email protected]> * [US-004] Validate OIDC token signature and claims 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 <[email protected]> * [US-005] Look up GitHub App installation for repository 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 <[email protected]> * [US-006] Generate GitHub App installation access token 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 <[email protected]> * [US-007] Handle all error cases with proper HTTP status codes - 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 <[email protected]> * [US-008] Create test utilities for JWT generation 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 <[email protected]> * [US-009] Create route tests for token exchange endpoint 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 <[email protected]> * bug fixes * lints * remove from openapi spec * changeset --------- Co-authored-by: Claude Opus 4.5 <[email protected]>
1 parent 67418c5 commit caad379

File tree

16 files changed

+1639
-28
lines changed

16 files changed

+1639
-28
lines changed

.ai-dev/Dockerfile.claude

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,22 @@ RUN apt-get update && apt-get install -y \
55
git \
66
curl \
77
ca-certificates \
8+
sudo \
89
&& rm -rf /var/lib/apt/lists/*
910

10-
# Install Claude Code globally
11-
RUN npm install -g @anthropic-ai/claude-code
11+
# Create non-root user with sudo access for CA cert updates
12+
RUN useradd -m -s /bin/bash user && \
13+
echo "user ALL=(ALL) NOPASSWD: /usr/sbin/update-ca-certificates, /bin/cp" >> /etc/sudoers.d/user
1214

13-
# Create non-root user
14-
RUN useradd -m -s /bin/bash user
1515
USER user
16+
WORKDIR /home/user
17+
18+
# Install Claude Code using official installer
19+
RUN curl -fsSL https://claude.ai/install.sh | bash
20+
21+
# Set PATH to include Claude binary
22+
ENV PATH="/home/user/.claude/bin:$PATH"
23+
1624
WORKDIR /workspace
1725

18-
ENTRYPOINT ["claude"]
26+
ENTRYPOINT ["/bin/bash"]

.ai-dev/squid.conf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ acl github_api_inkeep urlpath_regex ^/repos/inkeep(/|$)
3535
# Anthropic API (required for Claude Code)
3636
acl anthropic dstdomain .anthropic.com
3737

38+
# Claude Code
39+
acl claude dstdomain .claude.com
40+
3841
# === Access Rules ===
3942
acl SSL_ports port 443
4043
acl Safe_ports port 80
@@ -48,6 +51,7 @@ http_access allow github_inkeep github_inkeep_path
4851
http_access allow githubusercontent githubusercontent_inkeep
4952
http_access allow github_api github_api_inkeep
5053
http_access allow anthropic
54+
http_access allow claude
5155

5256
# Deny everything else
5357
http_access deny all
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@inkeep/agents-api": patch
3+
---
4+
5+
Add github token exchange endpoint

.env.example

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,3 +87,10 @@ PUBLIC_DISABLE_AUTH=true
8787
# PUBLIC_POSTHOG_KEY=
8888
# PUBLIC_POSTHOG_HOST=https://us.i.posthog.com
8989
# PUBLIC_POSTHOG_SITE_TAG=
90+
91+
# ========== GITHUB APP CONFIGURATION ================
92+
# Required for GitHub OIDC token exchange endpoint (/api/github/token-exchange)
93+
# Used to exchange GitHub Actions OIDC tokens for installation access tokens
94+
# Get these from your GitHub App settings: https://github.com/settings/apps
95+
# GITHUB_APP_ID=123456
96+
# GITHUB_APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----"

agents-api/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@
8585
"hono": "^4.10.4",
8686
"hono-pino": "^0.10.1",
8787
"jmespath": "^0.16.0",
88+
"jose": "^6.1.0",
8889
"llm-info": "^1.0.69",
8990
"openid-client": "^6.8.1",
9091
"pg": "^8.16.3",

0 commit comments

Comments
 (0)