Skip to content

Machine publishing via central auth client_credentials tokens - #291

Merged
admdly merged 4 commits into
mainfrom
m2m-publisher-clients
Nov 1, 2026
Merged

admdly merged 4 commits into
mainfrom
m2m-publisher-clients

Conversation

@admdly

@admdly admdly commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

Adds the extensions API as an RFC 8707 resource server for the FOSSBilling/modules CI publisher, using tokens from the central auth service (FOSSBilling/auth#74 already registers https://api.fossbilling.net and the extensions:push/extensions:publish scopes).

Auth layer (src/lib/auth/)

  • AuthPrincipal gains an m2m variant: { clientId, scopes, userId } where userId is the client's synthetic users projection row (m2m:<client_id>) anchoring revision foreign keys and write budgets.
  • New verifier appending to the existing chain: delegates to verifyBearerToken from better-auth/oauth2 (local JWKS verification with caching/kid-miss cooldown), gated on typ: at+jwt, exact iss/aud, and sub === client_id. Scope enforcement stays at route level so insufficient scope answers 403 while bad tokens answer 401.
  • Config: M2M_ISSUER, M2M_JWKS_URL, M2M_AUDIENCE (defaults in wrangler.jsonc).

Extensions v2

  • requireContentWrite(): POST /extensions and PUT /extensions/{id} accept an active human or a mapped client with extensions:push; requireApproveAccess(): approve accepts a moderator or a can_publish client with extensions:publish (the first-party carve-out).
  • Approvals by a client are stamped [auto publisher=<client_id>] in review_note; the mapping and publish flag are re-asserted inside the claim statement, so a concurrent revoke cannot publish.
  • approve()'s ownership claim now matches the developer's ownership_epoch only — matching owner_user_id = submitted_by made machine submissions unapprovable by anyone (transfers bump the epoch, which is the actual staleness guard). Human submissions are unaffected.
  • Machine principals are rejected on all other routes (requireActiveAuth, requireModerator, requireUser, and role-aware reads fall back to anonymous).
  • Migration 0028 + m2m_publisher_clients table; moderator-gated PUT/DELETE/GET /m2m/clients for bindings. Write pacing keys machine traffic as client:<client_id>; per-developer budgets unchanged.

Registers the extensions API as an RFC 8707 resource: publisher
clients authenticate with resource-bound JWTs from the central auth
service (verified locally through Better Auth's resource-server API),
scoped to extensions:push and extensions:publish. Moderators bind
clients to developer profiles via /m2m/clients; can_publish clients
may approve their own developer's pending revisions, stamped
"[auto publisher=<client_id>]" in the review note. Machine
principals are rejected on all other routes; write pacing is keyed
per client.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 1, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
🔒 Security Review ✅ Completed 2026-11-01T17:20:43.346856Z d076889 PR opened

Security findings

Advisory findings (1)

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions

github-actions Bot commented Nov 1, 2026 •

Copy link
Copy Markdown

Dependency Review

The following issues were found:

  • ❌ 1 vulnerable package(s)
  • ✅ 0 package(s) with incompatible licenses
  • ✅ 0 package(s) with invalid SPDX license definitions
  • ✅ 0 package(s) with unknown licenses.
  • ⚠️ 10 packages with OpenSSF Scorecard issues.

View full job summary

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Nov 1, 2026 •

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
api 356a074 Nov 01 2026, 06:46 PM

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛡️ Codex Security Review · Automatically triggered

Here are some automated security review suggestions for this pull request.

Reviewed commit: d076889460

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 29 files

Reply with feedback, questions, or to request a fix.

Turn on auto-fix | Re-trigger cubic

Comment thread test/lib/auth/m2m-bearer.test.ts Resolved
Comment thread src/lib/auth/m2m-bearer.ts Resolved
Comment thread src/services/extensions/v2/README.md Resolved
Comment thread src/services/extensions/v2/db/schema.ts Resolved
Comment thread src/lib/auth/middleware.ts Resolved
Comment thread package.json Resolved
…eview fixes

- m2m_publisher_clients gains ownership_epoch, set from the developer's
  epoch at registration. Ownership transfers go stale: getActiveRef
  reports it, middleware rejects with a re-register hint, and the
  propose/create/approve claim statements re-assert the epoch match, so
  a former owner's client cannot publish under a recipient.
- Registration rebinds to the new epoch (also the un-revoke path).
- Deletion batches now clear publisher mappings on profile deletion and
  account deletion, in guard, so the developers delete can pass.
- Verifier chain runs m2m first (typ-filtered, no wasted assertion
  failures), rate-limits its failure warning, and jose is dropped from
  devDependencies.
- Route docs record the 403s on account routes and the widened approve
  403; m2m client registration distinguishes load errors from 404.
- Tests: wrong issuer, expired token, ownership-transfer staleness and
  rebind recovery; key generation memoized in the test helper.
@admdly

admdly commented Nov 1, 2026

Copy link
Copy Markdown
Contributor Author

CI note: dependency-review fails on esbuild@0.18.20 (moderate, GHSA-67mh-4wv8-2f99) — a pre-existing, dev-optional entry parked under @esbuild-kit/core-utils (drizzle-kit's deprecated dependency) that is unchanged by this PR. It enters the review's changed-package set only because better-auth is newly added, whose optional peer scope references drizzle-kit. Both commits on this branch trip it; every non-adding-better-auth PR passes. Left untouched here (dev tooling only, out of scope); a follow-up either upgrading drizzle-kit off the @esbuild-kit chain or overriding that nested esbuild would clear it repo-wide.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 21 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

Turn on auto-fix | Re-trigger cubic

Comment thread src/services/extensions/v2/db/users.ts Resolved

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 issues found across 2 files (changes from recent commits).

Requires human review: Auto-approval blocked by 1 unresolved issue from previous reviews.

Turn on auto-fix | Re-trigger cubic

drizzle-kit 0.31 loads its config through @esbuild-kit/esm-loader, whose
core-utils dep pins esbuild ~0.18.20 (GHSA-67mh-4wv8-2f99, moderate, fixed
in 0.25). Scoped npm override lifts it to the 0.25 already used elsewhere
in the tree; drizzle-kit check passes against both configs.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 issues found across 2 files (changes from recent commits).

Requires human review: Adds a new machine-to-machine publishing path: M2M bearer verification, a new D1 migration/table, and client-scoped write/approve authorization. This changes auth policy and operational configuration, so it needs human sign-off.

Turn on auto-fix | Re-trigger cubic

@admdly

admdly commented Nov 1, 2026

Copy link
Copy Markdown
Contributor Author

Fixed by 356a074: a scoped npm override lifts the esbuild@0.18.20 pin (GHSA-67mh-4wv8-2f99, fixed in 0.25) held by @esbuild-kit/core-utils in drizzle-kit's deprecated config-loader chain up to ^0.25.12 — the version already used elsewhere in the tree. drizzle-kit check passes against both configs with the bumped version; dependency-review is green.

@admdly admdly self-assigned this Nov 1, 2026
@admdly
admdly merged commit 03ef490 into main Nov 1, 2026
9 checks passed
@admdly
admdly deleted the m2m-publisher-clients branch November 1, 2026 18:57
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.

1 participant