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/nonce-refresh.php +69 -20 0.9.31.1.10 View file →
@@ -1,7 +1,7 @@
1 1 <?php
2 2 /**
3 - * Desktop Mode — Heartbeat-driven nonce refresh.
3 + * OpenStation — Heartbeat-driven nonce refresh.
4 4 *
5 5 * WordPress nonces are valid for `nonce_life` (24 hours by default).
6 6 * The desktop shell is a long-running SPA whose per-window config
7 7 * blobs bake `wp_create_nonce()` values into the page at render
@@ -27,16 +27,15 @@
27 27 * - `updates` — Core's wp.updates nonce used by
28 28 * `wp_ajax_install_plugin` / `wp_ajax_update_plugin`.
29 29 *
30 30 * Plugin authors who need to extend the set can hook
31 - * `desktop_mode_nonce_refresh_actions` and add their own nonce
31 + * `openstation_nonce_refresh_actions` and add their own nonce
32 32 * action strings. The client side picks the new fields up
33 33 * automatically through the same heartbeat field — feature modules
34 34 * just need to register a target for the field they care about via
35 35 * the JS-side `registerNonceTarget()` helper.
36 36 *
37 - * @package WPDesktopMode
38 - * @since 0.8.7
37 + * @package OpenStation
39 38 */
40 39
41 40 defined( 'ABSPATH' ) || exit;
42 41
@@ -43,12 +42,33 @@
43 42 /**
44 43 * Heartbeat field name. Public — `src/nonce-refresh.ts` subscribes
45 44 * to this string. Keep the value stable across versions or update
46 45 * both ends.
46 + *
47 + * The VALUE keeps its pre-rebrand spelling on purpose: it is a
48 + * persisted or externally-visible identifier, so renaming it would
49 + * orphan data already written by live installs (or break a live
50 + * URL). The mismatch between this constant's name and its value is
51 + * deliberate — it is NOT a half-finished rename.
47 52 */
48 -const DESKTOP_MODE_NONCE_REFRESH_FIELD = 'desktop_mode_nonces';
53 +const OPENSTATION_NONCE_REFRESH_FIELD = 'desktop_mode_nonces';
49 54
50 55 /**
56 + * Heartbeat field carrying the authenticated user's identity.
57 + * `src/auth-recovery/index.ts` compares `uid` against the shell's
58 + * boot-time viewer and hard-reloads when a *different* user logged
59 + * in through the session-expired prompt — in-place nonce refresh
60 + * would otherwise leave user A's desktop issuing user B's requests.
61 + *
62 + * The VALUE keeps its pre-rebrand spelling on purpose: it is a
63 + * persisted or externally-visible identifier, so renaming it would
64 + * orphan data already written by live installs (or break a live
65 + * URL). The mismatch between this constant's name and its value is
66 + * deliberate — it is NOT a half-finished rename.
67 + */
68 +const OPENSTATION_AUTH_FIELD = 'desktop_mode_auth';
69 +
70 +/**
51 71 * Mint a fresh map of `{ action => nonce }` for every action the
52 72 * shell needs to keep alive past `nonce_life`. The set is
53 73 * filterable so other native windows / third-party plugins can
54 74 * extend it; the only requirement is that the action string match
@@ -53,13 +73,11 @@
53 73 * filterable so other native windows / third-party plugins can
54 74 * extend it; the only requirement is that the action string match
55 75 * whatever was passed to `wp_create_nonce()` at registration.
56 76 *
57 - * @since 0.8.7
58 - *
59 77 * @return array<string,string> Map of nonce-action => current nonce value.
60 78 */
61 -function desktop_mode_nonce_refresh_build_payload() {
79 +function openstation_nonce_refresh_build_payload() {
62 80 $actions = array(
63 81 'wp_rest',
64 82 'desktop-mode-plugins',
65 83 'updates',
@@ -70,17 +88,15 @@
70 88 *
71 89 * Each entry must be a literal nonce action string (the same value
72 90 * passed to `wp_create_nonce()` wherever the original was minted).
73 91 *
74 - * @since 0.8.7
75 - *
76 92 * @param string[] $actions Default nonce actions.
77 93 */
78 - $actions = (array) apply_filters( 'desktop_mode_nonce_refresh_actions', $actions );
94 + $actions = (array) apply_filters( 'openstation_nonce_refresh_actions', $actions );
79 95
80 96 $payload = array();
81 97 foreach ( $actions as $action ) {
82 - if ( ! is_string( $action ) || $action === '' ) {
98 + if ( ! is_string( $action ) || '' === $action ) {
83 99 continue;
84 100 }
85 101 $payload[ $action ] = wp_create_nonce( $action );
86 102 }
@@ -88,11 +104,11 @@
88 104 }
89 105
90 106 /**
91 107 * Heartbeat handler — attach the fresh nonce map to every tick
92 - * from a user who has Desktop Mode enabled.
108 + * from a user who has OpenStation enabled.
93 109 *
94 - * Gated on `desktop_mode_is_enabled()` (not just `is_user_logged_in()`)
110 + * Gated on `openstation_is_enabled()` (not just `is_user_logged_in()`)
95 111 * so users on classic admin screens — editors on post-edit pages,
96 112 * subscribers reading the front-end heartbeat — don't carry the
97 113 * payload around. The shell's nonces only need refreshing for
98 114 * users who actually run the shell.
@@ -100,22 +116,55 @@
100 116 * The cost is tiny when fired (three `wp_create_nonce()` calls,
101 117 * all hot-cached inside a single request) — the gate is about
102 118 * not shipping irrelevant data to non-shell users on every tick.
103 119 *
104 - * @since 0.8.7
105 - *
106 120 * @param array $response Heartbeat response (filter return value).
107 121 * @param array $data Client-sent payload. Unused here.
108 122 * @return array
109 123 */
110 -function desktop_mode_nonce_refresh_heartbeat_received( $response, $data ) {
124 +function openstation_nonce_refresh_heartbeat_received( $response, $data ) {
111 125 unset( $data );
112 126 if ( ! is_array( $response ) ) {
113 127 $response = array();
114 128 }
115 - if ( ! function_exists( 'desktop_mode_is_enabled' ) || ! desktop_mode_is_enabled() ) {
129 + if ( ! function_exists( 'openstation_is_enabled' ) || ! openstation_is_enabled() ) {
116 130 return $response;
117 131 }
118 - $response[ DESKTOP_MODE_NONCE_REFRESH_FIELD ] = desktop_mode_nonce_refresh_build_payload();
132 + $response[ OPENSTATION_NONCE_REFRESH_FIELD ] = openstation_nonce_refresh_build_payload();
133 + $response[ OPENSTATION_AUTH_FIELD ] = array( 'uid' => get_current_user_id() );
119 134 return $response;
120 135 }
121 -add_filter( 'heartbeat_received', 'desktop_mode_nonce_refresh_heartbeat_received', 5, 2 );
136 +add_filter( 'heartbeat_received', 'openstation_nonce_refresh_heartbeat_received', 5, 2 );
137 +
138 +/**
139 + * Nonce-refresh rider for the `nonces_expired` heartbeat path.
140 + *
141 + * When the Heartbeat POST arrives with a stale `heartbeat-nonce`
142 + * (the first tick after a re-login, or any tick once the nonce
143 + * aged past `nonce_life`), core short-circuits before
144 + * `heartbeat_received` / `heartbeat_send` ever run — the response
145 + * is built solely from the `wp_refresh_nonces` filter. Without
146 + * this hook the shell would only receive fresh
147 + * `desktop_mode_nonces` on the FOLLOWING tick, leaving a window
148 + * where every cached nonce is rejected ("Cookie check failed").
149 + *
150 + * Riding the same payload here means one round-trip heals the
151 + * shell: the tick that says "your nonces expired" also delivers
152 + * the replacements. Client-side, `heartbeat.js` still fires
153 + * `heartbeat-tick` for this response, so the regular
154 + * `src/nonce-refresh.ts` subscriber picks the map up unchanged.
155 + *
156 + * @param array $response Heartbeat response (filter return value).
157 + * @return array
158 + */
159 +function openstation_nonce_refresh_on_expired( $response ) {
160 + if ( ! is_array( $response ) ) {
161 + $response = array();
162 + }
163 + if ( ! function_exists( 'openstation_is_enabled' ) || ! openstation_is_enabled() ) {
164 + return $response;
165 + }
166 + $response[ OPENSTATION_NONCE_REFRESH_FIELD ] = openstation_nonce_refresh_build_payload();
167 + $response[ OPENSTATION_AUTH_FIELD ] = array( 'uid' => get_current_user_id() );
168 + return $response;
169 +}
170 +add_filter( 'wp_refresh_nonces', 'openstation_nonce_refresh_on_expired', 5 );