summaryrefslogtreecommitdiffhomepage
path: root/packages
diff options
context:
space:
mode:
Diffstat (limited to 'packages')
-rw-r--r--packages/function/package.json4
-rw-r--r--packages/function/src/api.ts40
-rw-r--r--packages/function/sst-env.d.ts24
-rw-r--r--packages/opencode/sst-env.d.ts2
-rw-r--r--packages/web/sst-env.d.ts2
5 files changed, 62 insertions, 10 deletions
diff --git a/packages/function/package.json b/packages/function/package.json
index 81a1edc92..c033fa058 100644
--- a/packages/function/package.json
+++ b/packages/function/package.json
@@ -8,5 +8,9 @@
"@cloudflare/workers-types": "4.20250522.0",
"typescript": "catalog:",
"@types/node": "catalog:"
+ },
+ "dependencies": {
+ "@octokit/auth-app": "8.0.1",
+ "jose": "6.0.11"
}
}
diff --git a/packages/function/src/api.ts b/packages/function/src/api.ts
index be6ef1923..3ba3d71d1 100644
--- a/packages/function/src/api.ts
+++ b/packages/function/src/api.ts
@@ -1,5 +1,8 @@
import { DurableObject } from "cloudflare:workers"
import { randomUUID } from "node:crypto"
+import { jwtVerify, createRemoteJWKSet } from "jose"
+import { createAppAuth } from "@octokit/auth-app"
+import { Resource } from "sst"
type Env = {
SYNC_SERVER: DurableObjectNamespace<SyncServer>
@@ -218,5 +221,42 @@ export default {
},
)
}
+
+ if (request.method === "POST" && method === "exchange_github_app_token") {
+ 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 authHeader = request.headers.get("Authorization")
+ const token = authHeader?.replace(/^Bearer /, "")
+ if (!token) return new Response("Error: authorization header is required", { status: 401 })
+
+ // verify token
+ const JWKS = createRemoteJWKSet(new URL(JWKS_URL))
+ try {
+ await jwtVerify(token, JWKS, {
+ issuer: GITHUB_ISSUER,
+ audience: EXPECTED_AUDIENCE,
+ })
+ } catch (err) {
+ console.error("Token verification failed:", err)
+ return new Response(JSON.stringify({ error: "Invalid or expired token" }), {
+ status: 403,
+ headers: { "Content-Type": "application/json" },
+ })
+ }
+
+ // Create app token
+ const auth = createAppAuth({
+ appId: Resource.GITHUB_APP_ID.value,
+ privateKey: Resource.GITHUB_APP_PRIVATE_KEY.value,
+ })
+ const appAuthentication = await auth({ type: "app" })
+
+ return new Response(JSON.stringify({ token: appAuthentication.token }), {
+ headers: { "Content-Type": "application/json" },
+ })
+ }
},
}
diff --git a/packages/function/sst-env.d.ts b/packages/function/sst-env.d.ts
index fd95edbb4..dab7de3f3 100644
--- a/packages/function/sst-env.d.ts
+++ b/packages/function/sst-env.d.ts
@@ -6,20 +6,28 @@
import "sst"
declare module "sst" {
export interface Resource {
- Web: {
- type: "sst.cloudflare.Astro"
- url: string
+ "GITHUB_APP_ID": {
+ "type": "sst.sst.Secret"
+ "value": string
+ }
+ "GITHUB_APP_PRIVATE_KEY": {
+ "type": "sst.sst.Secret"
+ "value": string
+ }
+ "Web": {
+ "type": "sst.cloudflare.Astro"
+ "url": string
}
}
}
-// cloudflare
-import * as cloudflare from "@cloudflare/workers-types"
+// cloudflare
+import * as cloudflare from "@cloudflare/workers-types";
declare module "sst" {
export interface Resource {
- Api: cloudflare.Service
- Bucket: cloudflare.R2Bucket
+ "Api": cloudflare.Service
+ "Bucket": cloudflare.R2Bucket
}
}
import "sst"
-export {}
+export {} \ No newline at end of file
diff --git a/packages/opencode/sst-env.d.ts b/packages/opencode/sst-env.d.ts
index 0397645b5..b6a7e9066 100644
--- a/packages/opencode/sst-env.d.ts
+++ b/packages/opencode/sst-env.d.ts
@@ -6,4 +6,4 @@
/// <reference path="../../sst-env.d.ts" />
import "sst"
-export {}
+export {} \ No newline at end of file
diff --git a/packages/web/sst-env.d.ts b/packages/web/sst-env.d.ts
index 0397645b5..b6a7e9066 100644
--- a/packages/web/sst-env.d.ts
+++ b/packages/web/sst-env.d.ts
@@ -6,4 +6,4 @@
/// <reference path="../../sst-env.d.ts" />
import "sst"
-export {}
+export {} \ No newline at end of file