Skip to content

Add extensions API v2 with submissions and auth#149

Merged
admdly merged 5 commits into
mainfrom
feat/ext-v2
Jul 24, 2026
Merged

Add extensions API v2 with submissions and auth#149
admdly merged 5 commits into
mainfrom
feat/ext-v2

Conversation

@admdly

@admdly admdly commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Summary by cubic

Adds Extensions API v2 for self‑service submissions and moderation with bearer token auth. v1 stays read‑only; approved v2 submissions write through to v1 with safe concurrency and stricter validation.

  • New Features

    • Endpoints under /extensions/v2: POST /submissions, GET /submissions/mine, GET /submissions/queue, POST /submissions/{id}/approve, POST /submissions/{id}/reject.
    • Auth via HS256 bearer assertions (Authorization: Bearer <token>) using ASSERTION_SIGNING_SECRET; RFC‑compliant 401s with WWW-Authenticate; moderator checks via users.is_moderator.
    • Ownership enforced: you must own the target author for edits; author IDs are claimable if unowned; approvals upsert authors/extensions atomically, handle case‑insensitive IDs, and revert to pending on write‑through failure.
    • OpenAPI at /extensions/v2/openapi.json and docs UI at /extensions/v2/docs using @hono/zod-openapi and @scalar/hono-api-reference (server set to /extensions/v2); docs titled “FOSSBilling Extensions API (v2)”.
    • Storage and safety: adds owner_user_id to authors and a new extension_submissions table; approve/reject use atomic conditional updates to avoid races; IDs restricted to lowercase slugs; URLs restricted to http(s); rejects non‑finite iat/exp; author.URL matches v1; DB errors are logged and returned as a generic error.
  • Migration

    • Apply D1 migrations for DB_EXTENSIONS: npm run migrate:extensions-v2:local or npm run migrate:extensions-v2:remote (list with :list:local/:list:remote).
    • Set ASSERTION_SIGNING_SECRET in the environment.
    • Ensure a shared users table exists in DB_EXTENSIONS with columns id and is_moderator (1 for moderators).
    • wrangler.jsonc now points DB_EXTENSIONS to the v2 migrations directory.
    • Regenerated Cloudflare Worker type definitions; no action needed.

Written for commit 479a2e4. Summary will update on new commits.

@admdly admdly self-assigned this Jul 24, 2026
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 24, 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 Preview URL Updated (UTC)
✅ Deployment successful!
View logs
api 479a2e4 Commit Preview URL

Branch Preview URL
Jul 24 2026, 10:17 AM

@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 18 files

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

Re-trigger cubic

Comment thread package.json Outdated
Comment thread src/services/extensions/v2/submissions-database.ts Outdated
Comment thread src/services/extensions/v2/submissions-database.ts Outdated
Comment thread src/services/extensions/v2/submissions-database.ts
Comment thread src/services/extensions/v2/interfaces.ts
Comment thread src/services/extensions/v2/submissions-database.ts Outdated
Comment thread src/services/extensions/v2/index.ts
Comment thread src/lib/auth/bearer-assertion.ts
Comment thread src/lib/auth/middleware.ts
Comment thread src/services/extensions/v2/index.ts
Fixes several real correctness/security issues flagged by cubic-dev-ai
on PR #149:

- Concurrent moderators could race approve/reject; the status
  transition is now an atomic conditional UPDATE (WHERE status =
  'pending'), so only one wins and the other gets a clean 409.
- Editing an extension whose stored id differs only by case (legacy
  v1 data) inserted a duplicate row instead of updating the existing
  one; approvals now bind the resolved canonical id for edits.
- A failed approval batch (or a failed reject UPDATE) could be
  reported as success; both now check per-statement/result success.
- Non-finite exp/iat (e.g. 1e999 -> Infinity) passed the bearer
  assertion's payload guard, producing a token that never expires.
- Extension/author ids had no charset restriction; author/website/
  icon_url/download_url URLs accepted any scheme (javascript: etc).
  Restricted ids to a lowercase slug pattern and URLs to http(s).
- author.URL (matching v1's Author.URL) was named `url` in the v2
  schema, silently dropping the field for clients following the v1
  convention.
- DB exception text was returned directly to API callers; now logged
  server-side via the existing logger and replaced with a generic
  message across submissions-database.ts and users-database.ts.
- UsersDatabase.isModerator() didn't distinguish a DB failure from
  "not a moderator", so a DB error surfaced as a bare exception
  instead of the API's structured error shape.
- Bearer auth was case-sensitive on the "Bearer" scheme and didn't
  send WWW-Authenticate on 401s; both now follow RFC 6750.
- OpenAPI doc gaps: missing `servers` entry for the /extensions/v2
  mount path, missing 422 responses on two routes that can actually
  return one, and ErrorResponseSchema didn't include the `details`
  field the validation hook actually sends.
- migrate:extensions-v2 silently defaulted to local D1 (wrangler's
  default when neither --local/--remote is passed); split into
  explicit :local/:remote script variants.
- Documented ASSERTION_SIGNING_SECRET in AGENTS.md (.dev.vars is
  gitignored, so it wasn't visible to reviewers or new contributors).
- assertion-helper.ts's default exp was computed from `now` rather
  than the effective `iat`, so overriding iat alone could produce an
  already-expired test token.

Not changed, by design (see PR reply for reasoning): free-text name
fields aren't charset-restricted (that would break legitimate names;
output escaping belongs to the rendering consumer), legacy authors
with no owner remain unclaimable via v2 this pass, and list-endpoint
pagination is deferred as a feature addition rather than a bug fix.

@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 13 files (changes from recent commits).

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

Re-trigger cubic

Comment thread src/services/extensions/v2/submissions-database.ts
Comment thread src/services/extensions/v2/db/migrations/0001_add_v2_tables.sql Outdated
- submissions-database.ts: if the author/extension write-through fails
  after a successful atomic claim (approve()), best-effort revert the
  submission's status back to 'pending' instead of leaving it stuck
  'approved' with no matching live data. Added a test that forces a
  write failure, confirms the revert, then confirms a retry succeeds
  cleanly.
- 0001_add_v2_tables.sql: fixed the relative path to v1's schema.sql
  in the header comment (was two directories up, needed three from
  db/migrations/).

Addresses the two new cubic-dev-ai findings from the re-review of
94690b1.

@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 4 files (changes from recent commits).

Requires human review: Adds new API v2 with database migration, bearer auth, and moderation endpoints — requires human review for schema change, auth setup, and operational tradeoffs.

Re-trigger cubic

@admdly
admdly merged commit eaccc73 into main Jul 24, 2026
9 checks passed
@admdly
admdly deleted the feat/ext-v2 branch July 24, 2026 10:20
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