Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Blueprints] Login step – handle passwordless autologin via a PHP mu-plugin #1856

Merged
merged 4 commits into from
Oct 8, 2024

Conversation

bgrgicak
Copy link
Collaborator

@bgrgicak bgrgicak commented Oct 7, 2024

This PR was originally implemented in this branch

Fixes #1706

Motivation for the change, related issues

Handles auto login, e.g. in the login Blueprint step, using a platform-provided mu-plugin. This enables using the login step across all Playground runtimes without any of the issues with the previous implementation.

Before this PR, the login worked by making HTTP requests to the /wp-login.php endpoint. That approach had significant downsides:

  • It only worked in web browsers
  • It didn't support custom login mechanisms
  • It required storing plaintext passwords in the Blueprint files
  • It broke after a content import if the admin password changed
  • It broke if WordPress displayed the Site Admin Email Verification Screen.

Implementation details

To fix it and make the login step more robust login now uses a login PHP file that logs in the user with their credentials.
The new script only requires a username, the password is now deprecated.

There are two ways to trigger the auto-login:

The PLAYGROUND_AUTO_LOGIN_AS_USER constant

Used by the login Blueprint step does.

When the PLAYGROUND_AUTO_LOGIN_AS_USER constant is defined, this mu-plugin
will automatically log the user in on their first visit. The username is
the value of the constant.

On subsequent visits, the playground_auto_login_happened cookie will be
detected and the user will not be logged in. This means the "logout" feature
will work as expected.

The playground_force_auto_login_as_user GET parameter

Used by the "login" button in various Playground runtimes.

When the playground_force_auto_login_as_user GET parameter is present,
this mu-plugin will automatically log in any logged out visitor. This will
happen every time they visit, not just on their first visit.

Testing Instructions (or ideally a Blueprint)

cc @brandonpayton – the /?playground_force_auto_login_as_user=admin triggers a redirect to add the trailing slash, as in /?playground_force_auto_login_as_user=admin/. Let's track that separately.

@bgrgicak bgrgicak self-assigned this Oct 7, 2024
@bgrgicak bgrgicak added [Type] Bug An existing feature does not function as intended [Package][@wp-playground] Blueprints labels Oct 7, 2024
@adamziel adamziel changed the title Add changes from fix/1706-login-with-site-email-verification [Blueprints] Login step – handle passwordless autologin via a PHP mu-plugin Oct 7, 2024
* Under the hood, this function submits the [`wp-login.php`](https://developer.wordpress.org/reference/files/wp-login.php/) [form](https://developer.wordpress.org/reference/functions/wp_login_form/)
* just like a user would.
* Under the hood, this function sets the `PLAYGROUND_AUTO_LOGIN_AS_USER` constant.
* The `auto_login.php` mu-plugin uses that constant to log in the user on the first load.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's also document where does the auto_login.php mu-plugin live – it's not bundled with steps.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's updated now and mentiones the plugin is located in and loaded by the @wp-playground/wordpress package.

I didn't include the exact path as it's easy to locate in a editor, but the mention should make it clear that this feature depends on @wp-playground/wordpress.

Copy link
Collaborator

@adamziel adamziel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left a few nitpicks and otherwise this looks great. Thank you @bgrgicak!

@@ -48,6 +50,11 @@ export async function setupPlatformLevelMuPlugins(php: UniversalPHP) {
`
);

await php.writeFile(
'/internal/shared/mu-plugins/1-auto-login.php',
autoLoginMuPlugin
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this mu-plugin intended for use as broader platform feature? If not, could this mu-plugin be maintained and written as part of the login Blueprint step?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this mu-plugin intended for use as broader platform feature? I

Yes, it seems like the only way of supporting auto login in CLI, Studio, etc.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this mu-plugin intended for use as broader platform feature? I

Yes, it seems like the only way of supporting auto login in CLI, Studio, etc.

Ah, thanks for explaining!

@brandonpayton
Copy link
Member

This is such a nice change. Thanks for working on it, @bgrgicak!

@bgrgicak bgrgicak merged commit 3f1e998 into trunk Oct 8, 2024
9 checks passed
@bgrgicak bgrgicak deleted the add/autologin-to-the-login-step branch October 8, 2024 08:10
adamziel pushed a commit that referenced this pull request Oct 9, 2024
…php in index.ts instead of using import ?raw (#1869)

Fixes [the failing WordPress Major and Beta update
workflow](#1868).
It crashed on import "auto_login.php?raw" so this PR removes the import
and inlines the php script in a JavaScript variable

## Motivation for the change, related issues

We've introduced a bug in
#1856 by adding [a
`raw` import to the `@wp-playground/wordpress`
package](https://github.com/WordPress/wordpress-playground/pull/1856/files#diff-8afd615c3847003caebf2b35102f3a2a107ad9647e095495ecc9302862802191R7).

Raw imports aren't native to JavaScript, they are processed on build,
using Vite in our case.
This is why raw imports work well in our Remote package, but not in the
CLI.

When Playground is started using Node, the [CLI tries getting the raw
text from
`auto_login.php`](https://github.com/WordPress/wordpress-playground/pull/1856/files#diff-8afd615c3847003caebf2b35102f3a2a107ad9647e095495ecc9302862802191R7).
Instead of getting the text, it gets the path to the imported file (e.g.
`/Users/me/Projects/wordpress-playground/packages/playground/wordpress/src/mu-plugins/auto_login.php`).
This path is written into the `1-auto-login.php` mu-plugin as a string.

When Playground tries to boot, it [checks if WordPress is
installed](https://github.com/WordPress/wordpress-playground/blob/ccadc7d0daedda0b59b571e12aade32cf3abaeab/packages/playground/wordpress/src/boot.ts#L243).
The expected result of that check is `1`, but because `1-auto-login.php`
contains the path as a string, and because WordPress uses PHP `include`
to load that file, the path gets written to stdout and mixed with the
script output. The outcome is that `result.text` is
`/Users/me/Projects/wordpress-playground/packages/playground/wordpress/src/mu-plugins/auto_login.php1`
instead of `1`.

## Implementation details

This PR moves the auto-login code from a PHP file to inline code.

The approach matches what we did with other `mu-plugins` that must be
loaded both in the CLI and on the web.

TODO:
- [Add tests to ensure the CLI
works](#1871)
- Consider adding instructions for setting up the CLI as a global
package

## Testing Instructions (or ideally a Blueprint)

- Run `bun packages/playground/wordpress-builds/build/build.js
--wp-version=latest-minus-1
--output-js=packages/playground/wordpress-builds/src/wordpress
--output-assets=packages/playground/wordpress-builds/public`
- Confirm it works
adamziel added a commit that referenced this pull request Oct 25, 2024
This PR:

* Replaces the `wp_redirect()` call with `header("Location: ")` to ensure
support for all valid redirection URLs.
* Adds a timeout before probing for the embedded Playground `iframe` URL
  when the `load` event handler runs.

 ### Ditching `wp_redirect()`

The `wp_redirect()` call was introduced in #1856 to handle the post-autologin
redirect. Unfortunately, it strips valid sequences such as `%0A` and `%0D` from
the redirection URL. This isn't useful in Playground and breaks valid
use-cases such as using the `?url=` parameter to initialize `html-api-debugger`
with a valid HTML markup containing newlines.

 ### Timeout before probing `contentWindow.location.href`

When navigating to a page with %0A sequences (encoded newlines)
in the query string, the `location.href` property of the
iframe's content window doesn't seem to reflect them. Everything
else is in place, but not the %0A sequences.

Weirdly, these sequences are available after the next event
loop tick – hence the `setTimeout(0)`.

The exact cause is unclear to me. The WHATWG HTML Standard [1] has a few hints:

* Current and active session history entries may get out of
  sync for iframes.
* Documents inside iframes have "is delaying load events" set
  to true.

But there doesn't seem to be any concrete explanation and no
recommended remediation. If anyone has a clue, please share it
in a GitHub issue or start a new PR.

[1] https://html.spec.whatwg.org/multipage/document-sequences.html#nav-active-history-entry

 ## Testing instructions

CI tests aside, try this:

1. Go to http://localhost:5400/website-server/?plugin=html-api-debugger&url=%2Fwp-admin%2Fadmin.php%3Fpage%3Dhtml-api-debugger%26html%3D%253Cdiv%253E%250A1%250A2%250A3%250A%253C%252Fdiv%253E&wp=6.6&php=8.0&networking=no&language=&multisite=no&random=pf3oq1y3vx
2. Confirm the "address bar" on the Playground page reflects the actual,
   correct URL:
   `/wp-admin/admin.php?page=html-api-debugger&html=%3Cdiv%3E%0A1%0A2%0A3%0A%3C%2Fdiv%3E`
3. Confirm you can see the following HTML

```
<div>
1
2
3
</div>
```

cc @sirreal @bgrgicak
adamziel added a commit that referenced this pull request Nov 15, 2024
…ram (#1945)

This PR:

* Replaces the `wp_redirect()` call with `header("Location: ")` to
ensure support for all valid redirection URLs.
* Adds a timeout before probing for the embedded Playground `iframe` URL
when the `load` event handler runs.

 ### Ditching `wp_redirect()`

The `wp_redirect()` call was introduced in #1856 to handle the
post-autologin redirect. Unfortunately, it strips valid sequences such
as `%0A` and `%0D` from the redirection URL. This isn't useful in
Playground and breaks valid use-cases such as using the `?url=`
parameter to initialize `html-api-debugger` with a valid HTML markup
containing newlines.

 ### Timeout before probing `contentWindow.location.href`

When navigating to a page with %0A sequences (encoded newlines) in the
query string, the `location.href` property of the iframe's content
window doesn't seem to reflect them. Everything else is in place, but
not the %0A sequences.

Weirdly, these sequences are available after the next event loop tick –
hence the `setTimeout(0)`.

The exact cause is unclear to me. The WHATWG HTML Standard [1] has a few
hints:

* Current and active session history entries may get out of sync for
iframes.
* Documents inside iframes have "is delaying load events" set to true.

But there doesn't seem to be any concrete explanation and no recommended
remediation. If anyone has a clue, please share it in a GitHub issue or
start a new PR.

[1]
https://html.spec.whatwg.org/multipage/document-sequences.html#nav-active-history-entry

 ## Testing instructions

CI tests aside, try this:

1. Go to
http://localhost:5400/website-server/?plugin=html-api-debugger&url=%2Fwp-admin%2Fadmin.php%3Fpage%3Dhtml-api-debugger%26html%3D%253Cdiv%253E%250A1%250A2%250A3%250A%253C%252Fdiv%253E&wp=6.7&php=8.0&networking=no&language=&multisite=no&random=pf3oq1y3vx
2. Confirm the "address bar" on the Playground page reflects the actual,
correct URL:
`/wp-admin/admin.php?page=html-api-debugger&html=%3Cdiv%3E%0A1%0A2%0A3%0A%3C%2Fdiv%3E`
3. Confirm you can see the following HTML

```
<div>
1
2
3
</div>
```

cc @sirreal @bgrgicak

---------

Co-authored-by: Bero <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Package][@wp-playground] Blueprints [Type] Bug An existing feature does not function as intended
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

Login step doesn't execute if placed before import step in blueprint.
3 participants