From 14e66e0aad0bc8549c0f146f2227e1d9b8a552e8 Mon Sep 17 00:00:00 2001 From: Ryan Bas Date: Sun, 10 May 2026 18:09:23 -0600 Subject: [PATCH 1/2] feat: add Firefox extension support Add build target for Firefox (pnpm build:firefox) that swaps the manifest background field from service_worker to scripts and adds browser_specific_settings.gecko with data_collection_permissions. - Add --target=firefox flag to build.mjs - Add publish-firefox job to publish-extension workflow - Add Firefox build/publish steps to release snapshot job - Upload source zip for AMO review (minified builds) - Update READMEs with Firefox compatibility and load instructions - Add SOURCE_BUILD.md for AMO reviewer build reproduction - Update .gitignore to cover all .zip build artifacts Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/publish-extension.yml | 45 +++++++++++++++++++++--- .github/workflows/release.yml | 33 ++++++++++++++--- .gitignore | 2 +- README.md | 21 +++++++---- SOURCE_BUILD.md | 27 ++++++++++++++ packages/devtools-extension/README.md | 27 ++++++++++---- packages/devtools-extension/build.mjs | 19 ++++++++-- packages/devtools-extension/package.json | 1 + 8 files changed, 151 insertions(+), 24 deletions(-) create mode 100644 SOURCE_BUILD.md diff --git a/.github/workflows/publish-extension.yml b/.github/workflows/publish-extension.yml index 3be264b..928b151 100644 --- a/.github/workflows/publish-extension.yml +++ b/.github/workflows/publish-extension.yml @@ -1,11 +1,11 @@ -name: Publish Chrome Extension +name: Publish Extensions on: release: types: [published] jobs: - publish: + publish-chrome: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -24,14 +24,51 @@ jobs: - name: Zip extension working-directory: packages/devtools-extension - run: cd dist && zip -r ../extension.zip . + run: cd dist && zip -r ../extension-chrome.zip . - name: Upload to Chrome Web Store uses: mnao305/chrome-extension-upload@v5.0.0 with: - file-path: packages/devtools-extension/extension.zip + file-path: packages/devtools-extension/extension-chrome.zip extension-id: ${{ secrets.CHROME_EXTENSION_ID }} client-id: ${{ secrets.CHROME_CLIENT_ID }} client-secret: ${{ secrets.CHROME_CLIENT_SECRET }} refresh-token: ${{ secrets.CHROME_REFRESH_TOKEN }} publish: true + + publish-firefox: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 24 + cache: pnpm + + - run: pnpm install --frozen-lockfile + + - name: Build all packages + run: pnpm build + + - name: Build Firefox extension + working-directory: packages/devtools-extension + run: node build.mjs --target=firefox + + - name: Zip extension + working-directory: packages/devtools-extension + run: cd dist && zip -r ../extension-firefox.zip . + + - name: Zip source for AMO review + run: zip -r source.zip . -x 'node_modules/*' '*/node_modules/*' '*/dist/*' '.git/*' '*.zip' + + - name: Upload to Firefox Add-ons + uses: yayuyokitano/firefox-addon@v1.0.0 + with: + api_key: ${{ secrets.AMO_JWT_ISSUER }} + api_secret: ${{ secrets.AMO_JWT_SECRET }} + guid: oidc-devtool@wolfcola + xpi_path: packages/devtools-extension/extension-firefox.zip + src_path: source.zip diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5cbf5ef..53c7716 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -44,22 +44,47 @@ jobs: env: NPM_CONFIG_PROVENANCE: true - - name: Zip extension + - name: Zip Chrome extension if: inputs.extension working-directory: packages/devtools-extension - run: cd dist && zip -r ../extension.zip . + run: cd dist && zip -r ../extension-chrome.zip . - - name: Publish extension to testers + - name: Publish Chrome extension to testers if: inputs.extension uses: mnao305/chrome-extension-upload@v5.0.0 with: - file-path: packages/devtools-extension/extension.zip + file-path: packages/devtools-extension/extension-chrome.zip extension-id: ${{ secrets.CHROME_EXTENSION_ID }} client-id: ${{ secrets.CHROME_CLIENT_ID }} client-secret: ${{ secrets.CHROME_CLIENT_SECRET }} refresh-token: ${{ secrets.CHROME_REFRESH_TOKEN }} publish: true publish-target: trustedTesters + + - name: Build Firefox extension + if: inputs.extension + working-directory: packages/devtools-extension + run: node build.mjs --target=firefox + + - name: Zip Firefox extension + if: inputs.extension + working-directory: packages/devtools-extension + run: cd dist && zip -r ../extension-firefox.zip . + + - name: Zip source for AMO review + if: inputs.extension + run: zip -r source.zip . -x 'node_modules/*' '*/node_modules/*' '*/dist/*' '.git/*' '*.zip' + + - name: Publish Firefox extension (unlisted) + if: inputs.extension + uses: yayuyokitano/firefox-addon@v1.0.0 + with: + api_key: ${{ secrets.AMO_JWT_ISSUER }} + api_secret: ${{ secrets.AMO_JWT_SECRET }} + guid: oidc-devtool@wolfcola + xpi_path: packages/devtools-extension/extension-firefox.zip + src_path: source.zip + channel: unlisted release: if: github.event_name == 'push' runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index 6e1dbb2..71d1bf2 100644 --- a/.gitignore +++ b/.gitignore @@ -7,5 +7,5 @@ elm-stuff/ *.pem *.crx *.crx.zip -extension.zip +*.zip packged/ diff --git a/README.md b/README.md index 5692894..877f6c3 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ **Captures, correlates, and diagnoses** OIDC/OAuth 2.0 authentication flows in real time — works standalone with any OIDC provider or as an enhanced companion to the Ping Identity SDK. -A Chrome DevTools panel that replaces the Network-panel-and-jwt.io workflow with a single view: OIDC-annotated network traffic, SDK-level event correlation, inline JWT decoding, and an automated diagnosis engine that tells you what went wrong and how to fix it. +A browser DevTools panel for Chrome and Firefox that replaces the Network-panel-and-jwt.io workflow with a single view: OIDC-annotated network traffic, SDK-level event correlation, inline JWT decoding, and an automated diagnosis engine that tells you what went wrong and how to fix it. ![Flow view with diagnosis banner and node rail](packages/devtools-extension/screenshots/Flow-Screen.png) @@ -12,7 +12,7 @@ A Chrome DevTools panel that replaces the Network-panel-and-jwt.io workflow with | Package | Description | npm | | --- | --- | --- | -| [`@wolfcola/devtools-extension`](packages/devtools-extension) | Chrome extension — DevTools panel with Timeline, Flow, and Learn views | private | +| [`@wolfcola/devtools-extension`](packages/devtools-extension) | Browser extension (Chrome & Firefox) — DevTools panel with Timeline, Flow, and Learn views | private | | [`@wolfcola/devtools-bridge`](packages/devtools-bridge) | Opt-in SDK adapter — emits `AuthEvent`s from DaVinci, Journey, and OIDC clients | [![npm](https://img.shields.io/npm/v/@wolfcola/devtools-bridge)](https://www.npmjs.com/package/@wolfcola/devtools-bridge) | | [`@wolfcola/devtools-types`](packages/devtools-types) | Shared `AuthEvent` Effect Schema definitions and TypeScript types | [![npm](https://img.shields.io/npm/v/@wolfcola/devtools-types)](https://www.npmjs.com/package/@wolfcola/devtools-types) | @@ -22,11 +22,19 @@ A Chrome DevTools panel that replaces the Network-panel-and-jwt.io workflow with ```bash pnpm install -pnpm build +pnpm build # Chrome (default) +pnpm build:firefox # Firefox ``` Load the extension as unpacked from `packages/devtools-extension/dist/` — see the [extension README](packages/devtools-extension) for full instructions. +### Browser compatibility + +| Browser | Minimum version | +| --- | --- | +| Chrome | 88+ (Manifest V3) | +| Firefox | 128+ (`world: "MAIN"` content scripts) | + To wire up SDK-level events in your app: ```bash @@ -49,9 +57,10 @@ The bridge is a no-op when the extension is not installed. ```bash pnpm install -pnpm build # build all packages -pnpm lint # eslint -pnpm test # vitest +pnpm build # build all packages (Chrome) +pnpm build:firefox # build Firefox variant +pnpm lint # eslint +pnpm test # vitest ``` --- diff --git a/SOURCE_BUILD.md b/SOURCE_BUILD.md new file mode 100644 index 0000000..14139c6 --- /dev/null +++ b/SOURCE_BUILD.md @@ -0,0 +1,27 @@ +# Building from source + +## Prerequisites + +- Node.js 24 +- pnpm (see `packageManager` in `package.json` for exact version) + +## Steps + +```bash +pnpm install --frozen-lockfile +pnpm build # builds all workspace packages +# The Firefox extension is now at packages/devtools-extension/dist/ + +# To explicitly build the Firefox variant: +cd packages/devtools-extension +node build.mjs --target=firefox +``` + +The built extension is in `packages/devtools-extension/dist/`. + +## What the build does + +1. Bundles TypeScript entry points with esbuild (minified) +2. Compiles the Elm panel UI and minifies with terser +3. Generates the Firefox manifest (swaps `service_worker` to `scripts`, adds `browser_specific_settings`) +4. Copies static assets (icons, HTML) diff --git a/packages/devtools-extension/README.md b/packages/devtools-extension/README.md index d725bc8..41bf169 100644 --- a/packages/devtools-extension/README.md +++ b/packages/devtools-extension/README.md @@ -4,13 +4,15 @@ Most auth debugging starts in the Network panel and stays there — copying tokens into jwt.io, cross-referencing timestamps, guessing which 400 was the CORS preflight and which was a bad grant. WolfCola DevTools replaces that with a single panel that captures network traffic, annotates it with OIDC semantics, optionally merges in SDK-level events, and runs an automated diagnosis engine that tells you _what went wrong and how to fix it_. +Supports **Chrome 88+** and **Firefox 128+**. + ![Flow view with diagnosis banner and node rail](screenshots/Flow-Screen.png) --- ## Status -**v0.1.0 — alpha, active development.** The extension is functional and loadable as an unpacked Chrome extension. It is not published to the Chrome Web Store. The package is private (`@wolfcola/devtools-extension`). +**v0.1.0 — alpha, active development.** The extension is functional and loadable as an unpacked extension in Chrome and Firefox. The package is private (`@wolfcola/devtools-extension`). --- @@ -172,17 +174,18 @@ Events are linked by `flowId` and an optional `causedBy` reference pointing to t ## Security and privacy -The extension requests only `storage` and `clipboardWrite`/`clipboardRead` (for copying collectors and exported data) — no `cookies`, `webRequest`, `tabs`, or other sensitive APIs. Content scripts use a two-world architecture: `content-script.ts` runs in the MAIN world (page access, no `chrome.runtime`), while `relay.ts` runs in the isolated world (runtime access, guarded by a sentinel flag and same-source check), preventing arbitrary page code from injecting messages into the service worker. All SDK events are decoded through `AuthEventSchema` (Effect Schema) before reaching the EventStore — malformed payloads are dropped with a console warning. Captured data is stored in `chrome.storage.local` under a namespaced key and never transmitted off-device. No remote code is loaded or executed. +The extension requests only `storage` and `clipboardWrite`/`clipboardRead` (for copying collectors and exported data) — no `cookies`, `webRequest`, `tabs`, or other sensitive APIs. Content scripts use a two-world architecture: `content-script.ts` runs in the MAIN world (page access, no extension runtime), while `relay.ts` runs in the isolated world (runtime access, guarded by a sentinel flag and same-source check), preventing arbitrary page code from injecting messages into the background script. All SDK events are decoded through `AuthEventSchema` (Effect Schema) before reaching the EventStore — malformed payloads are dropped with a console warning. Captured data is stored in `storage.local` under a namespaced key and never transmitted off-device. No remote code is loaded or executed. --- ## Build ```bash -nx run devtools-extension:build +pnpm build # Chrome (default) +pnpm build:firefox # Firefox ``` -Output is written to `packages/devtools-extension/dist/`. +Output is written to `packages/devtools-extension/dist/`. Both targets produce the same JS bundles — only the manifest differs (Firefox uses `background.scripts` instead of `background.service_worker` and includes `browser_specific_settings.gecko`). > **Prerequisite:** [Elm](https://guide.elm-lang.org/install/elm.html) must be installed and on your `PATH`. The build step compiles `src/panel/Main.elm` into a single JS bundle. @@ -194,12 +197,24 @@ Output is written to `packages/devtools-extension/dist/`. 2. Enable **Developer mode** (top-right toggle) 3. Click **Load unpacked** 4. Select `packages/devtools-extension/dist/` -5. Open DevTools on any page with OIDC traffic -- the **WolfCola DevTools** tab appears +5. Open DevTools on any page with OIDC traffic — the **WolfCola DevTools** tab appears After rebuilding, click the refresh icon on the extension card at `chrome://extensions`, then close and reopen DevTools. --- +## Load in Firefox + +1. Run `pnpm build:firefox` +2. Open `about:debugging#/runtime/this-firefox` +3. Click **Load Temporary Add-on...** +4. Select `packages/devtools-extension/dist/manifest.json` +5. Open DevTools on any page with OIDC traffic — the **WolfCola DevTools** tab appears + +Temporary add-ons are removed when Firefox closes. For persistent installation, use a signed `.xpi` from [addons.mozilla.org](https://addons.mozilla.org). + +--- + ## Wiring up your app The extension captures and annotates all OIDC network traffic automatically — **no SDK integration is required**. To also see SDK-level events (node transitions, journey steps, OIDC phases, session diffs), add the bridge adapter to your app. @@ -286,6 +301,6 @@ When both SDK bridge events and network OIDC annotations are present, the Learn | Package | Description | | ------------------------------ | ---------------------------------------------------------------- | -| `@wolfcola/devtools-extension` | The Chrome extension (this package — private, not published) | +| `@wolfcola/devtools-extension` | The browser extension (this package — private, not published) | | `@wolfcola/devtools-bridge` | Opt-in SDK adapter — emits `AuthEvent`s from subscribable clients | | `@wolfcola/devtools-types` | Shared `AuthEvent` Effect Schema definitions and TypeScript types | diff --git a/packages/devtools-extension/build.mjs b/packages/devtools-extension/build.mjs index 32e5e8f..ef029bb 100644 --- a/packages/devtools-extension/build.mjs +++ b/packages/devtools-extension/build.mjs @@ -1,10 +1,13 @@ import { execFileSync } from 'node:child_process'; -import { cpSync, mkdirSync } from 'node:fs'; +import { cpSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'; +const target = process.argv.includes('--target=firefox') ? 'firefox' : 'chrome'; const cwd = import.meta.dirname; const run = (cmd, args) => execFileSync(cmd, args, { stdio: 'inherit', cwd }); const npx = (args) => run('npx', args); +console.log(`Building for ${target}...`); + mkdirSync('dist/panel', { recursive: true }); mkdirSync('dist/background', { recursive: true }); mkdirSync('dist/content', { recursive: true }); @@ -68,8 +71,18 @@ npx([ 'dist/panel/elm.js', ]); -// Static files -cpSync('manifest.json', 'dist/manifest.json'); +// Manifest — swap background field per target +const manifest = JSON.parse(readFileSync('manifest.json', 'utf8')); +if (target === 'firefox') { + manifest.background = { scripts: ['background/service-worker.js'], type: 'module' }; + manifest.browser_specific_settings = { + gecko: { + id: 'oidc-devtool@wolfcola', + data_collection_permissions: { required: ['none'] }, + }, + }; +} +writeFileSync('dist/manifest.json', JSON.stringify(manifest, null, 2)); cpSync('icons', 'dist/icons', { recursive: true }); cpSync('src/devtools/devtools.html', 'dist/devtools.html'); cpSync('src/panel/panel.html', 'dist/panel/panel.html'); diff --git a/packages/devtools-extension/package.json b/packages/devtools-extension/package.json index 3486fa5..964a8f6 100644 --- a/packages/devtools-extension/package.json +++ b/packages/devtools-extension/package.json @@ -13,6 +13,7 @@ "scripts": { "postinstall": "elm-tooling install", "build": "node build.mjs", + "build:firefox": "node build.mjs --target=firefox", "lint": "eslint .", "test": "vitest run" }, From 2a82f514acea0cd906de50eafc096c880c883780 Mon Sep 17 00:00:00 2001 From: Ryan Bas Date: Sun, 10 May 2026 18:15:04 -0600 Subject: [PATCH 2/2] chore: add changeset for Firefox support Remove devtools-extension from changeset ignore list (private: true already prevents npm publish) and add minor changeset entry. Co-Authored-By: Claude Opus 4.6 (1M context) --- .changeset/config.json | 2 +- .changeset/firefox-support.md | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/firefox-support.md diff --git a/.changeset/config.json b/.changeset/config.json index db29a5e..aa14eab 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -7,5 +7,5 @@ "access": "public", "baseBranch": "main", "updateInternalDependencies": "patch", - "ignore": ["@wolfcola/devtools-extension"] + "ignore": [] } diff --git a/.changeset/firefox-support.md b/.changeset/firefox-support.md new file mode 100644 index 0000000..f843dae --- /dev/null +++ b/.changeset/firefox-support.md @@ -0,0 +1,5 @@ +--- +'@wolfcola/devtools-extension': minor +--- + +Add Firefox extension support with build target (`pnpm build:firefox`), AMO publishing in CI, and Firefox load instructions. Minimum Firefox version: 128+.