feat(oauth): serve OAuth2 redirects from a dedicated route - #206
Merged
Conversation
✅ WordPress Plugin Check Report
📊 ReportAll checks passed! No errors or warnings found. 🤖 Generated by WordPress Plugin Check Action • Learn more about Plugin Check |
abdul-kaioum
approved these changes
Aug 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Adds a dedicated OAuth2 redirect endpoint at
/bit-integrations/oauth-callback/and makes it the URI the plugin hands to providers. The legacy REST endpointwp-json/bit-integrations/v1/redirectstays registered and shares the same implementation, so apps still configured with it keep working.Motivation & Context
The admin app is hash-routed, so a provider can never redirect straight to it — every OAuth flow has to bounce through a proxy endpoint that forwards the callback params to the URL carried in
state. That proxy lived underwp-json, which some providers reject as aredirect_uriand which reads as an API path rather than a plugin route. Bit-Pi already serves this from a rewrite-rule route; this brings Bit Integrations in line.While wiring it up, the redirect URI turned out to be hardcoded as
${btcbi.api}/redirectin four separate frontend files, so it now comes from one place in PHP and is filterable.Type of Change
The redirect URI sent to OAuth providers changes:
That URI is registered inside the user's own OAuth app (Google Cloud Console, Zoho, Facebook, …), which we cannot update for them. Existing users must add the new URI to their app before their next re-authorization, or it fails with
redirect_uri_mismatch. Existing tokens and refreshes are unaffected — only the authorization-code step sendsredirect_uri.Both endpoints work, so a site can pin back to the old one:
Key Changes
Backend
Core\Http\OauthCallbackController— serves the new route, forwards provider params onto thestateURL, and exposesredirectToState()shared by both endpoints.Core\Util\RewriteRuleProvider— maps a plugin route to WordPress'pagenamequery var, with a self-heal that re-flushes when another plugin drops the rule, rate limited to one flush per hour.Config::get('OAUTH_CALLBACK_URI')andConfig::get('REDIRECT_URI'), the latter filterable viabit_integrations_oauth_redirect_uriand localized to the frontend asredirectURI.Request::isRest()— detects a REST request beforeREST_REQUESTis defined, since that only happens onceparse_requestruns.parse_requestrather thantemplate_include, so it resolves before the main query and beforeredirect_canonicalcan rewrite the URL through 404 handling.Deactivationto flush rewrite rules, so the route does not linger in therewrite_rulesoption after deactivating.Frontend
${btcbi.api}/redirectsites to read the singlegetRedirectUri()helper, which now prefers the localizedredirectURI.Security
statewhose host and port differ from the site —javascript:, protocol-relative and userinfo-prefixed URLs are all refused beforewp_safe_redirect().X-Robots-Tag: noindex, nofollowandnocache_headers()on the callback, so a shared proxy cannot cache one user's redirect and replay it to the next.Bug fixes in the legacy endpoint
&, which produced a malformed URL for anystatewithout a?.list()method, an orphanedFilesystemIteratorimport and an unusedgetHostWithPort()helper.Checklist
Testing
Verified against a local install with pretty permalinks:
code+scope?pagename=…Locationstatestate/wp-json/,?rest_route=/,wp-login.phpChangelog
New Feature
OAuth: Authorizations now redirect through
/bit-integrations/oauth-callback/instead of awp-jsonURL. Add the new redirect URL to your app in the provider's console before re-authorizing; the old URL keeps working for apps already set up with it.Improvement
OAuth: The redirect URL is now filterable through
bit_integrations_oauth_redirect_uri.OAuth: Callback responses are marked no-index and no-store.
Bug Fixes
OAuth: Fixed a malformed redirect when the return URL carried no query string.