PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 0.8.7
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v0.8.7
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
desktop-mode / includes / nonce-refresh.php

nonce-refresh.php in OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin 0.8.7, at includes/nonce-refresh.php

122 lines 4.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Desktop Mode — Heartbeat-driven nonce refresh.
4 *
5 * WordPress nonces are valid for `nonce_life` (24 hours by default).
6 * The desktop shell is a long-running SPA whose per-window config
7 * blobs bake `wp_create_nonce()` values into the page at render
8 * time, so any session that stays open past the 24-hour mark hits
9 * `rest_cookie_invalid_nonce` ("Cookie check failed") on the next
10 * REST call — even though the auth cookie is still valid.
11 *
12 * Fix: on every Heartbeat tick, return a fresh copy of every nonce
13 * action the shell cares about, keyed by action string. The client
14 * subscribes via `src/nonce-refresh.ts` and rewrites the cached
15 * values in place. `wp_create_nonce()` returns the same value
16 * inside a single 12-hour tick window, so the actual nonce string
17 * only changes when the tick rolls — well before the 24-hour hard
18 * expiry catches the cached value.
19 *
20 * Default actions covered:
21 *
22 * - `wp_rest` — the canonical REST cookie nonce. Used by every
23 * window that stashes a `restNonce` in its config blob, plus
24 * the shell-wide auto-injection in `src/inject-rest-nonce.ts`.
25 * - `desktop-mode-plugins` — admin-ajax nonce for our
26 * browse/install/upload/reviews handlers.
27 * - `updates` — Core's wp.updates nonce used by
28 * `wp_ajax_install_plugin` / `wp_ajax_update_plugin`.
29 *
30 * Plugin authors who need to extend the set can hook
31 * `desktop_mode_nonce_refresh_actions` and add their own nonce
32 * action strings. The client side picks the new fields up
33 * automatically through the same heartbeat field — feature modules
34 * just need to register a target for the field they care about via
35 * the JS-side `registerNonceTarget()` helper.
36 *
37 * @package WPDesktopMode
38 * @since 0.8.7
39 */
40
41 defined( 'ABSPATH' ) || exit;
42
43 /**
44 * Heartbeat field name. Public — `src/nonce-refresh.ts` subscribes
45 * to this string. Keep the value stable across versions or update
46 * both ends.
47 */
48 const DESKTOP_MODE_NONCE_REFRESH_FIELD = 'desktop_mode_nonces';
49
50 /**
51 * Mint a fresh map of `{ action => nonce }` for every action the
52 * shell needs to keep alive past `nonce_life`. The set is
53 * filterable so other native windows / third-party plugins can
54 * extend it; the only requirement is that the action string match
55 * whatever was passed to `wp_create_nonce()` at registration.
56 *
57 * @since 0.8.7
58 *
59 * @return array<string,string> Map of nonce-action => current nonce value.
60 */
61 function desktop_mode_nonce_refresh_build_payload() {
62 $actions = array(
63 'wp_rest',
64 'desktop-mode-plugins',
65 'updates',
66 );
67
68 /**
69 * Filter the set of nonce actions refreshed on every Heartbeat tick.
70 *
71 * Each entry must be a literal nonce action string (the same value
72 * passed to `wp_create_nonce()` wherever the original was minted).
73 *
74 * @since 0.8.7
75 *
76 * @param string[] $actions Default nonce actions.
77 */
78 $actions = (array) apply_filters( 'desktop_mode_nonce_refresh_actions', $actions );
79
80 $payload = array();
81 foreach ( $actions as $action ) {
82 if ( ! is_string( $action ) || $action === '' ) {
83 continue;
84 }
85 $payload[ $action ] = wp_create_nonce( $action );
86 }
87 return $payload;
88 }
89
90 /**
91 * Heartbeat handler — attach the fresh nonce map to every tick
92 * from a user who has Desktop Mode enabled.
93 *
94 * Gated on `desktop_mode_is_enabled()` (not just `is_user_logged_in()`)
95 * so users on classic admin screens — editors on post-edit pages,
96 * subscribers reading the front-end heartbeat — don't carry the
97 * payload around. The shell's nonces only need refreshing for
98 * users who actually run the shell.
99 *
100 * The cost is tiny when fired (three `wp_create_nonce()` calls,
101 * all hot-cached inside a single request) — the gate is about
102 * not shipping irrelevant data to non-shell users on every tick.
103 *
104 * @since 0.8.7
105 *
106 * @param array $response Heartbeat response (filter return value).
107 * @param array $data Client-sent payload. Unused here.
108 * @return array
109 */
110 function desktop_mode_nonce_refresh_heartbeat_received( $response, $data ) {
111 unset( $data );
112 if ( ! is_array( $response ) ) {
113 $response = array();
114 }
115 if ( ! function_exists( 'desktop_mode_is_enabled' ) || ! desktop_mode_is_enabled() ) {
116 return $response;
117 }
118 $response[ DESKTOP_MODE_NONCE_REFRESH_FIELD ] = desktop_mode_nonce_refresh_build_payload();
119 return $response;
120 }
121 add_filter( 'heartbeat_received', 'desktop_mode_nonce_refresh_heartbeat_received', 5, 2 );
122