fix(sdk): close remaining unsigned audio upload paths from review - #14554
Closed
rickyrombo wants to merge 1 commit into
Closed
fix(sdk): close remaining unsigned audio upload paths from review#14554rickyrombo wants to merge 1 commit into
rickyrombo wants to merge 1 commit into
Conversation
Fixes from review of #14550/#14552: - useUpload: add userId to the two useCallback dep arrays that read it. Without it, an account that resolves after the callbacks are memoized leaves them closed over userId=undefined forever, silently producing unsigned (unclaimable) uploads. - stemsUpload saga: pass the acting user's id to uploadTrackFiles. This flow had no signature at all, so stems added to an existing track would fail to publish once content authorization is enforced. - UploadsApi.createAudioUpload: accept an optional encoded userId, and wire audiusWalletClient into the Storage built by createSdk, so the uploads API surface can sign at all. Examples updated to pass it. - publishTrack: use the already-parsed userId instead of re-decoding the raw param. - Storage.generatePreview: mark userId optional in the JSDoc; add tests covering signed/unsigned request construction and signer recovery. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
🦋 Changeset detectedLatest commit: aa69da2 The changes in this PR will be included in the next version bump. This PR includes changesets to release 4 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
rickyrombo
added a commit
that referenced
this pull request
Aug 8, 2026
… (no signatures) (#14555) Fresh implementation of upload/preview attribution on the unsigned design path. **Supersedes #14550, #14552 and #14554** — the client-side EIP-712 signing approach — which can be closed in favor of this. Server counterpart: `OpenAudio/go-openaudio` branch `feat/content-auth-unsigned` (PR opening alongside), stacked on OpenAudio/go-openaudio#459. ## Design Audio uploads and `generate_preview` carry the id of the user they are made for — plain tus metadata / query parameter, **no signature and no wallet client**. The id is an assertion; what makes the scheme safe is unchanged from the analysis on the server PR: - Asserting someone else's id only ever credits *them* — exercising a claim (naming the cid on a track) happens in a signed, grant-checked entity-manager write. - Upload claims derive from bytes the node received; preview claims require the asserted user to already claim the source cid. - A minted preview cid is useless to the requester: audio never serves by bare cid, and discovery only signs track-resolved cidstream URLs. Dropping the signature is what keeps every SDK flow working — including OAuth apps with no client-side wallet — with nothing but this parameter. No proxy, no wallet plumbing, no timestamp windows. ## BREAKING (major changeset included) - `tracks.uploadTrackFiles` requires `userId` (encoded) - `uploads.createAudioUpload` requires `userId` (encoded) - `Storage.generatePreview` requires `userId` (decoded) and sends it as a query param High-level methods (`createTrack`, `updateTrack`, `uploadTrack`, `publishTrack`) already required `userId` and now thread it through — their callers need no changes. Required rather than optional so an upgraded integrator cannot silently produce unclaimable uploads that fail later at publish. Always explicit, never derived from auth state — a manager or developer-app session can act for more than one user (same convention as `ChatsApi.currentUserId`). ## Client updates - `useUpload`: current account's id on track/stem/cover-art/collection-artwork uploads, `requireUserId()` guard so a missing account fails loudly at the call site, and the id is in every dependency array that reads it - Web stems saga passes the id it already looked up - All four upload examples (web upload / gated-upload / upload-server, mobile upload) + READMEs - Docs site: `sdk/uploads` and `sdk/tracks` pages document the parameter and why it exists ## Testing - sdk: vitest 20/20 (`Storage.test.ts` pins the `userId` query param on `generatePreview`), typecheck clean - common, web: typecheck clean; eslint clean on touched files (2 pre-existing warnings in examples untouched) - Example-app tsconfigs resolve the *published* npm SDK, so their pre-existing typecheck errors are unrelated and unchanged 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Marcus Pasell <marcus@audius.co> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Contributor
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #14552 (which is stacked on #14550). Fixes for the issues found reviewing those two — kept separate so the fixes can be reviewed on their own.
Bug: stale
userIdclosures inuseUploaduploadTrackFilesanduploadStemFilesreaduserIdbut did not list it in theiruseCallbackdeps. On a cold load where the upload page renders beforeuseCurrentAccountresolves, both callbacks memoize overuserId = undefinedand are never recreated when the account arrives — every upload from that session goes out unsigned. Silent today; once enforcement is on it becomes either a rejected upload or an unclaimable cid. One-line fix in each array.Gap: web stems saga never signed
stemsUpload/sagas.tscallsuploadTrackFiles({ audioFile })with no user, even though the decodeduserIdis already in scope a few lines up. Stems added to an existing track through this flow would publish fine today and fail once the gate opens. Now passesId.parse(userId)like theuseUploadstem path does.Gap: the uploads API surface could not sign at all
Two halves, both fixed:
UploadsApi.createAudioUploadtakes an optional encodeduserId(the form callers hold — both examples pass their OAuth profile id straight through) and threads the decoded id into the file metadata.Storagebuilt insidecreateSdknow receivesservices.audiusWalletClient, matching what feat(sdk): sign audio uploads and send the uploading user id #14550 did forcreateSdkWithServices. Without this, a wallet-configured SDK still uploaded unsigned throughsdk.uploads.Both examples updated to pass the id. As before, no wallet or no id means the upload proceeds unsigned and simply never earns an attestation.
Cleanups
publishTrackuseddecodeHashId(params.userId)with the schema-parseduserIdalready in scope; now passes the parsed value. (The identical expression inupdateTrackstays — nothing parsed is in scope there.)generatePreviewJSDoc marksuserIdoptional.Testing
The vitest failure reported in #14552 (
Object.defineProperty called on non-objectat collection) no longer reproduces — the previously-blocked suites now run and pass. AddedStorage.test.tscoveringgeneratePreview: signed requests carrysignature/userId/timestampquery params that recover to the signer against the pinned EIP-712 shape, no-wallet and no-userId requests go out with an empty query string, and non-ok responses throw.packages/sdk: 13/13 tests pass (signUpload,Storage,StorageNodeSelector), typecheck cleanpackages/common,packages/web: typecheck clean🤖 Generated with Claude Code