UNPKG

@sp-api-sdk/auth

Version:

Login with Amazon (LWA) authentication for the Amazon Selling Partner API (SP-API)

124 lines (119 loc) 4.43 kB
import process from "node:process"; import globalAxios, { AxiosError } from "axios"; import { sync } from "read-pkg-up"; //#region src/error.ts /** Error thrown when an LWA token request fails. Wraps the underlying Axios error with a human-readable message that includes the HTTP status code (or "No response" for network errors). */ var SellingPartnerApiAuthError = class extends AxiosError { /** The original error message from the failed HTTP request. */ innerMessage; constructor(error) { const message = error.response ? `access-token error: Response code ${error.response.status}` : "access-token error: No response"; super(message, error.code, error.config, error.request, error.response); this.innerMessage = error.message; this.name = this.constructor.name; } }; const packageJson = sync()?.packageJson ?? { _id: "", readme: "", name: "@sp-api-sdk/auth", version: "unknown" }; //#endregion //#region src/utils/axios.ts const axios = globalAxios.create({ baseURL: "https://api.amazon.com/auth/", timeout: 3e4, headers: { "user-agent": `${packageJson.name}/${packageJson.version}` } }); //#endregion //#region src/types/scope.ts /** Authorization scopes for grantless Selling Partner API operations. */ let AuthorizationScope = /* @__PURE__ */ function(AuthorizationScope) { /** Scope for the Notifications API. */ AuthorizationScope["NOTIFICATIONS"] = "sellingpartnerapi::notifications"; /** Scope for rotating application client credentials. */ AuthorizationScope["CLIENT_CREDENTIAL_ROTATION"] = "sellingpartnerapi::client_credential:rotation"; return AuthorizationScope; }({}); //#endregion //#region src/index.ts /** Handles Login with Amazon (LWA) OAuth token management for the Selling Partner API. Supports both refresh-token and grantless (scope-based) authentication flows. Tokens are cached and automatically refreshed when expired. Concurrent calls to {@link getAccessToken} are deduplicated into a single request. */ var SellingPartnerApiAuth = class { clientId; clientSecret; refreshToken; scopes; #accessToken; #accessTokenExpiration; #pendingTokenRequest; constructor(parameters) { const clientId = parameters.clientId ?? process.env.LWA_CLIENT_ID; if (!clientId) throw new Error("Missing required `clientId` configuration value"); const clientSecret = parameters.clientSecret ?? process.env.LWA_CLIENT_SECRET; if (!clientSecret) throw new Error("Missing required `clientSecret` configuration value"); const refreshToken = parameters.refreshToken ?? process.env.LWA_REFRESH_TOKEN; if (!refreshToken && !parameters.scopes) throw new TypeError("Either `refreshToken` or `scopes` must be specified"); this.clientId = clientId; this.clientSecret = clientSecret; this.refreshToken = refreshToken; this.scopes = parameters.scopes; } async #refreshAccessToken() { const body = { client_id: this.clientId, client_secret: this.clientSecret, ...this.refreshToken ? { grant_type: "refresh_token", refresh_token: this.refreshToken } : { grant_type: "client_credentials", scope: this.scopes.join(" ") } }; try { const expiration = /* @__PURE__ */ new Date(); const { data } = await axios.post("/o2/token", body); expiration.setSeconds(expiration.getSeconds() + data.expires_in); this.#accessToken = data.access_token; this.#accessTokenExpiration = expiration; return data.access_token; } catch (error) { if (error instanceof AxiosError) throw new SellingPartnerApiAuthError(error); throw error; } } /** Returns a valid LWA access token, refreshing it if expired. Concurrent calls while a refresh is in progress share the same request. @returns The access token string. */ async getAccessToken() { if (this.#accessToken && (!this.#accessTokenExpiration || Date.now() < this.#accessTokenExpiration.getTime())) return this.#accessToken; if (this.#pendingTokenRequest) return this.#pendingTokenRequest; this.#pendingTokenRequest = this.#refreshAccessToken(); try { return await this.#pendingTokenRequest; } finally { this.#pendingTokenRequest = void 0; } } /** Expiration date of the currently cached access token, or `undefined` if no token has been fetched yet. */ get accessTokenExpiration() { return this.#accessTokenExpiration; } }; //#endregion export { AuthorizationScope, SellingPartnerApiAuth, SellingPartnerApiAuthError }; //# sourceMappingURL=index.js.map