This script was exported from Postman and edited to provide Basic Authentication.

$curl = curl_init();

// Your credentials
$username = "<user>";
$password = "<password>";

// Encode credentials in Base64
$credentials = base64_encode($username . ":" . $password);

curl_setopt_array($curl, array(
  CURLOPT_URL => 'https://peter900.example.org/web/node?_format=json',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => '',
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 0,
  CURLOPT_FOLLOWLOCATION => true,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_POSTFIELDS =>'{

   "type":[

      {

         "target_id":"article"

      }

   ],

   "title":[

      {

         "value":"Create node99999"

      }

   ],

   "body":[

      {

         "value":"Node Body",

         "format":"basic_html"

      }

   ]

}',
 CURLOPT_HTTPHEADER => array(
    'Content-Type: application/json',
    'Authorization: Basic {$credentials}', // Add Basic Auth header
  ),
));

$response = curl_exec($curl);

curl_close($curl);
echo $response;

Running this script gives the following error message..

Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException: No authentication credentials provided. in Drupal\basic_auth\Authentication\Provider\BasicAuth->challengeException() (line 180 of /var/www/vhosts/xxx.org/peter900.xxx.org/web/core/modules/basic_auth/src/Authentication/Provider/BasicAuth.php).

I don't understand while the parameters and authentication included in Postman creates a node but when I run in a browser what I believe is identical code, it fails saying "No authentication credentials provided".  Any help much appreciated.  Thanks.

Comments

peterk900’s picture

I now realise why no authentication credentials were picked up - a php variable was included inside the string! 

I've replaced the variable with the base64 encoded value...

'Authorization: Basic cGV0ZXJmmmmOjAzTGVpbDEzMC0=',

But I still have a problem.  The error message is now...

Path: /web/node?_format=json. Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException: in Drupal\Core\Routing\AccessAwareRouter->checkAccess() (line 117 of /var/www/vhosts/xxx.org/peter.xxx.org/web/core/lib/Drupal/Core/Routing/AccessAwareRouter.php).

shruti prajapati’s picture

Now auth seems correct. The new error looks like a permission issue, not an authentication issue. Please check whether this user has permission to create Article nodes via the JSON/REST endpoint.

peterk900’s picture

The credentials used in my first post - administrator - used in the cURL code were the same as I used in Postman. But, based on your suggestion, I created a new role - content editor and tested this in Postman (node created successfully, as before) and then with the same cURL script, updated for the new user and password, bas64_encoded. This failed with the same AccessDeniedHttpException error.   Interestingly the Postman run shows the content editor's user name in log messages but the cURL script shows the user as  Anonymous (not verified).

I checked that permissions for both administrator and content editor are granted for External Entities REST: Create new external entity and JSONAPI: Create new external entity.

There must be some difference in the way Postman posts the data compared to the browser run cURL script.

If there's any more information you need or tests to carry out, please let me know. Thanks again for your help so far.

shruti prajapati’s picture

Looks like in cURL the request is still going as anonymous. Maybe the Authorization header isn’t being sent correctly. Try using CURLOPT_USERPWD or check headers once.

peterk900’s picture

I found this code here...

$User = "<user>";
$Passwd = "<password>";
$encodedAuth = base64_encode(self::$User.":".self::$Passwd);
curl_setopt($curl, CURLOPT_HTTPHEADER, array("Authorization: Basic ".$encodedAuth));

OR

$User = "<user>";
$Passwd = "<password>";
$encodedAuth = base64_encode(self::$User.":".self::$Passwd);
curl_setopt($ch, CURLOPT_USERPWD,$encodedAuth);

And does it matter where I put this in the script?

[I forgot to mention that $response displayed when I run the script is...

{"message":""}

... and the script log message is classified as a warning, not an error. ]

Thanks for sticking with this.

peterk900’s picture

I've changed the code as follows...

<?php

$curl = curl_init();

// Your credentials
$username = "<user>";
$password = "<password>";


$encodedAuth = base64_encode($username.":".$password);

curl_setopt($curl, CURLOPT_HTTPHEADER, array("Authorization:Basic ".$encodedAuth));

curl_setopt_array($curl, array(
  CURLOPT_URL => 'https://peter900.example.org/web/node?_format=json',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => '',
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 0,
  CURLOPT_FOLLOWLOCATION => true,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_POSTFIELDS =>'{

   "type":[

      {

         "target_id":"article"

      }

   ],

   "title":[

      {

         "value":"Create node99999"

      }

   ],

   "body":[

      {

         "value":"Node Body",

         "format":"basic_html"

      }

   ]

}',
 
));

$response = curl_exec($curl);

curl_close($curl);
echo $response;

The error message is now...

Symfony\Component\HttpKernel\Exception\NotAcceptableHttpException: No route found for the specified format. Supported formats: html. in Drupal\Core\Routing\RequestFormatRouteFilter->filter() (line 65 of /var/www/vhosts/moormission.org/peter900.example.org/web/core/lib/Drupal/Core/Routing/RequestFormatRouteFilter.php).

Does supported formats refer to the body format basic_html?  I don't see how this can be a problem because there is no error with a Postman post. But interestingly the error type is now "Client Error", not "Access denied". The user remains "Anonymous (not verified)" and $response is {"message":"No route found for the specified format. Supported formats: html."}

The link I quoted in my last post said that some services don't like a space in  Authorization:Basic, so I've made that change.

Does this give any further clues?

P.S  Removing the JSON which sets a body value gives the same error message, so it's not referring to the node's text format. 

peterk900’s picture

Here is an extract of the code which creates nodes in D11.3.1 from JSON submitted by an https post. In this code the articles page is created by a module which takes the JSON and creates the article node, rather than the earlier posts which included the JSON in the cURL script. [This new script is one that worked fine in D10.x with un-encoded credentials, something with D11 doesn't allow.]

$login = "<username>";
$password = "<password>";
$xxx = '{"title":' . $title . ',  "body": ' . $yyy. '}';

$encodedAuth = base64_encode($login.":".$password);


curl_setopt($ch, CURLOPT_URL, 'https://peter9XX.example.org/web/articles');
curl_setopt($ch, CURLOPT_USERPWD, "$login:$password");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xxx);

$headers = array();
$headers[] = 'Content-Type: application/json';
$headers[] = 'Authorization:Basic ".$encodedAuth';

curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

$result = curl_exec($ch);
if (curl_errno($ch)) {
    echo 'Error:' . curl_error($ch);
}
curl_close($ch);

It uses base64 encoded credentials which are included in an array, $headers, along with Content-Type. This array is then processed with the statemement curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);