ebay-mcp 1.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +586 -0
- package/build/api/account-management/account.d.ts +216 -0
- package/build/api/account-management/account.js +305 -0
- package/build/api/analytics-and-report/analytics.d.ts +33 -0
- package/build/api/analytics-and-report/analytics.js +102 -0
- package/build/api/client.d.ts +89 -0
- package/build/api/client.js +343 -0
- package/build/api/communication/feedback.d.ts +45 -0
- package/build/api/communication/feedback.js +119 -0
- package/build/api/communication/message.d.ts +55 -0
- package/build/api/communication/message.js +131 -0
- package/build/api/communication/negotiation.d.ts +39 -0
- package/build/api/communication/negotiation.js +97 -0
- package/build/api/communication/notification.d.ts +128 -0
- package/build/api/communication/notification.js +373 -0
- package/build/api/index.d.ts +96 -0
- package/build/api/index.js +121 -0
- package/build/api/listing-management/inventory.d.ts +216 -0
- package/build/api/listing-management/inventory.js +633 -0
- package/build/api/listing-metadata/metadata.d.ts +154 -0
- package/build/api/listing-metadata/metadata.js +485 -0
- package/build/api/listing-metadata/taxonomy.d.ts +38 -0
- package/build/api/listing-metadata/taxonomy.js +58 -0
- package/build/api/marketing-and-promotions/marketing.d.ts +395 -0
- package/build/api/marketing-and-promotions/marketing.js +565 -0
- package/build/api/marketing-and-promotions/recommendation.d.ts +20 -0
- package/build/api/marketing-and-promotions/recommendation.js +32 -0
- package/build/api/order-management/dispute.d.ts +65 -0
- package/build/api/order-management/dispute.js +69 -0
- package/build/api/order-management/fulfillment.d.ts +80 -0
- package/build/api/order-management/fulfillment.js +89 -0
- package/build/api/other/compliance.d.ts +26 -0
- package/build/api/other/compliance.js +47 -0
- package/build/api/other/edelivery.d.ts +153 -0
- package/build/api/other/edelivery.js +219 -0
- package/build/api/other/identity.d.ts +17 -0
- package/build/api/other/identity.js +24 -0
- package/build/api/other/translation.d.ts +14 -0
- package/build/api/other/translation.js +22 -0
- package/build/api/other/vero.d.ts +30 -0
- package/build/api/other/vero.js +48 -0
- package/build/auth/oauth-metadata.d.ts +46 -0
- package/build/auth/oauth-metadata.js +59 -0
- package/build/auth/oauth-middleware.d.ts +35 -0
- package/build/auth/oauth-middleware.js +99 -0
- package/build/auth/oauth-types.d.ts +66 -0
- package/build/auth/oauth-types.js +4 -0
- package/build/auth/oauth.d.ts +93 -0
- package/build/auth/oauth.js +383 -0
- package/build/auth/scope-utils.d.ts +70 -0
- package/build/auth/scope-utils.js +304 -0
- package/build/auth/token-verifier.d.ts +57 -0
- package/build/auth/token-verifier.js +172 -0
- package/build/config/environment.d.ts +61 -0
- package/build/config/environment.js +260 -0
- package/build/index.d.ts +1 -0
- package/build/index.js +98 -0
- package/build/schemas/account-management/account.d.ts +5324 -0
- package/build/schemas/account-management/account.js +366 -0
- package/build/schemas/analytics/analytics.d.ts +167 -0
- package/build/schemas/analytics/analytics.js +191 -0
- package/build/schemas/communication/messages.d.ts +1872 -0
- package/build/schemas/communication/messages.js +348 -0
- package/build/schemas/fulfillment/orders.d.ts +4655 -0
- package/build/schemas/fulfillment/orders.js +317 -0
- package/build/schemas/index.d.ts +2100 -0
- package/build/schemas/index.js +68 -0
- package/build/schemas/inventory-management/inventory.d.ts +6419 -0
- package/build/schemas/inventory-management/inventory.js +450 -0
- package/build/schemas/marketing/marketing.d.ts +14181 -0
- package/build/schemas/marketing/marketing.js +1088 -0
- package/build/schemas/metadata/metadata.d.ts +5259 -0
- package/build/schemas/metadata/metadata.js +614 -0
- package/build/schemas/other/other-apis.d.ts +257 -0
- package/build/schemas/other/other-apis.js +372 -0
- package/build/schemas/taxonomy/taxonomy.d.ts +215 -0
- package/build/schemas/taxonomy/taxonomy.js +571 -0
- package/build/scripts/auto-setup.d.ts +12 -0
- package/build/scripts/auto-setup.js +277 -0
- package/build/scripts/diagnostics.d.ts +8 -0
- package/build/scripts/diagnostics.js +299 -0
- package/build/scripts/download-specs.d.ts +1 -0
- package/build/scripts/download-specs.js +116 -0
- package/build/scripts/interactive-setup.d.ts +21 -0
- package/build/scripts/interactive-setup.js +723 -0
- package/build/server-http.d.ts +11 -0
- package/build/server-http.js +361 -0
- package/build/tools/definitions/account-with-schemas.d.ts +39 -0
- package/build/tools/definitions/account-with-schemas.js +170 -0
- package/build/tools/definitions/account.d.ts +12 -0
- package/build/tools/definitions/account.js +428 -0
- package/build/tools/definitions/analytics.d.ts +25 -0
- package/build/tools/definitions/analytics.js +66 -0
- package/build/tools/definitions/communication.d.ts +12 -0
- package/build/tools/definitions/communication.js +151 -0
- package/build/tools/definitions/fulfillment.d.ts +12 -0
- package/build/tools/definitions/fulfillment.js +326 -0
- package/build/tools/definitions/index.d.ts +25 -0
- package/build/tools/definitions/index.js +37 -0
- package/build/tools/definitions/inventory.d.ts +12 -0
- package/build/tools/definitions/inventory.js +429 -0
- package/build/tools/definitions/marketing.d.ts +12 -0
- package/build/tools/definitions/marketing.js +1095 -0
- package/build/tools/definitions/metadata.d.ts +12 -0
- package/build/tools/definitions/metadata.js +188 -0
- package/build/tools/definitions/other.d.ts +13 -0
- package/build/tools/definitions/other.js +309 -0
- package/build/tools/definitions/taxonomy.d.ts +25 -0
- package/build/tools/definitions/taxonomy.js +64 -0
- package/build/tools/definitions/token-management.d.ts +35 -0
- package/build/tools/definitions/token-management.js +103 -0
- package/build/tools/index.d.ts +11 -0
- package/build/tools/index.js +1003 -0
- package/build/tools/schemas.d.ts +14764 -0
- package/build/tools/schemas.js +667 -0
- package/build/tools/tool-definitions.d.ts +35 -0
- package/build/tools/tool-definitions.js +3534 -0
- package/build/types/application-settings/developerAnalyticsV1BetaOas3.d.ts +197 -0
- package/build/types/application-settings/developerAnalyticsV1BetaOas3.js +5 -0
- package/build/types/application-settings/developerClientRegistrationV1Oas3.d.ts +155 -0
- package/build/types/application-settings/developerClientRegistrationV1Oas3.js +5 -0
- package/build/types/application-settings/developerKeyManagementV1Oas3.d.ts +246 -0
- package/build/types/application-settings/developerKeyManagementV1Oas3.js +5 -0
- package/build/types/ebay-enums.d.ts +1204 -0
- package/build/types/ebay-enums.js +1330 -0
- package/build/types/ebay.d.ts +143 -0
- package/build/types/ebay.js +123 -0
- package/build/types/index.d.ts +6 -0
- package/build/types/index.js +10 -0
- package/build/types/sell-apps/account-management/sellAccountV1Oas3.d.ts +2579 -0
- package/build/types/sell-apps/account-management/sellAccountV1Oas3.js +5 -0
- package/build/types/sell-apps/analytics-and-report/sellAnalyticsV1Oas3.d.ts +446 -0
- package/build/types/sell-apps/analytics-and-report/sellAnalyticsV1Oas3.js +5 -0
- package/build/types/sell-apps/communication/commerceFeedbackV1BetaOas3.d.ts +705 -0
- package/build/types/sell-apps/communication/commerceFeedbackV1BetaOas3.js +5 -0
- package/build/types/sell-apps/communication/commerceMessageV1Oas3.d.ts +590 -0
- package/build/types/sell-apps/communication/commerceMessageV1Oas3.js +5 -0
- package/build/types/sell-apps/communication/commerceNotificationV1Oas3.d.ts +1276 -0
- package/build/types/sell-apps/communication/commerceNotificationV1Oas3.js +5 -0
- package/build/types/sell-apps/communication/sellNegotiationV1Oas3.d.ts +277 -0
- package/build/types/sell-apps/communication/sellNegotiationV1Oas3.js +5 -0
- package/build/types/sell-apps/listing-management/sellInventoryV1Oas3.d.ts +3133 -0
- package/build/types/sell-apps/listing-management/sellInventoryV1Oas3.js +5 -0
- package/build/types/sell-apps/listing-metadata/sellMetadataV1Oas3.d.ts +2289 -0
- package/build/types/sell-apps/listing-metadata/sellMetadataV1Oas3.js +5 -0
- package/build/types/sell-apps/markeitng-and-promotions/sellMarketingV1Oas3.d.ts +6650 -0
- package/build/types/sell-apps/markeitng-and-promotions/sellMarketingV1Oas3.js +5 -0
- package/build/types/sell-apps/markeitng-and-promotions/sellRecommendationV1Oas3.d.ts +172 -0
- package/build/types/sell-apps/markeitng-and-promotions/sellRecommendationV1Oas3.js +5 -0
- package/build/types/sell-apps/order-management/sellFulfillmentV1Oas3.d.ts +1869 -0
- package/build/types/sell-apps/order-management/sellFulfillmentV1Oas3.js +5 -0
- package/build/types/sell-apps/other-apis/commerceIdentityV1Oas3.d.ts +178 -0
- package/build/types/sell-apps/other-apis/commerceIdentityV1Oas3.js +5 -0
- package/build/types/sell-apps/other-apis/commerceTranslationV1BetaOas3.d.ts +128 -0
- package/build/types/sell-apps/other-apis/commerceTranslationV1BetaOas3.js +5 -0
- package/build/types/sell-apps/other-apis/commerceVeroV1Oas3.d.ts +417 -0
- package/build/types/sell-apps/other-apis/commerceVeroV1Oas3.js +5 -0
- package/build/types/sell-apps/other-apis/sellComplianceV1Oas3.d.ts +273 -0
- package/build/types/sell-apps/other-apis/sellComplianceV1Oas3.js +5 -0
- package/build/types/sell-apps/other-apis/sellEdeliveryInternationalShippingOas3.d.ts +2537 -0
- package/build/types/sell-apps/other-apis/sellEdeliveryInternationalShippingOas3.js +5 -0
- package/build/types/sell-apps/other-apis/sellMarketingV1Oas3.d.ts +6650 -0
- package/build/types/sell-apps/other-apis/sellMarketingV1Oas3.js +5 -0
- package/build/types/sell-apps/other-apis/sellRecommendationV1Oas3.d.ts +172 -0
- package/build/types/sell-apps/other-apis/sellRecommendationV1Oas3.js +5 -0
- package/build/utils/account-management/account.d.ts +1094 -0
- package/build/utils/account-management/account.js +831 -0
- package/build/utils/communication/feedback.d.ts +152 -0
- package/build/utils/communication/feedback.js +216 -0
- package/build/utils/communication/message.d.ts +174 -0
- package/build/utils/communication/message.js +242 -0
- package/build/utils/communication/negotiation.d.ts +123 -0
- package/build/utils/communication/negotiation.js +150 -0
- package/build/utils/communication/notification.d.ts +370 -0
- package/build/utils/communication/notification.js +369 -0
- package/build/utils/date-converter.d.ts +59 -0
- package/build/utils/date-converter.js +160 -0
- package/build/utils/llm-client-detector.d.ts +54 -0
- package/build/utils/llm-client-detector.js +318 -0
- package/build/utils/oauth-helper.d.ts +37 -0
- package/build/utils/oauth-helper.js +315 -0
- package/build/utils/order-management/dispute.d.ts +346 -0
- package/build/utils/order-management/dispute.js +369 -0
- package/build/utils/order-management/fulfillment.d.ts +200 -0
- package/build/utils/order-management/fulfillment.js +205 -0
- package/build/utils/other/compliance.d.ts +49 -0
- package/build/utils/other/compliance.js +76 -0
- package/build/utils/other/edelivery.d.ts +310 -0
- package/build/utils/other/edelivery.js +241 -0
- package/build/utils/other/identity.d.ts +13 -0
- package/build/utils/other/identity.js +13 -0
- package/build/utils/other/translation.d.ts +28 -0
- package/build/utils/other/translation.js +41 -0
- package/build/utils/other/vero.d.ts +61 -0
- package/build/utils/other/vero.js +90 -0
- package/build/utils/scope-helper.d.ts +49 -0
- package/build/utils/scope-helper.js +207 -0
- package/build/utils/security-checker.d.ts +46 -0
- package/build/utils/security-checker.js +248 -0
- package/build/utils/setup-validator.d.ts +25 -0
- package/build/utils/setup-validator.js +305 -0
- package/build/utils/token-utils.d.ts +40 -0
- package/build/utils/token-utils.js +40 -0
- package/package.json +115 -0
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { getBaseUrl, getDefaultScopes } from '../config/environment.js';
|
|
3
|
+
import { LocaleEnum } from '../types/ebay-enums.js';
|
|
4
|
+
import { readFileSync, writeFileSync } from 'fs';
|
|
5
|
+
import { join } from 'path';
|
|
6
|
+
/**
|
|
7
|
+
* Update .env file with new token values
|
|
8
|
+
*/
|
|
9
|
+
function updateEnvFile(updates) {
|
|
10
|
+
try {
|
|
11
|
+
const envPath = join(process.cwd(), '.env');
|
|
12
|
+
let envContent = readFileSync(envPath, 'utf-8');
|
|
13
|
+
// Update each key-value pair
|
|
14
|
+
for (const [key, value] of Object.entries(updates)) {
|
|
15
|
+
// Match the key with or without value, handling comments
|
|
16
|
+
const regex = new RegExp(`^(#\\s*)?${key}=.*$`, 'gm');
|
|
17
|
+
const newLine = `${key}=${value}`;
|
|
18
|
+
if (regex.test(envContent)) {
|
|
19
|
+
// Update existing key (uncomment if needed)
|
|
20
|
+
envContent = envContent.replace(regex, newLine);
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
// Add new key at the end
|
|
24
|
+
envContent += `\n${newLine}`;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
writeFileSync(envPath, envContent, 'utf-8');
|
|
28
|
+
console.log('✅ Updated .env file with new tokens');
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
console.error('⚠️ Failed to update .env file:', error instanceof Error ? error.message : error);
|
|
32
|
+
console.error(' Please manually update your .env file with the new tokens');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Manages eBay OAuth 2.0 authentication
|
|
37
|
+
* Loads tokens exclusively from environment variables (.env file)
|
|
38
|
+
* Supports both client credentials (app tokens) and user access tokens with refresh
|
|
39
|
+
*/
|
|
40
|
+
export class EbayOAuthClient {
|
|
41
|
+
config;
|
|
42
|
+
appAccessToken = null;
|
|
43
|
+
appAccessTokenExpiry = 0;
|
|
44
|
+
userTokens = null;
|
|
45
|
+
constructor(config) {
|
|
46
|
+
this.config = config;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Initialize user tokens from environment variables only
|
|
50
|
+
* If EBAY_USER_REFRESH_TOKEN exists, automatically refresh to get a valid access token
|
|
51
|
+
*/
|
|
52
|
+
async initialize() {
|
|
53
|
+
const envRefreshToken = process.env.EBAY_USER_REFRESH_TOKEN;
|
|
54
|
+
const envAccessToken = process.env.EBAY_USER_ACCESS_TOKEN;
|
|
55
|
+
const envAppToken = process.env.EBAY_APP_ACCESS_TOKEN ?? '';
|
|
56
|
+
const locale = this.config?.locale || LocaleEnum.en_US;
|
|
57
|
+
if (envRefreshToken) {
|
|
58
|
+
console.log('📝 Loading refresh token, access token and app to env file...');
|
|
59
|
+
// Create token object with just the refresh token from environment
|
|
60
|
+
// Note: We don't set scopes here - eBay will return the scopes when we refresh
|
|
61
|
+
const now = Date.now();
|
|
62
|
+
this.userTokens = {
|
|
63
|
+
clientId: this.config.clientId,
|
|
64
|
+
clientSecret: this.config.clientSecret,
|
|
65
|
+
userAccessToken: envAccessToken || '', // Can be empty, will be filled by refresh
|
|
66
|
+
userRefreshToken: envRefreshToken,
|
|
67
|
+
redirectUri: this.config.redirectUri,
|
|
68
|
+
envAppToken,
|
|
69
|
+
tokenType: 'Bearer',
|
|
70
|
+
locale,
|
|
71
|
+
userAccessTokenExpiry: now + 7200 * 1000, // Default 2 hours
|
|
72
|
+
userRefreshTokenExpiry: now + 18 * 30 * 24 * 60 * 60 * 1000, // Default 18 months
|
|
73
|
+
// scope is not set - will be populated by the refresh response
|
|
74
|
+
};
|
|
75
|
+
// Immediately refresh to get a valid access token and scopes
|
|
76
|
+
console.log('🔄 Refreshing access token using refresh token from .env...');
|
|
77
|
+
try {
|
|
78
|
+
await this.refreshUserToken();
|
|
79
|
+
console.log('✅ Access token refreshed successfully from .env configuration.');
|
|
80
|
+
await this.getOrRefreshAppAccessToken();
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
console.error('❌ Failed to refresh access token:', error instanceof Error ? error.message : error);
|
|
84
|
+
console.error(' The EBAY_USER_REFRESH_TOKEN in .env may be invalid or expired.');
|
|
85
|
+
console.error(' Please update EBAY_USER_REFRESH_TOKEN or use ebay_set_user_tokens_with_expiry tool.');
|
|
86
|
+
// Clear invalid tokens
|
|
87
|
+
this.userTokens = null;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Check if user tokens are available
|
|
93
|
+
*/
|
|
94
|
+
hasUserTokens() {
|
|
95
|
+
return this.userTokens !== null;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Check if user access token is expired
|
|
99
|
+
*/
|
|
100
|
+
isUserAccessTokenExpired(tokens) {
|
|
101
|
+
return tokens.userAccessTokenExpiry ? Date.now() >= tokens.userAccessTokenExpiry : true;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Check if user refresh token is expired
|
|
105
|
+
*/
|
|
106
|
+
isUserRefreshTokenExpired(tokens) {
|
|
107
|
+
return tokens.userRefreshTokenExpiry ? Date.now() >= tokens.userRefreshTokenExpiry : true;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Get a valid access token, with priority order:
|
|
111
|
+
* 1. User access token (if available and valid, or refreshable)
|
|
112
|
+
* 2. App access token from client credentials (fallback)
|
|
113
|
+
*/
|
|
114
|
+
async getAccessToken() {
|
|
115
|
+
// Try to use user token first
|
|
116
|
+
if (this.userTokens) {
|
|
117
|
+
// Check if access token is still valid
|
|
118
|
+
if (!this.isUserAccessTokenExpired(this.userTokens)) {
|
|
119
|
+
return this.userTokens.userAccessToken;
|
|
120
|
+
}
|
|
121
|
+
// Try to refresh if refresh token is valid
|
|
122
|
+
if (!this.isUserRefreshTokenExpired(this.userTokens)) {
|
|
123
|
+
try {
|
|
124
|
+
await this.refreshUserToken();
|
|
125
|
+
return this.userTokens.userAccessToken;
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
console.error('Failed to refresh user token, falling back to app access token:', error);
|
|
129
|
+
// Clear invalid tokens
|
|
130
|
+
this.userTokens = null;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
// Refresh token expired
|
|
135
|
+
console.error('User refresh token expired. User needs to re-authorize.');
|
|
136
|
+
this.userTokens = null;
|
|
137
|
+
throw new Error('User authorization expired. Please update EBAY_USER_REFRESH_TOKEN in .env with a new refresh token.');
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
// Fallback to app access token (client credentials)
|
|
141
|
+
if (this.appAccessToken && Date.now() < this.appAccessTokenExpiry) {
|
|
142
|
+
return this.appAccessToken;
|
|
143
|
+
}
|
|
144
|
+
await this.getOrRefreshAppAccessToken();
|
|
145
|
+
return this.appAccessToken;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Set user access token and refresh token
|
|
149
|
+
* Stores tokens in memory and updates .env file for persistence
|
|
150
|
+
*/
|
|
151
|
+
setUserTokens(accessToken, refreshToken, accessTokenExpiry, refreshTokenExpiry) {
|
|
152
|
+
// Store tokens in memory with default expiry
|
|
153
|
+
// Access tokens typically expire in 2 hours (7200 seconds)
|
|
154
|
+
// Refresh tokens typically expire in 18 months
|
|
155
|
+
const now = Date.now();
|
|
156
|
+
this.userTokens = {
|
|
157
|
+
clientId: this.config.clientId,
|
|
158
|
+
clientSecret: this.config.clientSecret,
|
|
159
|
+
redirectUri: this.config.redirectUri,
|
|
160
|
+
userAccessToken: accessToken,
|
|
161
|
+
userRefreshToken: refreshToken,
|
|
162
|
+
tokenType: 'Bearer',
|
|
163
|
+
userAccessTokenExpiry: accessTokenExpiry ?? (now + 7200 * 1000), // Default 2 hours
|
|
164
|
+
userRefreshTokenExpiry: refreshTokenExpiry ?? (now + 18 * 30 * 24 * 60 * 60 * 1000), // Default 18 months
|
|
165
|
+
};
|
|
166
|
+
// Update .env file with new tokens
|
|
167
|
+
updateEnvFile({
|
|
168
|
+
EBAY_USER_ACCESS_TOKEN: accessToken,
|
|
169
|
+
EBAY_USER_REFRESH_TOKEN: refreshToken,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Get or refresh the app access token using the client credentials flow.
|
|
174
|
+
* This method ensures that a valid app access token is always available.
|
|
175
|
+
* Rate limit: 1,000 requests/day
|
|
176
|
+
*/
|
|
177
|
+
async getOrRefreshAppAccessToken() {
|
|
178
|
+
// Return cached token if still valid
|
|
179
|
+
if (this.appAccessToken) {
|
|
180
|
+
return this.appAccessToken;
|
|
181
|
+
}
|
|
182
|
+
const authUrl = `${getBaseUrl(this.config.environment)}/identity/v1/oauth2/token`;
|
|
183
|
+
const credentials = Buffer.from(`${this.config.clientId}:${this.config.clientSecret}`).toString('base64');
|
|
184
|
+
// Client credentials flow only supports basic scope
|
|
185
|
+
// User authorization flows can request additional scopes
|
|
186
|
+
const scopeParam = 'https://api.ebay.com/oauth/api_scope';
|
|
187
|
+
try {
|
|
188
|
+
const response = await axios.post(authUrl, new URLSearchParams({
|
|
189
|
+
grant_type: 'client_credentials',
|
|
190
|
+
scope: scopeParam,
|
|
191
|
+
}).toString(), {
|
|
192
|
+
headers: {
|
|
193
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
194
|
+
Authorization: `Basic ${credentials}`,
|
|
195
|
+
},
|
|
196
|
+
});
|
|
197
|
+
this.appAccessToken = response.data.access_token;
|
|
198
|
+
// Set expiry with 60 second buffer
|
|
199
|
+
this.appAccessTokenExpiry = Date.now() + (response.data.expires_in - 60) * 1000;
|
|
200
|
+
// Update .env file with app access token
|
|
201
|
+
updateEnvFile({
|
|
202
|
+
EBAY_APP_ACCESS_TOKEN: this.appAccessToken,
|
|
203
|
+
});
|
|
204
|
+
return this.appAccessToken;
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
if (axios.isAxiosError(error)) {
|
|
208
|
+
throw new Error(`Failed to get app access token: ${error.response?.data?.error_description || error.message}`);
|
|
209
|
+
}
|
|
210
|
+
throw error;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Exchange authorization code for user access token
|
|
215
|
+
* Note: After receiving tokens, manually add EBAY_USER_REFRESH_TOKEN to .env file
|
|
216
|
+
*/
|
|
217
|
+
async exchangeCodeForToken(code) {
|
|
218
|
+
if (!this.config.redirectUri) {
|
|
219
|
+
throw new Error('Redirect URI is required for authorization code exchange');
|
|
220
|
+
}
|
|
221
|
+
const tokenUrl = `${getBaseUrl(this.config.environment)}/identity/v1/oauth2/token`;
|
|
222
|
+
const credentials = Buffer.from(`${this.config.clientId}:${this.config.clientSecret}`).toString('base64');
|
|
223
|
+
try {
|
|
224
|
+
const response = await axios.post(tokenUrl, new URLSearchParams({
|
|
225
|
+
grant_type: 'authorization_code',
|
|
226
|
+
code,
|
|
227
|
+
redirect_uri: this.config.redirectUri,
|
|
228
|
+
}).toString(), {
|
|
229
|
+
headers: {
|
|
230
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
231
|
+
Authorization: `Basic ${credentials}`,
|
|
232
|
+
},
|
|
233
|
+
});
|
|
234
|
+
const tokenData = response.data;
|
|
235
|
+
// Store the user tokens in memory
|
|
236
|
+
const now = Date.now();
|
|
237
|
+
this.userTokens = {
|
|
238
|
+
clientId: this.config.clientId,
|
|
239
|
+
clientSecret: this.config.clientSecret,
|
|
240
|
+
redirectUri: this.config.redirectUri,
|
|
241
|
+
userAccessToken: tokenData.access_token,
|
|
242
|
+
userRefreshToken: tokenData.refresh_token,
|
|
243
|
+
tokenType: tokenData.token_type,
|
|
244
|
+
userAccessTokenExpiry: now + tokenData.expires_in * 1000,
|
|
245
|
+
userRefreshTokenExpiry: now + tokenData.refresh_token_expires_in * 1000,
|
|
246
|
+
scope: tokenData.scope,
|
|
247
|
+
};
|
|
248
|
+
// Inform user to save refresh token to .env
|
|
249
|
+
console.log('\nToken exchange successful!');
|
|
250
|
+
console.log('To persist your authentication, add this to your .env file:');
|
|
251
|
+
console.log(`EBAY_USER_REFRESH_TOKEN="${tokenData.refresh_token}"\n`);
|
|
252
|
+
return tokenData;
|
|
253
|
+
}
|
|
254
|
+
catch (error) {
|
|
255
|
+
if (axios.isAxiosError(error)) {
|
|
256
|
+
throw new Error(`Failed to exchange code for token: ${error.response?.data?.error_description || error.message}`);
|
|
257
|
+
}
|
|
258
|
+
throw error;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Refresh user access token using refresh token from .env
|
|
263
|
+
* This method is public and can be called by LLMs when encountering authentication errors
|
|
264
|
+
*/
|
|
265
|
+
async refreshUserToken() {
|
|
266
|
+
if (!this.userTokens) {
|
|
267
|
+
throw new Error('No user tokens available to refresh');
|
|
268
|
+
}
|
|
269
|
+
// Use the token endpoint, not the authorization endpoint
|
|
270
|
+
const authUrl = `${getBaseUrl(this.config.environment)}/identity/v1/oauth2/token`;
|
|
271
|
+
const credentials = Buffer.from(`${this.config.clientId}:${this.config.clientSecret}`).toString('base64');
|
|
272
|
+
try {
|
|
273
|
+
// Prepare refresh token request parameters
|
|
274
|
+
// Note: We do NOT include scopes in refresh requests as eBay will return
|
|
275
|
+
// the scopes that were originally granted to the refresh token
|
|
276
|
+
const params = {
|
|
277
|
+
grant_type: 'refresh_token',
|
|
278
|
+
refresh_token: this.userTokens.userRefreshToken,
|
|
279
|
+
};
|
|
280
|
+
const response = await axios.post(authUrl, new URLSearchParams(params).toString(), {
|
|
281
|
+
headers: {
|
|
282
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
283
|
+
Authorization: `Basic ${credentials}`,
|
|
284
|
+
},
|
|
285
|
+
});
|
|
286
|
+
const tokenData = response.data;
|
|
287
|
+
// Update tokens in memory
|
|
288
|
+
const now = Date.now();
|
|
289
|
+
this.userTokens = {
|
|
290
|
+
clientId: this.config.clientId,
|
|
291
|
+
clientSecret: this.config.clientSecret,
|
|
292
|
+
redirectUri: this.config.redirectUri,
|
|
293
|
+
userAccessToken: tokenData.access_token,
|
|
294
|
+
userRefreshToken: tokenData.refresh_token || this.userTokens.userRefreshToken, // Use new refresh token if provided
|
|
295
|
+
tokenType: tokenData.token_type,
|
|
296
|
+
userAccessTokenExpiry: now + tokenData.expires_in * 1000,
|
|
297
|
+
userRefreshTokenExpiry: tokenData.refresh_token_expires_in
|
|
298
|
+
? now + tokenData.refresh_token_expires_in * 1000
|
|
299
|
+
: this.userTokens.userRefreshTokenExpiry, // Keep existing expiry if not provided
|
|
300
|
+
// eBay may not return scope in refresh response - preserve existing scope if not returned
|
|
301
|
+
scope: tokenData.scope || this.userTokens.scope,
|
|
302
|
+
};
|
|
303
|
+
// Update .env file with new tokens
|
|
304
|
+
const envUpdates = {
|
|
305
|
+
EBAY_USER_ACCESS_TOKEN: tokenData.access_token,
|
|
306
|
+
};
|
|
307
|
+
// If eBay provided a new refresh token, update it too
|
|
308
|
+
if (tokenData.refresh_token && tokenData.refresh_token !== process.env.EBAY_USER_REFRESH_TOKEN) {
|
|
309
|
+
envUpdates.EBAY_USER_REFRESH_TOKEN = tokenData.refresh_token;
|
|
310
|
+
console.log('\n🔄 eBay issued a new refresh token - updating .env file');
|
|
311
|
+
}
|
|
312
|
+
// Write updates to .env file
|
|
313
|
+
updateEnvFile(envUpdates);
|
|
314
|
+
}
|
|
315
|
+
catch (error) {
|
|
316
|
+
if (axios.isAxiosError(error)) {
|
|
317
|
+
throw new Error(`Failed to refresh token: ${error.response?.data?.error_description || error.message}`);
|
|
318
|
+
}
|
|
319
|
+
throw error;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Check if currently authenticated (either user or app credentials)
|
|
324
|
+
*/
|
|
325
|
+
isAuthenticated() {
|
|
326
|
+
if (this.userTokens && !this.isUserAccessTokenExpired(this.userTokens)) {
|
|
327
|
+
return true;
|
|
328
|
+
}
|
|
329
|
+
return this.appAccessToken !== null && Date.now() < this.appAccessTokenExpiry;
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Clear all authentication tokens from memory
|
|
333
|
+
* Note: To persist this change, remove EBAY_USER_REFRESH_TOKEN from .env
|
|
334
|
+
*/
|
|
335
|
+
clearAllTokens() {
|
|
336
|
+
this.appAccessToken = null;
|
|
337
|
+
this.appAccessTokenExpiry = 0;
|
|
338
|
+
this.userTokens = null;
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Get current token info for debugging
|
|
342
|
+
*/
|
|
343
|
+
getTokenInfo() {
|
|
344
|
+
const info = {
|
|
345
|
+
hasUserToken: this.userTokens !== null && !this.isUserAccessTokenExpired(this.userTokens),
|
|
346
|
+
hasAppAccessToken: this.appAccessToken !== null && Date.now() < this.appAccessTokenExpiry,
|
|
347
|
+
};
|
|
348
|
+
// Add scope comparison info if user tokens are available
|
|
349
|
+
if (this.userTokens?.scope) {
|
|
350
|
+
const tokenScopes = this.userTokens.scope.split(' ');
|
|
351
|
+
const environmentScopes = getDefaultScopes(this.config.environment);
|
|
352
|
+
const tokenScopeSet = new Set(tokenScopes);
|
|
353
|
+
const missingScopes = environmentScopes.filter((scope) => !tokenScopeSet.has(scope));
|
|
354
|
+
info.scopeInfo = {
|
|
355
|
+
tokenScopes,
|
|
356
|
+
environmentScopes,
|
|
357
|
+
missingScopes,
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
return info;
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Get internal user tokens (for debugging/status tools)
|
|
364
|
+
* @internal
|
|
365
|
+
*/
|
|
366
|
+
getUserTokens() {
|
|
367
|
+
return this.userTokens;
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Get internal app access token cached value (for debugging/status tools)
|
|
371
|
+
* @internal
|
|
372
|
+
*/
|
|
373
|
+
getCachedAppAccessToken() {
|
|
374
|
+
return this.appAccessToken;
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Get internal app access token expiry (for debugging/status tools)
|
|
378
|
+
* @internal
|
|
379
|
+
*/
|
|
380
|
+
getCachedAppAccessTokenExpiry() {
|
|
381
|
+
return this.appAccessTokenExpiry;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility functions for working with eBay OAuth scopes
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Result of scope validation
|
|
6
|
+
*/
|
|
7
|
+
export interface ValidationResult {
|
|
8
|
+
isValid: boolean;
|
|
9
|
+
warnings: string[];
|
|
10
|
+
validScopes: string[];
|
|
11
|
+
invalidScopes: string[];
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Comparison between two sets of scopes
|
|
15
|
+
*/
|
|
16
|
+
export interface ScopeDifference {
|
|
17
|
+
inBothEnvironments: string[];
|
|
18
|
+
productionOnly: string[];
|
|
19
|
+
sandboxOnly: string[];
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Scope requirement information for a tool
|
|
23
|
+
*/
|
|
24
|
+
export interface ScopeRequirement {
|
|
25
|
+
requiredScopes: string[];
|
|
26
|
+
optionalScopes?: string[];
|
|
27
|
+
minimumScope: string;
|
|
28
|
+
description: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Validate scopes and provide detailed validation results
|
|
32
|
+
*/
|
|
33
|
+
export declare function validateScopesDetailed(scopes: string[], environment: 'production' | 'sandbox'): ValidationResult;
|
|
34
|
+
/**
|
|
35
|
+
* Get required scopes for a specific tool
|
|
36
|
+
* This maps tool names to their required eBay OAuth scopes
|
|
37
|
+
*/
|
|
38
|
+
export declare function getRequiredScopesForTool(toolName: string): ScopeRequirement | null;
|
|
39
|
+
/**
|
|
40
|
+
* Check if a token has all required scopes for a tool
|
|
41
|
+
*/
|
|
42
|
+
export declare function hasRequiredScopes(tokenScopes: string[], requiredScopes: string[]): boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Get the differences between production and sandbox scopes
|
|
45
|
+
*/
|
|
46
|
+
export declare function getScopeDifferences(): ScopeDifference;
|
|
47
|
+
/**
|
|
48
|
+
* Format scope for display (remove common prefix for readability)
|
|
49
|
+
*/
|
|
50
|
+
export declare function formatScopeForDisplay(scope: string): string;
|
|
51
|
+
/**
|
|
52
|
+
* Group scopes by API category
|
|
53
|
+
*/
|
|
54
|
+
export declare function groupScopesByCategory(scopes: string[]): Record<string, string[]>;
|
|
55
|
+
/**
|
|
56
|
+
* Check if a scope is readonly
|
|
57
|
+
*/
|
|
58
|
+
export declare function isScopeReadonly(scope: string): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Get the write version of a readonly scope
|
|
61
|
+
*/
|
|
62
|
+
export declare function getWriteScope(readonlyScope: string): string | null;
|
|
63
|
+
/**
|
|
64
|
+
* Get the readonly version of a write scope
|
|
65
|
+
*/
|
|
66
|
+
export declare function getReadonlyScope(writeScope: string): string | null;
|
|
67
|
+
/**
|
|
68
|
+
* Get scope description from scope name
|
|
69
|
+
*/
|
|
70
|
+
export declare function getScopeDescription(scope: string): string;
|