| 1 |
<?php |
| 2 |
|
| 3 |
namespace Yoast\WP\SEO\Config; |
| 4 |
|
| 5 |
use WPSEO_Utils; |
| 6 |
use Yoast\WP\SEO\Exceptions\OAuth\Authentication_Failed_Exception; |
| 7 |
use Yoast\WP\SEO\Exceptions\OAuth\Tokens\Empty_Property_Exception; |
| 8 |
use Yoast\WP\SEO\Exceptions\OAuth\Tokens\Empty_Token_Exception; |
| 9 |
use Yoast\WP\SEO\Helpers\Options_Helper; |
| 10 |
use Yoast\WP\SEO\Values\OAuth\OAuth_Token; |
| 11 |
use Yoast\WP\SEO\Wrappers\WP_Remote_Handler; |
| 12 |
use YoastSEO_Vendor\GuzzleHttp\Client; |
| 13 |
use YoastSEO_Vendor\League\OAuth2\Client\Provider\Exception\IdentityProviderException; |
| 14 |
|
| 15 |
/** |
| 16 |
* Class Wincher_Client |
| 17 |
*/ |
| 18 |
class Wincher_Client extends OAuth_Client { |
| 19 |
|
| 20 |
/** |
| 21 |
* The option's key. |
| 22 |
*/ |
| 23 |
const TOKEN_OPTION = 'wincher_tokens'; |
| 24 |
|
| 25 |
/** |
| 26 |
* Name of the temporary PKCE cookie. |
| 27 |
*/ |
| 28 |
const PKCE_TRANSIENT_NAME = 'yoast_wincher_pkce'; |
| 29 |
|
| 30 |
/** |
| 31 |
* The WP_Remote_Handler instance. |
| 32 |
* |
| 33 |
* @var WP_Remote_Handler |
| 34 |
*/ |
| 35 |
protected $wp_remote_handler; |
| 36 |
|
| 37 |
/** |
| 38 |
* Wincher_Client constructor. |
| 39 |
* |
| 40 |
* @param Options_Helper $options_helper The Options_Helper instance. |
| 41 |
* @param WP_Remote_Handler $wp_remote_handler The request handler. |
| 42 |
* |
| 43 |
* @throws Empty_Property_Exception Exception thrown if a token property is empty. |
| 44 |
*/ |
| 45 |
public function __construct( |
| 46 |
Options_Helper $options_helper, |
| 47 |
WP_Remote_Handler $wp_remote_handler |
| 48 |
) { |
| 49 |
|
| 50 |
$provider = new Wincher_PKCE_Provider( |
| 51 |
[ |
| 52 |
'clientId' => 'yoast', |
| 53 |
'redirectUri' => 'https://auth.wincher.com/yoast/setup', |
| 54 |
'urlAuthorize' => 'https://auth.wincher.com/connect/authorize', |
| 55 |
'urlAccessToken' => 'https://auth.wincher.com/connect/token', |
| 56 |
'urlResourceOwnerDetails' => 'https://api.wincher.com/beta/user', |
| 57 |
'scopes' => [ 'profile', 'account', 'websites:read', 'websites:write', 'offline_access' ], |
| 58 |
'scopeSeparator' => ' ', |
| 59 |
'pkceMethod' => 'S256', |
| 60 |
], |
| 61 |
[ |
| 62 |
'httpClient' => new Client( [ 'handler' => $wp_remote_handler ] ), |
| 63 |
] |
| 64 |
); |
| 65 |
|
| 66 |
parent::__construct( |
| 67 |
self::TOKEN_OPTION, |
| 68 |
$provider, |
| 69 |
$options_helper |
| 70 |
); |
| 71 |
} |
| 72 |
|
| 73 |
/** |
| 74 |
* Return the authorization URL. |
| 75 |
* |
| 76 |
* @return string The authentication URL. |
| 77 |
*/ |
| 78 |
public function get_authorization_url() { |
| 79 |
$parsed_site_url = \wp_parse_url( \get_site_url() ); |
| 80 |
|
| 81 |
$url = $this->provider->getAuthorizationUrl( |
| 82 |
[ |
| 83 |
'state' => WPSEO_Utils::format_json_encode( [ 'domain' => $parsed_site_url['host'] ] ), |
| 84 |
] |
| 85 |
); |
| 86 |
|
| 87 |
$pkce_code = $this->provider->getPkceCode(); |
| 88 |
|
| 89 |
// Store a transient value with the PKCE code that we need in order to |
| 90 |
// exchange the returned code for a token after authorization. |
| 91 |
\set_transient( self::PKCE_TRANSIENT_NAME, $pkce_code, \DAY_IN_SECONDS ); |
| 92 |
|
| 93 |
return $url; |
| 94 |
} |
| 95 |
|
| 96 |
/** |
| 97 |
* Requests the access token and refresh token based on the passed code. |
| 98 |
* |
| 99 |
* @param string $code The code to send. |
| 100 |
* |
| 101 |
* @return OAuth_Token The requested tokens. |
| 102 |
* |
| 103 |
* @throws Authentication_Failed_Exception Exception thrown if authentication has failed. |
| 104 |
*/ |
| 105 |
public function request_tokens( $code ) { |
| 106 |
$pkce_code = \get_transient( self::PKCE_TRANSIENT_NAME ); |
| 107 |
if ( $pkce_code ) { |
| 108 |
$this->provider->setPkceCode( $pkce_code ); |
| 109 |
} |
| 110 |
return parent::request_tokens( $code ); |
| 111 |
} |
| 112 |
|
| 113 |
/** |
| 114 |
* Performs the specified request. |
| 115 |
* |
| 116 |
* @codeCoverageIgnore |
| 117 |
* |
| 118 |
* @param string $method The HTTP method to use. |
| 119 |
* @param string $url The URL to send the request to. |
| 120 |
* @param array $options The options to pass along to the request. |
| 121 |
* |
| 122 |
* @return mixed The parsed API response. |
| 123 |
* |
| 124 |
* @throws IdentityProviderException Exception thrown if there's something wrong with the identifying data. |
| 125 |
* @throws Authentication_Failed_Exception Exception thrown if authentication has failed. |
| 126 |
* @throws Empty_Token_Exception Exception thrown if the token is empty. |
| 127 |
*/ |
| 128 |
protected function do_request( $method, $url, array $options ) { |
| 129 |
$options['headers'] = [ 'Content-Type' => 'application/json' ]; |
| 130 |
return parent::do_request( $method, $url, $options ); |
| 131 |
} |
| 132 |
} |
| 133 |
|