Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .changeset/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": ["@wolfcola/devtools-extension"]
"ignore": []
}
5 changes: 5 additions & 0 deletions .changeset/firefox-support.md
Original file line number Diff line number Diff line change
@@ -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+.
45 changes: 41 additions & 4 deletions .github/workflows/publish-extension.yml
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
33 changes: 29 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ elm-stuff/
*.pem
*.crx
*.crx.zip
extension.zip
*.zip
packged/
21 changes: 15 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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) |

Expand All @@ -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
Expand All @@ -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
```

---
Expand Down
27 changes: 27 additions & 0 deletions SOURCE_BUILD.md
Original file line number Diff line number Diff line change
@@ -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)
27 changes: 21 additions & 6 deletions packages/devtools-extension/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`).

---

Expand Down Expand Up @@ -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.

Expand All @@ -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.
Expand Down Expand Up @@ -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 |
19 changes: 16 additions & 3 deletions packages/devtools-extension/build.mjs
Original file line number Diff line number Diff line change
@@ -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 });
Expand Down Expand Up @@ -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');
Expand Down
1 change: 1 addition & 0 deletions packages/devtools-extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
},
Expand Down
Loading