Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
85b65b9
feat: add OIDC provider support with .well-known endpoints
alukach Apr 2, 2026
0908dfa
feat: replace static api_secret with flexible ApiAuth enum
alukach Apr 2, 2026
f877b4b
chore: add OIDC provider environment variables to wrangler.toml
alukach Apr 2, 2026
ec81b41
fix: improve JWKS unwrap messages and clarify wrangler.toml comments
alukach Apr 2, 2026
2ab9f85
docs: update README with OIDC provider setup, key rotation, and missi…
alukach Apr 2, 2026
9e1dd9f
chore: ignore RUSTSEC-2023-0071 (rsa Marvin Attack)
alukach Apr 2, 2026
5d27216
feat: update dependencies to use multistore from git and implement OI…
alukach Apr 3, 2026
4992d1d
Add secret to deployment
alukach Apr 3, 2026
a831c36
refactor: streamline previous OIDC key loading logic
alukach Apr 3, 2026
4b0c4c3
fix: avoid rewriting .well-known links
alukach Apr 3, 2026
ffb6ae2
fix: set issuer on previews
alukach Apr 3, 2026
e1aab7f
refactor auth to separate module
alukach Apr 7, 2026
549d155
Add STS endpoint to make use of OIDC provider
alukach Apr 7, 2026
dee07ba
fix: update multistore dependencies to use rewritten path support branch
alukach Apr 7, 2026
cf2ae02
fix: update multistore dependencies to latest commit for rewritten pa…
alukach Apr 7, 2026
6f1545a
chore: use visibility rather than data_mode
alukach Apr 7, 2026
353e170
fix: avoid fetching /.well-known data
alukach Apr 8, 2026
492c3c3
refactor: simplify
alukach Apr 8, 2026
62f9ee3
fix: update multistore dependencies to latest commit for rewritten pa…
alukach Apr 8, 2026
2601e35
chore: add .dev.vars to .gitignore
alukach Apr 16, 2026
3b96fc1
fix: populate required env vars
alukach Apr 16, 2026
e6afeeb
chore: cargo audit
alukach Apr 16, 2026
3a40cac
fix: fix host override on local dev
alukach Apr 17, 2026
22aa09c
ci: extend wait for dev server readiness
alukach Apr 17, 2026
5f4b8da
ci: accelerate build
alukach Apr 17, 2026
af1f065
ci: fix integration test
alukach Apr 17, 2026
50417b6
ci: fix no-build
alukach Apr 17, 2026
2848ded
ci: attempt build fix
alukach Apr 17, 2026
94efdd8
fix: cache with identity
alukach Apr 18, 2026
86733a6
refactor: utilize new features from multistore 0.4.0
alukach May 22, 2026
865d4e8
chore: cargo audit fix
alukach May 22, 2026
55ce70e
Merge branch 'main' into feat/oidc-provider
alukach May 29, 2026
07d665d
fix: set AUTH_ISSUER var for prod and staging
alukach Jun 1, 2026
ca40cb4
fix: forward OIDC and session secrets to all deploy callers
alukach Jun 1, 2026
d70b7c0
chore: remove empty orphan src/oidc.rs
alukach Jun 1, 2026
90e9353
fix: reject object writes with 405 instead of 404
alukach Jun 1, 2026
cba7468
fix: keep cache key URL-safe by hex-encoding subject
alukach Jun 1, 2026
ca1f7e5
chore: fix versions
alukach Jun 1, 2026
af1751f
fix: return S3 AccessDenied (403) for upstream auth failures
alukach Jun 1, 2026
b52aa89
refactor: render 405 via multistore ProxyResult instead of local helper
alukach Jun 1, 2026
75810e1
feat(sts): support audience restriction on /.sts token exchange
alukach Jun 9, 2026
08b3d3b
fix(cors): stop advertising write methods on read-only paths
alukach Jun 9, 2026
e1007ff
fix: accept trailing slash on the STS endpoint
alukach Jun 9, 2026
507b522
fix(analytics): skip logging for special endpoints
alukach Jun 9, 2026
3fa1337
chore(cache): assert cache-key URLs are query-free
alukach Jun 9, 2026
99d19e5
ci(deploy): manage OIDC_PROVIDER_KEY_PREVIOUS and harden secret upload
alukach Jun 9, 2026
5b3f497
ci: scope build-command rewrite to the [build] section
alukach Jun 9, 2026
3a4639e
docs: align key-rotation runbook with CI-managed secrets
alukach Jun 9, 2026
b54eb5b
Merge branch 'main' into feat/oidc-provider
alukach Jun 10, 2026
8046b07
fix(sts): fail closed when AUTH_AUDIENCE is unset
alukach Jun 10, 2026
3afbd50
fix(cache): percent-encode path segments to prevent API request forgery
alukach Jun 10, 2026
4e08ee0
ci(deploy): validate required secrets and smoke-test the deploy
alukach Jun 10, 2026
b9f17cd
Merge branch 'main' into feat/oidc-provider
alukach Jun 14, 2026
ab75c67
fix(config): warn when previous OIDC key is set without its kid
alukach Jun 15, 2026
e304673
docs(auth): clarify None fall-through in authorization_header
alukach Jun 15, 2026
c4e3f5a
docs(deps): explain why reqwest needs no explicit features on wasm32
alukach Jun 15, 2026
a5e9312
Merge branch 'main' into feat/oidc-provider
alukach Jun 15, 2026
be24094
Apply suggestions from code review
alukach Jun 15, 2026
2317065
docs(readme): correct AUTH_AUDIENCE unset behavior
alukach Jun 15, 2026
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
49 changes: 49 additions & 0 deletions .cargo/audit.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
[advisories]
ignore = [
# RUSTSEC-2023-0071: Marvin Attack on the `rsa` crate (medium severity, 5.9)
#
# This advisory covers a timing side-channel in RSA PKCS#1 v1.5 *decryption*
# that could allow an attacker to recover plaintext by observing response
# times across many crafted ciphertexts.
#
# This does not affect us because:
#
# 1. We only use RSA for *signing* (PKCS#1 v1.5 with SHA-256 via
# `SigningKey::<Sha256>`). The vulnerable code path is in the decryption
# routine, which we never call.
#
# 2. The private key never processes attacker-controlled input. The proxy
# signs its own JWTs with claims it constructs — no external ciphertext
# is ever decrypted.
#
# 3. No fixed version exists. The `rsa` crate has not released a patch as
# of 0.9.x. If a fix or alternative crate becomes available upstream in
# `multistore-oidc-provider`, we should adopt it and remove this ignore.
#
# Revisit when: the `rsa` crate ships a fix, or `multistore-oidc-provider`
# migrates to a different RSA/signing implementation (e.g. ES256).
"RUSTSEC-2023-0071",

# RUSTSEC-2026-0097: `rand` is unsound with a custom logger using `rand::rng()`
#
# This is a soundness warning (not a vulnerability) affecting `rand` 0.8.5
# and 0.9.2. The unsoundness is only triggerable when a custom `log`
# implementation calls back into `rand::rng()` from within its `log()` method,
# creating reentrant access to thread-local RNG state.
#
# This does not affect us because:
#
# 1. We pull `rand` in only via transitive dependencies (`rsa`,
# `num-bigint-dig`, `quinn-proto`, `object_store`) — we do not call
# `rand::rng()` directly, and our logging setup does not invoke the RNG
# from inside log handlers.
#
# 2. No fixed version exists in the 0.8.x or 0.9.x series. The advisory
# lists no Solution; upstream has not released a patch on a compatible
# semver line, and our transitive deps pin these majors.
#
# Revisit when: `rand` ships a patched 0.8.x/0.9.x release, or our
# transitive deps (`rsa`, `quinn-proto`, `object_store`) upgrade to
# `rand` 0.10+.
"RUSTSEC-2026-0097",
]
41 changes: 29 additions & 12 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,35 +65,52 @@ jobs:
run: cargo install worker-build@0.7.5
- name: Build WASM worker
run: worker-build --release
- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: worker-build
path: build/
retention-days: 1

integration:
name: Integration Tests
runs-on: ubuntu-latest
needs: [build]
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: actions/download-artifact@v4
with:
targets: wasm32-unknown-unknown
- uses: Swatinem/rust-cache@v2
name: worker-build
path: build/
- uses: actions/setup-node@v4
with:
node-version: "22"
- uses: astral-sh/setup-uv@v5
- name: Install wrangler
run: npm install -g wrangler@3
- name: Install worker-build
run: cargo install worker-build@0.7.5
- name: Generate test secrets
# Required by `load_config` in src/config.rs. The actual key material
# doesn't matter for these read-only public-data tests — we just need
# values that parse, since `JwtSigner::from_pem` and `TokenKey::from_base64`
# validate at startup.
run: |
openssl genpkey -algorithm RSA -out /tmp/oidc.pem -pkeyopt rsa_keygen_bits:2048
{
printf 'OIDC_PROVIDER_KEY="'
cat /tmp/oidc.pem
printf '"\n'
echo "SESSION_TOKEN_KEY=$(openssl rand -base64 32)"
echo "AUTH_ISSUER=https://auth.test.example.com"
} > .dev.vars
- name: Start wrangler dev
run: |
# Skip the build command (the artifact is prebuilt). Scope the
# rewrite to the [build] section so other `command =` lines that
# may appear later in the config are untouched.
sed -i '/^\[build\]/,/^\[/ s/^command = .*/command = "echo skip"/' wrangler.toml
wrangler dev --port 8787 &
for i in $(seq 1 60); do
if curl -s --max-time 2 http://localhost:8787/ > /dev/null 2>&1; then
echo "Server ready after ${i}s"
break
fi
sleep 1
done
curl --retry 120 --retry-delay 1 --retry-connrefused --silent --fail http://localhost:8787/ > /dev/null
echo "Server ready"
- name: Run integration tests
run: uvx --with requests pytest tests/ -v

Expand Down
54 changes: 54 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ on:
required: true
CLOUDFLARE_ACCOUNT_ID:
required: true
OIDC_PROVIDER_KEY:
required: true
SESSION_TOKEN_KEY:
required: true
OIDC_PROVIDER_KEY_PREVIOUS:
required: false

jobs:
deploy:
Expand Down Expand Up @@ -88,9 +94,57 @@ jobs:
done
npx wrangler@3 deploy --config ${{ inputs.wrangler_config }} $EXTRA_ARGS

- name: Upload worker secrets
# GitHub environment secrets are the source of truth — anything set
# here is re-uploaded on every deploy, overwriting values set manually
# via `wrangler secret put`. OIDC_PROVIDER_KEY_PREVIOUS is included
# only while a rotation is in progress (i.e. the GitHub secret exists).
env:
OIDC_PROVIDER_KEY: ${{ secrets.OIDC_PROVIDER_KEY }}
SESSION_TOKEN_KEY: ${{ secrets.SESSION_TOKEN_KEY }}
OIDC_PROVIDER_KEY_PREVIOUS: ${{ secrets.OIDC_PROVIDER_KEY_PREVIOUS }}
run: |
# Fail loudly if a required secret is missing/empty in this GitHub
# environment. Without this, jq emits an empty value, `secret bulk`
# succeeds, the job goes green — and every request to the worker
# panics in load_config (the worker cannot boot without these).
[ -n "$OIDC_PROVIDER_KEY" ] || { echo "::error::OIDC_PROVIDER_KEY is not set in the '${{ inputs.environment }}' environment"; exit 1; }
[ -n "$SESSION_TOKEN_KEY" ] || { echo "::error::SESSION_TOKEN_KEY is not set in the '${{ inputs.environment }}' environment"; exit 1; }
WORKER_ARGS=""
if [ -n "${{ inputs.wrangler_env }}" ]; then
WORKER_ARGS="--env ${{ inputs.wrangler_env }}"
fi
if [ -n "${{ inputs.worker_name }}" ]; then
WORKER_ARGS="--name ${{ inputs.worker_name }}"
fi
jq -n '{OIDC_PROVIDER_KEY: env.OIDC_PROVIDER_KEY, SESSION_TOKEN_KEY: env.SESSION_TOKEN_KEY}
+ (if (env.OIDC_PROVIDER_KEY_PREVIOUS // "") != "" then {OIDC_PROVIDER_KEY_PREVIOUS: env.OIDC_PROVIDER_KEY_PREVIOUS} else {} end)' \
| npx wrangler@3 secret bulk $WORKER_ARGS

- name: Get deploy URL
id: url
run: |
WORKER="${{ inputs.worker_name || 'source-data-proxy' }}"
URL="https://${WORKER}.${{ vars.CLOUDFLARE_WORKERS_SUBDOMAIN }}.workers.dev"
echo "deploy_url=$URL" >> "$GITHUB_OUTPUT"

- name: Smoke test
# Confirm the worker actually booted. A missing/malformed secret makes
# load_config panic on the first request (Cloudflare 1101 → HTTP 5xx),
# so this turns a silently-broken deploy into a red job.
run: |
URL="${{ steps.url.outputs.deploy_url }}"
echo "Smoke-testing $URL"
for path in "/" "/.well-known/jwks.json"; do
ok=""
for attempt in 1 2 3 4 5; do
if code=$(curl -fsS -o /dev/null -w '%{http_code}' "$URL$path"); then
echo "ok: $path -> $code"
ok=1
break
fi
echo "attempt $attempt: $path not ready, retrying in 3s…"
sleep 3
done
[ -n "$ok" ] || { echo "::error::smoke test failed for $URL$path"; exit 1; }
done
13 changes: 11 additions & 2 deletions .github/workflows/preview.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,21 @@ jobs:
with:
worker_name: source-data-proxy-pr-${{ github.event.pull_request.number }}
wrangler_config: wrangler.preview.toml
var_overrides: "LOG_LEVEL:DEBUG SOURCE_API_URL:https://staging.source.coop"
service_overrides: "PUBLIC_LOG_STREAM:public-log-stream-pr-${{ github.event.pull_request.number }}"
var_overrides: >-
LOG_LEVEL:DEBUG
SOURCE_API_URL:https://staging.source.coop
OIDC_PROVIDER_ISSUER:https://source-data-proxy-pr-${{ github.event.pull_request.number }}.source-coop.workers.dev
OIDC_PROVIDER_KID:source-proxy-1
AUTH_ISSUER:https://auth.staging.source.coop
service_overrides: >-
PUBLIC_LOG_STREAM:public-log-stream-pr-${{ github.event.pull_request.number }}
environment: preview
secrets:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
OIDC_PROVIDER_KEY: ${{ secrets.OIDC_PROVIDER_KEY }}
SESSION_TOKEN_KEY: ${{ secrets.SESSION_TOKEN_KEY }}
OIDC_PROVIDER_KEY_PREVIOUS: ${{ secrets.OIDC_PROVIDER_KEY_PREVIOUS }}

deploy-public-log-stream:
name: Deploy Public Log Stream
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/production.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ jobs:
secrets:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
OIDC_PROVIDER_KEY: ${{ secrets.OIDC_PROVIDER_KEY }}
SESSION_TOKEN_KEY: ${{ secrets.SESSION_TOKEN_KEY }}
OIDC_PROVIDER_KEY_PREVIOUS: ${{ secrets.OIDC_PROVIDER_KEY_PREVIOUS }}

deploy-public-log-stream:
name: Deploy Public Log Stream
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/staging.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ jobs:
secrets:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
OIDC_PROVIDER_KEY: ${{ secrets.OIDC_PROVIDER_KEY }}
SESSION_TOKEN_KEY: ${{ secrets.SESSION_TOKEN_KEY }}
OIDC_PROVIDER_KEY_PREVIOUS: ${{ secrets.OIDC_PROVIDER_KEY_PREVIOUS }}

deploy-public-log-stream:
name: Deploy Public Log Stream
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
node_modules
docs/plans
.worktrees
.dev.vars
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"rust-analyzer.cargo.target": "wasm32-unknown-unknown"
}
Loading
Loading