site-kit-wp: Fix OAuth callback logic to not run before WordPress login redirect
Bug Description
It is currently possible to access the OAuth callback URL on any site to trigger Site Kit’s logic to handle it. While this is not a real security flaw since the necessary permission checks are in place, it is still concerning, and more importantly, it can result in a user-facing problem in some certain circumstances, e.g. when for some reason being logged out while going through the Site Kit setup flow.
Do not alter or remove anything below. The following sections will be managed by moderators only.
Acceptance criteria
- When accessing the
{adminURL}/index.php?oauth2callback=1URL on a site where you’re not logged in, you should get redirected towp-login.php, like it is expected to happen for admin URLs. - Any Site Kit handler logic for specific query parameters (i.e. also
googlesitekit_connect,googlesitekit_disconnect) should be run at a hook late enough so that it is only reached if the user is already logged in.
Implementation Brief
Allow authentication urls for logged in users only
Change handle_oauth hook to run on admin_init in
https://github.com/google/site-kit-wp/blob/2bac88b/includes/Core/Authentication/Authentication.php#L268
Remove the is_admin() call in handle_oauth
https://github.com/google/site-kit-wp/blob/2bac88b/includes/Core/Authentication/Authentication.php#L633-L635
Allow logout after starting authentication If we logout during authentication, when we first go to the google oauth screen, all of our WordPress nonce verification will fail due to https://core.trac.wordpress.org/browser/tags/5.7/src/wp-includes/pluggable.php#L2187. To get around this we can create a custom nonce verification approach:
function get_nonce() {
// Checks a user option: googlesitekit_proxy_nonce
// If an option exists it is verified with wp_verify_nonce(), if it returns false, or no nonce existed, one is generated and saved using `wp_create_nonce( self::ACTION_SETUP )`
}
function verify_nonce( $nonce ) {
// Verify the supplied option against the option value, returning true if they are identical, false otherwise.
}
function delete_nonce() {} // delete the option if it exists
Update nonce verification function https://github.com/google/site-kit-wp/blob/2bac88b/includes/Core/Authentication/Authentication.php#L1040-L1051
Update the nonce from url: https://github.com/google/site-kit-wp/blob/2bac88b/includes/Core/Authentication/Google_Proxy.php#L113
Delete the nonce option after we no longer need it:
prior to $this->set_connected_proxy_url();, delete the nonce option from there.
https://github.com/google/site-kit-wp/blob/2bac88b/includes/Core/Authentication/Authentication.php#L311
Test Coverage
- Update existing tests if any is failing due to the aforementioned changes.
Visual Regression Changes
- N/A
QA Brief
Ensure unit tests pass correctly.
Changelog entry
- Fix problem where OAuth callback login would be triggered before WordPress’s login redirect mechanism, immediately failing instead of redirecting as expected.
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Comments: 18 (14 by maintainers)
@ivankruchkoff – after discussing with @felixarntz, we’ve decided to hold on the requirement to support the case where the request continues to work after logging in as part of this issue and I’ve removed that point from the ACs accordingly. For now, this only needs to be concerned with ensuring the plugin handlers for those are only executed for logged in users.
We’ll open a follow-up issue to see about improving the experience for the logged out case, but that’s less important than the other requirements for now.
@felixarntz we do create real proxy URLs on demand now which we need to do to make the URL available via the datastore. This doesn’t currently create a persistent nonce though, I think the problem is related to a change in the PR that perhaps should not have been made.
@ivankruchkoff – you updated the
Authentication::get_proxy_setup_urlmethod to use this persistent on-demand nonce where this nonce should only be used inGoogle_Proxy::get_proxy_setup_url. The method on theAuthenticationclass is only to reach the internal redirect which sends you to the proxy using the latter method there.More importantly though, I’m not really sure we need to introduce a persistent nonce again (we had something like this a long time ago as well). The ACs state that “when going through the Site Kit setup flow and logging yourself out of the site (e.g. in another tab), the flow should still work successfully (after having been prompted to re-login).” So if the nonce verification fails because the nonce was created for a different session, we could simply provide a retry link with a fresh nonce when it fails. WP does the same thing for some actions. E.g. logout:
/wp-login.php?action=logoutThe result would be one more click in this edge case rather than adding a persistent nonce and then doing a less-secure simple string comparison on them. We would still check the user has permission to navigate to the proxy in
Authentication::handle_site_codeIB ✅