PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / trunk
Yoast SEO – Advanced SEO with real-time guidance and built-in AI vtrunk
28.5 28.4 28.3 28.2 28.1 28.0 27.9 27.8 27.7 27.6 27.5 trunk 18.0 18.1 18.2 18.3 18.4 18.4.1 18.5 18.5.1 18.6 18.7 18.8 18.9 19.0 All 129 releases
← All changes | src/myyoast-client/application/myyoast-client.php +70 -35 27.9 → trunk View file →
@@ -13,8 +13,9 @@
13 13 use Yoast\WP\SEO\MyYoast_Client\Application\Grants\Client_Credentials_Grant;
14 14 use Yoast\WP\SEO\MyYoast_Client\Application\Grants\Refresh_Token_Grant;
15 15 use Yoast\WP\SEO\MyYoast_Client\Application\Ports\Client_Registration_Interface;
16 16 use Yoast\WP\SEO\MyYoast_Client\Application\Ports\OAuth_Server_Client_Interface;
17 +use Yoast\WP\SEO\MyYoast_Client\Application\Ports\Redirect_URI_Provider_Interface;
17 18 use Yoast\WP\SEO\MyYoast_Client\Application\Ports\Site_URL_Provider_Interface;
18 19 use Yoast\WP\SEO\MyYoast_Client\Application\Ports\Token_Storage_Interface;
19 20 use Yoast\WP\SEO\MyYoast_Client\Application\Ports\User_Token_Storage_Interface;
20 21 use Yoast\WP\SEO\MyYoast_Client\Domain\Exceptions\Invalid_Resource_Exception;
@@ -103,19 +104,27 @@
103 104 */
104 105 private $site_url_provider;
105 106
106 107 /**
108 + * The redirect URI provider port.
109 + *
110 + * @var Redirect_URI_Provider_Interface
111 + */
112 + private $redirect_uri_provider;
113 +
114 + /**
107 115 * MyYoast_Client constructor.
108 116 *
109 - * @param Client_Registration_Interface $client_registration The client registration port.
110 - * @param Authorization_Code_Handler $auth_code_handler The authorization code handler.
111 - * @param OAuth_Grant_Handler $grant_handler The OAuth grant handler.
112 - * @param Token_Revocation_Handler $revocation_handler The token revocation handler.
113 - * @param OAuth_Server_Client_Interface $http_client The OAuth server client port.
114 - * @param Lock_Helper $lock_helper The lock helper.
115 - * @param Token_Storage_Interface $token_storage The site-level token storage port.
116 - * @param User_Token_Storage_Interface $user_token_storage The user-level token storage port.
117 - * @param Site_URL_Provider_Interface $site_url_provider The site URL provider port.
117 + * @param Client_Registration_Interface $client_registration The client registration port.
118 + * @param Authorization_Code_Handler $auth_code_handler The authorization code handler.
119 + * @param OAuth_Grant_Handler $grant_handler The OAuth grant handler.
120 + * @param Token_Revocation_Handler $revocation_handler The token revocation handler.
121 + * @param OAuth_Server_Client_Interface $http_client The OAuth server client port.
122 + * @param Lock_Helper $lock_helper The lock helper.
123 + * @param Token_Storage_Interface $token_storage The site-level token storage port.
124 + * @param User_Token_Storage_Interface $user_token_storage The user-level token storage port.
125 + * @param Site_URL_Provider_Interface $site_url_provider The site URL provider port.
126 + * @param Redirect_URI_Provider_Interface $redirect_uri_provider The redirect URI provider port.
118 127 */
119 128 public function __construct(
120 129 Client_Registration_Interface $client_registration,
121 130 Authorization_Code_Handler $auth_code_handler,
@@ -124,54 +133,67 @@
124 133 OAuth_Server_Client_Interface $http_client,
125 134 Lock_Helper $lock_helper,
126 135 Token_Storage_Interface $token_storage,
127 136 User_Token_Storage_Interface $user_token_storage,
128 - Site_URL_Provider_Interface $site_url_provider
137 + Site_URL_Provider_Interface $site_url_provider,
138 + Redirect_URI_Provider_Interface $redirect_uri_provider
129 139 ) {
130 - $this->client_registration = $client_registration;
131 - $this->auth_code_handler = $auth_code_handler;
132 - $this->grant_handler = $grant_handler;
133 - $this->revocation_handler = $revocation_handler;
134 - $this->http_client = $http_client;
135 - $this->lock_helper = $lock_helper;
136 - $this->token_storage = $token_storage;
137 - $this->user_token_storage = $user_token_storage;
138 - $this->site_url_provider = $site_url_provider;
139 - $this->logger = new NullLogger();
140 + $this->client_registration = $client_registration;
141 + $this->auth_code_handler = $auth_code_handler;
142 + $this->grant_handler = $grant_handler;
143 + $this->revocation_handler = $revocation_handler;
144 + $this->http_client = $http_client;
145 + $this->lock_helper = $lock_helper;
146 + $this->token_storage = $token_storage;
147 + $this->user_token_storage = $user_token_storage;
148 + $this->site_url_provider = $site_url_provider;
149 + $this->redirect_uri_provider = $redirect_uri_provider;
150 + $this->logger = new NullLogger();
140 151 }
141 152
142 153 /**
143 - * Ensures the plugin is registered as an OAuth client.
154 + * Ensures the plugin is registered as an OAuth client with the provider's redirect URIs.
144 155 *
145 - * @param string[] $redirect_uris The OAuth redirect URIs to register with.
146 - *
147 156 * @return Registered_Client The registered client.
148 157 *
149 158 * @throws Registration_Failed_Exception If registration fails.
150 159 */
151 - public function ensure_registered( array $redirect_uris = [] ): Registered_Client {
152 - return $this->client_registration->ensure_registered( $redirect_uris );
160 + public function ensure_registered(): Registered_Client {
161 + return $this->client_registration->ensure_registered( $this->redirect_uri_provider->get_redirect_uris() );
153 162 }
154 163
155 164 /**
165 + * Returns the stored registered client, or null if not registered.
166 + *
167 + * The returned client exposes per-URI verification state via Registered_Client::is_uri_validated().
168 + *
169 + * @return Registered_Client|null The registered client, or null if not registered.
170 + */
171 + public function get_registered_client(): ?Registered_Client {
172 + return $this->client_registration->get_registered_client();
173 + }
174 +
175 + /**
156 176 * Whether the plugin is registered as an OAuth client.
157 177 *
158 - * @param string[] $redirect_uris Optional redirect URIs to verify against the stored registration.
159 - *
160 178 * @return bool
161 179 */
162 - public function is_registered( array $redirect_uris = [] ): bool {
163 - return $this->client_registration->is_registered( $redirect_uris );
180 + public function is_registered(): bool {
181 + return $this->client_registration->get_registered_client() !== null;
164 182 }
165 183
166 184 /**
167 - * Reads the current client registration from the server.
185 + * Refreshes the local registration status against the server.
168 186 *
187 + * Reads the current client registration (RFC 7592 GET) to confirm it is
188 + * still live; the underlying read self-heals by forgetting the local
189 + * registration when the server reports it gone.
190 + *
169 191 * @return array<string, string|string[]> The registration metadata.
170 192 *
171 193 * @throws Registration_Failed_Exception If the read fails.
172 194 */
173 - public function verify_registration(): array {
195 + public function refresh_registration_status(): array {
174 196 return $this->client_registration->read_registration();
175 197 }
176 198
177 199 /**
@@ -206,9 +228,8 @@
206 228 /**
207 229 * Builds the authorization URL for the user authorization flow.
208 230 *
209 231 * @param int $user_id The WordPress user ID.
210 - * @param string $redirect_uri The callback redirect URI.
211 232 * @param string[] $scopes The scopes to request.
212 233 * @param string|null $resource_indicator The RFC 8707 resource indicator the issued token should be bound to.
213 234 * @param string|null $return_url The URL to return the user to after authorization completes.
214 235 *
@@ -216,10 +237,10 @@
216 237 *
217 238 * @throws Authorization_Flow_Exception If registration, discovery, or parameter validation fails.
218 239 * @throws Invalid_Resource_Exception If the resource indicator is malformed.
219 240 */
220 - public function get_authorization_url( int $user_id, string $redirect_uri, array $scopes = [], ?string $resource_indicator = null, ?string $return_url = null ): string {
221 - return $this->auth_code_handler->get_authorization_url( $user_id, $redirect_uri, $scopes, new Resource_Indicator( $resource_indicator ), $return_url );
241 + public function get_authorization_url( int $user_id, array $scopes = [], ?string $resource_indicator = null, ?string $return_url = null ): string {
242 + return $this->auth_code_handler->get_authorization_url( $user_id, $scopes, new Resource_Indicator( $resource_indicator ), $return_url );
222 243 }
223 244
224 245 /**
225 246 * Exchanges an authorization code for tokens and stores them for the user.
@@ -238,8 +259,10 @@
238 259 $this->user_token_storage->store( $user_id, $token_set );
239 260 return $token_set;
240 261 }
241 262
263 + // phpcs:disable Squiz.Commenting.FunctionCommentThrowTag.WrongNumber -- Token_Storage_Exception is thrown by an injected service, not directly here.
264 +
242 265 /**
243 266 * Returns a valid site-level access token (client_credentials).
244 267 *
245 268 * @param string[] $scopes The service:* scopes to request.
@@ -247,9 +270,9 @@
247 270 *
248 271 * @return Token_Set The site-level token set.
249 272 *
250 273 * @throws Invalid_Resource_Exception If the resource indicator is malformed.
251 - * @throws Token_Request_Failed_Exception If the token request fails. May also throw Token_Storage_Exception when encrypting the token set for storage fails.
274 + * @throws Token_Request_Failed_Exception If the site is not registered or the token request fails. May also throw Token_Storage_Exception when encrypting the token set for storage fails.
252 275 */
253 276 public function get_site_token( array $scopes = [], ?string $resource_indicator = null ): Token_Set {
254 277 $indicator = new Resource_Indicator( $resource_indicator );
255 278
@@ -254,17 +277,29 @@
254 277 $indicator = new Resource_Indicator( $resource_indicator );
255 278
256 279 $cached = $this->token_storage->get( $indicator );
257 280 if ( $cached !== null && ! $cached->is_expired() && $cached->has_scopes( $scopes ) ) {
281 + $this->logger->debug( 'MyYoast site token: cache hit; reusing cached token (scopes: {scopes}).', [ 'scopes' => \implode( ' ', $scopes ) ] );
258 282 return $cached;
259 283 }
284 + $this->logger->debug( 'MyYoast site token: cache miss; preparing to request a fresh token (scopes: {scopes}).', [ 'scopes' => \implode( ' ', $scopes ) ] );
260 285
286 + if ( ! $this->is_registered() ) {
287 + $this->logger->debug( 'MyYoast site token: site not registered; refusing to request a token. Run the connect flow first.' );
288 + throw new Token_Request_Failed_Exception( 'not_registered', 'Site is not registered with MyYoast; complete the connect flow first.' );
289 + }
290 + $this->logger->debug( 'MyYoast site token: site already registered; proceeding with token request.' );
291 +
261 292 $grant = new Client_Credentials_Grant( $scopes, $this->site_url_provider->get() );
262 293 $token_set = $this->grant_handler->request_token( $grant, $indicator );
263 294 $this->token_storage->store( $token_set );
264 295
296 + $this->logger->debug( 'MyYoast site token: fresh token issued and cached.' );
297 +
265 298 return $token_set;
266 299 }
300 +
301 + // phpcs:enable Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
267 302
268 303 /**
269 304 * Returns a valid user-level access token, auto-refreshing if expired.
270 305 *