@sp-api-sdk/auth
Version:
Login with Amazon (LWA) authentication for the Amazon Selling Partner API (SP-API)
1 lines • 8.6 kB
Source Map (JSON)
{"version":3,"file":"index.cjs","names":["AxiosError","axios","globalAxios","process","#refreshAccessToken","axios","#accessToken","#accessTokenExpiration","AxiosError","#pendingTokenRequest"],"sources":["../src/error.ts","../src/utils/package.ts","../src/utils/axios.ts","../src/types/scope.ts","../src/index.ts"],"sourcesContent":["import {AxiosError} from 'axios'\n\nimport type {AccessTokenData, AccessTokenQuery} from './types/access-token.js'\n\n/**\n Error thrown when an LWA token request fails.\n\n Wraps the underlying Axios error with a human-readable message that includes\n the HTTP status code (or \"No response\" for network errors).\n */\nexport class SellingPartnerApiAuthError extends AxiosError<AccessTokenData, AccessTokenQuery> {\n /** The original error message from the failed HTTP request. */\n public readonly innerMessage: string\n\n constructor(error: AxiosError<AccessTokenData, AccessTokenQuery>) {\n const message = error.response\n ? `access-token error: Response code ${error.response.status}`\n : 'access-token error: No response'\n\n super(message, error.code, error.config, error.request, error.response)\n\n this.innerMessage = error.message\n this.name = this.constructor.name\n }\n}\n","import {type NormalizedPackageJson, sync as readPackageJson} from 'read-pkg-up'\n\nconst result = readPackageJson()\n\nexport const packageJson: NormalizedPackageJson = result?.packageJson ?? {\n _id: '',\n readme: '',\n name: '@sp-api-sdk/auth',\n version: 'unknown',\n}\n","import globalAxios from 'axios'\n\nimport {packageJson} from './package.js'\n\nexport const axios = globalAxios.create({\n baseURL: 'https://api.amazon.com/auth/',\n timeout: 30_000,\n headers: {\n 'user-agent': `${packageJson.name}/${packageJson.version}`,\n },\n})\n","/** Authorization scopes for grantless Selling Partner API operations. */\nexport enum AuthorizationScope {\n /** Scope for the Notifications API. */\n NOTIFICATIONS = 'sellingpartnerapi::notifications',\n /** Scope for rotating application client credentials. */\n CLIENT_CREDENTIAL_ROTATION = 'sellingpartnerapi::client_credential:rotation',\n}\n","import process from 'node:process'\n\nimport {AxiosError} from 'axios'\nimport {type RequireExactlyOne} from 'type-fest'\n\nimport {SellingPartnerApiAuthError} from './error.js'\nimport {type AccessTokenData, type AccessTokenQuery} from './types/access-token.js'\nimport {type AuthorizationScope} from './types/scope.js'\nimport {axios} from './utils/axios.js'\n\n/**\n Configuration parameters for Selling Partner API authentication.\n\n Both `clientId` and `clientSecret` fall back to the `LWA_CLIENT_ID` and\n `LWA_CLIENT_SECRET` environment variables when omitted.\n `refreshToken` falls back to `LWA_REFRESH_TOKEN`.\n */\nexport interface SellingPartnerAuthParameters {\n /** LWA client identifier. Defaults to the `LWA_CLIENT_ID` environment variable. */\n clientId?: string\n /** LWA client secret. Defaults to the `LWA_CLIENT_SECRET` environment variable. */\n clientSecret?: string\n /** LWA refresh token. Defaults to the `LWA_REFRESH_TOKEN` environment variable. Mutually exclusive with `scopes`. */\n refreshToken?: string\n /** Authorization scopes for grantless operations. Mutually exclusive with `refreshToken`. */\n scopes?: AuthorizationScope[]\n}\n\n/**\n Handles Login with Amazon (LWA) OAuth token management for the Selling Partner API.\n\n Supports both refresh-token and grantless (scope-based) authentication flows.\n Tokens are cached and automatically refreshed when expired. Concurrent calls\n to {@link getAccessToken} are deduplicated into a single request.\n */\nexport class SellingPartnerApiAuth {\n private readonly clientId: string\n private readonly clientSecret: string\n private readonly refreshToken?: string\n private readonly scopes?: AuthorizationScope[]\n\n #accessToken?: string\n #accessTokenExpiration?: Date\n #pendingTokenRequest?: Promise<string>\n\n constructor(\n parameters: RequireExactlyOne<SellingPartnerAuthParameters, 'refreshToken' | 'scopes'>,\n ) {\n const clientId = parameters.clientId ?? process.env.LWA_CLIENT_ID\n\n if (!clientId) {\n throw new Error('Missing required `clientId` configuration value')\n }\n\n const clientSecret = parameters.clientSecret ?? process.env.LWA_CLIENT_SECRET\n\n if (!clientSecret) {\n throw new Error('Missing required `clientSecret` configuration value')\n }\n\n const refreshToken = parameters.refreshToken ?? process.env.LWA_REFRESH_TOKEN\n\n if (!refreshToken && !parameters.scopes) {\n throw new TypeError('Either `refreshToken` or `scopes` must be specified')\n }\n\n this.clientId = clientId\n this.clientSecret = clientSecret\n\n this.refreshToken = refreshToken\n this.scopes = parameters.scopes\n }\n\n async #refreshAccessToken() {\n const body: AccessTokenQuery = {\n client_id: this.clientId,\n client_secret: this.clientSecret,\n ...(this.refreshToken\n ? {\n grant_type: 'refresh_token',\n refresh_token: this.refreshToken,\n }\n : {\n grant_type: 'client_credentials',\n scope: this.scopes!.join(' '),\n }),\n }\n\n try {\n const expiration = new Date()\n\n const {data} = await axios.post<AccessTokenData>('/o2/token', body)\n\n expiration.setSeconds(expiration.getSeconds() + data.expires_in)\n\n this.#accessToken = data.access_token\n this.#accessTokenExpiration = expiration\n\n return data.access_token\n } catch (error: unknown) {\n if (error instanceof AxiosError) {\n throw new SellingPartnerApiAuthError(error)\n }\n\n throw error\n }\n }\n\n /**\n Returns a valid LWA access token, refreshing it if expired.\n\n Concurrent calls while a refresh is in progress share the same request.\n\n @returns The access token string.\n */\n async getAccessToken() {\n if (\n this.#accessToken &&\n (!this.#accessTokenExpiration || Date.now() < this.#accessTokenExpiration.getTime())\n ) {\n return this.#accessToken\n }\n\n // Deduplicate concurrent calls: share the same in-flight request\n if (this.#pendingTokenRequest) {\n return this.#pendingTokenRequest\n }\n\n this.#pendingTokenRequest = this.#refreshAccessToken()\n\n try {\n return await this.#pendingTokenRequest\n } finally {\n this.#pendingTokenRequest = undefined\n }\n }\n\n /**\n Expiration date of the currently cached access token, or `undefined` if no token has been fetched yet.\n */\n protected get accessTokenExpiration() {\n return this.#accessTokenExpiration\n }\n}\n\nexport {SellingPartnerApiAuthError} from './error.js'\nexport {AuthorizationScope} from './types/scope.js'\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAUA,IAAa,6BAAb,cAAgDA,MAAAA,WAA8C;;CAE5F;CAEA,YAAY,OAAsD;EAChE,MAAM,UAAU,MAAM,WAClB,qCAAqC,MAAM,SAAS,WACpD;EAEJ,MAAM,SAAS,MAAM,MAAM,MAAM,QAAQ,MAAM,SAAS,MAAM,QAAQ;EAEtE,KAAK,eAAe,MAAM;EAC1B,KAAK,OAAO,KAAK,YAAY;CAC/B;AACF;ACpBA,MAAa,eAFP,GAAA,YAAA,KAAA,CAE4C,CAAA,EAAQ,eAAe;CACvE,KAAK;CACL,QAAQ;CACR,MAAM;CACN,SAAS;AACX;;;ACLA,MAAaC,UAAQC,MAAAA,QAAY,OAAO;CACtC,SAAS;CACT,SAAS;CACT,SAAS,EACP,cAAc,GAAG,YAAY,KAAK,GAAG,YAAY,UACnD;AACF,CAAC;;;;ACTD,IAAY,qBAAL,yBAAA,oBAAA;;CAEL,mBAAA,mBAAA;;CAEA,mBAAA,gCAAA;;AACF,EAAA,CAAA,CAAA;;;;;;;;;;AC6BA,IAAa,wBAAb,MAAmC;CACjC;CACA;CACA;CACA;CAEA;CACA;CACA;CAEA,YACE,YACA;EACA,MAAM,WAAW,WAAW,YAAYC,aAAAA,QAAQ,IAAI;EAEpD,IAAI,CAAC,UACH,MAAM,IAAI,MAAM,iDAAiD;EAGnE,MAAM,eAAe,WAAW,gBAAgBA,aAAAA,QAAQ,IAAI;EAE5D,IAAI,CAAC,cACH,MAAM,IAAI,MAAM,qDAAqD;EAGvE,MAAM,eAAe,WAAW,gBAAgBA,aAAAA,QAAQ,IAAI;EAE5D,IAAI,CAAC,gBAAgB,CAAC,WAAW,QAC/B,MAAM,IAAI,UAAU,qDAAqD;EAG3E,KAAK,WAAW;EAChB,KAAK,eAAe;EAEpB,KAAK,eAAe;EACpB,KAAK,SAAS,WAAW;CAC3B;CAEA,MAAMC,sBAAsB;EAC1B,MAAM,OAAyB;GAC7B,WAAW,KAAK;GAChB,eAAe,KAAK;GACpB,GAAI,KAAK,eACL;IACE,YAAY;IACZ,eAAe,KAAK;GACtB,IACA;IACE,YAAY;IACZ,OAAO,KAAK,OAAQ,KAAK,GAAG;GAC9B;EACN;EAEA,IAAI;GACF,MAAM,6BAAa,IAAI,KAAK;GAE5B,MAAM,EAAC,SAAQ,MAAMC,QAAM,KAAsB,aAAa,IAAI;GAElE,WAAW,WAAW,WAAW,WAAW,IAAI,KAAK,UAAU;GAE/D,KAAKC,eAAe,KAAK;GACzB,KAAKC,yBAAyB;GAE9B,OAAO,KAAK;EACd,SAAS,OAAgB;GACvB,IAAI,iBAAiBC,MAAAA,YACnB,MAAM,IAAI,2BAA2B,KAAK;GAG5C,MAAM;EACR;CACF;;;;;;;;CASA,MAAM,iBAAiB;EACrB,IACE,KAAKF,iBACJ,CAAC,KAAKC,0BAA0B,KAAK,IAAI,IAAI,KAAKA,uBAAuB,QAAQ,IAElF,OAAO,KAAKD;EAId,IAAI,KAAKG,sBACP,OAAO,KAAKA;EAGd,KAAKA,uBAAuB,KAAKL,oBAAoB;EAErD,IAAI;GACF,OAAO,MAAM,KAAKK;EACpB,UAAU;GACR,KAAKA,uBAAuB,KAAA;EAC9B;CACF;;;;CAKA,IAAc,wBAAwB;EACpC,OAAO,KAAKF;CACd;AACF"}