I posted this on the plugin support page, but maybe someone here has experienced the same thing.
I am developing a web application that uses WordPress as a backend (Headless). That is, I consume the WordPress resources and database from the app created in Vue with Node.js through the Rest API.
User creation is simple and I have it figured out. What I’m having problems with is the login.
Apparently (and according to WordPress) the plugin Simple Wordpress Membership is blocking the login.
More details now:
I send my app username and password to WordPress by Rest API using node-fetch (GET method).
In WordPress I have an Endpoint inside a Custom Plugin (code below).
This endpoint retrieves the URI parameters and sends them to wp_signon to sign in.
If wp_signon succeeds in logging in, it returns the user’s data. If not, it returns an error array.
This is the custom endpoint en WP:
`// Register REST API endpoints
class Login_REST_API_Endpoints {
/**
* Register the routes for the objects of the controller.
*/
public static function register_endpoints() {
// endpoints will be registered here
register_rest_route( 'wp', '/login', array(
'methods' => 'GET',
'callback' => array( 'Login_REST_API_Endpoints', 'login' ),
'permission_callback' => '__return_true'
) );
}
/**
* #param WP_REST_Request $request Full data about the request.
* #return WP_Error|WP_REST_Request
*/
public static function login( $request ) {
$data = array();
$data['user_login'] = $request["email"];
$data['user_password'] = $request["password"];
$data['remember'] = true;
$user = wp_signon( $data, false );
if ( !is_wp_error($user) ){
return $user;
} else {
return $error = json_encode(array('error' => true));
}
}
}
add_action( 'rest_api_init', array( 'Login_REST_API_Endpoints', 'register_endpoints' ) );`
So far so good. The problem appears when I activate the SWPM plugin.
I start getting this response (the asterisks are intentional, to hide the real info):
{ "code": "wp_die", "message": "<p>Warning! The Simple Membership plugin cannot process this access request to prevent you from accidentally logging out as WP admin.<\/p><p><a href=\"https:\/\/*********\/wp-admin\/profile.php\" target=\"_blank\">Click here<\/a>to see the profile with which you are logged in in this browser.<\/p><p>In this browser you have connected to the site as an administrator user. First, log out as WP admin and then you will be able to log in as a member.<\/p><p>Alternatively, you can use a different browser (where you are not logged in as an administrator) to test membership access.<\/p><p>Your frequent visitors or members will never see this message. This message is ONLY for the admin user.<\/p>", "data": { "status": 500 }, "additional_errors": [] }
And these are the headers when the error happens (the asterisks are intentional, to hide the real info):
Date: Sat, 11 Feb 2023 02:58:36 GMT Server: Apache Set-Cookie: swpm_session=52883ad4e8ad887e7***************; path=/ X-Robots-Tag: noindex Link: <https://******/wp-json/>; rel="https://api.w.org/" X-Content-Type-Options: nosniff Access-Control-Expose-Headers: X-WP-Total, X-WP-TotalPages, Link Access-Control-Allow-Headers: Authorization, X-WP-Nonce, Content-Disposition, Content-MD5, Content-Type Expires: Wed, 11 Jan 1984 05:00:00 GMT Cache-Control: no-cache, must-revalidate, max-age=0 Content-Length: 890 Connection: close Content-Type: application/json; charset=UTF-8
I already tried:
Close all sessions.
Delete cookies and cache.
Deactivate plugin and activate them one by one.
Test with different browsers.
Try with Insomnia.
And nothing works.
The only thing I have noticed is that when SWPM is disabled, the request is successful (code 200).
Could you please give me some idea of what might be going on and how I could fix it?
In use:
Simple Wordpress Membership 4.2.4. (link)
Wordpress 6.1.1.
New data:
When I GET request from Insomnia, if the SWPM plugin is active, I get a 500 error in the response body. But if it’s inactive, I get the 200 code, user data in the body, and WP login cookies in the header.
Insomnia’s settings say that it automatically stores these cookies and sends them when they are needed.
Now (the interesting thing) if I reactivate the SWPM plugin and do GET, I get code 200, the user data and NEW WP login cookies + SWPM login cookies.
From then on, all the requests you make to WP will be code 200. Logically, in each login request the cookies change.
Now, if I copy these cookies to my fetch code in Node.js, I also get 200 code from WP, even if the SWPM plugin is enabled.
Logically, these cookies expire and it is not something that can be used in production, but this information may help to solve the problem.
I am trying to send the user data (email and password) from Node.js to Wordpress via Rest API (GET) so that the user can log in to Wordpress.
I hope to receive an answer code 200.
I am currently getting response code 500.
Related
I am using symfony to create an API to update some dataset. One of the possible update is coming from a third party through an HTTP request which does not include user agent specifications. As a result, the call is returning a 403 Forbidden error.
I kept the code to its simpliest form (see below):
/**
* #Route("/Debug", name="Debug")
*/
public function Debug(Request $request)
{
return new Response(
'hurray !!' ,
Response::HTTP_OK
);;
}
I am sure that the issue is coming from the user agent, I have been able to reproduce it with postman by taking this header item out.
Any idea how to fix this issue ?
Thanks !
Adrien
I have a React app which calls a Wordpress v5 API.
const api = `${WAPI}`;
const headers = {
'Content-Type': 'application/json'
} ;
fetch(api, {
headers: headers
})
.then(function(data){
console.log(data);
})
.then(this.handleposts)
.catch(err => console.log(err));
}
Which returns this error in my development tools' console:
Access to fetch at 'http://XXX.XXX.XXX.XX/firstcivdivcareers/wp-json/wp/v2/posts/' from origin 'http://localhost:9000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request.
I used to call a single Wordpress site API but now it doesn't' work. I assumed Wordpress API would work with cross-origin domain calls to be used as a third-party service.
I added changes to the theme's functions.php. When I go to my site in the browser and check the header's in dev tool console. I can see I sent my response with the proper headers. However, doesn't work the same when I call through my JS's fetch call.
Changes added to functions.php:
/**
* Only allow GET requests
*/
function add_cors_http_header(){
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: GET");
header("Access-Control-Allow-Headers: origin");
}
Locate the file in your wordpress code which is serving the API.
You just have to add at the start of that file:
<? header("Access-Control-Allow-Origin: *"); ?>
Edit:
Instead of editing the core files, the better option is to use the filter as explained in this thread. You can put the following code in your functions.php
add_filter('init', 'add_cors_header');
function add_cors_header() {
header(...);
}
I had this issue as well with my Vue.js code. This is what I added to my GET request and I have had no further issues: https://cors-anywhere.herokuapp.com and it immediately precedes the URL:
let url = 'https://cors-anywhere.herokuapp.com/https://jobs.github.com/positions.json?&markdown=true&page=1';
I hope that helps!
You can use this MS Edge plugin t quickly disable Cors.
CORS Unblock - Microsoft Edge Addons
It will work just fine :)
When using ASP.Net Core's authentication for Google, I'm performing the following scenario:
Click to login via Google.
Log into Google successfully. At this point I am returned back to my application and I am able to move on my with process. The user claims were returned as expected.
Immediately go back to step 1 and try to login via Google again with the same account. If prompted at Google, select the same account/enter the credentials again.
At this point I now receive the below error.
If I wait a period of time, perhaps 30 minutes, if I start at step 1 again, I don't encounter the issue until I again reach step 4. If I restart my IIS ApplicationPool for my Core project, I can follow the above scenario where step 1 works, but then step 4 shows the issue.
I have searched what feels like endlessly online to no avail. Does anyone have anything they can suggest? Why would this work the first time, and then fail on second, third attempts?
I'm receiving the below error when following the scenario above on my Google Pixel 3 XL phone:
System.Exception:
SocialLoginController|Error|OAuth
token endpoint failure: Status: BadRequest;Headers: Vary: X-Origin,
Referer, Origin,Accept-Encoding Date: Sun, 03 Mar 2019 09:35:45 GMT
Server: ESF Cache-Control: private X-XSS-Protection: 1; mode=block
X-Frame-Options: SAMEORIGIN X-Content-Type-Options: nosniff
Alt-Svc: quic=":443"; ma=2592000; v="44,43,39" Accept-Ranges: none
Transfer-Encoding: chunked ;Body: { "error": "invalid_grant",
"error_description": "Bad Request" };
The code in my Startup.cs class for Google's authentication is as follows:
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie(options =>
{
options.LoginPath = "/login";
options.LogoutPath = "/signout";
});
services.AddAuthentication().AddGoogle(socialProvider.ProviderName, o =>
{
o.ClientId = [REMOVED]
o.ClientSecret = [REMOVED]
o.UserInformationEndpoint = "https://www.googleapis.com/oauth2/v2/userinfo";
o.ClaimActions.Clear();
o.ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "id");
o.ClaimActions.MapJsonKey(ClaimTypes.Name, "name");
o.ClaimActions.MapJsonKey(ClaimTypes.GivenName, "given_name");
o.ClaimActions.MapJsonKey(ClaimTypes.Surname, "family_name");
o.ClaimActions.MapJsonKey("urn:google:profile", "link");
o.ClaimActions.MapJsonKey(ClaimTypes.Email, "email");
o.CallbackPath = string.Format("/signin-{0}", socialProvider.ProviderName.ToLower());
o.SaveTokens = true;
o.Events.OnRemoteFailure = ctx =>
{
string message = UrlEncoder.Default.Encode(ctx.Failure.Message);
if (!string.IsNullOrEmpty(message) && message.Length > 1500)
{
message = message.Substring(0, 1499);
}
ctx.Response.Redirect(errorRedirectUrl + message);
ctx.HandleResponse();
return Task.FromResult(0);
};
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
Please also see the image below that shows the error when I have enabled "UseDeveloperExceptionPage".
FYI, I am completely unable to replicate the issue on my iPhone and my Desktop PC - I never receive the issue and I can make as many login attempts as I want, the issue never seems to arise on these devices.
I'm lost!
I was able to identify my issue here which turns out to be unique to any other answer I found with the same error I experienced above.
Issue: On my Pixel device, I have an application (My-App) that runs against the same domain that I was accessing in my Chrome browser. When clicking to login via Google, I remember it asked me if I wanted to 'Open in Chrome' or 'Open in My-App', and I always selected 'Open in Chrome'.
I uninstalled My-App, and now I am unable to replicate the issue. The issue is gone. I tried the scenario many times to no avail, every time it worked! When I re-installed My-App, the issue came back.
Fix: the problem that I need to resolve is running my Core project on a different domain other than the domain My-App is running, so there won't be a confusion on the device about whether Chrome should open or My-App should open. Hopefully that will be my fix!
Thanks for reading.
I am trying to get a gravity form from my wordpress website to serve as a login form for another application (CakePhp website). The form has two fields-username and password. I have added a hook to submit the form to the other application using gform_after_submission as follows:
add_action( 'gform_after_submission_6', 'mysite_gform_after_submission', 10, 2 );
function mysite_gform_after_submission( $entry, $form ) {
$post_url = 'http://otherapplicationurl.com/login';
$body = array(
"data[User][username]" => $entry[1],
"data[User][password]" => $entry[2],
);
$request = new WP_Http();
$response = $request->post($post_url, array('body' => $body));
//this is to delete the entry
GFAPI::delete_entry( $entry['id'] );
}
The form's confirmation setting is to display some text. But what I essentially want it to do is login the user to the other application and show the homepage of that application i.e redirect to the url "http://otherapplicationurl.com/home".
I keep getting the following error.
WP_Error Object
(
[errors] => Array
(
[http_request_failed] => Array
(
[0] => Too many redirects.
)
)
[error_data] => Array()
)
I don't know how to get the form to log the user in and redirect to the other applications home page.
Thanks in advance.
You've hit a very tricky process here. What you're trying to do is two things simultaneously:
Log in to an external service
Redirect to the user to the service
Technically, this can't be done the way you're looking to do it. And here's why:
You're dealing with cookies. When you log in, data is stored as a cookie/session that will remember who you are as you navigate through the site. The info is also only available to the relevant domain/path, and can only be set from that same domain/path.
The server, not the user, is logging in.
In other words, you're trying to log the user into the service from another domain via the server. In this case, the server will log in on behalf of the user (as it's the server doing the request), but will do nothing with the cookies. Even if we sent the cookies back to the user, they would apply under the original domain and we'd be no closer to being logged in.
Solution 1: Simple, but insecure
What you could do, is make the redirect and the login process the same thing. That is, using a URL such as http://otherapplication.com/login?user=adomnom&pass=awesome (though I strongly discourage that for security reasons).
Solution 2: Secure, but (you guessed it) complex
A safer approach would be to use the structure you have at the moment to generate a one-time login code. That is, the server will request a unique, one-time 'token' from the other application using the login details and use these as part of the redirect. The user is taken to this other page and is logged in using this token as a substitute for credentials. After this, the token should then become invalid.
That way, the other application is the one setting the cookies and no sensitive information is being directly transferred.
And here's how I'd do it...
1. Create new endpoint on the CakePHP side: /get-token
This endpoint will receive the username and password from GET data, then generate, store and return a unique token.
2. Extend /login on the CakePHP side to allow for a 'token' GET variable
Submitting 'token' to /login should also log in the user and delete the token - preventing it from being used again.
3. Update the submission process to use the correct hook
You'll want to use the gform_confirmation hook to do this - it's the hook that deals with redirects and thank you page contents (ie. the stuff that the user is shown after submitting the form).
add_action( 'gform_confirmation_6', 'mysite_gform_confirmation', 10, 3 );
function mysite_gform_confirmation( $confirmation, $form, $entry ) {
// Send login request
$token = wp_remote_post(
'http://otherapplicationurl.com/login',
array(
'body' => array(
"data[User][username]" => $entry[1],
"data[User][password]" => $entry[2]
)
)
);
// Delete entry
GFAPI::delete_entry( $entry['id'] );
// Redirect
return array('redirect', "http://otherapplicationurl.com/login?token=$token");
}
Hope that helps! Good luck!
I've desperately tried to figure this out on my own, and did not want to come to SO with this question, but I'm at my wits end (no thanks to the api / oauth docs).
I'm working in PHP and I'm trying to avoid using the Google_Client and AnalyticsService classes, by using REST on the analytics.data.ga.get method.
STEP #1: Create an API Project for a Web Application
I go to the api console and create a project with analytics services and get an OAuth Client ID and Secret.
I'm under the impression that I can create a Client ID for an Installed Application or a Web Application because I'm doing the initial token handshaking manually. Please correct me if I'm wrong.
I create a Client ID for web applications and get my Client ID xxxxxxxxxxxxxx.apps.googleusercontent.com, Client secret yyyyyyyyyyyyyyy, and my Redirect URI is http://localhost:9002
STEP #2: Request initial API access
I enter this link; https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=xxxxxxxxxxxxxx.apps.googleusercontent.com&redirect_uri=http://localhost:9002&scope=https://www.googleapis.com/auth/analytics.readonly&access_type=offline
The access_type=offline is because I'm using REST, and do not expect "the user" (myself) to manually deal with redirects / popups every time I need a refreshed token.
The above request returns http://localhost:9002?code=4/KModH0K_xxxxxxxxxxxxxxxxxxx9Iw.gikOaYRDWywTshQV0ieZDArCOX8XdwI
Code 4/KModH0K_xxxxxxxxxxxxxxxxxxx9Iw.gikOaYRDWywTshQV0ieZDArCOX8XdwI is my permission to request the API Token.
STEP #3: Request First Token
Because of my company’s IT issues, I’m forced to use PHP 5.2.17 and I do not have access to PHP cURL, so I’m using file_get_contents and stream_context_create.
The first token is requested with a PHP file_get_contents();
$opts = array(
'http' => array(
'method' => 'POST',
'header' => 'Content-Type: application/x-www-form-urlencoded',
'content' => 'code=4/KModH0K_xxxxxxxxxxxxxxxxxxx9Iw.gikOaYRDWywTshQV0ieZDArCOX8XdwI&client_id=xxxxxxxxxxxxxx.apps.googleusercontent.com&client_secret=yyyyyyyyyyyyyyy&redirect_uri=http://localhost:9002&grant_type=authorization_code'
)
);
$context = stream_context_create($opts);
$result = file_get_contents('https://accounts.google.com/o/oauth2/token', false, $context);
var_dump($result);
The content parameters must be in a single line.
The above code returns my access_token and refresh_token in json format
string(195) "{ "access_token" : "ya29.AHES6wwwwwwwwwwwwwwwVEBXE6XRbC-Q-pP0wZWdoIm9H804ro", "token_type" : "Bearer", "expires_in" : 3600, "refresh_token" : "1/8tXvdUKcSEcaaxVqqqqqqqqqqqqqoYpj2KSS9qwWI" }"
The refresh token I must store in a safe place, like a DB or protected txt file, which is called upon when my access_token has timed out.
STEP #4: Request Analytics Data
Now from what I understand, I’m ready to roll and should be able to use my access_token to make requests to https://www.googleapis.com/analytics/v3/data/ga.
I do this by sending this request;
$request = 'https://www.googleapis.com/analytics/v3/data/ga' .
'?ids=ga%3Aaaaaaaaa' .
'&start-date=2012-12-07' .
'&end-date=2012-12-09' .
'&metrics=ga%3Avisits';
$opts = array(
'http' => array(
'method' => 'GET',
'header' => 'Content-Type: application/x-www-form-urlencoded\r\n' .
'Authorization: Bearer ya29.AHES6wwwwwwwwwwwwwwwVEBXE6XRbC-Q-pP0wZWdoIm9H804ro \r\n'
)
);
$context = stream_context_create($opts);
$result = file_get_contents($request, FALSE, $context);
var_dump($result);
This request returns a 401 Unauthorized error. I take this as meaning my request is properly formed and making the connection to https://www.googleapis.com/analytics/v3/data/ga.
Also, according to this doc Getting Full Quota, I can make the request with the access_token in the URL like this;
$request = 'https://www.googleapis.com/analytics/v3/data/ga' .
'?ids=ga%3A48564799' .
'&access_token=ya29.AHES6wwwwwwwwwwwwwwwVEBXE6XRbC-Q-pP0wZWdoIm9H804ro' .
'&start-date=2012-12-07' .
'&end-date=2012-12-09' .
'&metrics=ga%3Avisits';
$result = file_get_contents($request, FALSE);
$result = json_decode($result);
var_dump($result);
This time I receive 403 error, in which google includes the response User does not have sufficient permissions for this profile.
QUESTION #1
Am I’m missing something in the API console or a process in the token acquisition? I’m assuming I’m not, because I’m ultimately acquiring the access_token=ya29 and refresh token.
QUESTION #2
Maybe I’m completely off basis in assuming I can do this with simple https reqests? Do I have to use the Google_Client and AnalyticsService classes? I don’t think this is the case, but maybe I’m wrong.
QUESTION #3
Do I need to use a ‘key’ in my request?
&key=bbbbbbbbbbbbbbbb
QUESTION #4
By using PHP 5.2.17 am I missing something? (besides 5.3 or 5.4 themselves)
For example, in some versions of PHP, in stream_context_create, the header should be in an array and not a string, like this;
$opts = array(
'http' => array(
'method' => 'GET',
'header' => array(
'Content-Type: application/x-www-form-urlencoded',
'Authorization: Bearer ya29.AHES6wwwwwwwwwwwwwwwVEBXE6XRbC-Q-pP0wZWdoIm9H804ro '
)
)
);
But I don’t think that it’s an issue in my case. I’m just curious if these HTTP request need to be formed a different way (without using curl).
Any insights and thoughts would be greatly appreciated
Here’s my dim witted mistake that nearly gave me a heart attack.
I typically do my development work in Chrome. And my Chrome browser was signed into my gmail account personal#gmail.com. However, my analytics account, which is under my work#gmail.com was open in FireFox (try not to laugh to hard).
I’m not 100% sure this is correct, but I think this is the general flow. When I did STEP #2: Request initial API access, I did this in my Chrome browser. The endpoint https://accounts.google.com/o/oauth2/auth was authorizing my personal#gmail.com account. And my STEP #4 API request https://www.googleapis.com/analytics/v3/data/ga was looking for an analytics profile under my personal#gmail.com account. Which of course doesn’t exist.
I literally wasted 15 hours on this. This is dumber than trying to troubleshoot an update… and forgetting to flush the cache.
Sorry for wasting your time.
EDIT REFRESH TOKENS
I've once again run into issues with this API and found out the hard way that GA will revoke Refresh Tokens if too many different clients use the token, at least I think that was the problem.
Further reading can be found here.
I got a 403 today and found the cause: in the get function I was using the account ID instead of the profile ID. Switching to the profile ID fixed it for me.
It could be a problem with CURL request. In the GoogleAnalyticsAPI.class.php > class Http > function curl (around line 720) add one more option to stop CURL from verifying the peer's certificate:
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);