mcp-wordpress 1.1.2
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 +21 -0
- package/README.md +568 -0
- package/bin/mcp-wordpress.js +12 -0
- package/bin/setup.js +302 -0
- package/bin/status.js +359 -0
- package/dist/client/WordPressClient.d.ts +81 -0
- package/dist/client/WordPressClient.d.ts.map +1 -0
- package/dist/client/WordPressClient.js +354 -0
- package/dist/client/WordPressClient.js.map +1 -0
- package/dist/client/api.d.ts +140 -0
- package/dist/client/api.d.ts.map +1 -0
- package/dist/client/api.js +727 -0
- package/dist/client/api.js.map +1 -0
- package/dist/client/auth.d.ts +121 -0
- package/dist/client/auth.d.ts.map +1 -0
- package/dist/client/auth.js +430 -0
- package/dist/client/auth.js.map +1 -0
- package/dist/client/managers/AuthenticationManager.d.ts +39 -0
- package/dist/client/managers/AuthenticationManager.d.ts.map +1 -0
- package/dist/client/managers/AuthenticationManager.js +159 -0
- package/dist/client/managers/AuthenticationManager.js.map +1 -0
- package/dist/client/managers/BaseManager.d.ts +22 -0
- package/dist/client/managers/BaseManager.d.ts.map +1 -0
- package/dist/client/managers/BaseManager.js +47 -0
- package/dist/client/managers/BaseManager.js.map +1 -0
- package/dist/client/managers/RequestManager.d.ts +45 -0
- package/dist/client/managers/RequestManager.d.ts.map +1 -0
- package/dist/client/managers/RequestManager.js +161 -0
- package/dist/client/managers/RequestManager.js.map +1 -0
- package/dist/client/managers/index.d.ts +8 -0
- package/dist/client/managers/index.d.ts.map +1 -0
- package/dist/client/managers/index.js +8 -0
- package/dist/client/managers/index.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +264 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +7 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +7 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/auth.d.ts +44 -0
- package/dist/tools/auth.d.ts.map +1 -0
- package/dist/tools/auth.js +126 -0
- package/dist/tools/auth.js.map +1 -0
- package/dist/tools/base.d.ts +37 -0
- package/dist/tools/base.d.ts.map +1 -0
- package/dist/tools/base.js +60 -0
- package/dist/tools/base.js.map +1 -0
- package/dist/tools/comments.d.ts +33 -0
- package/dist/tools/comments.d.ts.map +1 -0
- package/dist/tools/comments.js +228 -0
- package/dist/tools/comments.js.map +1 -0
- package/dist/tools/index.d.ts +9 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +9 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/media.d.ts +29 -0
- package/dist/tools/media.d.ts.map +1 -0
- package/dist/tools/media.js +208 -0
- package/dist/tools/media.js.map +1 -0
- package/dist/tools/pages.d.ts +30 -0
- package/dist/tools/pages.d.ts.map +1 -0
- package/dist/tools/pages.js +211 -0
- package/dist/tools/pages.js.map +1 -0
- package/dist/tools/posts.d.ts +30 -0
- package/dist/tools/posts.d.ts.map +1 -0
- package/dist/tools/posts.js +240 -0
- package/dist/tools/posts.js.map +1 -0
- package/dist/tools/site.d.ts +31 -0
- package/dist/tools/site.d.ts.map +1 -0
- package/dist/tools/site.js +192 -0
- package/dist/tools/site.js.map +1 -0
- package/dist/tools/taxonomies.d.ts +37 -0
- package/dist/tools/taxonomies.d.ts.map +1 -0
- package/dist/tools/taxonomies.js +280 -0
- package/dist/tools/taxonomies.js.map +1 -0
- package/dist/tools/users.d.ts +28 -0
- package/dist/tools/users.d.ts.map +1 -0
- package/dist/tools/users.js +201 -0
- package/dist/tools/users.js.map +1 -0
- package/dist/types/client.d.ts +215 -0
- package/dist/types/client.d.ts.map +1 -0
- package/dist/types/client.js +72 -0
- package/dist/types/client.js.map +1 -0
- package/dist/types/index.d.ts +157 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +12 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/mcp.d.ts +178 -0
- package/dist/types/mcp.d.ts.map +1 -0
- package/dist/types/mcp.js +7 -0
- package/dist/types/mcp.js.map +1 -0
- package/dist/types/wordpress.d.ts +443 -0
- package/dist/types/wordpress.d.ts.map +1 -0
- package/dist/types/wordpress.js +7 -0
- package/dist/types/wordpress.js.map +1 -0
- package/dist/utils/debug.d.ts +63 -0
- package/dist/utils/debug.d.ts.map +1 -0
- package/dist/utils/debug.js +195 -0
- package/dist/utils/debug.js.map +1 -0
- package/dist/utils/error.d.ts +19 -0
- package/dist/utils/error.d.ts.map +1 -0
- package/dist/utils/error.js +71 -0
- package/dist/utils/error.js.map +1 -0
- package/dist/utils/toolWrapper.d.ts +36 -0
- package/dist/utils/toolWrapper.d.ts.map +1 -0
- package/dist/utils/toolWrapper.js +90 -0
- package/dist/utils/toolWrapper.js.map +1 -0
- package/package.json +115 -0
- package/src/client/api.ts +1043 -0
- package/src/client/auth.ts +527 -0
- package/src/client/managers/AuthenticationManager.ts +190 -0
- package/src/client/managers/BaseManager.ts +73 -0
- package/src/client/managers/RequestManager.ts +214 -0
- package/src/client/managers/index.ts +8 -0
- package/src/index.ts +337 -0
- package/src/server.ts +7 -0
- package/src/tools/auth.ts +153 -0
- package/src/tools/comments.ts +263 -0
- package/src/tools/index.ts +8 -0
- package/src/tools/media.ts +240 -0
- package/src/tools/pages.ts +246 -0
- package/src/tools/posts.ts +277 -0
- package/src/tools/site.ts +227 -0
- package/src/tools/taxonomies.ts +322 -0
- package/src/tools/users.ts +233 -0
- package/src/types/client.ts +304 -0
- package/src/types/index.ts +207 -0
- package/src/types/mcp.ts +247 -0
- package/src/types/wordpress.ts +491 -0
- package/src/utils/debug.ts +258 -0
- package/src/utils/error.ts +88 -0
- package/src/utils/toolWrapper.ts +105 -0
|
@@ -0,0 +1,527 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WordPress Authentication Handler
|
|
3
|
+
* Manages different authentication methods for WordPress REST API
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { logger, debug } from "../utils/debug.js";
|
|
7
|
+
import * as http from "http";
|
|
8
|
+
import { URL } from "url";
|
|
9
|
+
import type {
|
|
10
|
+
IAuthProvider,
|
|
11
|
+
IWordPressClient,
|
|
12
|
+
AuthMethod,
|
|
13
|
+
AuthConfig,
|
|
14
|
+
AuthenticationError,
|
|
15
|
+
} from "../types/client.js";
|
|
16
|
+
import type { WordPressUser } from "../types/wordpress.js";
|
|
17
|
+
|
|
18
|
+
export class WordPressAuth {
|
|
19
|
+
private client: IWordPressClient;
|
|
20
|
+
private authType: AuthMethod;
|
|
21
|
+
|
|
22
|
+
constructor(client: IWordPressClient) {
|
|
23
|
+
this.client = client;
|
|
24
|
+
this.authType = client.config.auth.method;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Handle authentication based on type
|
|
29
|
+
*/
|
|
30
|
+
async authenticate(): Promise<boolean> {
|
|
31
|
+
try {
|
|
32
|
+
switch (this.authType) {
|
|
33
|
+
case "app-password":
|
|
34
|
+
return await this.handleAppPasswordAuth();
|
|
35
|
+
case "jwt":
|
|
36
|
+
return await this.handleJWTAuth();
|
|
37
|
+
case "basic":
|
|
38
|
+
return await this.handleBasicAuth();
|
|
39
|
+
case "api-key":
|
|
40
|
+
return await this.handleAPIKeyAuth();
|
|
41
|
+
case "cookie":
|
|
42
|
+
return await this.handleCookieAuth();
|
|
43
|
+
default:
|
|
44
|
+
throw new Error(`Unsupported authentication type: ${this.authType}`);
|
|
45
|
+
}
|
|
46
|
+
} catch (error) {
|
|
47
|
+
logger.error("Authentication failed:", error);
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Handle Application Password authentication
|
|
54
|
+
*/
|
|
55
|
+
private async handleAppPasswordAuth(): Promise<boolean> {
|
|
56
|
+
const { username, appPassword } = this.client.config.auth;
|
|
57
|
+
|
|
58
|
+
if (!username || !appPassword) {
|
|
59
|
+
throw new Error(
|
|
60
|
+
"Application Password authentication requires WORDPRESS_USERNAME and WORDPRESS_APP_PASSWORD. " +
|
|
61
|
+
"Visit your WordPress admin → Users → Profile → Application Passwords to create one.",
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Test the credentials by attempting to get current user
|
|
66
|
+
try {
|
|
67
|
+
const user = await this.client.getCurrentUser();
|
|
68
|
+
logger.log(
|
|
69
|
+
`✅ Application Password authentication successful for user: ${user.name} (${user.username})`,
|
|
70
|
+
);
|
|
71
|
+
return true;
|
|
72
|
+
} catch (error) {
|
|
73
|
+
const message =
|
|
74
|
+
"Application Password authentication failed. Please check your credentials and ensure the application password is valid.";
|
|
75
|
+
logger.error(message, error);
|
|
76
|
+
throw new Error(message);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Handle Basic authentication (username + password)
|
|
82
|
+
*/
|
|
83
|
+
private async handleBasicAuth(): Promise<boolean> {
|
|
84
|
+
const { username, password } = this.client.config.auth;
|
|
85
|
+
|
|
86
|
+
if (!username || !password) {
|
|
87
|
+
throw new Error(
|
|
88
|
+
"Basic authentication requires WORDPRESS_USERNAME and WORDPRESS_PASSWORD",
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
const user = await this.client.getCurrentUser();
|
|
94
|
+
logger.log(
|
|
95
|
+
`✅ Basic authentication successful for user: ${user.name} (${user.username})`,
|
|
96
|
+
);
|
|
97
|
+
return true;
|
|
98
|
+
} catch (error) {
|
|
99
|
+
const message =
|
|
100
|
+
"Basic authentication failed. Please check your username and password.";
|
|
101
|
+
logger.error(message, error);
|
|
102
|
+
throw new Error(message);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Handle JWT authentication
|
|
108
|
+
*/
|
|
109
|
+
private async handleJWTAuth(): Promise<boolean> {
|
|
110
|
+
const { username, password, secret } = this.client.config.auth;
|
|
111
|
+
|
|
112
|
+
if (!username || !password || !secret) {
|
|
113
|
+
throw new Error(
|
|
114
|
+
"JWT authentication requires WORDPRESS_USERNAME, WORDPRESS_PASSWORD, and WORDPRESS_JWT_SECRET. " +
|
|
115
|
+
"Install and configure the JWT Authentication plugin first.",
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
// The JWT token should be obtained during client authentication
|
|
121
|
+
const user = await this.client.getCurrentUser();
|
|
122
|
+
logger.log(
|
|
123
|
+
`✅ JWT authentication successful for user: ${user.name} (${user.username})`,
|
|
124
|
+
);
|
|
125
|
+
return true;
|
|
126
|
+
} catch (error) {
|
|
127
|
+
const message =
|
|
128
|
+
"JWT authentication failed. Please check your credentials and ensure the JWT plugin is installed and configured.";
|
|
129
|
+
logger.error(message, error);
|
|
130
|
+
throw new Error(message);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Handle API Key authentication
|
|
136
|
+
*/
|
|
137
|
+
private async handleAPIKeyAuth(): Promise<boolean> {
|
|
138
|
+
const { apiKey } = this.client.config.auth;
|
|
139
|
+
|
|
140
|
+
if (!apiKey) {
|
|
141
|
+
throw new Error("API Key authentication requires WORDPRESS_API_KEY");
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
// Test API key by making a simple request
|
|
146
|
+
await this.client.getSiteInfo();
|
|
147
|
+
logger.log("✅ API Key authentication successful");
|
|
148
|
+
return true;
|
|
149
|
+
} catch (error) {
|
|
150
|
+
const message =
|
|
151
|
+
"API Key authentication failed. Please check your API key.";
|
|
152
|
+
logger.error(message, error);
|
|
153
|
+
throw new Error(message);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Handle Cookie authentication
|
|
159
|
+
*/
|
|
160
|
+
private async handleCookieAuth(): Promise<boolean> {
|
|
161
|
+
const { nonce } = this.client.config.auth;
|
|
162
|
+
|
|
163
|
+
if (!nonce) {
|
|
164
|
+
logger.warn(
|
|
165
|
+
"Cookie authentication: No nonce provided, authentication may fail for write operations",
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
try {
|
|
170
|
+
// Test with a simple read operation
|
|
171
|
+
await this.client.getSiteInfo();
|
|
172
|
+
logger.log(
|
|
173
|
+
"✅ Cookie authentication configured (note: write operations may require valid nonce)",
|
|
174
|
+
);
|
|
175
|
+
return true;
|
|
176
|
+
} catch (error) {
|
|
177
|
+
const message =
|
|
178
|
+
"Cookie authentication failed. Please ensure you are properly logged into WordPress.";
|
|
179
|
+
logger.error(message, error);
|
|
180
|
+
throw new Error(message);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Refresh authentication (for JWT and OAuth)
|
|
186
|
+
*/
|
|
187
|
+
async refreshAuth(): Promise<boolean> {
|
|
188
|
+
switch (this.authType) {
|
|
189
|
+
case "jwt":
|
|
190
|
+
return await this.refreshJWTToken();
|
|
191
|
+
default:
|
|
192
|
+
logger.log(`Authentication refresh not supported for ${this.authType}`);
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Refresh JWT token
|
|
199
|
+
*/
|
|
200
|
+
private async refreshJWTToken(): Promise<boolean> {
|
|
201
|
+
try {
|
|
202
|
+
// Re-authenticate to get a new token
|
|
203
|
+
return await this.handleJWTAuth();
|
|
204
|
+
} catch (error) {
|
|
205
|
+
logger.error("Failed to refresh JWT token:", error);
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Validate current authentication
|
|
212
|
+
*/
|
|
213
|
+
async validateAuth(): Promise<boolean> {
|
|
214
|
+
try {
|
|
215
|
+
await this.client.getCurrentUser();
|
|
216
|
+
return true;
|
|
217
|
+
} catch (error) {
|
|
218
|
+
logger.error("Authentication validation failed:", error);
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Get authentication status information
|
|
225
|
+
*/
|
|
226
|
+
async getAuthStatus(): Promise<{
|
|
227
|
+
authenticated: boolean;
|
|
228
|
+
method: AuthMethod;
|
|
229
|
+
user?: WordPressUser;
|
|
230
|
+
error?: string;
|
|
231
|
+
}> {
|
|
232
|
+
try {
|
|
233
|
+
const user = await this.client.getCurrentUser();
|
|
234
|
+
return {
|
|
235
|
+
authenticated: true,
|
|
236
|
+
method: this.authType,
|
|
237
|
+
user,
|
|
238
|
+
};
|
|
239
|
+
} catch (error) {
|
|
240
|
+
return {
|
|
241
|
+
authenticated: false,
|
|
242
|
+
method: this.authType,
|
|
243
|
+
error: (error as Error).message,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Switch authentication method
|
|
250
|
+
*/
|
|
251
|
+
async switchAuthMethod(newConfig: AuthConfig): Promise<boolean> {
|
|
252
|
+
// Update client configuration
|
|
253
|
+
(this.client.config as any).auth = newConfig;
|
|
254
|
+
this.authType = newConfig.method;
|
|
255
|
+
|
|
256
|
+
// Re-authenticate with new method
|
|
257
|
+
return await this.authenticate();
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Start OAuth 2.0 flow (for future implementation)
|
|
262
|
+
*/
|
|
263
|
+
async startOAuthFlow(): Promise<{ authUrl: string; state: string }> {
|
|
264
|
+
const { clientId } = this.client.config.auth;
|
|
265
|
+
|
|
266
|
+
if (!clientId) {
|
|
267
|
+
throw new Error("OAuth requires client ID");
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const state = this.generateRandomState();
|
|
271
|
+
const redirectUri = "http://localhost:8080/oauth/callback";
|
|
272
|
+
|
|
273
|
+
const authUrl = new URL("/oauth/authorize", this.client.config.baseUrl);
|
|
274
|
+
authUrl.searchParams.append("client_id", clientId);
|
|
275
|
+
authUrl.searchParams.append("redirect_uri", redirectUri);
|
|
276
|
+
authUrl.searchParams.append("response_type", "code");
|
|
277
|
+
authUrl.searchParams.append("state", state);
|
|
278
|
+
authUrl.searchParams.append("scope", "read write");
|
|
279
|
+
|
|
280
|
+
return {
|
|
281
|
+
authUrl: authUrl.toString(),
|
|
282
|
+
state,
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Complete OAuth 2.0 flow (for future implementation)
|
|
288
|
+
*/
|
|
289
|
+
async completeOAuthFlow(code: string, state: string): Promise<boolean> {
|
|
290
|
+
// This would implement the OAuth token exchange
|
|
291
|
+
// For now, this is a placeholder
|
|
292
|
+
throw new Error("OAuth flow not yet implemented");
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Generate random state for OAuth
|
|
297
|
+
*/
|
|
298
|
+
private generateRandomState(length = 32): string {
|
|
299
|
+
const chars =
|
|
300
|
+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
301
|
+
let result = "";
|
|
302
|
+
for (let i = 0; i < length; i++) {
|
|
303
|
+
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
304
|
+
}
|
|
305
|
+
return result;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Get authentication headers for the current method
|
|
310
|
+
*/
|
|
311
|
+
getAuthHeaders(): Record<string, string> {
|
|
312
|
+
const headers: Record<string, string> = {};
|
|
313
|
+
const auth = this.client.config.auth;
|
|
314
|
+
|
|
315
|
+
switch (this.authType) {
|
|
316
|
+
case "app-password":
|
|
317
|
+
if (auth.username && auth.appPassword) {
|
|
318
|
+
const credentials = Buffer.from(
|
|
319
|
+
`${auth.username}:${auth.appPassword}`,
|
|
320
|
+
).toString("base64");
|
|
321
|
+
headers["Authorization"] = `Basic ${credentials}`;
|
|
322
|
+
}
|
|
323
|
+
break;
|
|
324
|
+
case "basic":
|
|
325
|
+
if (auth.username && auth.password) {
|
|
326
|
+
const credentials = Buffer.from(
|
|
327
|
+
`${auth.username}:${auth.password}`,
|
|
328
|
+
).toString("base64");
|
|
329
|
+
headers["Authorization"] = `Basic ${credentials}`;
|
|
330
|
+
}
|
|
331
|
+
break;
|
|
332
|
+
|
|
333
|
+
case "jwt":
|
|
334
|
+
if (auth.token) {
|
|
335
|
+
headers["Authorization"] = `Bearer ${auth.token}`;
|
|
336
|
+
}
|
|
337
|
+
break;
|
|
338
|
+
|
|
339
|
+
case "api-key":
|
|
340
|
+
if (auth.apiKey) {
|
|
341
|
+
headers["X-API-Key"] = auth.apiKey;
|
|
342
|
+
}
|
|
343
|
+
break;
|
|
344
|
+
|
|
345
|
+
case "cookie":
|
|
346
|
+
if (auth.nonce) {
|
|
347
|
+
headers["X-WP-Nonce"] = auth.nonce;
|
|
348
|
+
}
|
|
349
|
+
break;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
return headers;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Check if authentication method requires additional setup
|
|
357
|
+
*/
|
|
358
|
+
requiresSetup(): boolean {
|
|
359
|
+
switch (this.authType) {
|
|
360
|
+
case "jwt":
|
|
361
|
+
return !this.client.config.auth.secret;
|
|
362
|
+
case "api-key":
|
|
363
|
+
return !this.client.config.auth.apiKey;
|
|
364
|
+
case "app-password":
|
|
365
|
+
return (
|
|
366
|
+
!this.client.config.auth.username ||
|
|
367
|
+
!this.client.config.auth.appPassword
|
|
368
|
+
);
|
|
369
|
+
case "basic":
|
|
370
|
+
return (
|
|
371
|
+
!this.client.config.auth.username || !this.client.config.auth.password
|
|
372
|
+
);
|
|
373
|
+
case "cookie":
|
|
374
|
+
return false; // Cookie auth can work without additional setup
|
|
375
|
+
default:
|
|
376
|
+
return true;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Get setup instructions for the current authentication method
|
|
382
|
+
*/
|
|
383
|
+
getSetupInstructions(): string {
|
|
384
|
+
switch (this.authType) {
|
|
385
|
+
case "app-password":
|
|
386
|
+
return `
|
|
387
|
+
To set up Application Password authentication:
|
|
388
|
+
1. Log into your WordPress admin dashboard
|
|
389
|
+
2. Go to Users → Profile (or Users → All Users → Edit your user)
|
|
390
|
+
3. Scroll down to "Application Passwords" section
|
|
391
|
+
4. Enter a name for this application (e.g., "MCP WordPress Server")
|
|
392
|
+
5. Click "Add New Application Password"
|
|
393
|
+
6. Copy the generated password and set it as WORDPRESS_APP_PASSWORD
|
|
394
|
+
7. Set WORDPRESS_USERNAME to your WordPress username
|
|
395
|
+
`;
|
|
396
|
+
|
|
397
|
+
case "jwt":
|
|
398
|
+
return `
|
|
399
|
+
To set up JWT authentication:
|
|
400
|
+
1. Install the "JWT Authentication for WP REST API" plugin
|
|
401
|
+
2. Add JWT_AUTH_SECRET_KEY to your wp-config.php file
|
|
402
|
+
3. Configure the plugin settings
|
|
403
|
+
4. Set WORDPRESS_JWT_SECRET environment variable
|
|
404
|
+
5. Set WORDPRESS_USERNAME and WORDPRESS_PASSWORD
|
|
405
|
+
`;
|
|
406
|
+
|
|
407
|
+
case "api-key":
|
|
408
|
+
return `
|
|
409
|
+
To set up API Key authentication:
|
|
410
|
+
1. Install an API Key plugin (varies by plugin)
|
|
411
|
+
2. Generate an API key in the plugin settings
|
|
412
|
+
3. Set WORDPRESS_API_KEY environment variable
|
|
413
|
+
`;
|
|
414
|
+
|
|
415
|
+
case "basic":
|
|
416
|
+
return `
|
|
417
|
+
To set up Basic authentication:
|
|
418
|
+
1. Set WORDPRESS_USERNAME to your WordPress username
|
|
419
|
+
2. Set WORDPRESS_PASSWORD to your WordPress password
|
|
420
|
+
Note: This method is less secure than Application Passwords
|
|
421
|
+
`;
|
|
422
|
+
|
|
423
|
+
case "cookie":
|
|
424
|
+
return `
|
|
425
|
+
Cookie authentication is automatically configured when you're logged into WordPress.
|
|
426
|
+
For write operations, you may need to set WORDPRESS_COOKIE_NONCE.
|
|
427
|
+
`;
|
|
428
|
+
|
|
429
|
+
default:
|
|
430
|
+
return "No setup instructions available for this authentication method.";
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Authentication Provider implementations
|
|
437
|
+
*/
|
|
438
|
+
|
|
439
|
+
export class AppPasswordAuthProvider implements IAuthProvider {
|
|
440
|
+
readonly method: AuthMethod = "app-password";
|
|
441
|
+
|
|
442
|
+
async authenticate(client: IWordPressClient): Promise<boolean> {
|
|
443
|
+
const auth = new WordPressAuth(client);
|
|
444
|
+
return auth.authenticate();
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
addAuthHeaders(headers: Record<string, string>): void {
|
|
448
|
+
// Implementation handled by WordPressAuth
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
export class JWTAuthProvider implements IAuthProvider {
|
|
453
|
+
readonly method: AuthMethod = "jwt";
|
|
454
|
+
|
|
455
|
+
async authenticate(client: IWordPressClient): Promise<boolean> {
|
|
456
|
+
const auth = new WordPressAuth(client);
|
|
457
|
+
return auth.authenticate();
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
addAuthHeaders(headers: Record<string, string>): void {
|
|
461
|
+
// Implementation handled by WordPressAuth
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
async refreshAuth(): Promise<boolean> {
|
|
465
|
+
// JWT token refresh logic
|
|
466
|
+
return true;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
export class BasicAuthProvider implements IAuthProvider {
|
|
471
|
+
readonly method: AuthMethod = "basic";
|
|
472
|
+
|
|
473
|
+
async authenticate(client: IWordPressClient): Promise<boolean> {
|
|
474
|
+
const auth = new WordPressAuth(client);
|
|
475
|
+
return auth.authenticate();
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
addAuthHeaders(headers: Record<string, string>): void {
|
|
479
|
+
// Implementation handled by WordPressAuth
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
export class APIKeyAuthProvider implements IAuthProvider {
|
|
484
|
+
readonly method: AuthMethod = "api-key";
|
|
485
|
+
|
|
486
|
+
async authenticate(client: IWordPressClient): Promise<boolean> {
|
|
487
|
+
const auth = new WordPressAuth(client);
|
|
488
|
+
return auth.authenticate();
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
addAuthHeaders(headers: Record<string, string>): void {
|
|
492
|
+
// Implementation handled by WordPressAuth
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
export class CookieAuthProvider implements IAuthProvider {
|
|
497
|
+
readonly method: AuthMethod = "cookie";
|
|
498
|
+
|
|
499
|
+
async authenticate(client: IWordPressClient): Promise<boolean> {
|
|
500
|
+
const auth = new WordPressAuth(client);
|
|
501
|
+
return auth.authenticate();
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
addAuthHeaders(headers: Record<string, string>): void {
|
|
505
|
+
// Implementation handled by WordPressAuth
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Factory function to create appropriate auth provider
|
|
511
|
+
*/
|
|
512
|
+
export function createAuthProvider(method: AuthMethod): IAuthProvider {
|
|
513
|
+
switch (method) {
|
|
514
|
+
case "app-password":
|
|
515
|
+
return new AppPasswordAuthProvider();
|
|
516
|
+
case "jwt":
|
|
517
|
+
return new JWTAuthProvider();
|
|
518
|
+
case "basic":
|
|
519
|
+
return new BasicAuthProvider();
|
|
520
|
+
case "api-key":
|
|
521
|
+
return new APIKeyAuthProvider();
|
|
522
|
+
case "cookie":
|
|
523
|
+
return new CookieAuthProvider();
|
|
524
|
+
default:
|
|
525
|
+
throw new Error(`Unsupported authentication method: ${method}`);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authentication Manager
|
|
3
|
+
* Handles all authentication methods and token management
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { AuthConfig, AuthMethod } from "../../types/client.js";
|
|
7
|
+
import { AuthenticationError } from "../../types/client.js";
|
|
8
|
+
import { BaseManager } from "./BaseManager.js";
|
|
9
|
+
import { debug } from "../../utils/debug.js";
|
|
10
|
+
|
|
11
|
+
export class AuthenticationManager extends BaseManager {
|
|
12
|
+
private jwtToken: string | null = null;
|
|
13
|
+
private authenticated: boolean = false;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Get authentication from environment variables
|
|
17
|
+
*/
|
|
18
|
+
static getAuthFromEnv(): AuthConfig {
|
|
19
|
+
const method: AuthMethod =
|
|
20
|
+
(process.env.WORDPRESS_AUTH_METHOD as AuthMethod) || "app-password";
|
|
21
|
+
|
|
22
|
+
switch (method) {
|
|
23
|
+
case "app-password":
|
|
24
|
+
return {
|
|
25
|
+
method: "app-password",
|
|
26
|
+
username: process.env.WORDPRESS_USERNAME || "",
|
|
27
|
+
appPassword: process.env.WORDPRESS_APP_PASSWORD || "",
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
case "jwt":
|
|
31
|
+
return {
|
|
32
|
+
method: "jwt",
|
|
33
|
+
username: process.env.WORDPRESS_USERNAME || "",
|
|
34
|
+
password: process.env.WORDPRESS_JWT_PASSWORD || process.env.WORDPRESS_PASSWORD || "",
|
|
35
|
+
secret: process.env.WORDPRESS_JWT_SECRET || "",
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
case "basic":
|
|
39
|
+
return {
|
|
40
|
+
method: "basic",
|
|
41
|
+
username: process.env.WORDPRESS_USERNAME || "",
|
|
42
|
+
password: process.env.WORDPRESS_PASSWORD || "",
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
case "api-key":
|
|
46
|
+
return {
|
|
47
|
+
method: "api-key",
|
|
48
|
+
apiKey: process.env.WORDPRESS_API_KEY || "",
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
default:
|
|
52
|
+
throw new AuthenticationError(`Unsupported authentication method: ${method}`, method);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Get authentication headers for requests
|
|
58
|
+
*/
|
|
59
|
+
async getAuthHeaders(): Promise<Record<string, string>> {
|
|
60
|
+
const auth = this.config.auth;
|
|
61
|
+
|
|
62
|
+
switch (auth.method) {
|
|
63
|
+
case "app-password":
|
|
64
|
+
if (!auth.username || !auth.appPassword) {
|
|
65
|
+
throw new AuthenticationError("Username and app password are required", auth.method);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const credentials = Buffer.from(`${auth.username}:${auth.appPassword}`).toString("base64");
|
|
69
|
+
return { Authorization: `Basic ${credentials}` };
|
|
70
|
+
|
|
71
|
+
case "jwt":
|
|
72
|
+
if (!this.jwtToken) {
|
|
73
|
+
await this.authenticateJWT();
|
|
74
|
+
}
|
|
75
|
+
return { Authorization: `Bearer ${this.jwtToken}` };
|
|
76
|
+
|
|
77
|
+
case "basic":
|
|
78
|
+
if (!auth.username || !auth.password) {
|
|
79
|
+
throw new AuthenticationError("Username and password are required", auth.method);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const basicCredentials = Buffer.from(`${auth.username}:${auth.password}`).toString("base64");
|
|
83
|
+
return { Authorization: `Basic ${basicCredentials}` };
|
|
84
|
+
|
|
85
|
+
case "api-key":
|
|
86
|
+
if (!auth.apiKey) {
|
|
87
|
+
throw new AuthenticationError("API key is required", auth.method);
|
|
88
|
+
}
|
|
89
|
+
return { "X-API-Key": auth.apiKey };
|
|
90
|
+
|
|
91
|
+
default:
|
|
92
|
+
throw new AuthenticationError(`Unsupported authentication method: ${auth.method}`, auth.method);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Authenticate using JWT
|
|
98
|
+
*/
|
|
99
|
+
private async authenticateJWT(): Promise<void> {
|
|
100
|
+
const auth = this.config.auth;
|
|
101
|
+
|
|
102
|
+
if (auth.method !== "jwt" || !auth.username || !auth.password) {
|
|
103
|
+
throw new AuthenticationError("JWT authentication requires username and password", "jwt");
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
// This would need the RequestManager instance to make the request
|
|
108
|
+
// For now, we'll throw an error indicating this needs to be implemented
|
|
109
|
+
throw new AuthenticationError("JWT authentication requires RequestManager integration", "jwt");
|
|
110
|
+
|
|
111
|
+
} catch (error) {
|
|
112
|
+
this.handleError(error, "JWT authentication");
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Test authentication
|
|
118
|
+
*/
|
|
119
|
+
async testAuthentication(): Promise<boolean> {
|
|
120
|
+
try {
|
|
121
|
+
const headers = await this.getAuthHeaders();
|
|
122
|
+
debug.log("Authentication headers prepared", { method: this.config.auth.method });
|
|
123
|
+
|
|
124
|
+
// This would need the RequestManager to actually test the connection
|
|
125
|
+
// For now, we'll return true if headers can be generated
|
|
126
|
+
this.authenticated = true;
|
|
127
|
+
return true;
|
|
128
|
+
|
|
129
|
+
} catch (error) {
|
|
130
|
+
this.authenticated = false;
|
|
131
|
+
debug.log("Authentication test failed", error);
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Get authentication status
|
|
138
|
+
*/
|
|
139
|
+
isAuthenticated(): boolean {
|
|
140
|
+
return this.authenticated;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Clear authentication state
|
|
145
|
+
*/
|
|
146
|
+
clearAuthentication(): void {
|
|
147
|
+
this.jwtToken = null;
|
|
148
|
+
this.authenticated = false;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Validate authentication configuration
|
|
153
|
+
*/
|
|
154
|
+
validateAuthConfig(): void {
|
|
155
|
+
const auth = this.config.auth;
|
|
156
|
+
|
|
157
|
+
if (!auth.method) {
|
|
158
|
+
throw new AuthenticationError("Authentication method is required", "app-password");
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
switch (auth.method) {
|
|
162
|
+
case "app-password":
|
|
163
|
+
if (!auth.username || !auth.appPassword) {
|
|
164
|
+
throw new AuthenticationError("App password authentication requires username and appPassword", "app-password");
|
|
165
|
+
}
|
|
166
|
+
break;
|
|
167
|
+
|
|
168
|
+
case "jwt":
|
|
169
|
+
if (!auth.username || !auth.password || !auth.secret) {
|
|
170
|
+
throw new AuthenticationError("JWT authentication requires username, password, and secret", "jwt");
|
|
171
|
+
}
|
|
172
|
+
break;
|
|
173
|
+
|
|
174
|
+
case "basic":
|
|
175
|
+
if (!auth.username || !auth.password) {
|
|
176
|
+
throw new AuthenticationError("Basic authentication requires username and password", "basic");
|
|
177
|
+
}
|
|
178
|
+
break;
|
|
179
|
+
|
|
180
|
+
case "api-key":
|
|
181
|
+
if (!auth.apiKey) {
|
|
182
|
+
throw new AuthenticationError("API key authentication requires apiKey", "api-key");
|
|
183
|
+
}
|
|
184
|
+
break;
|
|
185
|
+
|
|
186
|
+
default:
|
|
187
|
+
throw new AuthenticationError(`Unsupported authentication method: ${auth.method}`, auth.method);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|