diff --git a/.changeset/relay-rate-limit-auth.md b/.changeset/relay-rate-limit-auth.md new file mode 100644 index 00000000000..d8cdc0cf5ba --- /dev/null +++ b/.changeset/relay-rate-limit-auth.md @@ -0,0 +1,5 @@ +--- +'@audius/sdk': patch +--- + +Add Authorization header and API identification to relay calls to support proper rate limiting diff --git a/packages/sdk/src/sdk/createSdkWithServices.ts b/packages/sdk/src/sdk/createSdkWithServices.ts index 0a6bfcb6c99..dc6b9797f4a 100644 --- a/packages/sdk/src/sdk/createSdkWithServices.ts +++ b/packages/sdk/src/sdk/createSdkWithServices.ts @@ -95,6 +95,7 @@ export const createSdkWithServices = (config: SdkConfig) => { let apiKey = 'apiKey' in config ? config.apiKey : undefined const appName = 'appName' in config ? config.appName : undefined const apiSecret = 'apiSecret' in config ? config.apiSecret : undefined + const bearerToken = 'bearerToken' in config ? config.bearerToken : undefined // Default apiKey to derived key from apiSecret if not provided if (apiSecret && !apiKey) { @@ -102,7 +103,12 @@ export const createSdkWithServices = (config: SdkConfig) => { } // Initialize services - const services = initializeServices({ config, apiKey, apiSecret }) + const services = initializeServices({ + config, + apiKey, + apiSecret, + bearerToken + }) // Warn if using private key in the browser if (apiSecret && isBrowser) { @@ -145,11 +151,13 @@ export const createSdkWithServices = (config: SdkConfig) => { const initializeServices = ({ config, apiKey, - apiSecret + apiSecret, + bearerToken }: { config: SdkConfig apiKey?: string apiSecret?: string + bearerToken?: string }) => { const servicesConfig = config.environment === 'development' ? developmentConfig : productionConfig @@ -179,7 +187,11 @@ const initializeServices = ({ new EntityManagerClient({ ...getDefaultEntityManagerConfig(servicesConfig), audiusWalletClient, - logger + logger, + apiKey, + apiSecret, + bearerToken, + appName: 'appName' in config ? config.appName : undefined }) const storage = diff --git a/packages/sdk/src/sdk/services/EntityManager/EntityManagerClient.ts b/packages/sdk/src/sdk/services/EntityManager/EntityManagerClient.ts index 4477b2616e2..efadb679b6b 100644 --- a/packages/sdk/src/sdk/services/EntityManager/EntityManagerClient.ts +++ b/packages/sdk/src/sdk/services/EntityManager/EntityManagerClient.ts @@ -40,6 +40,10 @@ export class EntityManagerClient implements EntityManagerService { private readonly chainId: number private readonly contractAddress: string private readonly endpoint: string + private readonly apiKey?: string + private readonly apiSecret?: string + private readonly bearerToken?: string + private readonly appName?: string constructor(config_: EntityManagerConfig) { const config = mergeConfigWithDefaults( @@ -51,6 +55,10 @@ export class EntityManagerClient implements EntityManagerService { this.contractAddress = config.contractAddress this.logger = config.logger.createPrefixedLogger('[entity-manager]') this.endpoint = config.endpoint + this.apiKey = config.apiKey + this.apiSecret = config.apiSecret + this.bearerToken = config.bearerToken + this.appName = config.appName } /** @@ -87,13 +95,28 @@ export class EntityManagerClient implements EntityManagerService { const [senderAddress] = await this.audiusWalletClient.getAddresses() const signature = await this.audiusWalletClient.signTypedData(typedData) - const url = `${this.endpoint}/relay` + const params = new URLSearchParams() + if (this.apiKey) params.set('api_key', this.apiKey) + if (this.appName) params.set('app_name', this.appName) + const qs = params.toString() + const url = `${this.endpoint}/relay${qs ? `?${qs}` : ''}` this.logger.info(`Making relay request to ${url}`) + + const headers = new Headers({ + 'Content-Type': 'application/json' + }) + + // Add auth header for API identification and rate limiting + if (this.bearerToken) { + headers.set('Authorization', `Bearer ${this.bearerToken}`) + } else if (this.apiKey && this.apiSecret) { + const credentials = btoa(`${this.apiKey}:${this.apiSecret}`) + headers.set('Authorization', `Basic ${credentials}`) + } + const response = await fetch(url, { method: 'POST', - headers: new Headers({ - 'Content-Type': 'application/json' - }), + headers, body: JSON.stringify({ contractAddress: this.contractAddress, contractRegistryKey: 'EntityManager', diff --git a/packages/sdk/src/sdk/services/EntityManager/types.ts b/packages/sdk/src/sdk/services/EntityManager/types.ts index 54decce02c9..4a12cffecb8 100644 --- a/packages/sdk/src/sdk/services/EntityManager/types.ts +++ b/packages/sdk/src/sdk/services/EntityManager/types.ts @@ -26,6 +26,14 @@ export type EntityManagerConfigInternal = { * The endpoint to use for relays */ endpoint: string + /** API key for relay calls */ + apiKey?: string + /** API secret for relay calls */ + apiSecret?: string + /** Bearer token for relay calls */ + bearerToken?: string + /** App name for relay calls */ + appName?: string } export type EntityManagerConfig = Partial & { audiusWalletClient: AudiusWalletClient