Invalid download links due to query string order modification

Easy Digital Downloads has a robust system in place to keep your file download links secure and prevent them from being abused. When a file download link is clicked, the order of the parameters provided is important. From time to time, certain hosting configurations, caching layers, or CDN providers may re-order the query string parameters in order to allow some base level caching to be done. Re-ordering these items will cause Easy Digital Downloads to fail the token validation for a customer's download link.

How can I tell if this is my issue?

If, when a user clicks to download their files, they are given the error "You do not have permission to download this file", check that the URL still has the following format, paying close attention to the order of the parameters. 

http://example.org/index.php?eddfile=...&ttl=...&file=...&token=...

If the query string parameters are not in the above order, then you are experiencing the issue described above. We do have a few solutions to help prevent this from occurring.

How can I prevent this issue?

Hosting configuration changes

The ideal situation is to not have your providers re-order the query string parameters when the request is for a file download by preventing any request containing the eddfile query string parameter from being passed through the re-ordering rules.

Custom code snippet

We recognize that this may not always be possible with some providers, so we've written a small function that will run any failed validation a 2nd time, to verify that we received the items in the proper order.

function ck_custom_validate_url_token( $ret, $url, $query_args ) {
	// If basic validation failed, let's make sure the URL prarms were in the right order and try again
	if ( false === $ret ) {
		$parts   = parse_url( $url );
		wp_parse_str( $parts['query'], $query_args );

		$fix_arg_order = array(
			'eddfile' => $query_args['eddfile'],
			'ttl'     => $query_args['ttl'],
			'file'    => $query_args['file'],
			'token'   => $query_args['token'],
		);

		$fixed_url   = add_query_arg( $fix_arg_order, site_url() );
		$fixed_token = edd_get_download_token( $fixed_url );

		$ret         = $query_args['token'] === $fixed_token;
	}

	return $ret;
}
add_filter( 'edd_validate_url_token', 'ck_custom_validate_url_token', 10, 3 );

The above function should be tested in a staging environment prior to using it on a production environment, to verify it works correctly in your specific configuration.