PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 1.1.11
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v1.1.11
1.1.11 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 All 35 releases
← All changes | includes/oauth-relay.php +130 -98 0.9.8 → 1.1.11 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',
@@ -30,9 +30,9 @@
30 30 * ] );
31 31 *
32 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,15 +40,22 @@
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
44 + * @package OpenStation
45 45 */
46 46
47 47 defined( 'ABSPATH' ) || exit;
48 48
49 -const DESKTOP_MODE_OAUTH_TRANSIENT_PREFIX = 'desktop_mode_oauth_state_';
50 -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.
51 58
52 59 /**
53 60 * Register an OAuth relay for `$service`.
54 61 *
@@ -82,13 +89,13 @@
82 89 * logged-in user).
83 90 * }
84 91 * @return true|WP_Error `true` on success, `WP_Error` on validation failure.
85 92 */
86 -function desktop_mode_register_oauth_relay( $service, $args = array() ) {
93 +function openstation_register_oauth_relay( $service, $args = array() ) {
87 94 $service = sanitize_key( (string) $service );
88 95 if ( '' === $service ) {
89 96 return new WP_Error(
90 - 'desktop_mode_oauth_missing_service',
97 + 'openstation_oauth_missing_service',
91 98 __( 'OAuth relay registration requires a non-empty service slug.', 'desktop-mode' )
92 99 );
93 100 }
94 101
@@ -100,14 +107,14 @@
100 107 'scope' => '',
101 108 'on_success' => null,
102 109 'capabilities' => array( 'read' ),
103 110 );
104 - $args = wp_parse_args( $args, $defaults );
111 + $args = wp_parse_args( $args, $defaults );
105 112
106 113 foreach ( array( 'authorize_url', 'token_url', 'client_id', 'client_secret' ) as $required ) {
107 114 if ( '' === (string) $args[ $required ] ) {
108 115 return new WP_Error(
109 - 'desktop_mode_oauth_missing_' . $required,
116 + 'openstation_oauth_missing_' . $required,
110 117 /* translators: %s: missing field name. */
111 118 sprintf( __( 'OAuth relay registration requires a non-empty `%s`.', 'desktop-mode' ), $required ),
112 119 array( 'service' => $service )
113 120 );
@@ -115,9 +122,9 @@
115 122 }
116 123
117 124 if ( ! is_callable( $args['on_success'] ) ) {
118 125 return new WP_Error(
119 - 'desktop_mode_oauth_missing_on_success',
126 + 'openstation_oauth_missing_on_success',
120 127 __( 'OAuth relay registration requires a callable `on_success` handler.', 'desktop-mode' ),
121 128 array( 'service' => $service )
122 129 );
123 130 }
@@ -125,9 +132,9 @@
125 132 $authorize_url = esc_url_raw( (string) $args['authorize_url'], array( 'http', 'https' ) );
126 133 $token_url = esc_url_raw( (string) $args['token_url'], array( 'http', 'https' ) );
127 134 if ( '' === $authorize_url || '' === $token_url ) {
128 135 return new WP_Error(
129 - 'desktop_mode_oauth_invalid_url',
136 + 'openstation_oauth_invalid_url',
130 137 __( 'OAuth relay `authorize_url` and `token_url` must be valid http(s) URLs.', 'desktop-mode' ),
131 138 array( 'service' => $service )
132 139 );
133 140 }
@@ -141,9 +148,9 @@
141 148 'scope' => (string) $args['scope'],
142 149 'on_success' => $args['on_success'],
143 150 'capabilities' => array_values( array_filter( array_map( 'strval', (array) $args['capabilities'] ) ) ),
144 151 );
145 - desktop_mode_oauth_relay_registry( $service, $entry );
152 + openstation_oauth_relay_registry( $service, $entry );
146 153
147 154 /**
148 155 * Fires after an OAuth relay is registered. Use this to layer
149 156 * observability or to extend behaviour.
@@ -152,9 +159,9 @@
152 159 * @param array $entry The stored registry entry minus the secrets
153 160 * (`client_secret` is masked).
154 161 */
155 162 do_action(
156 - 'desktop_mode_oauth_relay_registered',
163 + 'openstation_oauth_relay_registered',
157 164 $service,
158 165 array_merge( $entry, array( 'client_secret' => '[redacted]' ) )
159 166 );
160 167
@@ -166,9 +173,9 @@
166 173 * window / wallpaper registries.
167 174 *
168 175 * @internal
169 176 */
170 -function desktop_mode_oauth_relay_registry( $service = '', $entry = null ) {
177 +function openstation_oauth_relay_registry( $service = '', $entry = null ) {
171 178 static $store = array();
172 179
173 180 if ( '' === (string) $service ) {
174 181 return $store;
@@ -184,20 +191,20 @@
184 191 }
185 192
186 193 /**
187 194 * Remove a previously registered OAuth relay. Mirror of
188 - * `desktop_mode_register_oauth_relay()` — handy for plugins that
195 + * `openstation_register_oauth_relay()` — handy for plugins that
189 196 * register conditionally and for PHPUnit teardowns.
190 197 *
191 198 * @param string $service Service slug passed to register.
192 199 * @return void
193 200 */
194 -function desktop_mode_unregister_oauth_relay( $service ) {
201 +function openstation_unregister_oauth_relay( $service ) {
195 202 $service = sanitize_key( (string) $service );
196 203 if ( '' === $service ) {
197 204 return;
198 205 }
199 - desktop_mode_oauth_relay_registry( $service, '__unset__' );
206 + openstation_oauth_relay_registry( $service, '__unset__' );
200 207 }
201 208
202 209 /**
203 210 * The redirect URI the popup posts back to. Same for every service —
@@ -205,9 +212,9 @@
205 212 * service query arg is needed.
206 213 *
207 214 * @return string
208 215 */
209 -function desktop_mode_oauth_redirect_uri() {
216 +function openstation_oauth_redirect_uri() {
210 217 return rest_url( 'desktop-mode/v1/oauth/callback' );
211 218 }
212 219
213 220 /**
@@ -218,20 +225,20 @@
218 225 * @param int $user_id The user starting the flow.
219 226 * @param string $service The service slug being authorized.
220 227 * @return string The state value to embed in the authorize URL.
221 228 */
222 -function desktop_mode_oauth_issue_state( $user_id, $service ) {
229 +function openstation_oauth_issue_state( $user_id, $service ) {
223 230 // 32 chars of letters+digits — `wp_generate_password` with the
224 231 // no-special-chars flag is the canonical WP shape.
225 232 $state = wp_generate_password( 32, false );
226 233 set_transient(
227 - DESKTOP_MODE_OAUTH_TRANSIENT_PREFIX . $state,
234 + OPENSTATION_OAUTH_TRANSIENT_PREFIX . $state,
228 235 array(
229 236 'user_id' => (int) $user_id,
230 237 'service' => (string) $service,
231 238 'issued' => time(),
232 239 ),
233 - DESKTOP_MODE_OAUTH_STATE_TTL
240 + OPENSTATION_OAUTH_STATE_TTL
234 241 );
235 242 return $state;
236 243 }
237 244
@@ -244,14 +251,14 @@
244 251 *
245 252 * @param string $state State value from the callback query.
246 253 * @return array{user_id:int,service:string,issued:int}|null
247 254 */
248 -function desktop_mode_oauth_consume_state( $state ) {
255 +function openstation_oauth_consume_state( $state ) {
249 256 $state = (string) $state;
250 257 if ( '' === $state ) {
251 258 return null;
252 259 }
253 - $key = DESKTOP_MODE_OAUTH_TRANSIENT_PREFIX . $state;
260 + $key = OPENSTATION_OAUTH_TRANSIENT_PREFIX . $state;
254 261 $entry = get_transient( $key );
255 262 if ( ! is_array( $entry ) || empty( $entry['user_id'] ) || empty( $entry['service'] ) ) {
256 263 return null;
257 264 }
@@ -269,14 +276,14 @@
269 276 *
270 277 * @param WP_REST_Request $request
271 278 * @return WP_REST_Response|WP_Error
272 279 */
273 -function desktop_mode_rest_oauth_start( WP_REST_Request $request ) {
280 +function openstation_rest_oauth_start( WP_REST_Request $request ) {
274 281 $service = sanitize_key( (string) $request->get_param( 'service' ) );
275 - $entry = desktop_mode_oauth_relay_registry( $service );
282 + $entry = openstation_oauth_relay_registry( $service );
276 283 if ( ! is_array( $entry ) ) {
277 284 return new WP_Error(
278 - 'desktop_mode_oauth_unknown_service',
285 + 'openstation_oauth_unknown_service',
279 286 __( 'No OAuth relay is registered for that service.', 'desktop-mode' ),
280 287 array( 'status' => 404 )
281 288 );
282 289 }
@@ -283,9 +290,9 @@
283 290
284 291 foreach ( $entry['capabilities'] as $cap ) {
285 292 if ( ! current_user_can( (string) $cap ) ) {
286 293 return new WP_Error(
287 - 'desktop_mode_oauth_capability_denied',
294 + 'openstation_oauth_capability_denied',
288 295 __( 'Current user lacks the capability required to start this OAuth flow.', 'desktop-mode' ),
289 296 array( 'status' => 403 )
290 297 );
291 298 }
@@ -291,14 +298,14 @@
291 298 }
292 299 }
293 300
294 301 $user_id = get_current_user_id();
295 - $state = desktop_mode_oauth_issue_state( $user_id, $service );
302 + $state = openstation_oauth_issue_state( $user_id, $service );
296 303
297 304 $query = array(
298 305 'response_type' => 'code',
299 306 'client_id' => $entry['client_id'],
300 - 'redirect_uri' => desktop_mode_oauth_redirect_uri(),
307 + 'redirect_uri' => openstation_oauth_redirect_uri(),
301 308 'state' => $state,
302 309 );
303 310 if ( '' !== $entry['scope'] ) {
304 311 $query['scope'] = $entry['scope'];
@@ -314,9 +321,9 @@
314 321 * @param string $service Service slug.
315 322 * @param array $entry Registry entry (with secrets redacted).
316 323 */
317 324 $query = apply_filters(
318 - 'desktop_mode_oauth_authorize_query',
325 + 'openstation_oauth_authorize_query',
319 326 $query,
320 327 $service,
321 328 array_merge( $entry, array( 'client_secret' => '[redacted]' ) )
322 329 );
@@ -338,49 +345,57 @@
338 345 *
339 346 * @param WP_REST_Request $request
340 347 * @return WP_REST_Response|WP_Error
341 348 */
342 -function desktop_mode_rest_oauth_callback( WP_REST_Request $request ) {
349 +function openstation_rest_oauth_callback( WP_REST_Request $request ) {
343 350 $state = (string) $request->get_param( 'state' );
344 351 $code = (string) $request->get_param( 'code' );
345 352 $error = (string) $request->get_param( 'error' );
346 353
347 - $consumed = desktop_mode_oauth_consume_state( $state );
354 + $consumed = openstation_oauth_consume_state( $state );
348 355 if ( null === $consumed ) {
349 - return desktop_mode_oauth_render_callback_html( array(
350 - 'ok' => false,
351 - 'reason' => 'invalid_state',
352 - 'message' => __( 'OAuth state nonce missing, expired, or already used.', 'desktop-mode' ),
353 - ) );
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 + );
354 363 }
355 364 $service = $consumed['service'];
356 365 $user_id = $consumed['user_id'];
357 366
358 367 if ( '' !== $error ) {
359 - return desktop_mode_oauth_render_callback_html( array(
360 - 'ok' => false,
361 - 'service' => $service,
362 - 'reason' => 'authorize_denied',
363 - 'message' => $error,
364 - ) );
368 + return openstation_oauth_render_callback_html(
369 + array(
370 + 'ok' => false,
371 + 'service' => $service,
372 + 'reason' => 'authorize_denied',
373 + 'message' => $error,
374 + )
375 + );
365 376 }
366 377
367 - $entry = desktop_mode_oauth_relay_registry( $service );
378 + $entry = openstation_oauth_relay_registry( $service );
368 379 if ( ! is_array( $entry ) ) {
369 - return desktop_mode_oauth_render_callback_html( array(
370 - 'ok' => false,
371 - 'reason' => 'unknown_service',
372 - 'message' => __( 'OAuth relay is no longer registered for that service.', 'desktop-mode' ),
373 - ) );
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 + );
374 387 }
375 388
376 389 if ( '' === $code ) {
377 - return desktop_mode_oauth_render_callback_html( array(
378 - 'ok' => false,
379 - 'service' => $service,
380 - 'reason' => 'missing_code',
381 - 'message' => __( 'OAuth callback did not return an authorization code.', 'desktop-mode' ),
382 - ) );
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 + );
383 398 }
384 399
385 400 $response = wp_remote_post(
386 401 $entry['token_url'],
@@ -390,46 +405,52 @@
390 405 'grant_type' => 'authorization_code',
391 406 'code' => $code,
392 407 'client_id' => $entry['client_id'],
393 408 'client_secret' => $entry['client_secret'],
394 - 'redirect_uri' => desktop_mode_oauth_redirect_uri(),
409 + 'redirect_uri' => openstation_oauth_redirect_uri(),
395 410 ),
396 411 'headers' => array( 'Accept' => 'application/json' ),
397 412 )
398 413 );
399 414 if ( is_wp_error( $response ) ) {
400 - return desktop_mode_oauth_render_callback_html( array(
401 - 'ok' => false,
402 - 'service' => $service,
403 - 'reason' => 'token_request_failed',
404 - 'message' => $response->get_error_message(),
405 - ) );
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 + );
406 423 }
407 424 $status = (int) wp_remote_retrieve_response_code( $response );
408 425 $body = wp_remote_retrieve_body( $response );
409 426 $tokens = json_decode( $body, true );
410 427 if ( $status < 200 || $status >= 300 || ! is_array( $tokens ) ) {
411 - return desktop_mode_oauth_render_callback_html( array(
412 - 'ok' => false,
413 - 'service' => $service,
414 - 'reason' => 'token_exchange_failed',
415 - '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(
416 434 /* translators: %d: HTTP status code. */
417 - __( 'Token exchange failed with HTTP %d.', 'desktop-mode' ),
418 - $status
419 - ),
420 - ) );
435 + __( 'Token exchange failed with HTTP %d.', 'desktop-mode' ),
436 + $status
437 + ),
438 + )
439 + );
421 440 }
422 441
423 442 try {
424 443 call_user_func( $entry['on_success'], $user_id, $tokens, $service );
425 444 } catch ( \Throwable $e ) {
426 - return desktop_mode_oauth_render_callback_html( array(
427 - 'ok' => false,
428 - 'service' => $service,
429 - 'reason' => 'on_success_threw',
430 - 'message' => $e->getMessage(),
431 - ) );
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 + );
432 453 }
433 454
434 455 /**
435 456 * Fires after a successful OAuth round-trip — after `on_success`
@@ -439,14 +460,16 @@
439 460 *
440 461 * @param string $service Service slug.
441 462 * @param int $user_id User who connected.
442 463 */
443 - do_action( 'desktop_mode_oauth_relay_connected', $service, $user_id );
464 + do_action( 'openstation_oauth_relay_connected', $service, $user_id );
444 465
445 - return desktop_mode_oauth_render_callback_html( array(
446 - 'ok' => true,
447 - 'service' => $service,
448 - ) );
466 + return openstation_oauth_render_callback_html(
467 + array(
468 + 'ok' => true,
469 + 'service' => $service,
470 + )
471 + );
449 472 }
450 473
451 474 /**
452 475 * Build the HTML string the OAuth callback popup renders.
@@ -451,9 +474,9 @@
451 474 /**
452 475 * Build the HTML string the OAuth callback popup renders.
453 476 *
454 477 * Pure function — no side effects. Split out from
455 - * {@see desktop_mode_oauth_render_callback_html()} so unit tests
478 + * {@see openstation_oauth_render_callback_html()} so unit tests
456 479 * can exercise the markup directly without going through a REST
457 480 * dispatch + output-buffer dance.
458 481 *
459 482 * **Why `wp_json_encode` and not `esc_js` for the inlined values.**
@@ -473,9 +496,9 @@
473 496 *
474 497 * @param array $payload `{ ok: bool, service?: string, reason?: string, message?: string }`.
475 498 * @return string
476 499 */
477 -function desktop_mode_oauth_build_callback_html( array $payload ) {
500 +function openstation_oauth_build_callback_html( array $payload ) {
478 501 // `JSON_HEX_TAG` escapes `<` and `>` as `\u003C` / `\u003E` so a
479 502 // `</script>` smuggled into any string value can't terminate the
480 503 // script block early. `JSON_UNESCAPED_SLASHES` keeps URLs
481 504 // readable in DevTools.
@@ -497,9 +520,9 @@
497 520 ( function () {
498 521 try {
499 522 if ( window.opener ) {
500 523 window.opener.postMessage(
501 - { type: 'desktop-mode-oauth-callback', payload: {$payload_literal} },
524 + { type: 'os-oauth-callback', payload: {$payload_literal} },
502 525 {$origin_literal}
503 526 );
504 527 }
505 528 } catch ( e ) {}
@@ -533,10 +556,10 @@
533 556 *
534 557 * @param array $payload `{ ok: bool, service?: string, reason?: string, message?: string }`.
535 558 * @return WP_REST_Response
536 559 */
537 -function desktop_mode_oauth_render_callback_html( array $payload ) {
538 - $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 );
539 562
540 563 $filter_cb = null;
541 564 $filter_cb = static function ( $served, $result, $request ) use ( $html, &$filter_cb ) {
542 565 // Scope tightly to the OAuth callback route — never affect
@@ -555,9 +578,9 @@
555 578 }
556 579 if ( ! headers_sent() ) {
557 580 header( 'Content-Type: text/html; charset=utf-8' );
558 581 }
559 - // 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`.
560 583 echo $html;
561 584 // `true` tells WP_REST_Server we already served the response,
562 585 // short-circuiting the json-encode + `echo` path that follows.
563 586 return true;
@@ -576,9 +599,9 @@
576 599 * error rather than the REST-level "forbidden".
577 600 *
578 601 * @return true|WP_Error
579 602 */
580 -function desktop_mode_rest_oauth_start_permission() {
603 +function openstation_rest_oauth_start_permission() {
581 604 if ( ! is_user_logged_in() ) {
582 605 return new WP_Error(
583 606 'rest_forbidden',
584 607 __( 'You must be logged in to start an OAuth flow.', 'desktop-mode' ),
@@ -592,16 +615,16 @@
592 615 * Register the OAuth REST routes on `rest_api_init`.
593 616 *
594 617 * @return void
595 618 */
596 -function desktop_mode_register_oauth_rest_routes() {
619 +function openstation_register_oauth_rest_routes() {
597 620 register_rest_route(
598 621 'desktop-mode/v1',
599 622 '/oauth/start',
600 623 array(
601 624 'methods' => WP_REST_Server::CREATABLE,
602 - 'callback' => 'desktop_mode_rest_oauth_start',
603 - 'permission_callback' => 'desktop_mode_rest_oauth_start_permission',
625 + 'callback' => 'openstation_rest_oauth_start',
626 + 'permission_callback' => 'openstation_rest_oauth_start_permission',
604 627 'args' => array(
605 628 'service' => array(
606 629 'required' => true,
607 630 'type' => 'string',
@@ -614,18 +637,27 @@
614 637 'desktop-mode/v1',
615 638 '/oauth/callback',
616 639 array(
617 640 'methods' => WP_REST_Server::READABLE,
618 - 'callback' => 'desktop_mode_rest_oauth_callback',
641 + 'callback' => 'openstation_rest_oauth_callback',
619 642 // Public — the route is reached via a redirect from the
620 643 // remote service. Auth is the state nonce + (later) the
621 644 // per-service capabilities check on the start side.
622 645 'permission_callback' => '__return_true',
623 646 'args' => array(
624 - 'state' => array( 'required' => true, 'type' => 'string' ),
625 - 'code' => array( 'required' => false, 'type' => 'string' ),
626 - '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 + ),
627 659 ),
628 660 )
629 661 );
630 662 }
631 -add_action( 'rest_api_init', 'desktop_mode_register_oauth_rest_routes' );
663 +add_action( 'rest_api_init', 'openstation_register_oauth_rest_routes' );