PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 1.1.10
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v1.1.10
1.1.10 1.1.9 1.1.8 1.1.7 1.1.6 1.1.5 1.1.4 1.1.3 1.1.2 1.1.1 1.1.0 1.0.1 1.0.0 0.9.8 0.9.7 0.9.6 0.9.4 0.9.5 0.9.3 0.9.2 0.9.1 0.9.0 0.8.9 0.8.8 0.8.7 All 34 releases
← All changes | includes/oauth-relay.php +131 -128 0.9.31.1.10 View file →
@@ -1,7 +1,7 @@
1 1 <?php
2 2 /**
3 - * Desktop Mode — OAuth relay scaffolding.
3 + * OpenStation — OAuth relay scaffolding.
4 4 *
5 5 * Every plugin that integrates with an external service (Tumblr,
6 6 * Mastodon, Bluesky, Spotify, Discord, …) reinvents the same
7 7 * fiddly OAuth dance: generate a `state` nonce, persist it in a
@@ -17,9 +17,9 @@
17 17 * here and is identical across consumers.
18 18 *
19 19 * Public PHP surface:
20 20 *
21 - * desktop_mode_register_oauth_relay( $service, [
21 + * openstation_register_oauth_relay( $service, [
22 22 * 'authorize_url' => 'https://www.example.com/oauth2/authorize',
23 23 * 'token_url' => 'https://api.example.com/oauth2/token',
24 24 * 'client_id' => 'CLIENT_ID',
25 25 * 'client_secret' => 'CLIENT_SECRET',
@@ -28,11 +28,11 @@
28 28 * // Persist tokens however your plugin needs.
29 29 * },
30 30 * ] );
31 31 *
32 - * Public JS surface (since 0.8.2):
32 + * Public JS surface:
33 33 *
34 - * const { ok, service } = await wp.desktop.startOAuth( 'example' );
34 + * const { ok, service } = await wp.os.startOAuth( 'example' );
35 35 * // Tokens stay server-side — persisted by your `on_success` callback.
36 36 *
37 37 * REST routes:
38 38 *
@@ -40,22 +40,26 @@
40 40 * → { authorize_url: string, state: string }
41 41 * GET /desktop-mode/v1/oauth/callback ?code&state&error
42 42 * → HTML page that postMessages the opener and closes
43 43 *
44 - * @package Desktop_Mode
45 - * @since 0.8.2
44 + * @package OpenStation
46 45 */
47 46
48 47 defined( 'ABSPATH' ) || exit;
49 48
50 -const DESKTOP_MODE_OAUTH_TRANSIENT_PREFIX = 'desktop_mode_oauth_state_';
51 -const DESKTOP_MODE_OAUTH_STATE_TTL = 600; // 10 minutes.
49 +/**
50 + * The VALUE keeps its pre-rebrand spelling on purpose: it is a
51 + * persisted or externally-visible identifier, so renaming it would
52 + * orphan data already written by live installs (or break a live
53 + * URL). The mismatch between this constant's name and its value is
54 + * deliberate — it is NOT a half-finished rename.
55 + */
56 +const OPENSTATION_OAUTH_TRANSIENT_PREFIX = 'desktop_mode_oauth_state_';
57 +const OPENSTATION_OAUTH_STATE_TTL = 600; // 10 minutes.
52 58
53 59 /**
54 60 * Register an OAuth relay for `$service`.
55 61 *
56 - * @since 0.8.2
57 - *
58 62 * @param string $service Slug identifying the service. Lowercased,
59 63 * sanitized via `sanitize_key`.
60 64 * @param array $args {
61 65 * OAuth relay configuration.
@@ -85,13 +89,13 @@
85 89 * logged-in user).
86 90 * }
87 91 * @return true|WP_Error `true` on success, `WP_Error` on validation failure.
88 92 */
89 -function desktop_mode_register_oauth_relay( $service, $args = array() ) {
93 +function openstation_register_oauth_relay( $service, $args = array() ) {
90 94 $service = sanitize_key( (string) $service );
91 95 if ( '' === $service ) {
92 96 return new WP_Error(
93 - 'desktop_mode_oauth_missing_service',
97 + 'openstation_oauth_missing_service',
94 98 __( 'OAuth relay registration requires a non-empty service slug.', 'desktop-mode' )
95 99 );
96 100 }
97 101
@@ -103,14 +107,14 @@
103 107 'scope' => '',
104 108 'on_success' => null,
105 109 'capabilities' => array( 'read' ),
106 110 );
107 - $args = wp_parse_args( $args, $defaults );
111 + $args = wp_parse_args( $args, $defaults );
108 112
109 113 foreach ( array( 'authorize_url', 'token_url', 'client_id', 'client_secret' ) as $required ) {
110 114 if ( '' === (string) $args[ $required ] ) {
111 115 return new WP_Error(
112 - 'desktop_mode_oauth_missing_' . $required,
116 + 'openstation_oauth_missing_' . $required,
113 117 /* translators: %s: missing field name. */
114 118 sprintf( __( 'OAuth relay registration requires a non-empty `%s`.', 'desktop-mode' ), $required ),
115 119 array( 'service' => $service )
116 120 );
@@ -118,9 +122,9 @@
118 122 }
119 123
120 124 if ( ! is_callable( $args['on_success'] ) ) {
121 125 return new WP_Error(
122 - 'desktop_mode_oauth_missing_on_success',
126 + 'openstation_oauth_missing_on_success',
123 127 __( 'OAuth relay registration requires a callable `on_success` handler.', 'desktop-mode' ),
124 128 array( 'service' => $service )
125 129 );
126 130 }
@@ -128,9 +132,9 @@
128 132 $authorize_url = esc_url_raw( (string) $args['authorize_url'], array( 'http', 'https' ) );
129 133 $token_url = esc_url_raw( (string) $args['token_url'], array( 'http', 'https' ) );
130 134 if ( '' === $authorize_url || '' === $token_url ) {
131 135 return new WP_Error(
132 - 'desktop_mode_oauth_invalid_url',
136 + 'openstation_oauth_invalid_url',
133 137 __( 'OAuth relay `authorize_url` and `token_url` must be valid http(s) URLs.', 'desktop-mode' ),
134 138 array( 'service' => $service )
135 139 );
136 140 }
@@ -144,22 +148,20 @@
144 148 'scope' => (string) $args['scope'],
145 149 'on_success' => $args['on_success'],
146 150 'capabilities' => array_values( array_filter( array_map( 'strval', (array) $args['capabilities'] ) ) ),
147 151 );
148 - desktop_mode_oauth_relay_registry( $service, $entry );
152 + openstation_oauth_relay_registry( $service, $entry );
149 153
150 154 /**
151 155 * Fires after an OAuth relay is registered. Use this to layer
152 156 * observability or to extend behaviour.
153 157 *
154 - * @since 0.8.2
155 - *
156 158 * @param string $service The service slug.
157 159 * @param array $entry The stored registry entry minus the secrets
158 160 * (`client_secret` is masked).
159 161 */
160 162 do_action(
161 - 'desktop_mode_oauth_relay_registered',
163 + 'openstation_oauth_relay_registered',
162 164 $service,
163 165 array_merge( $entry, array( 'client_secret' => '[redacted]' ) )
164 166 );
165 167
@@ -169,12 +171,11 @@
169 171 /**
170 172 * Static registry for OAuth relays. Mirror of the icon / native-
171 173 * window / wallpaper registries.
172 174 *
173 - * @since 0.8.2
174 175 * @internal
175 176 */
176 -function desktop_mode_oauth_relay_registry( $service = '', $entry = null ) {
177 +function openstation_oauth_relay_registry( $service = '', $entry = null ) {
177 178 static $store = array();
178 179
179 180 if ( '' === (string) $service ) {
180 181 return $store;
@@ -190,22 +191,20 @@
190 191 }
191 192
192 193 /**
193 194 * Remove a previously registered OAuth relay. Mirror of
194 - * `desktop_mode_register_oauth_relay()` — handy for plugins that
195 + * `openstation_register_oauth_relay()` — handy for plugins that
195 196 * register conditionally and for PHPUnit teardowns.
196 197 *
197 - * @since 0.8.2
198 - *
199 198 * @param string $service Service slug passed to register.
200 199 * @return void
201 200 */
202 -function desktop_mode_unregister_oauth_relay( $service ) {
201 +function openstation_unregister_oauth_relay( $service ) {
203 202 $service = sanitize_key( (string) $service );
204 203 if ( '' === $service ) {
205 204 return;
206 205 }
207 - desktop_mode_oauth_relay_registry( $service, '__unset__' );
206 + openstation_oauth_relay_registry( $service, '__unset__' );
208 207 }
209 208
210 209 /**
211 210 * The redirect URI the popup posts back to. Same for every service —
@@ -211,13 +210,11 @@
211 210 * The redirect URI the popup posts back to. Same for every service —
212 211 * the framework recovers the service from the state transient, so no
213 212 * service query arg is needed.
214 213 *
215 - * @since 0.8.2
216 - *
217 214 * @return string
218 215 */
219 -function desktop_mode_oauth_redirect_uri() {
216 +function openstation_oauth_redirect_uri() {
220 217 return rest_url( 'desktop-mode/v1/oauth/callback' );
221 218 }
222 219
223 220 /**
@@ -224,26 +221,24 @@
224 221 * Generate a fresh state nonce, persist it in a transient keyed by
225 222 * the state value (with `user_id` + `service` stored in the transient
226 223 * payload), and return the value the popup will round-trip.
227 224 *
228 - * @since 0.8.2
229 - *
230 225 * @param int $user_id The user starting the flow.
231 226 * @param string $service The service slug being authorized.
232 227 * @return string The state value to embed in the authorize URL.
233 228 */
234 -function desktop_mode_oauth_issue_state( $user_id, $service ) {
229 +function openstation_oauth_issue_state( $user_id, $service ) {
235 230 // 32 chars of letters+digits — `wp_generate_password` with the
236 231 // no-special-chars flag is the canonical WP shape.
237 232 $state = wp_generate_password( 32, false );
238 233 set_transient(
239 - DESKTOP_MODE_OAUTH_TRANSIENT_PREFIX . $state,
234 + OPENSTATION_OAUTH_TRANSIENT_PREFIX . $state,
240 235 array(
241 236 'user_id' => (int) $user_id,
242 237 'service' => (string) $service,
243 238 'issued' => time(),
244 239 ),
245 - DESKTOP_MODE_OAUTH_STATE_TTL
240 + OPENSTATION_OAUTH_STATE_TTL
246 241 );
247 242 return $state;
248 243 }
249 244
@@ -253,19 +248,17 @@
253 248 *
254 249 * Single-use: a successful read deletes the transient so a replay
255 250 * with the same state fails.
256 251 *
257 - * @since 0.8.2
258 - *
259 252 * @param string $state State value from the callback query.
260 253 * @return array{user_id:int,service:string,issued:int}|null
261 254 */
262 -function desktop_mode_oauth_consume_state( $state ) {
255 +function openstation_oauth_consume_state( $state ) {
263 256 $state = (string) $state;
264 257 if ( '' === $state ) {
265 258 return null;
266 259 }
267 - $key = DESKTOP_MODE_OAUTH_TRANSIENT_PREFIX . $state;
260 + $key = OPENSTATION_OAUTH_TRANSIENT_PREFIX . $state;
268 261 $entry = get_transient( $key );
269 262 if ( ! is_array( $entry ) || empty( $entry['user_id'] ) || empty( $entry['service'] ) ) {
270 263 return null;
271 264 }
@@ -280,19 +273,17 @@
280 273 /**
281 274 * REST: `POST /desktop-mode/v1/oauth/start` — issue a state and
282 275 * return the assembled authorize URL.
283 276 *
284 - * @since 0.8.2
285 - *
286 277 * @param WP_REST_Request $request
287 278 * @return WP_REST_Response|WP_Error
288 279 */
289 -function desktop_mode_rest_oauth_start( WP_REST_Request $request ) {
280 +function openstation_rest_oauth_start( WP_REST_Request $request ) {
290 281 $service = sanitize_key( (string) $request->get_param( 'service' ) );
291 - $entry = desktop_mode_oauth_relay_registry( $service );
282 + $entry = openstation_oauth_relay_registry( $service );
292 283 if ( ! is_array( $entry ) ) {
293 284 return new WP_Error(
294 - 'desktop_mode_oauth_unknown_service',
285 + 'openstation_oauth_unknown_service',
295 286 __( 'No OAuth relay is registered for that service.', 'desktop-mode' ),
296 287 array( 'status' => 404 )
297 288 );
298 289 }
@@ -299,9 +290,9 @@
299 290
300 291 foreach ( $entry['capabilities'] as $cap ) {
301 292 if ( ! current_user_can( (string) $cap ) ) {
302 293 return new WP_Error(
303 - 'desktop_mode_oauth_capability_denied',
294 + 'openstation_oauth_capability_denied',
304 295 __( 'Current user lacks the capability required to start this OAuth flow.', 'desktop-mode' ),
305 296 array( 'status' => 403 )
306 297 );
307 298 }
@@ -307,14 +298,14 @@
307 298 }
308 299 }
309 300
310 301 $user_id = get_current_user_id();
311 - $state = desktop_mode_oauth_issue_state( $user_id, $service );
302 + $state = openstation_oauth_issue_state( $user_id, $service );
312 303
313 304 $query = array(
314 305 'response_type' => 'code',
315 306 'client_id' => $entry['client_id'],
316 - 'redirect_uri' => desktop_mode_oauth_redirect_uri(),
307 + 'redirect_uri' => openstation_oauth_redirect_uri(),
317 308 'state' => $state,
318 309 );
319 310 if ( '' !== $entry['scope'] ) {
320 311 $query['scope'] = $entry['scope'];
@@ -325,16 +316,14 @@
325 316 * Lets plugins inject service-specific extras (`access_type=offline`
326 317 * for Google, `force_login=true` for Twitter, `prompt=consent`,
327 318 * etc.) without having to fork the relay.
328 319 *
329 - * @since 0.8.2
330 - *
331 320 * @param array $query Default query params.
332 321 * @param string $service Service slug.
333 322 * @param array $entry Registry entry (with secrets redacted).
334 323 */
335 324 $query = apply_filters(
336 - 'desktop_mode_oauth_authorize_query',
325 + 'openstation_oauth_authorize_query',
337 326 $query,
338 327 $service,
339 328 array_merge( $entry, array( 'client_secret' => '[redacted]' ) )
340 329 );
@@ -353,54 +342,60 @@
353 342 * REST: `GET /desktop-mode/v1/oauth/callback` — exchange the auth
354 343 * code for tokens, fire the registered `on_success` handler, then
355 344 * render an HTML page that `postMessage`s the opener and closes.
356 345 *
357 - * @since 0.8.2
358 - *
359 346 * @param WP_REST_Request $request
360 347 * @return WP_REST_Response|WP_Error
361 348 */
362 -function desktop_mode_rest_oauth_callback( WP_REST_Request $request ) {
349 +function openstation_rest_oauth_callback( WP_REST_Request $request ) {
363 350 $state = (string) $request->get_param( 'state' );
364 351 $code = (string) $request->get_param( 'code' );
365 352 $error = (string) $request->get_param( 'error' );
366 353
367 - $consumed = desktop_mode_oauth_consume_state( $state );
354 + $consumed = openstation_oauth_consume_state( $state );
368 355 if ( null === $consumed ) {
369 - return desktop_mode_oauth_render_callback_html( array(
370 - 'ok' => false,
371 - 'reason' => 'invalid_state',
372 - 'message' => __( 'OAuth state nonce missing, expired, or already used.', 'desktop-mode' ),
373 - ) );
356 + return openstation_oauth_render_callback_html(
357 + array(
358 + 'ok' => false,
359 + 'reason' => 'invalid_state',
360 + 'message' => __( 'OAuth state nonce missing, expired, or already used.', 'desktop-mode' ),
361 + )
362 + );
374 363 }
375 364 $service = $consumed['service'];
376 365 $user_id = $consumed['user_id'];
377 366
378 367 if ( '' !== $error ) {
379 - return desktop_mode_oauth_render_callback_html( array(
380 - 'ok' => false,
381 - 'service' => $service,
382 - 'reason' => 'authorize_denied',
383 - 'message' => $error,
384 - ) );
368 + return openstation_oauth_render_callback_html(
369 + array(
370 + 'ok' => false,
371 + 'service' => $service,
372 + 'reason' => 'authorize_denied',
373 + 'message' => $error,
374 + )
375 + );
385 376 }
386 377
387 - $entry = desktop_mode_oauth_relay_registry( $service );
378 + $entry = openstation_oauth_relay_registry( $service );
388 379 if ( ! is_array( $entry ) ) {
389 - return desktop_mode_oauth_render_callback_html( array(
390 - 'ok' => false,
391 - 'reason' => 'unknown_service',
392 - 'message' => __( 'OAuth relay is no longer registered for that service.', 'desktop-mode' ),
393 - ) );
380 + return openstation_oauth_render_callback_html(
381 + array(
382 + 'ok' => false,
383 + 'reason' => 'unknown_service',
384 + 'message' => __( 'OAuth relay is no longer registered for that service.', 'desktop-mode' ),
385 + )
386 + );
394 387 }
395 388
396 389 if ( '' === $code ) {
397 - return desktop_mode_oauth_render_callback_html( array(
398 - 'ok' => false,
399 - 'service' => $service,
400 - 'reason' => 'missing_code',
401 - 'message' => __( 'OAuth callback did not return an authorization code.', 'desktop-mode' ),
402 - ) );
390 + return openstation_oauth_render_callback_html(
391 + array(
392 + 'ok' => false,
393 + 'service' => $service,
394 + 'reason' => 'missing_code',
395 + 'message' => __( 'OAuth callback did not return an authorization code.', 'desktop-mode' ),
396 + )
397 + );
403 398 }
404 399
405 400 $response = wp_remote_post(
406 401 $entry['token_url'],
@@ -410,46 +405,52 @@
410 405 'grant_type' => 'authorization_code',
411 406 'code' => $code,
412 407 'client_id' => $entry['client_id'],
413 408 'client_secret' => $entry['client_secret'],
414 - 'redirect_uri' => desktop_mode_oauth_redirect_uri(),
409 + 'redirect_uri' => openstation_oauth_redirect_uri(),
415 410 ),
416 411 'headers' => array( 'Accept' => 'application/json' ),
417 412 )
418 413 );
419 414 if ( is_wp_error( $response ) ) {
420 - return desktop_mode_oauth_render_callback_html( array(
421 - 'ok' => false,
422 - 'service' => $service,
423 - 'reason' => 'token_request_failed',
424 - 'message' => $response->get_error_message(),
425 - ) );
415 + return openstation_oauth_render_callback_html(
416 + array(
417 + 'ok' => false,
418 + 'service' => $service,
419 + 'reason' => 'token_request_failed',
420 + 'message' => $response->get_error_message(),
421 + )
422 + );
426 423 }
427 424 $status = (int) wp_remote_retrieve_response_code( $response );
428 425 $body = wp_remote_retrieve_body( $response );
429 426 $tokens = json_decode( $body, true );
430 427 if ( $status < 200 || $status >= 300 || ! is_array( $tokens ) ) {
431 - return desktop_mode_oauth_render_callback_html( array(
432 - 'ok' => false,
433 - 'service' => $service,
434 - 'reason' => 'token_exchange_failed',
435 - 'message' => sprintf(
428 + return openstation_oauth_render_callback_html(
429 + array(
430 + 'ok' => false,
431 + 'service' => $service,
432 + 'reason' => 'token_exchange_failed',
433 + 'message' => sprintf(
436 434 /* translators: %d: HTTP status code. */
437 - __( 'Token exchange failed with HTTP %d.', 'desktop-mode' ),
438 - $status
439 - ),
440 - ) );
435 + __( 'Token exchange failed with HTTP %d.', 'desktop-mode' ),
436 + $status
437 + ),
438 + )
439 + );
441 440 }
442 441
443 442 try {
444 443 call_user_func( $entry['on_success'], $user_id, $tokens, $service );
445 444 } catch ( \Throwable $e ) {
446 - return desktop_mode_oauth_render_callback_html( array(
447 - 'ok' => false,
448 - 'service' => $service,
449 - 'reason' => 'on_success_threw',
450 - 'message' => $e->getMessage(),
451 - ) );
445 + return openstation_oauth_render_callback_html(
446 + array(
447 + 'ok' => false,
448 + 'service' => $service,
449 + 'reason' => 'on_success_threw',
450 + 'message' => $e->getMessage(),
451 + )
452 + );
452 453 }
453 454
454 455 /**
455 456 * Fires after a successful OAuth round-trip — after `on_success`
@@ -456,19 +457,19 @@
456 457 * persists the tokens. Plugins use this to refresh badges,
457 458 * re-render dock items, or surface a "connected" toast in
458 459 * sibling windows via the activity bus.
459 460 *
460 - * @since 0.8.2
461 - *
462 461 * @param string $service Service slug.
463 462 * @param int $user_id User who connected.
464 463 */
465 - do_action( 'desktop_mode_oauth_relay_connected', $service, $user_id );
464 + do_action( 'openstation_oauth_relay_connected', $service, $user_id );
466 465
467 - return desktop_mode_oauth_render_callback_html( array(
468 - 'ok' => true,
469 - 'service' => $service,
470 - ) );
466 + return openstation_oauth_render_callback_html(
467 + array(
468 + 'ok' => true,
469 + 'service' => $service,
470 + )
471 + );
471 472 }
472 473
473 474 /**
474 475 * Build the HTML string the OAuth callback popup renders.
@@ -473,9 +474,9 @@
473 474 /**
474 475 * Build the HTML string the OAuth callback popup renders.
475 476 *
476 477 * Pure function — no side effects. Split out from
477 - * {@see desktop_mode_oauth_render_callback_html()} so unit tests
478 + * {@see openstation_oauth_render_callback_html()} so unit tests
478 479 * can exercise the markup directly without going through a REST
479 480 * dispatch + output-buffer dance.
480 481 *
481 482 * **Why `wp_json_encode` and not `esc_js` for the inlined values.**
@@ -490,15 +491,14 @@
490 491 * neutralising any `</script>` substrings in string values
491 492 * (defence-in-depth — our payload values are server-built, but
492 493 * filters could mutate them).
493 494 *
494 - * @since 0.8.2
495 495 * @internal
496 496 *
497 497 * @param array $payload `{ ok: bool, service?: string, reason?: string, message?: string }`.
498 498 * @return string
499 499 */
500 -function desktop_mode_oauth_build_callback_html( array $payload ) {
500 +function openstation_oauth_build_callback_html( array $payload ) {
501 501 // `JSON_HEX_TAG` escapes `<` and `>` as `\u003C` / `\u003E` so a
502 502 // `</script>` smuggled into any string value can't terminate the
503 503 // script block early. `JSON_UNESCAPED_SLASHES` keeps URLs
504 504 // readable in DevTools.
@@ -520,9 +520,9 @@
520 520 ( function () {
521 521 try {
522 522 if ( window.opener ) {
523 523 window.opener.postMessage(
524 - { type: 'desktop-mode-oauth-callback', payload: {$payload_literal} },
524 + { type: 'os-oauth-callback', payload: {$payload_literal} },
525 525 {$origin_literal}
526 526 );
527 527 }
528 528 } catch ( e ) {}
@@ -553,15 +553,13 @@
553 553 * The returned `WP_REST_Response` carries the HTML as `data` so
554 554 * unit tests reading `$response->get_data()` still see the body
555 555 * (the filter only fires when the response is actually served).
556 556 *
557 - * @since 0.8.2
558 - *
559 557 * @param array $payload `{ ok: bool, service?: string, reason?: string, message?: string }`.
560 558 * @return WP_REST_Response
561 559 */
562 -function desktop_mode_oauth_render_callback_html( array $payload ) {
563 - $html = desktop_mode_oauth_build_callback_html( $payload );
560 +function openstation_oauth_render_callback_html( array $payload ) {
561 + $html = openstation_oauth_build_callback_html( $payload );
564 562
565 563 $filter_cb = null;
566 564 $filter_cb = static function ( $served, $result, $request ) use ( $html, &$filter_cb ) {
567 565 // Scope tightly to the OAuth callback route — never affect
@@ -580,9 +578,9 @@
580 578 }
581 579 if ( ! headers_sent() ) {
582 580 header( 'Content-Type: text/html; charset=utf-8' );
583 581 }
584 - // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- statically-built HTML; embedded payload is wp_json_encode( …, JSON_HEX_TAG )-escaped during build in `desktop_mode_oauth_build_callback_html`.
582 + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- statically-built HTML; embedded payload is wp_json_encode( …, JSON_HEX_TAG )-escaped during build in `openstation_oauth_build_callback_html`.
585 583 echo $html;
586 584 // `true` tells WP_REST_Server we already served the response,
587 585 // short-circuiting the json-encode + `echo` path that follows.
588 586 return true;
@@ -599,13 +597,11 @@
599 597 * The per-relay `capabilities` gate runs in the callback itself
600 598 * so capability denial returns the canonical service-not-allowed
601 599 * error rather than the REST-level "forbidden".
602 600 *
603 - * @since 0.8.2
604 - *
605 601 * @return true|WP_Error
606 602 */
607 -function desktop_mode_rest_oauth_start_permission() {
603 +function openstation_rest_oauth_start_permission() {
608 604 if ( ! is_user_logged_in() ) {
609 605 return new WP_Error(
610 606 'rest_forbidden',
611 607 __( 'You must be logged in to start an OAuth flow.', 'desktop-mode' ),
@@ -617,20 +613,18 @@
617 613
618 614 /**
619 615 * Register the OAuth REST routes on `rest_api_init`.
620 616 *
621 - * @since 0.8.2
622 - *
623 617 * @return void
624 618 */
625 -function desktop_mode_register_oauth_rest_routes() {
619 +function openstation_register_oauth_rest_routes() {
626 620 register_rest_route(
627 621 'desktop-mode/v1',
628 622 '/oauth/start',
629 623 array(
630 624 'methods' => WP_REST_Server::CREATABLE,
631 - 'callback' => 'desktop_mode_rest_oauth_start',
632 - 'permission_callback' => 'desktop_mode_rest_oauth_start_permission',
625 + 'callback' => 'openstation_rest_oauth_start',
626 + 'permission_callback' => 'openstation_rest_oauth_start_permission',
633 627 'args' => array(
634 628 'service' => array(
635 629 'required' => true,
636 630 'type' => 'string',
@@ -643,18 +637,27 @@
643 637 'desktop-mode/v1',
644 638 '/oauth/callback',
645 639 array(
646 640 'methods' => WP_REST_Server::READABLE,
647 - 'callback' => 'desktop_mode_rest_oauth_callback',
641 + 'callback' => 'openstation_rest_oauth_callback',
648 642 // Public — the route is reached via a redirect from the
649 643 // remote service. Auth is the state nonce + (later) the
650 644 // per-service capabilities check on the start side.
651 645 'permission_callback' => '__return_true',
652 646 'args' => array(
653 - 'state' => array( 'required' => true, 'type' => 'string' ),
654 - 'code' => array( 'required' => false, 'type' => 'string' ),
655 - 'error' => array( 'required' => false, 'type' => 'string' ),
647 + 'state' => array(
648 + 'required' => true,
649 + 'type' => 'string',
650 + ),
651 + 'code' => array(
652 + 'required' => false,
653 + 'type' => 'string',
654 + ),
655 + 'error' => array(
656 + 'required' => false,
657 + 'type' => 'string',
658 + ),
656 659 ),
657 660 )
658 661 );
659 662 }
660 -add_action( 'rest_api_init', 'desktop_mode_register_oauth_rest_routes' );
663 +add_action( 'rest_api_init', 'openstation_register_oauth_rest_routes' );