190 lines · 6.5 KB
import { env } from "cloudflare:workers";
import { z } from "zod";
import { randomId, sha256Hex } from "../crypto";
import { allows } from "./permissions";
import { jobTokenAccess } from "./job-token";
import { isCloudJob, loadJobExecution } from "./jobs";
import { repoDb } from "../scoped-db";

const MAX_ATTEMPT_DEPTH = 100;

const TOKEN_TTL_SECONDS = 5 * 60;
const MAX_AUDIENCE_LENGTH = 200;
const JWKS_MAX_AGE_SECONDS = 3600;

const rsaPrivateJwk = z.object({
  kty: z.literal("RSA"),
  n: z.string(),
  e: z.string(),
  d: z.string(),
  p: z.string(),
  q: z.string(),
  dp: z.string(),
  dq: z.string(),
  qi: z.string(),
});

type SigningKey = {
  key: CryptoKey;
  publicJwk: { kty: "RSA"; n: string; e: string; alg: "RS256"; use: "sig"; kid: string };
};

let cached: Promise<SigningKey | null> | null = null;

/** The RSA signing key is a private JWK held in Secrets Store and never leaves the Worker. */
async function loadSigningKey(): Promise<SigningKey | null> {
  let raw: string;
  try {
    raw = await env.OIDC_SIGNING_KEY.get();
  } catch (error) {
    console.error("OIDC signing key unavailable", error);