Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@sp-api-sdk/auth

Package Overview
Dependencies
Maintainers
1
Versions
118
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@sp-api-sdk/auth - npm Package Compare versions

Comparing version
2.2.25
to
3.0.0
+170
dist/index.cjs
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
AuthorizationScope: () => AuthorizationScope,
SellingPartnerApiAuth: () => SellingPartnerApiAuth,
SellingPartnerApiAuthError: () => SellingPartnerApiAuthError
});
module.exports = __toCommonJS(index_exports);
var import_node_process = __toESM(require("process"), 1);
var import_axios3 = require("axios");
// src/error.ts
var import_axios = require("axios");
var SellingPartnerApiAuthError = class extends import_axios.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;
}
};
// src/utils/axios.ts
var import_axios2 = __toESM(require("axios"), 1);
// src/utils/package.ts
var import_read_pkg_up = require("read-pkg-up");
var result = (0, import_read_pkg_up.sync)();
var packageJson = result?.packageJson ?? {
_id: "",
readme: "",
name: "@sp-api-sdk/auth",
version: "unknown"
};
// src/utils/axios.ts
var axios = import_axios2.default.create({
baseURL: "https://api.amazon.com/auth/",
timeout: 3e4,
headers: {
"user-agent": `${packageJson.name}/${packageJson.version}`
}
});
// src/types/scope.ts
var AuthorizationScope = /* @__PURE__ */ ((AuthorizationScope2) => {
AuthorizationScope2["NOTIFICATIONS"] = "sellingpartnerapi::notifications";
AuthorizationScope2["CLIENT_CREDENTIAL_ROTATION"] = "sellingpartnerapi::client_credential:rotation";
return AuthorizationScope2;
})(AuthorizationScope || {});
// src/index.ts
var SellingPartnerApiAuth = class {
clientId;
clientSecret;
refreshToken;
scopes;
#accessToken;
#accessTokenExpiration;
#pendingTokenRequest;
constructor(parameters) {
const clientId = parameters.clientId ?? import_node_process.default.env.LWA_CLIENT_ID;
const clientSecret = parameters.clientSecret ?? import_node_process.default.env.LWA_CLIENT_SECRET;
const refreshToken = parameters.refreshToken ?? import_node_process.default.env.LWA_REFRESH_TOKEN;
if (!clientId) {
throw new Error("Missing required `clientId` configuration value");
}
if (!clientSecret) {
throw new Error("Missing required `clientSecret` configuration value");
}
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;
}
/**
* 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;
}
}
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 import_axios3.AxiosError) {
throw new SellingPartnerApiAuthError(error);
}
throw error;
}
}
/**
* Expiration date of the currently cached access token, or `undefined` if no token has been fetched yet.
*/
get accessTokenExpiration() {
return this.#accessTokenExpiration;
}
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
AuthorizationScope,
SellingPartnerApiAuth,
SellingPartnerApiAuthError
});
//# sourceMappingURL=index.cjs.map
{"version":3,"sources":["../src/index.ts","../src/error.ts","../src/utils/axios.ts","../src/utils/package.ts","../src/types/scope.ts"],"sourcesContent":["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 const clientSecret = parameters.clientSecret ?? process.env.LWA_CLIENT_SECRET\n const refreshToken = parameters.refreshToken ?? process.env.LWA_REFRESH_TOKEN\n\n if (!clientId) {\n throw new Error('Missing required `clientId` configuration value')\n }\n\n if (!clientSecret) {\n throw new Error('Missing required `clientSecret` configuration value')\n }\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 /**\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 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 * 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","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 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","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","/** 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"],"mappings":"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","names":["import_axios","import_axios","readPackageJson","globalAxios","AuthorizationScope","process"]}
import { RequireExactlyOne } from 'type-fest';
import { AxiosError } from 'axios';
/** Authorization scopes for grantless Selling Partner API operations. */
declare enum AuthorizationScope {
/** Scope for the Notifications API. */
NOTIFICATIONS = "sellingpartnerapi::notifications",
/** Scope for rotating application client credentials. */
CLIENT_CREDENTIAL_ROTATION = "sellingpartnerapi::client_credential:rotation"
}
interface BaseAccessTokenQuery {
client_id: string;
client_secret: string;
}
type RefreshTokenAccessTokenQuery = {
grant_type: 'refresh_token';
refresh_token: string;
} & BaseAccessTokenQuery;
type ClientCredentialsAccessTokenQuery = {
grant_type: 'client_credentials';
scope: string;
} & BaseAccessTokenQuery;
/** Request body for the LWA token endpoint. */
type AccessTokenQuery = RefreshTokenAccessTokenQuery | ClientCredentialsAccessTokenQuery;
/** Response body from the LWA token endpoint. */
interface AccessTokenData {
access_token: string;
refresh_token?: string;
token_type: string;
expires_in: number;
}
/**
* 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).
*/
declare class SellingPartnerApiAuthError extends AxiosError<AccessTokenData, AccessTokenQuery> {
/** The original error message from the failed HTTP request. */
readonly innerMessage: string;
constructor(error: AxiosError<AccessTokenData, AccessTokenQuery>);
}
/**
* Configuration parameters for Selling Partner API authentication.
*
* Both `clientId` and `clientSecret` fall back to the `LWA_CLIENT_ID` and
* `LWA_CLIENT_SECRET` environment variables when omitted.
* `refreshToken` falls back to `LWA_REFRESH_TOKEN`.
*/
interface SellingPartnerAuthParameters {
/** LWA client identifier. Defaults to the `LWA_CLIENT_ID` environment variable. */
clientId?: string;
/** LWA client secret. Defaults to the `LWA_CLIENT_SECRET` environment variable. */
clientSecret?: string;
/** LWA refresh token. Defaults to the `LWA_REFRESH_TOKEN` environment variable. Mutually exclusive with `scopes`. */
refreshToken?: string;
/** Authorization scopes for grantless operations. Mutually exclusive with `refreshToken`. */
scopes?: AuthorizationScope[];
}
/**
* 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.
*/
declare class SellingPartnerApiAuth {
#private;
private readonly clientId;
private readonly clientSecret;
private readonly refreshToken?;
private readonly scopes?;
constructor(parameters: RequireExactlyOne<SellingPartnerAuthParameters, 'refreshToken' | 'scopes'>);
/**
* 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.
*/
getAccessToken(): Promise<string>;
/**
* Expiration date of the currently cached access token, or `undefined` if no token has been fetched yet.
*/
protected get accessTokenExpiration(): Date | undefined;
}
export { AuthorizationScope, SellingPartnerApiAuth, SellingPartnerApiAuthError, type SellingPartnerAuthParameters };
import { RequireExactlyOne } from 'type-fest';
import { AxiosError } from 'axios';
/** Authorization scopes for grantless Selling Partner API operations. */
declare enum AuthorizationScope {
/** Scope for the Notifications API. */
NOTIFICATIONS = "sellingpartnerapi::notifications",
/** Scope for rotating application client credentials. */
CLIENT_CREDENTIAL_ROTATION = "sellingpartnerapi::client_credential:rotation"
}
interface BaseAccessTokenQuery {
client_id: string;
client_secret: string;
}
type RefreshTokenAccessTokenQuery = {
grant_type: 'refresh_token';
refresh_token: string;
} & BaseAccessTokenQuery;
type ClientCredentialsAccessTokenQuery = {
grant_type: 'client_credentials';
scope: string;
} & BaseAccessTokenQuery;
/** Request body for the LWA token endpoint. */
type AccessTokenQuery = RefreshTokenAccessTokenQuery | ClientCredentialsAccessTokenQuery;
/** Response body from the LWA token endpoint. */
interface AccessTokenData {
access_token: string;
refresh_token?: string;
token_type: string;
expires_in: number;
}
/**
* 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).
*/
declare class SellingPartnerApiAuthError extends AxiosError<AccessTokenData, AccessTokenQuery> {
/** The original error message from the failed HTTP request. */
readonly innerMessage: string;
constructor(error: AxiosError<AccessTokenData, AccessTokenQuery>);
}
/**
* Configuration parameters for Selling Partner API authentication.
*
* Both `clientId` and `clientSecret` fall back to the `LWA_CLIENT_ID` and
* `LWA_CLIENT_SECRET` environment variables when omitted.
* `refreshToken` falls back to `LWA_REFRESH_TOKEN`.
*/
interface SellingPartnerAuthParameters {
/** LWA client identifier. Defaults to the `LWA_CLIENT_ID` environment variable. */
clientId?: string;
/** LWA client secret. Defaults to the `LWA_CLIENT_SECRET` environment variable. */
clientSecret?: string;
/** LWA refresh token. Defaults to the `LWA_REFRESH_TOKEN` environment variable. Mutually exclusive with `scopes`. */
refreshToken?: string;
/** Authorization scopes for grantless operations. Mutually exclusive with `refreshToken`. */
scopes?: AuthorizationScope[];
}
/**
* 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.
*/
declare class SellingPartnerApiAuth {
#private;
private readonly clientId;
private readonly clientSecret;
private readonly refreshToken?;
private readonly scopes?;
constructor(parameters: RequireExactlyOne<SellingPartnerAuthParameters, 'refreshToken' | 'scopes'>);
/**
* 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.
*/
getAccessToken(): Promise<string>;
/**
* Expiration date of the currently cached access token, or `undefined` if no token has been fetched yet.
*/
protected get accessTokenExpiration(): Date | undefined;
}
export { AuthorizationScope, SellingPartnerApiAuth, SellingPartnerApiAuthError, type SellingPartnerAuthParameters };
// src/index.ts
import process from "process";
import { AxiosError as AxiosError2 } from "axios";
// src/error.ts
import { AxiosError } from "axios";
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;
}
};
// src/utils/axios.ts
import globalAxios from "axios";
// src/utils/package.ts
import { sync as readPackageJson } from "read-pkg-up";
var result = readPackageJson();
var packageJson = result?.packageJson ?? {
_id: "",
readme: "",
name: "@sp-api-sdk/auth",
version: "unknown"
};
// src/utils/axios.ts
var axios = globalAxios.create({
baseURL: "https://api.amazon.com/auth/",
timeout: 3e4,
headers: {
"user-agent": `${packageJson.name}/${packageJson.version}`
}
});
// src/types/scope.ts
var AuthorizationScope = /* @__PURE__ */ ((AuthorizationScope2) => {
AuthorizationScope2["NOTIFICATIONS"] = "sellingpartnerapi::notifications";
AuthorizationScope2["CLIENT_CREDENTIAL_ROTATION"] = "sellingpartnerapi::client_credential:rotation";
return AuthorizationScope2;
})(AuthorizationScope || {});
// src/index.ts
var SellingPartnerApiAuth = class {
clientId;
clientSecret;
refreshToken;
scopes;
#accessToken;
#accessTokenExpiration;
#pendingTokenRequest;
constructor(parameters) {
const clientId = parameters.clientId ?? process.env.LWA_CLIENT_ID;
const clientSecret = parameters.clientSecret ?? process.env.LWA_CLIENT_SECRET;
const refreshToken = parameters.refreshToken ?? process.env.LWA_REFRESH_TOKEN;
if (!clientId) {
throw new Error("Missing required `clientId` configuration value");
}
if (!clientSecret) {
throw new Error("Missing required `clientSecret` configuration value");
}
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;
}
/**
* 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;
}
}
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 AxiosError2) {
throw new SellingPartnerApiAuthError(error);
}
throw error;
}
}
/**
* Expiration date of the currently cached access token, or `undefined` if no token has been fetched yet.
*/
get accessTokenExpiration() {
return this.#accessTokenExpiration;
}
};
export {
AuthorizationScope,
SellingPartnerApiAuth,
SellingPartnerApiAuthError
};
//# sourceMappingURL=index.js.map
{"version":3,"sources":["../src/index.ts","../src/error.ts","../src/utils/axios.ts","../src/utils/package.ts","../src/types/scope.ts"],"sourcesContent":["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 const clientSecret = parameters.clientSecret ?? process.env.LWA_CLIENT_SECRET\n const refreshToken = parameters.refreshToken ?? process.env.LWA_REFRESH_TOKEN\n\n if (!clientId) {\n throw new Error('Missing required `clientId` configuration value')\n }\n\n if (!clientSecret) {\n throw new Error('Missing required `clientSecret` configuration value')\n }\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 /**\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 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 * 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","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 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","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","/** 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"],"mappings":"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","names":["AxiosError","AuthorizationScope","AxiosError"]}
+22
-11

@@ -5,19 +5,30 @@ {

"description": "Amazon Selling Partner API authentication package",
"version": "2.2.25",
"main": "dist/cjs/index.js",
"module": "dist/es/index.js",
"types": "dist/types/index.d.ts",
"version": "3.0.0",
"license": "MIT",
"type": "module",
"sideEffects": false,
"source": "./src/index.ts",
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
}
},
"publishConfig": {
"access": "public"
},
"directories": {
"lib": "dist"
},
"files": [
"dist/**/*.js",
"dist/**/*.d.ts"
"dist"
],
"dependencies": {
"axios": "^1.15.2",
"axios": "^1.16.1",
"read-pkg-up": "^7.0.1"

@@ -44,3 +55,3 @@ },

],
"gitHead": "cc3ed7e58346bf7a4110ed8f1353aae840f294e2"
"gitHead": "40177b741b5e9299d449d10c8e41a9a798aec593"
}

@@ -23,6 +23,6 @@ # `@sp-api-sdk/auth`

// `clientId` and `clientSecret` default to the `LWA_CLIENT_ID` and
// `LWA_CLIENT_SECRET` environment variables.
const auth = new SellingPartnerApiAuth({
clientId: process.env.LWA_CLIENT_ID,
clientSecret: process.env.LWA_CLIENT_SECRET,
refreshToken: "Atzr|…",
refreshToken: await getRefreshTokenForSeller(sellerId),
});

@@ -35,3 +35,3 @@

These constructor options can be passed using environment variables:
The following constructor options default to environment variables when omitted. You can still pass them explicitly to override the defaults.

@@ -53,5 +53,5 @@ | Property Name | Environment variable |

// `clientId` and `clientSecret` default to the `LWA_CLIENT_ID` and
// `LWA_CLIENT_SECRET` environment variables.
const auth = new SellingPartnerApiAuth({
clientId: process.env.LWA_CLIENT_ID,
clientSecret: process.env.LWA_CLIENT_SECRET,
scopes: [AuthorizationScope.NOTIFICATIONS],

@@ -58,0 +58,0 @@ });

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SellingPartnerApiAuthError = void 0;
const axios_1 = require("axios");
/**
* 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).
*/
class SellingPartnerApiAuthError extends axios_1.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;
}
}
exports.SellingPartnerApiAuthError = SellingPartnerApiAuthError;
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AuthorizationScope = exports.SellingPartnerApiAuthError = exports.SellingPartnerApiAuth = void 0;
const node_process_1 = __importDefault(require("node:process"));
const axios_1 = require("axios");
const error_1 = require("./error");
const axios_2 = require("./utils/axios");
/**
* 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.
*/
class SellingPartnerApiAuth {
clientId;
clientSecret;
refreshToken;
scopes;
#accessToken;
#accessTokenExpiration;
#pendingTokenRequest;
constructor(parameters) {
const clientId = parameters.clientId ?? node_process_1.default.env.LWA_CLIENT_ID;
const clientSecret = parameters.clientSecret ?? node_process_1.default.env.LWA_CLIENT_SECRET;
const refreshToken = parameters.refreshToken ?? node_process_1.default.env.LWA_REFRESH_TOKEN;
if (!clientId) {
throw new Error('Missing required `clientId` configuration value');
}
if (!clientSecret) {
throw new Error('Missing required `clientSecret` configuration value');
}
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;
}
/**
* 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;
}
// Deduplicate concurrent calls: share the same in-flight request
if (this.#pendingTokenRequest) {
return this.#pendingTokenRequest;
}
this.#pendingTokenRequest = this.#refreshAccessToken();
try {
return await this.#pendingTokenRequest;
}
finally {
this.#pendingTokenRequest = undefined;
}
}
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 = new Date();
const { data } = await axios_2.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 axios_1.AxiosError) {
throw new error_1.SellingPartnerApiAuthError(error);
}
throw error;
}
}
/**
* Expiration date of the currently cached access token, or `undefined` if no token has been fetched yet.
*/
get accessTokenExpiration() {
return this.#accessTokenExpiration;
}
}
exports.SellingPartnerApiAuth = SellingPartnerApiAuth;
var error_2 = require("./error");
Object.defineProperty(exports, "SellingPartnerApiAuthError", { enumerable: true, get: function () { return error_2.SellingPartnerApiAuthError; } });
var scope_1 = require("./types/scope");
Object.defineProperty(exports, "AuthorizationScope", { enumerable: true, get: function () { return scope_1.AuthorizationScope; } });
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AuthorizationScope = void 0;
/** Authorization scopes for grantless Selling Partner API operations. */
var AuthorizationScope;
(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";
})(AuthorizationScope || (exports.AuthorizationScope = AuthorizationScope = {}));
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.axios = void 0;
const axios_1 = __importDefault(require("axios"));
const package_1 = require("./package");
exports.axios = axios_1.default.create({
baseURL: 'https://api.amazon.com/auth/',
timeout: 30_000,
headers: {
'user-agent': `${package_1.packageJson.name}/${package_1.packageJson.version}`,
},
});
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.packageJson = void 0;
const read_pkg_up_1 = require("read-pkg-up");
const result = (0, read_pkg_up_1.sync)();
exports.packageJson = result?.packageJson ?? {
_id: '',
readme: '',
name: '@sp-api-sdk/auth',
version: 'unknown',
};
import { AxiosError } from 'axios';
/**
* 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).
*/
export class SellingPartnerApiAuthError 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;
}
}
import process from 'node:process';
import { AxiosError } from 'axios';
import { SellingPartnerApiAuthError } from './error';
import { axios } from './utils/axios';
/**
* 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.
*/
export class SellingPartnerApiAuth {
clientId;
clientSecret;
refreshToken;
scopes;
#accessToken;
#accessTokenExpiration;
#pendingTokenRequest;
constructor(parameters) {
const clientId = parameters.clientId ?? process.env.LWA_CLIENT_ID;
const clientSecret = parameters.clientSecret ?? process.env.LWA_CLIENT_SECRET;
const refreshToken = parameters.refreshToken ?? process.env.LWA_REFRESH_TOKEN;
if (!clientId) {
throw new Error('Missing required `clientId` configuration value');
}
if (!clientSecret) {
throw new Error('Missing required `clientSecret` configuration value');
}
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;
}
/**
* 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;
}
// Deduplicate concurrent calls: share the same in-flight request
if (this.#pendingTokenRequest) {
return this.#pendingTokenRequest;
}
this.#pendingTokenRequest = this.#refreshAccessToken();
try {
return await this.#pendingTokenRequest;
}
finally {
this.#pendingTokenRequest = undefined;
}
}
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 = 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;
}
}
/**
* Expiration date of the currently cached access token, or `undefined` if no token has been fetched yet.
*/
get accessTokenExpiration() {
return this.#accessTokenExpiration;
}
}
export { SellingPartnerApiAuthError } from './error';
export { AuthorizationScope } from './types/scope';
/** Authorization scopes for grantless Selling Partner API operations. */
export var AuthorizationScope;
(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";
})(AuthorizationScope || (AuthorizationScope = {}));
import globalAxios from 'axios';
import { packageJson } from './package';
export const axios = globalAxios.create({
baseURL: 'https://api.amazon.com/auth/',
timeout: 30_000,
headers: {
'user-agent': `${packageJson.name}/${packageJson.version}`,
},
});
import { sync as readPackageJson } from 'read-pkg-up';
const result = readPackageJson();
export const packageJson = result?.packageJson ?? {
_id: '',
readme: '',
name: '@sp-api-sdk/auth',
version: 'unknown',
};
import { AxiosError } from 'axios';
import type { AccessTokenData, AccessTokenQuery } from './types/access-token';
/**
* 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).
*/
export declare class SellingPartnerApiAuthError extends AxiosError<AccessTokenData, AccessTokenQuery> {
/** The original error message from the failed HTTP request. */
readonly innerMessage: string;
constructor(error: AxiosError<AccessTokenData, AccessTokenQuery>);
}
import { type RequireExactlyOne } from 'type-fest';
import { type AuthorizationScope } from './types/scope';
/**
* Configuration parameters for Selling Partner API authentication.
*
* Both `clientId` and `clientSecret` fall back to the `LWA_CLIENT_ID` and
* `LWA_CLIENT_SECRET` environment variables when omitted.
* `refreshToken` falls back to `LWA_REFRESH_TOKEN`.
*/
export interface SellingPartnerAuthParameters {
/** LWA client identifier. Defaults to the `LWA_CLIENT_ID` environment variable. */
clientId?: string;
/** LWA client secret. Defaults to the `LWA_CLIENT_SECRET` environment variable. */
clientSecret?: string;
/** LWA refresh token. Defaults to the `LWA_REFRESH_TOKEN` environment variable. Mutually exclusive with `scopes`. */
refreshToken?: string;
/** Authorization scopes for grantless operations. Mutually exclusive with `refreshToken`. */
scopes?: AuthorizationScope[];
}
/**
* 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.
*/
export declare class SellingPartnerApiAuth {
#private;
private readonly clientId;
private readonly clientSecret;
private readonly refreshToken?;
private readonly scopes?;
constructor(parameters: RequireExactlyOne<SellingPartnerAuthParameters, 'refreshToken' | 'scopes'>);
/**
* 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.
*/
getAccessToken(): Promise<string>;
/**
* Expiration date of the currently cached access token, or `undefined` if no token has been fetched yet.
*/
protected get accessTokenExpiration(): Date | undefined;
}
export { SellingPartnerApiAuthError } from './error';
export { AuthorizationScope } from './types/scope';
interface BaseAccessTokenQuery {
client_id: string;
client_secret: string;
}
type RefreshTokenAccessTokenQuery = {
grant_type: 'refresh_token';
refresh_token: string;
} & BaseAccessTokenQuery;
type ClientCredentialsAccessTokenQuery = {
grant_type: 'client_credentials';
scope: string;
} & BaseAccessTokenQuery;
/** Request body for the LWA token endpoint. */
export type AccessTokenQuery = RefreshTokenAccessTokenQuery | ClientCredentialsAccessTokenQuery;
/** Response body from the LWA token endpoint. */
export interface AccessTokenData {
access_token: string;
refresh_token?: string;
token_type: string;
expires_in: number;
}
export {};
/** Authorization scopes for grantless Selling Partner API operations. */
export declare enum AuthorizationScope {
/** Scope for the Notifications API. */
NOTIFICATIONS = "sellingpartnerapi::notifications",
/** Scope for rotating application client credentials. */
CLIENT_CREDENTIAL_ROTATION = "sellingpartnerapi::client_credential:rotation"
}
import globalAxios from 'axios';
export declare const axios: globalAxios.AxiosInstance;
import { type NormalizedPackageJson } from 'read-pkg-up';
export declare const packageJson: NormalizedPackageJson;