Skip to content

Allow XML-RPC to be disabled on self-hosted sites#25183

Open
crazytonyli wants to merge 13 commits intotrunkfrom
xmlrpc-optional
Open

Allow XML-RPC to be disabled on self-hosted sites#25183
crazytonyli wants to merge 13 commits intotrunkfrom
xmlrpc-optional

Conversation

@crazytonyli
Copy link
Contributor

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.

@crazytonyli crazytonyli added this to the 26.7 milestone Jan 30, 2026
@crazytonyli crazytonyli requested review from jkmassel and kean January 30, 2026 05:17
@wpmobilebot
Copy link
Contributor

wpmobilebot commented Jan 30, 2026

App Icon📲 You can test the changes from this Pull Request in Jetpack by scanning the QR code below to install the corresponding build.
App NameJetpack
ConfigurationRelease-Alpha
Build Number30705
VersionPR #25183
Bundle IDcom.jetpack.alpha
Commit71bdc79
Installation URL32gabu4egqrcg
Automatticians: You can use our internal self-serve MC tool to give yourself access to those builds if needed.

@wpmobilebot
Copy link
Contributor

wpmobilebot commented Jan 30, 2026

App Icon📲 You can test the changes from this Pull Request in WordPress by scanning the QR code below to install the corresponding build.
App NameWordPress
ConfigurationRelease-Alpha
Build Number30705
VersionPR #25183
Bundle IDorg.wordpress.alpha
Commit71bdc79
Installation URL4flrtnfad5kbg
Automatticians: You can use our internal self-serve MC tool to give yourself access to those builds if needed.

@jkmassel
Copy link
Contributor

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).

Does it just not show up if the user hasn't switched to Application Passwords yet?

@crazytonyli
Copy link
Contributor Author

@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.

Copy link
Contributor

@kean kean left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Screenshot 2026-02-02 at 4 30 09 PM

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.

Screenshot 2026-02-02 at 4 31 30 PM

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.

Screenshot 2026-02-02 at 4 31 46 PM

After I connected Jetpack, posts started to load. In that scenario, shouldn't the app remove the "XML-RPC disabled" banner?

Screenshot 2026-02-02 at 4 32 32 PM

callMethod("wp.getOptions", parameters: parameters, success: success, failure: failure)
}

public func isEnabled(username: String, password: String) async -> Bool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(nit) I'd suggest naming it isXMLRPCEnabled() for clarity.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I omitted the "XMLRPC" part because the type is WordPressOrgXMLRPCApi.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you an example? It's just 403 usually means "not authorized". What happens if it's a false positive?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case, would a false positive also include "the username and password are incorrect"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"the username and password are incorrect" will be thrown as an endpointError, with a strong-typed fault: WordPressOrgXMLRPCApiFault.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(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".
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in fd789fc

response?.value(forHTTPHeaderField: "Content-Type")?.hasPrefix("text/xml") == false {
return false
}
return true
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@wpmobilebot
Copy link
Contributor

wpmobilebot commented Feb 2, 2026

🤖 Build Failure Analysis

This 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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method should be able to emit an Error type that reproduces whatever error the server emits

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think this is still needed after the changes in 1a7fde2?

@jkmassel
Copy link
Contributor

jkmassel commented Feb 2, 2026

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.

@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.

@crazytonyli
Copy link
Contributor Author

@kean @jkmassel What you think about this messaging and UI?

@kean
Copy link
Contributor

kean commented Feb 4, 2026

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.

@crazytonyli
Copy link
Contributor Author

@kean Sounds good. Is it okay if I follow up with a dedicated PR for that?

@kean
Copy link
Contributor

kean commented Feb 4, 2026

Sure, I'm just suggesting it as an option. Also, if they are using the WordPress app, we might want to handle it differently.

@crazytonyli
Copy link
Contributor Author

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

@sonarqubecloud
Copy link

sonarqubecloud bot commented Feb 5, 2026

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants