diff --git a/packages/sdk/src/sdk/api/playlists/PlaylistsApi.ts b/packages/sdk/src/sdk/api/playlists/PlaylistsApi.ts index 4edbc6e1be4..8bd0a4ce543 100644 --- a/packages/sdk/src/sdk/api/playlists/PlaylistsApi.ts +++ b/packages/sdk/src/sdk/api/playlists/PlaylistsApi.ts @@ -80,7 +80,8 @@ export class PlaylistsApi extends GeneratedPlaylistsApi { await this.storage.uploadFile({ file: coverArtFile, onProgress, - template: 'img_square' + template: 'img_square', + auth: this.auth }), (e) => { this.logger.info('Retrying uploadPlaylistCoverArt', e) @@ -461,7 +462,8 @@ export class PlaylistsApi extends GeneratedPlaylistsApi { await this.storage.uploadFile({ file: coverArtFile, onProgress, - template: 'img_square' + template: 'img_square', + auth: this.auth }), (e) => { this.logger.info('Retrying uploadPlaylistCoverArt', e) @@ -477,7 +479,8 @@ export class PlaylistsApi extends GeneratedPlaylistsApi { template: 'audio', options: this.trackUploadHelper.extractMediorumUploadOptions( trackMetadatas[idx]! - ) + ), + auth: this.auth }), (e) => { this.logger.info('Retrying uploadTrackAudio', e) @@ -591,7 +594,8 @@ export class PlaylistsApi extends GeneratedPlaylistsApi { await this.storage.uploadFile({ file: coverArtFile, onProgress, - template: 'img_square' + template: 'img_square', + auth: this.auth }), (e) => { this.logger.info('Retrying uploadPlaylistCoverArt', e) diff --git a/packages/sdk/src/sdk/api/tracks/TracksApi.ts b/packages/sdk/src/sdk/api/tracks/TracksApi.ts index 585c1516880..e756b860fd9 100644 --- a/packages/sdk/src/sdk/api/tracks/TracksApi.ts +++ b/packages/sdk/src/sdk/api/tracks/TracksApi.ts @@ -128,7 +128,8 @@ export class TracksApi extends GeneratedTracksApi { await this.storage.uploadFile({ file: coverArtFile, onProgress, - template: 'img_square' + template: 'img_square', + auth: this.auth }), (e) => { this.logger.info('Retrying uploadTrackCoverArt', e) @@ -141,7 +142,8 @@ export class TracksApi extends GeneratedTracksApi { onProgress, template: 'audio', options: - this.trackUploadHelper.extractMediorumUploadOptions(metadata) + this.trackUploadHelper.extractMediorumUploadOptions(metadata), + auth: this.auth }), (e) => { this.logger.info('Retrying uploadTrackAudio', e) @@ -211,7 +213,8 @@ export class TracksApi extends GeneratedTracksApi { await this.storage.uploadFile({ file: coverArtFile, onProgress, - template: 'img_square' + template: 'img_square', + auth: this.auth }), (e) => { this.logger.info('Retrying uploadTrackCoverArt', e) diff --git a/packages/sdk/src/sdk/api/users/UsersApi.ts b/packages/sdk/src/sdk/api/users/UsersApi.ts index 68ff2d18d41..7b44a079395 100644 --- a/packages/sdk/src/sdk/api/users/UsersApi.ts +++ b/packages/sdk/src/sdk/api/users/UsersApi.ts @@ -70,7 +70,8 @@ export class UsersApi extends GeneratedUsersApi { await this.storage.uploadFile({ file: profilePictureFile, onProgress, - template: 'img_square' + template: 'img_square', + auth: this.auth }), (e) => { this.logger.info('Retrying uploadProfilePicture', e) @@ -82,7 +83,8 @@ export class UsersApi extends GeneratedUsersApi { await this.storage.uploadFile({ file: coverArtFile, onProgress, - template: 'img_backdrop' + template: 'img_backdrop', + auth: this.auth }), (e) => { this.logger.info('Retrying uploadProfileCoverArt', e) diff --git a/packages/sdk/src/sdk/services/Auth/UserAuth.ts b/packages/sdk/src/sdk/services/Auth/UserAuth.ts index 5b6e69ceeba..c5da151d646 100644 --- a/packages/sdk/src/sdk/services/Auth/UserAuth.ts +++ b/packages/sdk/src/sdk/services/Auth/UserAuth.ts @@ -1,4 +1,5 @@ import { GetFn, Hedgehog, SetAuthFn, SetUserFn } from '@audius/hedgehog' +import { personalSign } from '@metamask/eth-sig-util' import { keccak_256 } from '@noble/hashes/sha3' import * as secp from '@noble/secp256k1' import { EIP712TypedData, MessageData, signTypedData } from 'eth-sig-util' @@ -138,8 +139,13 @@ export class UserAuth implements AuthService { }) } - hashAndSign: (data: string) => Promise = () => { - throw new Error('hashAndSign not initialized') + hashAndSign: (data: string) => Promise = async (data) => { + const wallet = this.hedgehog.getWallet() + if (!wallet) throw new Error('No wallet') + return personalSign({ + privateKey: wallet.getPrivateKey(), + data: keccak_256(data) + }) } signTransaction: ( diff --git a/packages/sdk/src/sdk/services/EntityManager/EntityManager.ts b/packages/sdk/src/sdk/services/EntityManager/EntityManager.ts index 3fd4100f5a5..52f87664f44 100644 --- a/packages/sdk/src/sdk/services/EntityManager/EntityManager.ts +++ b/packages/sdk/src/sdk/services/EntityManager/EntityManager.ts @@ -148,7 +148,7 @@ export class EntityManager implements EntityManagerService { ) } else { throw new Error( - `Error making relay request${ + `Error making relay request ${response.status} ${ jsonResponse?.error?.message ? `: ${jsonResponse.error.message}` : '.' }` ) diff --git a/packages/sdk/src/sdk/services/Storage/Storage.ts b/packages/sdk/src/sdk/services/Storage/Storage.ts index 09d013381e1..75ccb91bfa5 100644 --- a/packages/sdk/src/sdk/services/Storage/Storage.ts +++ b/packages/sdk/src/sdk/services/Storage/Storage.ts @@ -60,15 +60,15 @@ export class Storage implements StorageService { auth: AuthService }) { // Generate signature + const signatureData = { upload_id: uploadId, timestamp: Date.now() } - const signature = await auth.hashAndSign( - JSON.stringify(sortObjectKeys(signatureData)) - ) + const sigJson = JSON.stringify(sortObjectKeys(signatureData)) + const signature = await auth.hashAndSign(sigJson) const signatureEnvelope = { - data: JSON.stringify(signatureData), + data: sigJson, signature } @@ -107,12 +107,14 @@ export class Storage implements StorageService { file, onProgress, template, - options = {} + options = {}, + auth }: { file: File onProgress?: ProgressCB template: FileTemplate options?: { [key: string]: string } + auth: AuthService }) { const formData: FormData = new FormData() formData.append('template', template) @@ -125,6 +127,16 @@ export class Storage implements StorageService { file.name ?? 'blob' ) + const signatureData = { + timestamp: Date.now() + } + const sigJson = JSON.stringify(sortObjectKeys(signatureData)) + const signature = await auth.hashAndSign(sigJson) + const signatureEnvelope = { + data: sigJson, + signature + } + // Using axios for now because it supports upload progress, // and Node doesn't support XmlHttpRequest let response: AxiosResponse | null = null @@ -132,6 +144,7 @@ export class Storage implements StorageService { method: 'post', maxContentLength: Infinity, data: formData, + params: { signature: JSON.stringify(signatureEnvelope) }, headers: formData.getBoundary ? { 'Content-Type': `multipart/form-data; boundary=${formData.getBoundary()}` diff --git a/packages/sdk/src/sdk/services/Storage/types.ts b/packages/sdk/src/sdk/services/Storage/types.ts index c2ae820dc9f..d35f8d2e71e 100644 --- a/packages/sdk/src/sdk/services/Storage/types.ts +++ b/packages/sdk/src/sdk/services/Storage/types.ts @@ -26,12 +26,14 @@ export type StorageService = { file, onProgress, template, - options + options, + auth }: { file: File onProgress?: ProgressCB template: FileTemplate options?: { [key: string]: string } + auth: AuthService }) => Promise editFile: ({ uploadId, diff --git a/pkg/mediorum/server/serve_upload.go b/pkg/mediorum/server/serve_upload.go index b4f8d66fcf7..e1e0cf2c519 100644 --- a/pkg/mediorum/server/serve_upload.go +++ b/pkg/mediorum/server/serve_upload.go @@ -127,14 +127,22 @@ func (ss *MediorumServer) postUpload(c echo.Context) error { return c.String(http.StatusServiceUnavailable, "disk is too full to accept new uploads") } - // Parse X-User-Wallet header - userWalletHeader := c.Request().Header.Get("X-User-Wallet-Addr") + // read user wallet from ?signature query string + // ... fall back to (legacy) X-User-Wallet header userWallet := sql.NullString{Valid: false} - if userWalletHeader != "" { + if signerWallet, ok := c.Get("signer-wallet").(string); ok { userWallet = sql.NullString{ - String: userWalletHeader, + String: signerWallet, Valid: true, } + } else { + userWalletHeader := c.Request().Header.Get("X-User-Wallet-Addr") + if userWalletHeader != "" { + userWallet = sql.NullString{ + String: userWalletHeader, + Valid: true, + } + } } // Multipart form diff --git a/pkg/mediorum/server/server_basic_auth.go b/pkg/mediorum/server/server_basic_auth.go index 31ffd0f3802..8775199421f 100644 --- a/pkg/mediorum/server/server_basic_auth.go +++ b/pkg/mediorum/server/server_basic_auth.go @@ -73,7 +73,7 @@ func (ss *MediorumServer) requireUserSignature(next echo.HandlerFunc) echo.Handl }) } else { // check it is for this upload id - if sig.Data.UploadID != id { + if id != "" && sig.Data.UploadID != id { return c.JSON(401, map[string]string{ "error": "signature contains incorrect ID", "detail": fmt.Sprintf("url: %s, signature %s", id, sig.Data.UploadID),