Allow XML-RPC to be disabled on self-hosted sites#25183
Allow XML-RPC to be disabled on self-hosted sites#25183crazytonyli wants to merge 13 commits intotrunkfrom
Conversation
|
| App Name | Jetpack | |
| Configuration | Release-Alpha | |
| Build Number | 30705 | |
| Version | PR #25183 | |
| Bundle ID | com.jetpack.alpha | |
| Commit | 71bdc79 | |
| Installation URL | 32gabu4egqrcg |
|
| App Name | WordPress | |
| Configuration | Release-Alpha | |
| Build Number | 30705 | |
| Version | PR #25183 | |
| Bundle ID | org.wordpress.alpha | |
| Commit | 71bdc79 | |
| Installation URL | 4flrtnfad5kbg |
Does it just not show up if the user hasn't switched to Application Passwords yet? |
|
@jkmassel No, it does not show up at the moment. I thought I had turned it on. I probably got it mixed up with something else. |
d4d102e to
e501a98
Compare
kean
left a comment
There was a problem hiding this comment.
Hey,
I tested the described flow and was able to add a self-hosted site with XMLRPC disabled using a plugin. I saw the banner described in the ticket.
(nit) It would be nice to show these messages in the same section.
A lot of the features didn't work - as expected. For example, I was not able to view my posts. This feels like a pretty serious limitaion and not quite what you would expect with "some features may be limited" message.
I was able to connect the Jetpack plugin.
(nit) The "Connect Site" button is tiny and looks like a non-interactive pill/banner rather than a button you should tap.
After I connected Jetpack, posts started to load. In that scenario, shouldn't the app remove the "XML-RPC disabled" banner?
| callMethod("wp.getOptions", parameters: parameters, success: success, failure: failure) | ||
| } | ||
|
|
||
| public func isEnabled(username: String, password: String) async -> Bool { |
There was a problem hiding this comment.
(nit) I'd suggest naming it isXMLRPCEnabled() for clarity.
There was a problem hiding this comment.
I omitted the "XMLRPC" part because the type is WordPressOrgXMLRPCApi.
There was a problem hiding this comment.
I understand the logic behind omitting it, but IMHO it's a bit clearer if we re-state it. Alternatively, a good docblock would help here.
| if case let .endpointError(fault) = error, fault.code == 405 { | ||
| return false | ||
| } | ||
| // Some plugins send HTTP 403 Forbidden response. |
There was a problem hiding this comment.
Do you an example? It's just 403 usually means "not authorized". What happens if it's a false positive?
There was a problem hiding this comment.
The "Disable XML-RPC-API" plugin sends 403.
By "false positive", do you mean like the site is temporarily down, or maybe user changes the password on website but not on the app?
There was a problem hiding this comment.
In this case, would a false positive also include "the username and password are incorrect"?
There was a problem hiding this comment.
"the username and password are incorrect" will be thrown as an endpointError, with a strong-typed fault: WordPressOrgXMLRPCApiFault.
There was a problem hiding this comment.
I have updated this part in 1a7fde2. Let me know what you think.
| // in the app. We can't get the same "options" via REST API. We'll need to investigate the impact of missing | ||
| // "options". | ||
|
|
||
| let blog: TaggedManagedObjectID<Blog> |
There was a problem hiding this comment.
(nit) should probably be named blogID
| try await ContextManager.shared.performAndSave { context in | ||
| let blog = try context.existingObject(with: blog) | ||
|
|
||
| // Here we'll use the "application password" as the "account password". |
There was a problem hiding this comment.
It's stored in keychain under the xmlrpc name, which can be a bit confusing.
[SFHFKeychainUtils storeUsername:self.username
andPassword:password
forServiceName:self.xmlrpc
accessGroup:nil
updateExisting:YES
error:nil];
Is it possible to add a new field and store it separately to avoid any confusion in the future?
| response?.value(forHTTPHeaderField: "Content-Type")?.hasPrefix("text/xml") == false { | ||
| return false | ||
| } | ||
| return true |
There was a problem hiding this comment.
(nit) This method seems to return true even if there is a network connection or other unrelated error. It seems to work fine based on how it's used in the UI, but it'd be clearer to make it throwing and ignore errors in the view layer if it needs to.
| throw .loadingSiteInfoFailure | ||
| } | ||
|
|
||
| // FIXME: The XML-RPC version stores `wp.getOptions` result in `Blog.options`, which is used in a few places |
There was a problem hiding this comment.
I don't know how accurate this is, but I did the CC check for this:
Based on my search, here's a comprehensive analysis of what getOptionValue is used for and the impact of missing Blog.options:
Current Usage of getOptionValue in the App
The Blog.options dictionary (populated via XML-RPC's wp.getOptions) is used for 22 different settings across the codebase:
Critical Features (High Impact)
1. Jetpack Integration (BlogSyncFacade.m:69, Blog.m:797)
- jetpack_user_login - Links Jetpack site to WordPress.com account
- jetpack_active_modules - Determines which Jetpack features are available
- jetpack_connection_active_plugins - Identifies active Jetpack plugins
2. Site Type Detection (Blog.m:166-180)
- is_wpcom_atomic - Atomic site detection
- is_wpforteams_site - WP for Teams detection
- is_automated_transfer - Automated Transfer detection
- These affect feature availability and UI throughout the app
3. Media Upload (Blog+Quota.swift:67)
- max_upload_size - Maximum file upload size
- Without this, uploads may fail or the app can't show size limits
4. Image Resizing (Blog.m:400-405)
- thumbnail_size_w/h, medium_size_w/h, large_size_w/h
- Used to match site's configured image sizes
- Fallbacks to defaults if missing
Moderate Impact
5. URL Construction (Blog.m:215, 244, 265, 290)
- home_url - Site home URL (falls back to blog.url)
- login_url - Login page URL (falls back to /wp-login.php)
- admin_url - Admin dashboard URL (falls back to /wp-admin/)
- unmapped_url - For custom domain mapping detection
6. Post Previews (PreviewNonceHandler.swift:24, 39)
- frame_nonce - Security nonce for previewing posts
- unmapped_url - Needed for preview URL rewriting
- May break post preview functionality
7. Timezone (Blog.m:307-309)
- timezone, gmt_offset, time_zone - Site timezone configuration
- Used for displaying dates/times correctly
8. Feature Capabilities (Blog.m:488, 493, 622)
- can_blaze - Blaze promotional campaigns availability
- post_thumbnail - Featured images support (has fallback)
- publicize_disabled - Social sharing availability
9. WordPress Version (Blog.m:429)
- software_version - WordPress version number
- May affect feature compatibility checks
---
What's Available from REST API
The WordPress REST API /wp/v2/settings endpoint provides only 20 fields:
- ✅ title, description, url, email
- ✅ timezone, dateFormat, timeFormat, startOfWeek
- ✅ language, postsPerPage
- ✅ defaultCategory, defaultPostFormat
- ✅ useSmilies, showOnFront, pageOnFront, pageForPosts
- ✅ defaultPingStatus, defaultCommentStatus
- ✅ siteLogo, siteIcon
Missing from REST API (18 fields)
❌ All Jetpack-related fields
❌ All site type detection flags (Atomic, WP for Teams, etc.)
❌ All image size settings
❌ max_upload_size
❌ frame_nonce (for previews)
❌ unmapped_url (for custom domains)
❌ can_blaze
❌ login_url, admin_url, home_url
❌ software_version
---
Impact Analysis for This PR
Features That Will Break
1. Post Previews - Missing frame_nonce may cause preview failures
2. Jetpack Features - Can't detect active modules or link to WP.com account
3. Media Upload Limits - No way to know max upload size
4. Custom Domain Sites - Missing unmapped_url for URL rewriting
Features That Will Degrade
1. Image Resizing - Falls back to hardcoded defaults
2. Timezone Display - Partial support via REST API timezone field
3. URL Construction - Falls back to default paths
4. Blaze - Can't determine availability
5. Site Type Detection - Can't identify Atomic/WP for Teams sites
Features That Work
1. Basic site info (title, description, URL)
2. WordPress.com sites (don't use XML-RPC options)
3. Most core functionality that doesn't depend on these settings
---
Recommendations
Option 1: Fetch Additional Data via REST API
Some missing fields might be available through other REST API endpoints:
- Check /wp/v2/media endpoint for upload size limits
- Check /jetpack/v4/* endpoints for Jetpack data
- Check site metadata endpoints for version info
Option 2: Graceful Degradation
Add fallbacks for critical features:
- Show warning when previewing posts without nonce
- Default to conservative upload size limits
- Disable Jetpack-specific features
Option 3: Optional XML-RPC
Allow the app to make individual XML-RPC calls even when XML-RPC is "disabled":
- Some security plugins block certain methods but not wp.getOptions
- Try fetching options and fail gracefully if blocked
Option 4: Feature Documentation
Document exactly which features are unavailable when XML-RPC is disabled and show this to users in the alert/modal.
---
The FIXME at SelfHostedSiteAuthenticator.swift:317-319 is well-placed - this is a significant limitation that affects multiple features, particularly
Jetpack integration, media uploads, and post previews.
🤖 Build Failure AnalysisThis build has failures. Claude has analyzed them - check the build annotations for details. |
| callMethod("wp.getOptions", parameters: parameters, success: success, failure: failure) | ||
| } | ||
|
|
||
| public func isEnabled(username: String, password: String) async -> Bool { |
There was a problem hiding this comment.
This method should be able to emit an Error type that reproduces whatever error the server emits
There was a problem hiding this comment.
Do you think this is still needed after the changes in 1a7fde2?
@kean – this is a good point. This is new functionality though – if the site doesn't support XMLRPC right now you can't even login to the app at all. This way it shows that the app works fine, but your site isn't supported. The work in #25157 can be exposed in a beta state, and we'll just be clear to users that it's still in development. I think we can make the "functionality limited" UI a bit more prominent to outline this to users. |
|
It's good that the app allows you to login, but it largely will not function without XML-RPC. The available options are: enable XML-RPC or connect/activate Jetpack. The app could pro-actively help the user with both. I suggest showing a larger screen with these two sections:
I don't think we need to tell users about the "transitioning", but there might be a note that XML-RPC is currently required. |
|
@kean Sounds good. Is it okay if I follow up with a dedicated PR for that? |
|
Sure, I'm just suggesting it as an option. Also, if they are using the WordPress app, we might want to handle it differently. |
|
I plan to address the Blog.options FIXME in a separate PR, too. It'd need some changes from the wprs library. Any other suggestions for this PR? @kean |
|






Description
This change comes out of a discussion with Jeremy. We'll allow self-hosted sites with XML-RPC disabled to use the app with an application password.
For those sites, we'll show a card at the top to info the user. At the moment, tapping the banner shows an alert. But I plan to change it to a modal later.
The card reuse the same UI as the extensive logging one. I'm open to extract that into a shared view to be used in these two cards. But I feel like that's probably a work for later, if we continue to introduce new cards using the same UI.
Also, I noticed that the user management feature flag is off. I turned it on, so that one more feature will be available to self-hosted site users (with or without an application password).
This PR is not big. But you can review each commit to see isolated changes.
Testing instructions
Previously, the application password login flow shows "XML-RPC is disabled" error when adding such sites. Verify that the flow now does not show the error, and those sites can be used in the app.