Skip to content

Automated Cloudflare deploys (cert/production) + company-scoped OAuth tokens - #32

Merged
AlexKempen merged 11 commits into
certfrom
claude/cloudflare-deployment-plan-lumow6
Jul 24, 2026
Merged

Automated Cloudflare deploys (cert/production) + company-scoped OAuth tokens#32
AlexKempen merged 11 commits into
certfrom
claude/cloudflare-deployment-plan-lumow6

Conversation

@AlexKempen

Copy link
Copy Markdown
Collaborator

This branch contains two related-but-independent changes.

1. Automated Cloudflare deployments (dev / cert / production)

Introduces named Wrangler environments and a CI-gated deploy workflow so pushes to cert deploy the cert Worker and (in the future) pushes to main deploy a separate production Worker.

  • wrangler.jsonc: top-level config is now the local dev environment (drops the unused database_id, since local dev runs --local). Adds env.cert (Worker frc-design-app-cert, reusing the existing remote D1/KV/R2) and env.production (Worker frc-design-app-production, with placeholder resource IDs to fill in once prod resources exist). Bindings/vars are redeclared per env since they don't inherit.
  • onshape-api.ts: the Onshape API base path and version are identical across environments, so they're hardcoded as constants in getBaseUrl() instead of repeated as per-env vars. Only ADMIN_TEAM remains a var.
  • .github/workflows/deploy.yml (new): runs tsc/lint/test, then deploys the matching environment via cloudflare/wrangler-action@v3. The Vite plugin selects the target env at build time via CLOUDFLARE_ENV; migrations run with --env. Production runs in a protected GitHub Environment and is dormant until a main branch exists.
  • ci.yml: drops the redundant push-to-cert trigger (now covered by the deploy workflow).
  • vite.config.ts: only enables the HTTPS dev server when the mkcert files exist, so vite build works in CI where they're absent (previously an unconditional readFileSync would crash the build).
  • package.json: replaces deploy with deploy:cert / deploy:production.

Follow-up needed before cert deploys go live (outside this PR)

  • Add repo secrets CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID.
  • Create GitHub Environments cert and production (required reviewer on production).
  • Cert rename cutover: the first cert deploy creates a new frc-design-app-cert Worker — set its runtime secrets, repoint the custom domain from the old frc-design-app Worker, then delete the old Worker. (Domain-keyed OAuth/Onshape URLs keep working.)
  • For production later: create real D1/KV/R2, replace the placeholder IDs, set prod secrets.

2. Company-scoped OAuth tokens

Onshape access tokens are company-scoped, but the app previously ignored the sessionCompanyId the Onshape iframe passes on /init, so a user who authorized in one context could end up with a token scoped to the wrong company when opening another company's document.

  • isAuthenticated fetches session info (which also serves as the auth ping) and compares the token's company (sessioninfo.company.id, null = personal "cad") against the request's sessionCompanyId. On a mismatch it returns false, so /init restarts the sign-in flow.
  • Every token request now passes company_id to the OAuth token endpoint via a companyTokenEndpoint helper — for both the authorization-code exchange in doCallback and refreshes in getOnshapeApiForSessionId — so the minted token is company-scoped.
  • The requested company is captured at sign-in (from the request or the /init redirectUrl) into the login session, and persisted onto the stored tokens so refreshes keep the scope.
  • getSessionInfo is now typed with a SessionInfo interface exposing company.id.

Confirm before merge: that Onshape's sessioninfo exposes the scoped company at company.id (the ?? "cad" default treats null as personal).

Verification

npm run tsc, npm run lint, and npm test (94 tests) all pass. wrangler deploy --dry-run / build validated for the base, cert, and production environments.

🤖 Generated with Claude Code


Generated by Claude Code

claude and others added 11 commits July 23, 2026 15:05
Introduce named Wrangler environments and a CI-gated deploy workflow so
pushes to `cert` deploy the cert Worker and (in the future) pushes to
`main` deploy a separate production Worker.

- wrangler.jsonc: top-level config is now the local "dev" environment
  (drops the unused `database_id`, since local dev runs with `--local`).
  Add `env.cert` (Worker `frc-design-app-cert`, reuses the existing remote
  D1/KV/R2) and `env.production` (Worker `frc-design-app-production`, with
  placeholder resource IDs to be filled in once prod resources exist).
- onshape-api.ts: hardcode the Onshape API base path and version as
  constants (identical across environments) instead of repeating them as
  per-environment vars. Only `ADMIN_TEAM` remains a var.
- deploy.yml: new workflow. Runs tsc/lint/test, then deploys the matching
  environment via cloudflare/wrangler-action. The Vite plugin selects the
  target env at build time via CLOUDFLARE_ENV; migrations run with `--env`.
  Production runs in a protected GitHub Environment and is dormant until a
  `main` branch exists.
- ci.yml: drop the redundant push-to-cert trigger (now covered by deploy).
- vite.config.ts: only enable the HTTPS dev server when the mkcert files
  exist, so `vite build` works in CI where they are absent.
- package.json: replace `deploy` with `deploy:cert` / `deploy:production`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EdsRyJRctf2K3v73QdtgGo
Onshape access tokens are company-scoped, but the app previously ignored the
`sessionCompanyId` the Onshape iframe passes on `/init`, so a user who
authorized in one context could end up with a token scoped to the wrong
company (or personal) when opening another company's document.

- isAuthenticated now fetches session info (which also serves as the auth
  ping) and compares the token's company (`sessioninfo.company.id`, null =
  personal "cad") against the request's `sessionCompanyId`. On a mismatch it
  returns false, so `/init` restarts the sign-in flow.
- Every token request now passes `company_id` to the OAuth token endpoint via
  a `companyTokenEndpoint` helper — for both the authorization-code exchange
  in doCallback and refreshes in getOnshapeApiForSessionId — so the minted
  token is company-scoped.
- The requested company is captured at sign-in (from the request or the
  `/init` redirectUrl) into the login session, and persisted onto the stored
  tokens so refreshes keep the scope.
- Type getSessionInfo with a SessionInfo interface exposing `company.id`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EdsRyJRctf2K3v73QdtgGo
@AlexKempen
AlexKempen merged commit e9fc15e into cert Jul 24, 2026
1 check passed
@AlexKempen
AlexKempen deleted the claude/cloudflare-deployment-plan-lumow6 branch July 24, 2026 03:44
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.

2 participants