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

Using WAP with WordPress Playgrounds #164

Open
asirota opened this issue Sep 23, 2024 · 20 comments · May be fixed by #177
Open

Using WAP with WordPress Playgrounds #164

asirota opened this issue Sep 23, 2024 · 20 comments · May be fixed by #177
Assignees
Milestone

Comments

@asirota
Copy link
Member

asirota commented Sep 23, 2024

I'd like to demonstrate the WAP plugin using the new WordPress Playgrounds feature. Playgrounds runs a full wordpress instance in your browser using a variety of cool technologies like wasm and node.js. The result is you can one-click install an instance and use their blueprint json to "spin up" a customized wordpress instance that runs in your browser.

Unforutnately our authorization (and maybe licensing) code doesn't work here.

The issue possibly is CORS.

I have turned on networking in the blueprint but for some reason when I try to apply the API keys to our configuration screen nothing happens. Networking is turned on in the blueprint so it should be able to call an external REST API.

Here's my test playground for the plugin WildApricot Press:

We can use your browser console as the debugger, as it’ll show all outgoing HTTP requests. Chances are, is that the WA API endpoint doesn’t allow CORS requests - ie. your browser isn’t allowed to communicate directly with it. If we enable CORS on all api endpoints that your plugin communicates with, it’ll probably be able to work in playground.

If we are getting no output, WAP is probably not handling HTTP request failures gracefully either, so we should probably add some error handling here of we don't have it yet.

@asirota asirota added this to the 1.0.3 milestone Sep 23, 2024
@asirota
Copy link
Member Author

asirota commented Sep 23, 2024

When trying to hit the oauth.wildapricot.org endpoint when saving API key, client ID/secret our response comes back with a 400 when trying it from the playground.wildapricot.net site (in browser WP)

HTTP/1.1 400 Bad Request
Cache-Control: no-cache
Pragma: no-cache
Expires: -1
Content-Security-Policy: report-uri https://csp.uel.wildapricot.com/report; default-src 'self' *.swagger.io 'unsafe-inline';frame-ancestors 'none';base-uri 'self'
Access-Control-Allow-Origin: *
Date: Mon, 23 Sep 2024 15:41:45 GMT
Connection: close
Content-Length: 0
X-Backend-Server: lap2wue1c-849a
X-LB-Server: llblue1c-3a4b
X-Content-Type-Options: nosniff
Reporting-Endpoints: wildapricot-csp-uel='https://csp.uel.wildapricot.com/report'
Strict-Transport-Security: max-age=31536000

@asirota
Copy link
Member Author

asirota commented Sep 23, 2024

and here is the request headers on that response:

OPTIONS /auth/token HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: en,fr;q=0.9,en-US;q=0.8,en-CA;q=0.7
Access-Control-Request-Headers: authorization,content-type
Access-Control-Request-Method: POST
Connection: keep-alive
Host: oauth.wildapricot.org
Origin: https://playground.wordpress.net/
Referer: https://playground.wordpress.net/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0

@asirota
Copy link
Member Author

asirota commented Sep 23, 2024

can see this in the Network tab in dev console

@nataliebrotherton
Copy link
Collaborator

could this be an issue having to do with the WA authorized applications? the wordpress dev site is authorized so the API works but the blueprint isn't and the API doesn't work.

@asirota
Copy link
Member Author

asirota commented Sep 23, 2024

could this be an issue having to do with the WA authorized applications? the wordpress dev site is authorized so the API works but the blueprint isn't and the API doesn't work.

That's the thing I cannot get the authorized application to even work -- the 400 response is concerning -- it's getting called but the response from the API is calling a 400 so something is wrong in our call.

@asirota
Copy link
Member Author

asirota commented Sep 23, 2024

@nataliebrotherton putting your notes here:

I looked at this and here’s what I would need to do:

  • hardcode encrypted WA credentials and license key — we don’t want to include the real ones in the file since it’s on the public github
  • use decrypt function to get the valid credentials — this is needed to obtain the access token from the WA API and the plugin functions are expecting decrypted/plaintext credentials
  • Or I could write specific functions in the plugin to call from the blueprint to handle the credentials.

The blueprint feature is cool but to get the credentials in the site it’s not exactly straightforward and requires some workarounds/backdoor methods to bypass some of the WAP functionality. It could be done but I don’t feel great about hardcoding the credentials in the public github even if they’re decrypted. Is there a reason the regular sandbox site couldn’t be used for a demo?

@asirota
Copy link
Member Author

asirota commented Sep 23, 2024

  • hardcode encrypted WA credentials and license key — we don’t want to include the real ones in the file since it’s on the public github

the idea is that we would use the DEV license checker and use a test key that disregards the authorized site. That's in another issue #165

I know this may be a loophole to allow people to use the plugin for free, but honestly we could monitor that and change the key very quickly to another one if we see abuse.

This is not intended for prepopulation yet of production site with real license keys, and even if tha was the case the blueprint would be owned by our customer and not published.

  • Or I could write specific functions in the plugin to call from the blueprint to handle the credentials.

the blueprint can execute PHP code in the context of WordPress so you can do quite a bit here. Basically call the PHP code with the licensekey obtained from somewhere that simulates it being entered and checked by the webhook. This is why I am asking for code that would simulate the prepopulation of a license key -- if we wanted to keep the license key out of the blueprint.json we could add it as a wp-config env parameter instead.

The blueprint feature is cool but to get the credentials in the site it’s not exactly straightforward and requires some workarounds/backdoor methods to bypass some of the WAP functionality. It could be done but I don’t feel great about hardcoding the credentials in the public github even if they’re decrypted. Is there a reason the regular sandbox site couldn’t be used for a demo?

Yes there is a good reason -- the demo site could be mucked with by users or even damaged. It also would hold authorization credentials that would need to be wiped out after use. And it would need to be maintained with the latest plugin, manage login credentials and in general is a pain because we cannot control the environment easily.

A WP playground disappears when you close the browser -- it literally doesn't actually exist anywhere except your particular browser, although you can save the state as a zip file and reload it if you want. It is maintenance free and we could test our plugin with various versions of php and wordpress going forward, and that makes it the ideal test env too.

With this not only can we control exactly what is added/removed, it's a "recipe" for the ideal environment that we can modify as we go and always have the exactly correct setup for each demo. Happy to discuss further.

@asirota asirota changed the title using WAP with WordPress Playgrounds Using WAP with WordPress Playgrounds Sep 23, 2024
@asirota
Copy link
Member Author

asirota commented Sep 23, 2024

@asirota
Copy link
Member Author

asirota commented Sep 23, 2024

Even if we could pre-fill the forms with sample API keys and licenses and have them click the button that would be ok too!

@nataliebrotherton
Copy link
Collaborator

if we wanted to keep the license key out of the blueprint.json we could add it as a wp-config env parameter instead

It seems like the blueprints have a config file independent from the server config. But the blueprints do have a step to set config variables so this could be a good option.

Even if we could pre-fill the forms with sample API keys and licenses and have them click the button that would be ok too!

We couldn't do this because all the data resets after each page refresh by default. But there is an option to store data in the browser (on the top bar) that would be useful.
image

That's the thing I cannot get the authorized application to even work -- the 400 response is concerning -- it's getting called but the response from the API is calling a 400 so something is wrong in our call.

As far as I know, the HTTP request to WildApricot is well-formed according to their documentation. Could you try adding the playground site as its own authorized app on WA?

@asirota
Copy link
Member Author

asirota commented Sep 25, 2024 via email

@asirota
Copy link
Member Author

asirota commented Sep 26, 2024

Gemini response:

Certainly, let's explore how to add CORS support to your Wild Apricot Press plugin, specifically in the context of the API calls made within class-wawp-wild-apricot-api.php.

Understanding CORS in WordPress

While CORS is typically handled on the server-side, WordPress offers hooks and functions to modify headers and enable CORS for your API endpoints.

Implementation Approach

  1. Leverage the rest_api_init Hook: The rest_api_init action hook allows us to add custom headers to REST API responses.

  2. Add CORS Headers: Within the hook callback, we can use the header() function to add the necessary CORS headers.

Code Example (in functions.php or a custom plugin file)

add_action('rest_api_init', function () {
    remove_filter('rest_pre_serve_request', 'rest_send_cors_headers');
    add_filter('rest_pre_serve_request', function ($value) {
        header('Access-Control-Allow-Origin: *'); // Or specify your frontend's origin
        header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS'); // Adjust allowed methods
        header('Access-Control-Allow-Headers: Content-Type, Authorization'); // Adjust allowed headers
        return $value;
    });
}, 15);

Explanation

  • add_action('rest_api_init', ...): This attaches our callback function to the rest_api_init hook.
  • remove_filter('rest_pre_serve_request', 'rest_send_cors_headers');: We remove the default CORS handling to gain more control.
  • add_filter('rest_pre_serve_request', ...): We add our custom CORS headers to the response.
  • header(...): We set the Access-Control-Allow-Origin, Access-Control-Allow-Methods, and Access-Control-Allow-Headers as needed.

Important Considerations

  • Origin Specificity: Replace * with the specific origin of your frontend application if you want to restrict access.
  • Method and Header Customization: Adjust the allowed methods and headers to match the requirements of your API calls.
  • Preflight Requests: Ensure your API handles OPTIONS requests (preflight requests) correctly by responding with the appropriate CORS headers.

Next Steps

  1. Add the Code: Insert the provided code snippet into your functions.php file or a custom plugin file.
  2. Configure CORS Settings: Adjust the allowed origin, methods, and headers as needed.
  3. Test Thoroughly: Verify that your API calls work correctly from your frontend application.

By implementing this CORS support, your Wild Apricot Press plugin should be able to handle cross-origin API requests smoothly. Let me know if you need any further assistance or have more questions!

@asirota
Copy link
Member Author

asirota commented Sep 26, 2024

If Wild Apricot doesn't enable CORS, we could consider setting up a proxy server on our own domain. The plugin would make requests to the proxy server, which would then forward them to the Wild Apricot API. The proxy server would be responsible for adding the necessary CORS headers to the responses. This approach adds complexity but provides full control over CORS configuration. The Playgrounds team is working on a ticket for this to add a PHP CORS proxy -- there is a commit on this, but it is not running for public consumption yet. Check the ticket to see updates.

@asirota
Copy link
Member Author

asirota commented Sep 26, 2024

There is a different approch to create the authentication with client side Javascript (rather than server side) requests. I've used this to call JS authentication requests directly in JS running in a browser and this may work without needing CORS support in the WA API

See this discussion.

a sample jQuery call:

https://gethelp.wildapricot.com/en/articles/1705-working-with-api-from-javascript

$.ajax({
  url: "/sys/api/v2/accounts/58293/contacts/me",
  type: "GET",
  dataType: "json",
  cache: false,
  async: true,
  headers: { "clientId": "APPLICATION_CLIENT_ID" },
  success: function (data, textStatus, jqXhr) {
    alert("Current contact email:" + data.Email);},
  error: function (jqXHR, textStatus, errorThrown) {
    alert(textStatus + " (" + jqXHR.status + ") : " + errorThrown);}
});

This will require the user to login to their Wildapricot website first to establish a cookie, and I wonder if it will work from our Playground environment?

@nataliebrotherton
Copy link
Collaborator

CORS is a mechanism controlled on the server side. Servers indicate which domains are allowed to request from them. The information you sent from gemini is what we would do if our plugin was the server, but in this case WAP is the client and it's interacting with WA as the server. I think WA doesn't like our requests because it's coming through the playground's javascript sandbox which runs in the browser instead of php code running in a server like normal. The WA server is configured such that it doesn't allow CORS requests like the ones we are sending through playground. PHP servers don't have this same restriction, so we are allowed to send requests from there. To resolve this we would add CORS to the WA server which we don't have access to. I think setting up a proxy server is a good solution and it should work both on a typical PHP server and on the playground sandbox.

@asirota
Copy link
Member Author

asirota commented Sep 26, 2024

Could we run a proxy server for the WP Playgrounds scenario and use that as the request mechanism? I bet there are some open source ones out there

@nataliebrotherton
Copy link
Collaborator

Could we set up a server on the newpath site? We could even use a make scenario.

@asirota
Copy link
Member Author

asirota commented Sep 26, 2024

Either one works- Make would cost money every time it is used but it would be a very novel use of Make no doubt. Would it work with a webhook call and a webhook response that redirects to the API? Want to get on a call to discuss? Book some time tomorrow newpathconsulting.com/alex

@asirota
Copy link
Member Author

asirota commented Sep 26, 2024

https://www.php-proxy.com/ has a plugin architecture to be able to rewrite HTTP responses as well as the content of the response -- could be useful for a lot of stuff

I installed php-proxy under waxm.newpathconsulting.com/npcproxy

This is the web project and this is the underlying php script

https://github.com/Athlon1600/php-proxy

@nataliebrotherton nataliebrotherton linked a pull request Oct 31, 2024 that will close this issue
@asirota asirota modified the milestones: 1.0.3, 1.1 Nov 7, 2024
@asirota
Copy link
Member Author

asirota commented Dec 21, 2024

Now that WildApricot implemented cors in their api this whole request is probably moot. Not a great use of time. I wish they would have told me earlier that they were going to implement it. It was a surprise to be sure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: 🏗 In progress
2 participants