-
Notifications
You must be signed in to change notification settings - Fork 799
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
REST API: Add Authentication method based on Jetpack Signatures #5418
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't actually tested this yet. But it seems like a great start. I left a few comments.
@@ -476,6 +483,11 @@ private function __construct() { | |||
Jetpack_Heartbeat::init(); | |||
} | |||
|
|||
if ( Jetpack::is_active() ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps we should do
if ( Jetpack::is_active() && defined( 'REST_REQUEST' ) ) {
to limit these filters to rest request.
* Authenticates REST API via user_tokens passed as GET query parameter | ||
*/ | ||
function authenticate_user_tokens( $user_id ) { | ||
$get_token = isset( $_GET[ 'user_token' ] ) ? $_GET[ 'user_token' ] : false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it have to be a $_GET
param?
Could it instead be an actual authorization header? Should we support both?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd vote for the header alternative
It looks like this sends user tokens unencrypted, which seems problematic for non-HTTPS sites. Does the existing XMLRPC-based authentication do this too? |
Thanks for the recommendations. I've just updated this PR description to take in account latest comments. Note This PR was created as a kick-off for an implementation of securing the REST API with a mechanism that is currently not supported, so if you reach here please take in account those observations under Remarks about this implementation as they represent things that would need to be addressed for this implementation to be solid. |
Using a header, the token would still be sent in plaintext over HTTP, which is not a great idea. I assume that our existing XMLRPC auth strategy has some form of encryption and/or request signing for this reason. Unfortunately I'm not the person to ask about how that works, but this possible lack of transport-level encryption is why the OAuth1 flow and request signing mechanism look the way they do. |
We should definitely try and re-use as much as possible from the XML-RPC authentication. @mdawaffe might be able to help out with some specifics. |
These Jetpack tokens can't be included "raw" in requests, for the reasons others have outlined above. This is why Jetpack uses the (The full background: Though we mostly only use A few things that will need to happen:
|
The method to use for this is If there are further changes needed there for different environments, let's get these changes into WP core as well. |
@@ -476,6 +483,11 @@ private function __construct() { | |||
Jetpack_Heartbeat::init(); | |||
} | |||
|
|||
if ( Jetpack::is_active() && defined( 'REST_REQUEST' ) ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was using WordPress 4.5.4 and REST_REQUEST
was not defined. Same thing happened in WordPress 4.6.1.
I've put some var_dump()
here and there, and what is happening is that this code is being executed, and after that, wp-includes/rest-api.php#rest_api_loaded()
is executed (and the REST_REQUEST
constant is set).
Which version of WordPress did you use to test this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was using 4.5 But I didn't update the code here to move the hooking to a safe place yet. I've just labeled this PR to "In Progress" as there's a lot to be changed.
I added a commit to address @DanReyLop 's feedback. I get this when trying to run the But i was also getting that prior to my commit. I think if we start addressing the feedback from @nb and @mdawaffe we will be on the right track. i'm happy to help out this week. :) |
Testing instructions have been updated. Please ping me on Slack for how to run tests from the Jetpack server. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a small comment, but this worked just fine for me, and looks great 👍
@@ -59,7 +59,7 @@ function sign_current_request( $override = array() ) { | |||
|
|||
$url = "{$scheme}://{$_SERVER['HTTP_HOST']}:{$port}" . stripslashes( $_SERVER['REQUEST_URI'] ); | |||
|
|||
if ( array_key_exists( 'body', $override ) && !is_null( $override['body'] ) ) { | |||
if ( array_key_exists( 'body', $override ) && ! is_null( $override['body'] ) && ! empty( $override['body'] ) ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
! empty()
will include a check for null
values, so the ! is_null()
check is now obsolete.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch
This needs unit tests for requests with both valid and invalid signatures. |
563460e
to
0307a06
Compare
With the latest changes this is ready for review again. |
4b794ab
to
b3aa973
Compare
b294245
to
616363b
Compare
High fives @nylen and @georgestephanis ! This looks good and works well. I think all concerns have been addressed. If George would give it a thumbs up, I am completely confident to merge. |
@nylen @roccotripaldi -- Last thing -- one thing I had to do with Application Passwords was this -- WordPress/application-passwords@4226502 -- because I was only allowing my override of edit: the code in the changeset linked above eventually changed to https://github.com/georgestephanis/application-passwords/blob/c975c312b4f448d6dab0d016e152ab75354d38cd/class.application-passwords.php#L40-L67 Apart from that, 💯 |
@vortfu is going to be reviewing shortly, once he signs off, let's go ahead and merge to master and |
Discussed #5418 (comment) a bit with George via DM. This comment highlights the fact that we are currently running our auth logic on every request, but we should probably only be running it for a As George points out, this is a bit tricky, because the @samhotchkiss Let me know if y'all would like to fix this here or in a separate PR, either one should be fine. |
@nylen -- if you can fix here in the next couple of hours, that's 👍 |
This is the simplest way to make sure that we only apply this authentication method when intended (i.e. for requests from the WP.com servers).
So, I am a little bit uncomfortable with adding the Where we might have run into problems is if someone did another API request (unrelated to Jetpack) that happened to contain a In 1937dcd I addressed this by following the existing convention of a Note - this new parameter will require changes to existing WP.com server code. See D3926-code. I still think this is good to ship. Possible future enhancements:
|
* Add Authentication mechanism for REST API based on Jetpack user tokens * Check if we're on a REST API request before enabling the token-based auth method for the rest API * Do some Jetpack specific stuff on action `wp_json_init` instead of checking for `REST_REQUEST` constant. * Use the existing XMLRPC signature verification to validate WP API requests coming from the Jetpack server. Modifies the algorithm to handle a request containing an empty string body. * Ensure that the authorization object is not a WP_Error before attempting to use it. * To prove that we can use this mechanism to post complex objects, I've modified the bulk module activation endpoint to support JSON in POST body. * The endpoint should still work with multi-part form data as it did before. * In core's phpunit suites, `WP_Test_REST_Controller_Testcase` is already defined. Jetpack should define it if the class already exists. * Updating auth logic so that we do not overwrite any work from other plugins. props @nylen * improve coverage of Jetpack::wp_rest_authenticate to cover generic errors as well as errors from verify_xml_rpc_signature * REST Authentication Logic - WP API returns simply `true` for generic auth errors - Jetpack's authorization will account for this by also returning `true` in these cases - Jetpack's authorizaiton will also account for other plugins that may be authenticating, by giving their errors priority * Update modules list endpoint parameters for WP 4.7 * Run new API tests under multisite too * Improve error message when signature verification fails * Once we decide to handle authentication, either succeed or fail: Eliminate undecided states caused by successful signature verification without a token that we know how to deal with. It looks like this could happen if we successfully authenticated with a token of type 'blog' - let's make this (and any other such conditions) unsupported (and return an error) instead. * When receiving a POST request with no body, authentication fails with body-hash errors. This is because our signature verification is looking explicitly for `null` in regards to body. If, after all of Jetpack's sniffing, we still find an empty body, we should explicitly set it to `null`. Not all POST requests will require a body. * Clean up comments * Ensure that the request body is read at most once PHP versions prior to 5.6 cannot handle multiple reads from php://input * Only allow tested and known-working request parameters * Add translation calls to error messages * Add a missing require * Update the tests to set the request body as expected by the new code * Use 'determine_current_user' filter for authentication checks This also means we need to change up the logic to parse the request body, because we are now running *before* the WP REST API body-parsing code rather than *after* it. * Remove vim swp file * Be nice and unset $HTTP_RAW_POST_DATA after each test * Only apply REST API authentication when ?_for=jetpack This is the simplest way to make sure that we only apply this authentication method when intended (i.e. for requests from the WP.com servers).
@nylen : noted. Thanks. |
CHangelog: add #5457 Changelog: add #5487 Changelog: add #5708 Changelog: add #5879 Changelog: add #5932 Changelog: add #5963 Changelog: add #5968 Changelog: add #5996 Changelog: add #5998 Changelog: add #5999 Changelog: add #6012 Changelog: add #6013 Changelog: add #6014 Changelog: add #6015 Changelog: add #6023 Changelog: add #6024 Changelog: add #6030 Changelog: add #5465 CHangelog: add #6063 Changelog: add #6025 Changelog: add #5974 Changelog: add #6059 Changelog: add #6046 Changelog: add #5418 Changelog: move things around and add missing information. Changelog: add #5565 Changelog: add #6087 Changelog: add #6095
Changelog: add #5867 Changelog: add #5874 Changelog: add #5905 Changelog: add #5906 Changelog: add #5931 Changelog: add #5933 Changelog: add #5934 Bring over 4.4.2 changelog from branch-4.4 @see 18012a3 Changelog: add #5976, #5978, #5983 Changelog: add #5917 Changelog: add #5832 Changelog: add 4.4.2 release post link. CHangelog: add #5457 Changelog: add #5487 Changelog: add #5708 Changelog: add #5879 Changelog: add #5932 Changelog: add #5963 Changelog: add #5968 Changelog: add #5996 Changelog: add #5998 Changelog: add #5999 Changelog: add #6012 Changelog: add #6013 Changelog: add #6014 Changelog: add #6015 Changelog: add #6023 Changelog: add #6024 Changelog: add #6030 Changelog: add #5465 CHangelog: add #6063 Changelog: add #6025 Changelog: add #5974 Changelog: add #6059 Changelog: add #6046 Changelog: add #5418 Changelog: move things around and add missing information. Changelog: add #5565 Changelog: add #6087 Changelog: add #6095 Readme: add @tyxla to the list of contributors. Improved changelog for your readability and enjoyment updated the release date finalizing the changelog with a few more edits
Part of Automattic/wp-calypso#9171.
Changes proposed in this Pull Request:
Adds a token based authentication mechanism for the REST API.
We already generate tokens for the users when they connect thir self-hosted account to a WordPress.com account by means of the Jetpack connection process.
Remarks about this implementation
Testing instructions:
Running Unit tests
In a proper WordPress testing environment, try this command:
phpunit --filter=test_jetpack_rest_api_authentication --debug
Why
We're going to implement Jetpack module settings in Calypso and this communication mechanism ensures that we can update the Jetpack site in real time without the need of going through the XML-RPC API.
Proposed changelog entry for your changes:
Adds a token based authentication mechanism to the REST API in order for the site to be able to receive authenticated requests from WordPress.com