Access over API with PHP (with token)

I want to build a small app in PHP.
For example, I would like to use this to simply query content via the API and output it in HTML. Possibly with its own read-only user.
I would also like to be able to edit content with another user. This should authenticate itself in my app (user/password). This should allow the user to work for a certain period of time. I know that from another CRM and its API, that you then get an access token that you save in a database or a file, for example, and with which you can then work until an expiration time.
Is that basically possible with Cockpit?

I already managed to implement an authentication with PHP and get the users api key:

The last statement to get all items from model “Seiten” doesn’t work. I get a “Permission denied” error.

function build_post_fields($data, $existingKeys = "", &$returnArray = [])
{
    if ($data instanceof CURLFile or !(is_array($data) or is_object($data))) {
        $returnArray[$existingKeys] = $data;
        return $returnArray;
    } else {
        foreach ($data as $key => $item) {
            build_post_fields(
                $item,
                $existingKeys ? $existingKeys . "[$key]" : $key,
                $returnArray
            );
        }
        return $returnArray;
    }
}

function callAPI($method, $url, $data)
{
    $curl = curl_init();
    switch ($method) {
        case "POST":
            curl_setopt($curl, CURLOPT_POST, 1);
            if ($data) {
                curl_setopt(
                    $curl,
                    CURLOPT_POSTFIELDS,
                    build_post_fields($data)
                );
            }
            echo $url . "<br>";
            break;
        case "PUT":
            curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "PUT");
            if ($data) {
                curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
            }
            break;
        default:
            if ($data) {
                $url = sprintf(
                    "%s?%s",
                    $url,
                    http_build_query(build_post_fields($data))
                );
            }
            curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "GET");
            echo $url . "<br>";
    }

    curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);

    curl_setopt($curl, CURLOPT_URL, $url);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);

    $headers = [];
    #$headers[] = "Api-Key: " . $authToken;
    #curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);

    $result = curl_exec($curl);
    if (curl_errno($curl)) {
        echo "Error:" . curl_error($curl);
    }

    if (!$result) {
        die("Connection Failure");
    }
    curl_close($curl);
    return json_decode($result);
}

$url = "https://cockpit.local/api/user/auth";
$data = [
    "user" => "test",
    "password" => "asdasd",
];

$test = callAPI("POST", "https://cockpit.local/api/user/auth", $data);

$data = [
    "api-key" => $test->apiKey,
];

print_r(
    callAPI("GET", "https://cockpit.local/api/content/items/Seiten", $data)
);

What kind of user are you testing with? As in, does the user’s role have permission to access this model?

I’d tried two different users. One with role “admin” (I guess, he has permissions on all modules, right?) and one with a special created role, that has only all permissions on my module “Seiten”.
The question for me is: Can I authenticate the user in the first step with the username and password (/api/user/auth) and after that use the authentication to send other requests (GET and POST on /api/content/items/)?

The interesting code is:

$url = "https://cockpit.local/api/user/auth";
$authData = [
    "user" => "username",
    "password" => "secretpassword",
];
$auth = callAPI("POST", $url, $authData);

$getData = [
    "api-key" => $auth->apiKey,
];

callAPI("GET", "https://cockpit.local/api/content/items/Seiten", $getData);

My Cockpit installation is a fresh 2.3.5, PHP 8.1.11 on a Mac (using MAMP Pro).
Calling e.g.
https://cockpit.local/api/content/items/Seiten?api-key=APIKEY
or https://cockpit.local/api/content/items/Seiten?token=APIKEY
also gives a simple

{"error":"Permission denied"}

EDIT: I tested the api-key in the REST API Playground (generated CURL):

curl -X GET "https://cockpit.local/api/content/items/Seiten" \
 -H "api-key: API-xxx" \

That was working fine in the playground, but not the direct call above :frowning:

Code looks alright to me. Are you getting the correct API key? A “permission denied” would be the result of an incorrect key or the user doesn’t have access.

An admin user has access to everything indeed. What if you hardcode the key in the PHP, does that work?

Unfortunately not.
As the documentation is currently not very good, I’m not sure whether to use “token” or “app-key” or …
I tried it on a different installation and called directly in a browser something like this:
https://…/api/content/item/Artikelcollection?app-key=API-xxx

Permission denied :-/

When activating a role within “Public API access permissions for unauthenticated requests.” I get a result, but that is not my aim.

Ok, I searched in the Cockpit code and found something like api_key! That is working for me!
https://cockpit.local/api/content/items/Seiten?api_key=xxx

It’s always about the documentation :wink:

But am I right for a secure setup:

  • authenticate with users credentials (getting from something like a web form)
  • check authentification with /api/user/auth
  • save the api_key safely, and temporary on the Webserver (e.g. in a session) for current actions.