Authenticate with Wordpress cookie through API from a subdomain - php

I want to access the current logged in Wordpress user in a separate Laravel installation.
Wordpress is running as website.com and I've got a subdomain with tool.website.com with the Laravel application (on another server but same domain).
I'm using the Native Wordpress API and created an authentication route.
The issue:
When I access the /authenticate route directly, the user ID is returned and works correctly. But when I access the route through tool.website.com false is returned..
Things I've got working:
I've created an API request which returns the user id in an API call:
add_action( 'rest_api_init', function () {
register_rest_route( '/authenticate', array(
'methods' => 'GET',
'callback' => 'authenticate',
) );
} );
The function looks like this:
$user_id = wp_validate_auth_cookie( $_COOKIE[LOGGED_IN_COOKIE], 'logged_in' );
The WP cookie is available on both the sub / main domain. I can see they are identical and toplevel.
define('COOKIE_DOMAIN', '.website.dev');
Things I've tried:
Using wp_get_current_user() to retrieve the user, this seems to need a nonce. I experimented hours and hours with the nonce approach on many different ways, but I could not get this to work (false or 0 was returned). I understand this is due to restrictions of using a nonce from outside of Wordpress.
Using the default native API approach to get the user, also needs the nonce.
Reading the https://developer.wordpress.org/rest-api/ manual, git repository & several articles / comments online.
Thinking about the OAuth approach, but I do not want users to login again as they are already logged in when they reach the tool.
Sending stuff like posts etc works without problems, so the API connection is not the problem.
I'm wondering if my approach is in the right direction. Hopefully someone can give me some guidance.

I found the following workaround:
- tool.website.com
Send the Cookies from tool.website.com to the API as post data.
$cookie_array = $_COOKIE;
// use key 'http' even if you send the request to https://...
$options = array(
'http' => array(
'header' => "Content-type: application/x-www-form-urlencoded\r\n",
'method' => 'POST',
'content' => http_build_query($cookie_array)
)
);
$context = stream_context_create($options);
$data = file_get_contents(self::BASE_URL . "authenticate", false, $context);
- website.com
Retrieve the cookie from Post data, and use the standard LOGGED_IN_COOKIE constant in Wordpress to select the correct one (this can be refactored to sending the correct cookie at once).
// We retrieve the cookie (which is sadly not available through the API call alone)
$the_cookie = $request->get_body_params();
// As the cookie lives at domain level, we can use the same Cookie key in the WP API and other subdomains of this domain
// The cookie key remains the same
$user_id = wp_validate_auth_cookie( $the_cookie[LOGGED_IN_COOKIE], 'logged_in' );
This solution seems steady; hopefully it will help someone. If there are other solutions, please add them in this topic; as I'm sure there must be different ways achieving this.

Related

Add cookie to wordpress wp-json api call

I am developing an app for iPhone that makes use of a Wordpress site as a backend. I am also making changes to the site and have created basic endpoints for servicing the app using the Wordpress wp-json restful api. I have installed the JSON API, JSON API Auth, and JSON API User plugins and support user authentication. I manage to login a user and get a session token while doing so, a cookie. How do I make the subsequent calls authenticated, i.e. how do I add the cookie as a required parameter to an existing endpoint?
For instance, I have this code that retrieves the latest menu for the week:
function get_latest_menu ( $params ){
$post = get_posts( array(
'category' => 69,
'posts_per_page' => 1,
'offset' => 0
) );
if( empty( $post ) ){
return null;
}
return $post[0]->post_content;
}
// Register the rest route here.
add_action( 'rest_api_init', function () {
register_rest_route( 'weeks-menu/v1', 'latest-menu',array(
'methods' => 'GET',
'callback' => 'get_latest_menu'
) );
} );
Where and how should I add the cookie parameter in the call so that it would become authenticated? Please provide specific code if possible.
This might seem quite basic but I have no real php/Wordpress knowledge
nor the time to acquire it. Thank you.
Not sure how much help this will be after the fact but recently for school I ran into a similar issue while developing a mobile application using Ionic. The things I had to do were:
Enable Authentication headers in the HTAccess file for wordpress (it usually fails if you do not do this, the docs for the JWT explain exactly what to add and it worked for me).
I used post man to test it but the first thing you do is get the token for the username / password.
Every subsequent call you add an authorization flag in the header in the form Authorization: Bearer <token> where is your token you had for the user. I had a package with Ionic that can force the token from my storage which worked every time I made a call.
Additionally, you can add a new check to verify if the token is valid but there are a few plugins that force any rest end point to be authenticated which ends up sending an unauthorized flag which you can check in your code.
My recommendation if your still unclear download and use postman and use it to verify if everything is set up correctly. Once you get that sorted everything else starts making more sense.

GET works but POST doesn't for silent login

We have 2 systems one in PHP and one in asp.net which currently require separate logins even though both use the same username/password information.
As a quick (and temporary) fix I thought I might be able to amend the PHP login page to pass the login details to the asp.net page behind the scenes so the user could be logged in automatically to both systems rather than having to enter their credentials again when navigating to a different page.
This works fine when I use a GET to pass the parameters but of course the GET parameters including the password get stored in the server log which is not a good idea.
So I would like to use POST instead, but I can't get it to work. I used the CURL-less method in How do I send a POST request with PHP? (code below) and it appears to work but the asp.net login process creates secure cookies and while they are stored if I GET the page they are not being stored when I POST to it instead.
$url = 'https://server.com/path';
$data = array('key1' => 'value1', 'key2' => 'value2');
// use key 'http' even if you send the request to https://...
$options = array(
'http' => array(
'header' => "Content-type: application/x-www-form-urlencoded\r\n",
'method' => 'POST',
'content' => http_build_query($data),
),
);
$context = stream_context_create($options);
$result = file_get_contents($url, false, $context);
var_dump($result);
I have tried something similar with CURL in the past so I know that CURL creates it's own browser context but I thought a straight POST request would work, however it seems to be behaving much the same as CURL.
Is there some way to get this to work or do I have a fundamental misunderstanding of how POST works? If so could somebody point me at a good explanation.

Why is Facebook api Privacy parameters- custom - allow ignoring friend id's?

I am unable to allow specific user id's to see my uploaded photo via the privacy parameters. This is my code:
$privacy = array(
'value' => 'CUSTOM',
'allow' => '619211114855652',
);
$photo = (new FacebookRequest(
$session,
'PHOTO',
'/me/photos',
array (
'source' => new CURLFile ($location.$name),
'message' => ($caption),
'privacy' => json_encode ($privacy)
)
))->execute()->getGraphObject()->asArray();
The friend id is retrieved using the get /me/friend request but this is being ignored as when I run this, it sets to "only me" as my privacy setting. Having googled this, I found out that if CUSTOM and allow is not specified, it will by default set it to "only me" who can see the post. When I replace the id with "ALL_FRIENDS" it works. I really do not understand why it isn't working, I assume facebook is ignoring the id but I can't think why. Has anyone managed to set their privacy to specific users?
Also note, this does not override my max privacy settings of the app, as my max setting is set to "friends". And I am just trying to limit the friends who can see the post.
I've tested your code with a new application and I've found that it's the initial privacy setting you sent when adding the application causes this behaviour.
Go to Settings > Apps > Your App and change the "Visibility of App" setting to Friends or Public.
Then re-test your code and you'll see that it does indeed work work with a valid friend ID.
It was a bug. I reported it and now it works :)

Firebase PHP CURL Authentication

I currently building a small chat using Firebase and PHP. This, I thought, would be a good learning project for Firebase, and so far I am very happy with it!
However, I have hit a wall. I am not sure how I can implement an authentication system to Firebase via PHP. It's quite specific what I need the authentication system to do:
To be able to use the chat, the user must login using my custom php login system. Then once they are logged in, they will also authenticate to be able to read/write in the chat.
I couldn't really understand how this (if even) is possible with PHP, using CURL.
In my __construct function in have the following:
require('/libs/FirebaseLib.php');
$this->firebase = new fireBase('https://<url>.firebaseio.com');
require('/libs/JWT.php');
require('/libs/FirebaseToken.php');
$tokenGen = new Services_FirebaseTokenGenerator('<firebase-secret>');
$this->authtoken = $tokenGen->createToken(
array(
'id' => $this->userid
)
);
How would I authenticate with Firebase to let the user be able to read/write in my chat and not allow non authenticated user to read/write?
Note: I have not done anything to the Firebase security rules - this is part of my question.
I've looked at the documentation, I might just be very thick, but I couldn't really find what I was looking for.
Hope anyone to point me in the right direction.
Thanks!
EDIT: I have intentionally not been using javascript for my chat, apart from ajax calls to my php script which then relays it to Firebase after I have done what I want to do with the user's messages.
EDIT 2: Added links to the used libraries: "Firebase Token Generator" and "Firebase PHP Client"
EDIT 3: My current code looks like this: (reference)
__construct:
$this->authtoken = JWT::encode(
array(
'admin' => true,
'debug' => true,
'v' => 0,
'iat' => time(),
'd' => array('user' => 'admin')
),
'<secret>',
'HS256'
);
New Message Function:
$response = $this->firebase->set('/chat.json?auth=' . $this->authtoken, array(
'message' => array(
'username' => 'Test',
'time' => time(),
'string' => 'Hello World!'
)
));
However it returns: { "error" : "invalid_token: Could not parse auth token." }. I basically want to get permission as the administrator. I have tried just using the Firebase secret as the auth, but its returns the same error.
Rules:
{
"rules": {
".read": "auth.username == 'admin'",
".write": "auth.username == 'admin'"
}
}
The general workflow would be:
After user has authenticated with your custom login system, generate a Firebase auth token in your server-side PHP code. (it looks like you got this far with the code snippet you pasted).
Pass that token back to the client.
Have the client call firebase.auth(<token>); to authenticate to Firebase using your server-generated token.
Use security rules to restrict what the client can read/write, depending on the contents of their auth token.
For a simple scenario where you just want to allow all Firebase access if they're authenticated, you could just have security rules like:
{
"rules": {
".read": "auth != null",
".write": "auth != null"
}
}
This would give authenticated users read/write access to your whole Firebase. You probably want to lock it down more than that though (i.e. only give them read/write access to certain parts of the Firebase). Check out our Security Quickstart for a walkthrough on how auth and security rules work.
If this doesn't help, perhaps you can elaborate on which part you're getting stumped at.

Wordpress wp_remote_post

I want to use wp_remote_post to send information from one server to my other website.
So basically, I have added this line to my code -
$sidebarHTTP = site_url(); // Retrieves HTTP Url of sidebar
$sidebarActivation = $sidebar.' , '.$sidebarHTTP; // Activate Sidebar
$args = array(
'method' => 'post',
'body' => array('sidebar' => $sidebar, 'sidebarHTTP' => $sidebarHTTP),
'user-agent' => 'My site'
);
wp_remote_post( 'http://mysite.com', $args ); // Loads all default data
So basically, it doesn't send anything. Yes, I have correct domain entered. Maybe it does send something, but I don't know how can I retrieve the $args['body'] from that site. Also, I tried adding $response = wp_remote_post.... and then sending mail $response['body'], but it just sends source code of homepage to email.
Would appreciate help.
You will need to enable cURL in your php.ini file.
wp_remote_post() uses a class called WP_Http that in turn can use one of three transport classes (see file class-http.php function _get_first_available_transport).
POST method will work with class WP_Http_Curl, but will not work with class WP_Http_Streams (the cURL fallback).
The alternative is to use wp_remote_get()

Categories