mirror of
https://github.com/anomalyco/opencode.git
synced 2026-11-15 12:41:37 +03:00
62 lines
2.3 KiB
TypeScript
62 lines
2.3 KiB
TypeScript
import { Hono } from "hono"
|
|
import { jwtVerify, createRemoteJWKSet } from "jose"
|
|
import { createAppAuth } from "@octokit/auth-app"
|
|
import { Octokit } from "@octokit/rest"
|
|
import { Resource } from "sst"
|
|
import { parseRepositoryClaim } from "./github"
|
|
|
|
export default new Hono()
|
|
.get("/", (c) => c.text("Hello, world!"))
|
|
/**
|
|
* Used by the GitHub action to get GitHub installation access token given the OIDC token
|
|
*/
|
|
.post("/exchange_github_app_token", async (c) => {
|
|
const EXPECTED_AUDIENCE = "opencode-github-action"
|
|
const GITHUB_ISSUER = "https://token.actions.githubusercontent.com"
|
|
const JWKS_URL = `${GITHUB_ISSUER}/.well-known/jwks`
|
|
|
|
// get Authorization header
|
|
const token = c.req.header("Authorization")?.replace(/^Bearer /, "")
|
|
if (!token) return c.json({ error: "Authorization header is required" }, { status: 401 })
|
|
|
|
// verify token
|
|
const JWKS = createRemoteJWKSet(new URL(JWKS_URL))
|
|
let repository: ReturnType<typeof parseRepositoryClaim>
|
|
try {
|
|
const { payload } = await jwtVerify(token, JWKS, {
|
|
issuer: GITHUB_ISSUER,
|
|
audience: EXPECTED_AUDIENCE,
|
|
})
|
|
repository = parseRepositoryClaim(payload)
|
|
} catch (err) {
|
|
console.error("Token verification failed:", err)
|
|
return c.json({ error: "Invalid or expired token" }, { status: 403 })
|
|
}
|
|
|
|
try {
|
|
const auth = createAppAuth({
|
|
appId: Resource.GITHUB_APP_ID.value,
|
|
privateKey: Resource.GITHUB_APP_PRIVATE_KEY.value,
|
|
})
|
|
const appAuth = await auth({ type: "app" })
|
|
const octokit = new Octokit({ auth: appAuth.token })
|
|
const { data: installation } = await octokit.apps.getRepoInstallation({
|
|
owner: repository.owner,
|
|
repo: repository.repo,
|
|
})
|
|
const installationAuth = await auth({
|
|
type: "installation",
|
|
installationId: installation.id,
|
|
...(repository.id ? { repositoryIds: [repository.id] } : { repositoryNames: [repository.repo] }),
|
|
})
|
|
return c.json({ token: installationAuth.token })
|
|
} catch (error) {
|
|
console.error("GitHub App token exchange failed:", error)
|
|
return c.json(
|
|
{ error: `Failed to exchange GitHub App token for ${repository.owner}/${repository.repo}` },
|
|
{ status: 502 },
|
|
)
|
|
}
|
|
})
|
|
.all("*", (c) => c.text("Not Found", 404))
|