@@ -26,6 +26,36 @@ function init_server_from_request()
2626 add_action ('plugins_loaded ' , __NAMESPACE__ . '\\init_server_from_request ' );
2727}
2828
29+ /**
30+ * Handle the second leg of the magic login on wp-login.php.
31+ *
32+ * CDN/proxy layers (e.g. Cloudflare with "Cache Everything") strip Set-Cookie
33+ * headers from responses on arbitrary URLs. wp-login.php is universally excluded
34+ * from CDN caching, so setting cookies here guarantees they reach the browser.
35+ */
36+ function handle_login_handoff ()
37+ {
38+ if (empty ($ _GET ['wp_cli_login_token ' ])) {
39+ return ;
40+ }
41+
42+ $ token = sanitize_text_field ($ _GET ['wp_cli_login_token ' ]);
43+ $ data = get_transient ('wp_cli_login/handoff/ ' . $ token );
44+ delete_transient ('wp_cli_login/handoff/ ' . $ token );
45+
46+ if (! $ data || ! ($ user = new \WP_User ($ data ['user_id ' ])) || ! $ user ->exists ()) {
47+ wp_die ('The magic login handoff has expired or is invalid. ' , '' , ['response ' => 410 ]);
48+ }
49+
50+ wp_set_auth_cookie ($ user ->ID );
51+ do_action ('wp_cli_login/login ' , $ user ->user_login , $ user );
52+ do_action ('wp_login ' , $ user ->user_login , $ user );
53+
54+ wp_safe_redirect ($ data ['redirect_url ' ] ?: admin_url ());
55+ exit ;
56+ }
57+ add_action ('login_init ' , __NAMESPACE__ . '\\handle_login_handoff ' );
58+
2959/**
3060 * @return bool
3161 */
@@ -133,8 +163,8 @@ public function run()
133163 try {
134164 $ magic = $ this ->loadMagic ();
135165 $ user = $ this ->validate ($ magic );
136- $ this ->loginUser ( $ user );
137- $ this ->loginRedirect ($ user , $ magic ->redirect_url );
166+ $ this ->deleteMagic ( );
167+ $ this ->handoffToLogin ($ user , $ magic ->redirect_url );
138168 } catch (Exception $ e ) {
139169 $ this ->deleteMagic ();
140170 $ this ->abort ($ e );
@@ -199,84 +229,22 @@ private function deleteMagic()
199229 }
200230
201231 /**
202- * Login the given user and redirect them to wp-admin.
203- *
204- * @param WP_User $user
205- */
206- private function loginUser (WP_User $ user )
207- {
208- $ this ->deleteMagic ();
209-
210- wp_set_auth_cookie ($ user ->ID );
211-
212- /**
213- * Fires after the user has successfully logged in via the WP-CLI Login Server.
214- *
215- * @param string $user_login Username.
216- * @param WP_User $user WP_User object of the logged-in user.
217- */
218- do_action ('wp_cli_login/login ' , $ user ->user_login , $ user );
219-
220- /**
221- * Fires after the user has successfully logged in.
222- *
223- * @param string $user_login Username.
224- * @param WP_User $user WP_User object of the logged-in user.
225- */
226- do_action ('wp_login ' , $ user ->user_login , $ user );
227- }
228-
229- /**
230- * Redirect the user after logging in.
231- *
232- * Mostly copied from wp-login.php
232+ * Hand off to wp-login.php via a short-lived token so that auth cookies
233+ * are set on a response that CDNs won't strip Set-Cookie headers from.
233234 *
234235 * @param WP_User $user
235236 * @param string $redirect_url
236237 */
237- private function loginRedirect (WP_User $ user , $ redirect_url )
238+ private function handoffToLogin (WP_User $ user , $ redirect_url )
238239 {
239- $ redirect_to = $ redirect_url ?: admin_url ();
240-
241- /**
242- * Filters the login redirect URL.
243- *
244- * @param string $redirect_to The redirect destination URL.
245- * @param string $requested_redirect_to The requested redirect destination URL passed as a parameter.
246- * @param WP_User $user WP_User object.
247- */
248- $ redirect_to = apply_filters ('login_redirect ' , $ redirect_to , '' , $ user );
249-
250- /**
251- * Filters the login redirect URL for WP-CLI Login Server requests.
252- *
253- * @param string $redirect_to The redirect destination URL.
254- * @param string $requested_redirect_to The requested redirect destination URL passed as a parameter.
255- * @param WP_User $user WP_User object.
256- */
257- $ redirect_to = apply_filters ('wp_cli_login/login_redirect ' , $ redirect_to , '' , $ user );
258-
259- /**
260- * Figure out where to redirect the user for the default wp-admin URL based on the user's capabilities.
261- */
262- if ((empty ($ redirect_to ) || $ redirect_to == 'wp-admin/ ' || $ redirect_to == admin_url ())) {
263- // If the user doesn't belong to a blog, send them to user admin. If the user can't edit posts, send them to their profile.
264- if (is_multisite () && ! get_active_blog_for_user ($ user ->ID ) && ! is_super_admin ($ user ->ID )) {
265- $ redirect_to = user_admin_url ();
266- } elseif (is_multisite () && ! $ user ->has_cap ('read ' )) {
267- $ redirect_to = get_dashboard_url ($ user ->ID );
268- } elseif (! $ user ->has_cap ('edit_posts ' )) {
269- $ redirect_to = $ user ->has_cap ('read ' ) ? admin_url ('profile.php ' ) : home_url ();
270- }
271-
272- wp_redirect ($ redirect_to );
273- exit ;
274- }
240+ $ token = bin2hex (random_bytes (16 ));
241+
242+ set_transient ('wp_cli_login/handoff/ ' . $ token , [
243+ 'user_id ' => $ user ->ID ,
244+ 'redirect_url ' => $ redirect_url ,
245+ ], 30 );
275246
276- /**
277- * Redirect safely to the URL provided.
278- */
279- wp_safe_redirect ($ redirect_to );
247+ wp_redirect (add_query_arg ('wp_cli_login_token ' , $ token , wp_login_url ()));
280248 exit ;
281249 }
282250
0 commit comments