Log-in to Wordpress from google app script - wordpress

I'm logging-in Wordpress account from google app script.
This is my try:
var url='http://www...it/wp-login.php';
var options = {
"method": "post",
"payload": {
"log": "user",
"pwd": "password",
"wp-submit": "Login",
"testcookie":'1',
},
"followRedirects": false,
};
var response = UrlFetchApp.fetch(url,options);
I get 200 code, instead of expected 302 code.
Where I get wrong?
Other solutions, such as this, give me 404 error code.

The 200 response code indicates that your request to fetch the desired URL was performed succesfully, while the 302 code means that you are redirected.
The Wordpress login page will redirect you, if your log-in credentials are correct.
In other words - your request is not correct. The sample you are referring to is valid for an ADMIN logging into the ADMIN log-in URL. Is this also your case? Can you log-in manually if you open the URL and use the same credentials as in your Apps Script code?

Just went through this in case someone else stumbles upon this question.
Things to check:
wp-submit: "Log In" or "Log+In"
Might need redirect_to field
It didn't work without passing Referer: header (doesn't seem t validate this)
Also you need to handle cookies, wp-login sets wordpress_test_cookie=WP+Cookie+check

Related

Browser not saving cookie sent by Golang backend

I know this question has been asked a bunch of times, but I tried most of the answers and still can't get it to work.
I have a Golang API with net/http package and a JS frontend. I have a function
func SetCookie(w *http.ResponseWriter, email string) string {
val := uuid.NewString()
http.SetCookie(*w, &http.Cookie{
Name: "goCookie",
Value: val,
Path: "/",
})
return val
}
This function is called when the user logs in, and I expect it to be sent to all the other endpoints. This works as expected with Postman. However, when it comes to the browser, I can't seem to get it to remember the cookie or even send it to other endpoints.
An example of JS using an endpoint
async function getDataWithQuery(query, schema){
let raw = `{"query":"${query}", "schema":"${schema}"}`;
let requestOptions = {
method: 'POST',
body: raw,
redirect: 'follow',
};
try{
let dataJson = await fetch("http://localhost:8080/query/", requestOptions)
data = await dataJson.json();
}catch(error){
console.log(error);
}
return data;
}
I tried answers like setting SameSite attribute in Golang, or using credential: "include" in JS with no luck.
Thanks to the discussion in the comments, I found some hints about the problem.
Saving cookies (both API and frontend on the same host)
I used document.cookie to save the cookie. I set the options by hand since calling res.cookie on the response of the API fetch only returned the value. An example is document.cookie = `goCookie=${res.cookie}; path=/; domain=localhost;.
Sending cookies
This has been answered before in previous questions and answered again in the comments. The problem was that I used credential:'include' instead of the correct credentials:'include' (plural).
CORS and cookies
In case the API and the frontend are not on the same host you will have to modify both the API and the frontend.
frontend
The cookie has to have the domain of the API since it's the API that requires it, not the frontend. So, for security reasons, you can't set a cookie for a domain (API) from another domain (frontend). A solution would be redirect the user to an API endpoint that returns Set-Cookie header in the response header. This solution signals the browser to register that cookie with the domain attached to it (the API's domain, since the API sent it).
Also, you still need to include credentials:'include' in the frontend.
API
You will need to set a few headers. The ones I set are
w.Header().Set("Access-Control-Allow-Origin", frontendOrigin)
w.Header().Set("Access-Control-Allow-Credentials", "true")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, withCredentials")
w.Header().Set("Access-Control-Allow-Methods", method) // use the endpoint's method: POST, GET, OPTIONS
You need to expose the endpoint where the frontend will redirect the user and set the cookie in the response. Instead of setting the domain of the API by hand, you can omit it, the browser will fill it with the domain automatically.
To handle the CORS and let JS send the cookie successfully, you will have to set the SameSite=None and Secure attributes in the cookie and serve the API over https (I used ngrok to make it simple).
Like so
func SetCookie(w *http.ResponseWriter, email string) string {
val := uuid.NewString()
http.SetCookie(*w, &http.Cookie{
Name: "goCookie",
Value: val,
SameSite: http.SameSiteNoneMode,
Secure: true,
Path: "/",
})
// rest of the code
}
I recommend you also read the difference between using localStorage and document.cookie, it was one of the problems I had.
Hope this helps.

How to make an authenticated PUT request to an App Engine service from an other origin

I have an App Engine service with a few methods implemented, where I restrict all routes with the login: admin option in the app.yaml.
Making a POST request to my service works:
fetch('http://localhost:8081/api/foo', {
credentials: 'include'});
But making a PUT request fails
await fetch('http://localhost:8081/api/foo', {
credentials: 'include',
method: 'PUT',
body: 'hi there'});
with the following error:
Response to preflight request doesn't pass access control check:
Redirect is not allowed for a preflight request.
I understand this is because my request is somehow not authenticated, and the server redirects my request to the login page. What I don't understand is how to authenticate it.
I'm using webapp2 to process the requests, and setting the following headers:
self.response.headers['Access-Control-Allow-Credentials'] = 'true'
self.response.headers['Content-Type'] = 'application/json'
# This feels wrong, but I still don't clearly understand what this header's purpose is...
self.response.headers['Access-Control-Allow-Origin'] = self.request.headers['Origin']
I think the deeper problem is that I don't undestand how this login feature works (is it cookie based? Why does it work with GET but not PUT? ...), and I don't truly understand CORS either.
Thanks for any help!
So, after discussing with Dan Cornilescu, here is the solution I came up with (Thanks Dan!)
Instead of having my classes inherit webapp2.RequestHandler, they inherit this custom HandlerWrapper.
The big difference is that when receiving an 'OPTIONS' request (ie. preflight), there is no login required. This is what was causing my problem: I couldn't get the preflight request to be authenticated, so now it doesn't need to be.
The CORS is also handled there, with a list of allowed origins
class HandlerWrapper(webapp2.RequestHandler):
def __init__(self, request, response):
super(HandlerWrapper, self).__init__(request, response)
self.allowed_origins = [
r'http://localhost(:\d{2,})?$', # localhost on any port
r'https://\w+-dot-myproject.appspot.com' # all services in the app engine project
]
self.allowed_methods = 'GET, PUT, POST, OPTIONS'
self.content_type = 'application/json'
# login mode: either 'admin', 'user', or 'public'
self.login = 'admin'
def dispatch(self):
# set the Allow-Origin header.
if self.request.headers.has_key('origin') and match_origin(self.request.headers['Origin'], self.allowed_origins):
self.response.headers['Access-Control-Allow-Origin'] = self.request.headers['Origin']
# set other headers
self.response.headers['Access-Control-Allow-Methods'] = self.allowed_methods
self.response.headers['Content-Type'] = 'application/json'
self.response.headers['Access-Control-Allow-Credentials'] = 'true'
# Handle preflight requests: Never require a login.
if self.request.method == "OPTIONS":
# For some reason, the following line raises a '405 (Method Not Allowed)'
# error, so we just skip the dispatch and it works.
# super(HandlerWrapper, self).dispatch()
return
# Handle regular requests
user = users.get_current_user()
if self.login == 'admin' and not users.is_current_user_admin():
self.abort(403)
elif self.login == 'user' and not user:
self.abort(403)
else:
super(HandlerWrapper, self).dispatch()
def match_origin(origin, allowed_origins):
for pattern in allowed_origins:
if re.match(pattern, origin): return True
return False
The login: admin configuration is based on the Users API, available only in the 1st generation standard environment. Not a CORS problem. From the login row in the handlers element table:
When a URL handler with a login setting other than optional
matches a URL, the handler first checks whether the user has signed in
to the application using its authentication option. If not, by
default, the user is redirected to the sign-in page. You can also use
auth_fail_action to configure the app to simply reject requests
for a handler from users who are not properly authenticated, instead
of redirecting the user to the sign-in page.
To use the Users API the user must literally login before the PUT request is made. Make a GET request first, which will redirect you to the login page, execute the login, then make the PUT request.
If that's not something you can achieve then you need to use a different authentication mechanism, not the one based on login: admin.
Update:
The above is true, but rather unrelated as the Users API authentication is addressed - you did mention that some other request method to the same URL works.
The error you get is indeed CORS-related, see Response to preflight request doesn't pass access control check. But I'd suggest not focusing on the accepted answer (which is only about working around CORS), but rather on this one, which is about doing CORS correctly.

WooCommerce - woocommerce_rest_cannot_view - Status 401

I have generated a consumer key and consumer secret. The website has SSL installed. I have also installed plugins required for JSON and REST services. This is how the url looks like:
https://<url>/wp-json/wc/v1/products
When I am trying to get(GET) the product details using Basic Auth by using POSTMAN, a Chrome plugin, I get a JSON response like:
{
"code": "woocommerce_rest_cannot_view",
"message": "Sorry, you cannot list resources.",
"data": {
"status": 401
}
}
I have both the READ and WRITE permissions corresponding to the Consumer key.
The 401 error you are getting is because you are using basic auth even though your website is not secure (does not have https).
The solution in postman is to use OAuth 1.0. Just add the consumer key and consumer secret and send the request.
I met same problem.
Here is how I solve it:
require "woocommerce_api"
woocommerce = WooCommerce::API.new(
"https://example.com",
"consumer_key",
"consumer_secret",
{
wp_json: true,
version: "wc/v1",
query_string_auth: true
}
)
The key is query_string_auth: true
you need to force basic authentication as query string true under HTTPS
This is how i stopped worrying and moved on.
In short, the woocommerce rest controllers pretty much all have a SOMEWPRESTCLASS::get_item_permissions_check() method which in turn calls wc_rest_check_post_permissions() to decide if it returns that error;
So you hook into that and validate whichever way you want:
add_filter( 'woocommerce_rest_check_permissions', 'my_woocommerce_rest_check_permissions', 90, 4 );
function my_woocommerce_rest_check_permissions( $permission, $context, $object_id, $post_type ){
return true;
}
Trying to help others:
I was struggling with the 401 response while trying to CURL, and also with VBA trying to request as content-type "application/json"
However, I was able to pull a valid response by just entering this in my browser address bar:
https://mywebsite.com/wp-json/wc/v2/products?consumer_key=ck_blahblah&consumer_secret=cs_blahblah
Following this line of thought, I went back to my VBA app and changed the content type to "application/text" and was able to pull a valid response text with response code 200.
Hope this helps someone.
Try this, I had the same issue with the automattic/woocommerce library and I just got it working by appending the customer_key and customer_secret to the query.
$woocommerce->get("customers/$userId?consumer_key={$this->key}&consumer_secret={$this->secret}");
Quick Edit
The above method works but I found a better solution for the automattic/woocommerce library.
Set query_string_auth to true
Had to dig into the code to find this setting.
Found nothing on it in the docs
return new Client($this->url, $this->key, $this->secret, [
"query_string_auth" => true
]);
I just ran into this. Apparently something was funny with how curl was handling the url, so I had to encapsulate it in double quotes.
This doesn't work:
curl https://www.my-site.com/wp-json/wc/v3/orders?consumer_key=ck_40097dbc2844ce7712e1820bcadf0149c2bedegh&consumer_secret=cs_ab57e19263af0b9ab4c596c310f1e7904bb20123
This does work:
curl "https://www.my-site.com/wp-json/wc/v3/orders?consumer_key=ck_40097dbc2844ce7712e1820bcadf0149c2bedegh&consumer_secret=cs_ab57e19263af0b9ab4c596c310f1e7904bb20123"
You can try Oauth 1.0 with postman:
Problem solved by adding this line below to the end of .htaccess file
All you need to add this line to .htaccess , this work with me
SetEnv HTTPS on
And make sure use OAuth 1.0 for Authorization
Try making the request using query parameter, like this:
https://www.exemple.com/wp-json/wc/v3/orders?consumer_key=ck_01234567890&consumer_secret=cs_01234567890
here: https://www.exemple.com you'll need to fill your url domain.
here: consumer_key and consumer_secret is your ck and cs that was previous genereted on WooCommerce > Settings > Advanced > REST API
Here is a modified answer to Quickredfox's anwer:
add_filter('woocommerce_rest_check_permissions', 'my_woocommerce_rest_check_permissions', 90, 4);
function my_woocommerce_rest_check_permissions($permission, $context, $object_id, $post_type) {
if($_GET['consumer_key'] == 'asdfghj' && $_GET['consumer_secret'] == 'qwerty') {
return true;
}
return $permission;
}
The downside to this is that the flexibility of adding and revoking access for users using a gui is lost. However, if nothing else works and you just can't figure out why, this will work and does not expose the API to the whole world.
Oh, and this requires passing the key and secret as parameters a la:
https://foo.bar.com/wp-json/wc/v3/products/123&consumer_key=asdfghj&consumer_secret=qwerty
This will work without https, but if you use it without https, remember that any credentials you send along with your request will be sent in plain text.
I just ran into this, I was getting the exact same error message as OP. I was using https and OAuth 1. The problem ended up being the domain. I was trying to access example.com when the correct domain for the site was www.example.com.
This URL returns 401 woocommerce_rest_cannot_view error:
https://example.com/wp-json/wc/v3/products
This URL works and returns results:
https://www.example.com/wp-json/wc/v3/products
For local development (localhost) you can also use Basic Auth (e.g. for Postman) instead of Consumer Key & Consumer Secret. It works seamlessly.
Add this code to function.php to fix the problem:
add_filter( 'woocommerce_rest_check_permissions', 'my_woocommerce_rest_check_permissions', 90, 4 );
function my_woocommerce_rest_check_permissions( $permission, $context, $object_id, $post_type ){
return true;
}
in node js code would be
const WooCommerceRestApi = require("#woocommerce/woocommerce-rest-api").default;
const api = new WooCommerceRestApi({
url: "http://example.com",
consumerKey: "ck_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
consumerSecret: "cs_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
queryStringAuth: true,
version: "wc/v3"
});
It's sometimes an error with wordpress htaccess configuration (only if you are accessing website by https).
For some reason woocommerce want you to authorize with basic authentication when your are connecting through https which some hosting blocks so you need to unlock it.
you need to change
RewriteRule ^index\.php$ - [L]
To
RewriteRule ^index\.php$ - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]

Internal Server Error in OAuth for Google

EDIT: I was able to resolve the original error here when I realized my ROOT URL was set to my IP address rather than my domain. However, I now have a new issue. My client ID is the same as the original post below. This works fine in the local app, but in production, the popup flashes for a second and then the login box displays "Internal Server Error". I can't see any other messages that would explain it.
I am using the service-configuration package to load the settings, as follows:
ServiceConfiguration.configurations.upsert(
{ service: "google" },
{
$set: {
clientId: "************",
loginStyle: "popup",
secret: "***********"
}
}
);
If I add ?close to the end of my Authorized redirect URI, the Google popup comes up with a redirect_uri_mismatch error, showing the URI without ?close. I think there was an issue resolved here but it at least shows me that my project in Google is being recognized.
ORIGINAL POST
I am setting up an OAuth 2.0 client ID for accounts-google in Meteor and am seeing the following error:
400. That’s an error.
Error: invalid_request
Invalid parameter value for redirect_uri: Raw IP addresses not allowed:
http://***.***.***.***/_oauth/google
My Client ID in Google:
Authorized Javascript Origins
http://localhost:3000
http://myApp.com
Authorized redirect URIs
http://localhost:3000/_oauth/google
http://myApp.com/_oauth/google
I understand I must not be properly pointing the domain to the IP address. I have already set up an A record and the site works fine in other regards though, so not sure what step I missed.

Error Message: redirect_uri is not owned by the application

::UPDATE:: LINKS DO NOT EXIST ANYMORE!
Very strange indeed, this is definitely a bug! I did a test with app_id from another application and it worked.
See for yourself:
https://apps.megalopes.com/megabraziltv/test.php (app_id correct)
https://apps.megalopes.com/megabraziltv/test2.php (app_id from another application)
---/---
I found several people with the same question and all the answers are equal:
Site URL is not same as REQUEST_URI (Redirecting URL)
My app setting are:
Secure Page Tab URL: apps.megalopes.com/megabraziltv/...
App Domain: megalopes.com
code:
<div id="fb-root"></div>
<script src="http://connect.facebook.net/pt_BR/all.js">
</script>
<script>
FB.init({
appId:'123456789', cookie:true,
status:true, xfbml:true
});
FB.ui({ method: 'apprequests',
message: 'Here is a new Requests dialog...'});
</script>
This simple code is not redirecting to any other url. I tested on the js console getting the same results. Sometimes works and sometimes I get this error message:
API Error Code: 191 API Error Description: The specified URL is not
owned by the application Error Message: redirect_uri is not owned by
the application.
Regardless of being page tab or canvas, you must identify the website Site URL in https://developers.facebook.com/apps
How I fixed:
App Domain: megalopes.com (domain)
Site URL: / Secure Canvas URL: / Secure Page Tab URL: https://www.megalopes.com (subdomain)
I think I have run into something similar before.
In the summary page of your app ensure both the Secure Canvas URL and Page Tab URL are populated.
The URL in my redirect_uri should have "http://" in the beginning. It was missing the protocol information, thus leading Facebook not to recognize my website and throw this annoying 191 error. I finally found out after one hour pulling the hair I (still) have left.
You have to create a channel page, which allows "cross domain communication in certain browsers"
This is an html page (saying /channel.html) on your server, which only contains :
<script src="//connect.facebook.net/en_US/all.js"></script>
And make the Javascript SDK aware of it :
FB.init({
appId: 'xxxxxx',
cookie: true,
channelUrl: location.protocol + '//' + location.host + '/channel.html'
});
More about this :
https://developers.facebook.com/docs/reference/javascript/FB.init/
https://developers.facebook.com/docs/reference/javascript/
It's because of domain URL that you mentioned in facebook's mistake. Domain URL wont be like www.site.com
Update your domain url like subdomain.site.com.
Now it surely work.

Resources