mevdschee / php-crud-api

Single file PHP script that adds a REST API to a SQL database
MIT License
3.6k stars 1.01k forks source link

New Authentication Middleware using Wordpress #982

Closed nkappler closed 1 year ago

nkappler commented 1 year ago

Hi @mevdschee,

I'm currently trying to marry your api to the wordpress user session. Unfortunately it is not as easy as pointing the api to the 'users' table, because WP uses neither the standard $_SESSION variable, nor standard password encryption (it uses some custom salt and appends an ID or something...)

So far I was able to write a custom script which checks if a wordpress session is available and which can verify a password from the database:

<?php
// Load the WordPress environment
define('WP_USE_THEMES', false);
require('path_to_wordpress_installation/wp-load.php'); // <- the base path needs to be configurable

// Function to check user's password
function verify_user_password($email, $plain_text_password)
{
    // Check if the user is logged in
    if (is_user_logged_in()) {
        // User is logged in, retrieve user ID and email
        $user = wp_get_current_user();

        // Do something useful here
        echo "User ID: $user->ID<br>";
        echo "User Email: $user->user_email<br>";
        echo "User Display Name: " . $user->display_name . "<br>";
        return;
    }

    // Retrieve the user data based on the provided email
    $user_data = get_user_by('email', $email);

    if (!$user_data) {
        echo "User not found with the provided email.";
        exit;
    }

    if (wp_check_password($plain_text_password, $user_data->user_pass)) {
        // do something useful here instead of echo...

        // Password is correct, display all user data
        echo "User ID: " . $user_data->ID . "<br>";
        echo "User Display Name: " . $user_data->display_name . "<br>";
        echo "User Email: " . $user_data->user_email . "<br>";
        // Add more fields as needed
    } else {
        echo "Password is incorrect.";
    }
}

// just for testing:
// Get user email and unhashed password from GET parameters
$email = isset($_GET['email']) ? $_GET['email'] : '';
$plain_text_password = isset($_GET['password']) ? $_GET['password'] : '';
verify_user_password($email, $plain_text_password);

I'd like to write a middleware similar to DBAuth and I would be happy to file a PR but I am not very good at php and I don't know where to start.

Any help is appreciated 🙂

pottertech commented 1 year ago

Once you load the required _require('path_to_wordpressinstallation/wp-load.php'); file, you should only have to add a _global $currentuser; line.

Then you can use the _$current_user->ID_ to do a curl out to the API, something like this:

//Get User Info

$url = "http://IP_or_URL/CRUDAPI.php/records/users?filter=id,eq," . $current_user->ID;

$ch = curl_init($url);

curl_setopt($ch, CURLOPT_URL,$url);

curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);

$json = curl_exec($ch);

$user = json_decode($json, true);

nkappler commented 1 year ago

@pottertech thanks for your reply, but I'm not trying to fetch a user from php-crud-api with the same ID as the wordpress user, I wan't to use wordpress as an authentication/identity provider for the API.

I've got it partially working by modifying the DBAuth middleware, but I want to write a proper middleware for it, which I might be able to do by myself, but it's going to take some more time 😆

pottertech commented 1 year ago

Good idea you have. I provided my code in hopes that it helps someone a little. Now that @mevdschee has tagged it as an enhancement, I am sure between it will get done and function well.

nkappler commented 1 year ago

Now that @mevdschee has tagged it as an enhancement, I am sure between it will get done and function well.

Yep, I'm almost flabbergasted that it isn't done already ðŸĪŠ

Jokes aside, @mevdschee do you expect me to prepare a PR? I don't have any estimate for how long it would take and I don't have a proper php dev setup. I'd prefer to do a code review instead, if you have the capacity to develop it yourself...

mevdschee commented 1 year ago

Yep, I'm almost flabbergasted that it isn't done already zany_face

Ok.. I'll start now.. time me.. ;-)

mevdschee commented 1 year ago

Stop the timer (<30 min)! The lines with bad indentation are added to make it work:

// file: src/index.php
namespace Tqdev\PhpCrudApi {

    use Tqdev\PhpCrudApi\Api;
    use Tqdev\PhpCrudApi\Config\Config;
    use Tqdev\PhpCrudApi\RequestFactory;
    use Tqdev\PhpCrudApi\ResponseUtils;

define( 'WP_USE_THEMES', false ); // Don't load theme support functionality
require( './wp-load.php' );

    $config = new Config([
        // 'driver' => 'mysql',
        // 'address' => 'localhost',
        // 'port' => '3306',
        'username' => 'php-crud-api',
        'password' => 'php-crud-api',
        'database' => 'php-crud-api',
'middlewares' => 'authorization',
'authorization.tableHandler' => function ($operation, $tableName) {
    return wp_get_current_user()->ID?true:false;
},
        // 'debug' => false
    ]);
    $request = RequestFactory::fromGlobals();
    $api = new Api($config);
    $response = $api->handle($request);
    ResponseUtils::output($response);

    //file_put_contents('request.log',RequestUtils::toString($request)."===\n",FILE_APPEND);
    //file_put_contents('request.log',ResponseUtils::toString($response)."===\n",FILE_APPEND);
}

We could put this into a middleware of some kind.

@nkappler I think Wordpress should verify the password, not our middleware. We only need to read the (session) cookie.

@pottertech I feel your approach should be implemented as a WordPress plugin, which would be nice-to-have.

mevdschee commented 1 year ago

Okay.. @nkappler and @pottertech

WpAuthMiddleware is added to the main branch..

It supports:

It doesn't support:

Enable it as 'wpAuth' middleware, with config parameters:

In the authorization middleware you can use functions:

Play with it and let me know how you like it.. :-)

nkappler commented 1 year ago

Works like a charm, thank you âĪïļ

Stop the timer (<30 min)!

Clues condense that you are, in fact, a robot 😀

One final remark: Currently the returned object upon login contains the "user_pass" hash: grafik

And the /me route returns it too...

I've made a few suggestions here: 8a424fe80d1517eaa206891027dba03b4568c13e

mevdschee commented 1 year ago

@nkappler Thank you for your review and your kind words. I'll merge your suggestions and release a new version with updated README. I do need one or two days as I'm a bit busy with some other things.

nkappler commented 1 year ago

Take your time and thanks a lot. I'll leave this issue open in case you want to link it with the next commit, but feel free to close it any time 🙂

mevdschee commented 1 year ago

Released in v2.14.23