Unkown error while trying to create Send via API using php script

Hello,

I’m currently developing a php script which aims to connect to the Bitwarden API and then create a SEND based on the data entered by the user. The script is currently able to connect to the API, however I can’t create a SEND. I am trying to retrieve the error when running the script, however, I am not getting any errors.
So I would like some help to know where I made an error.
I think the error may be in the data passed in the request or the url used in the request is not the right one.
If anyone could shed some light on this, I would appreciate it.

Here is a part of my script (I use object because this script will become a plugin):

public function createSend($recipient, $message) {
        // Get access token
        if (!$this->access_token) {
            $this->getAccessToken();
        }
        
        // Create send item
        $data = [
            "type" => 3,
            "name" => "My secure send",
            "notes" => "This is a secure send via Bitwarden",
            "fields" => [
                [
                    "name" => "Recipient",
                    "value" => $recipient,
                    "type" => 0
                ],
                [
                    "name" => "Message",
                    "value" => $message,
                    "type" => 1
                ]
            ]
        ];
        $headers = [
            "Authorization: Bearer " . $this->access_token,
            "Content-Type: application/json"
        ];
        $ch = curl_init();
        curl_setopt_array($ch, array(
            CURLOPT_URL => "https://api.bitwarden.com/object/send",
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POST => true,
            CURLOPT_POSTFIELDS => json_encode($data),
            CURLOPT_HTTPHEADER => $headers,
            CURLOPT_SSL_VERIFYPEER => false, // Disable SSL certificate verification
        ));
        $response = curl_exec($ch);
        $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        if ($http_code >= 400) {
            die("Error creating Bitwarden 'send' item: " . $response);
        }
        $item_data = json_decode($response, true);
        $item_id = $item_data["id"];
        return $item_id;
    }

I also used “https://api.bitwarden.com/items” as url, but this doesn’t work.

Also, if anyone knows how to verify the SSL certificate without getting an error indicating that the certificate is self-signed, I’m interested.

Thanks.

Hello @antoinemahieu and welcome to the community,

Curious are you trying to integrate the Bitwarden public API into your PHP script for creating Send or the Vault Management API which is used for creating and managing individual vault items.

More about the Vault Management API can be found in the Swagger on the Bitwarden website.

Hope this helps :slightly_smiling_face:

1 Like

Hello @cksapp,
I’m trying to integrate the Vault management API into my PHP script to create a SEND using the API. My goal is to turn this script into a plugin so that I can create a SEND from another application. I’m creating my plugin step by step by first creating php functions that make calls to the Bitwarden API.
I’ve already been able to read the documentation on APIs, however I’m stuck on the API call to create the SEND.
I have no error feedback which doesn’t help.
I noticed that in the vault API docs there is no url indicated whereas in the public API docs the url is indicated.

Do I have an error in my script?
Or am I doing it wrong?

Thanks.

As described in above the help documentation, the Bitwarden Public API is only for managing some Organizational aspects such as members, collections, groups, event logs, and policies.

Due to the end-to-end encryption method, vault items have to be managed locally with the use of the Vault Management API.
This is done with the CLI, which can serve on localhost for your local API calls and managing Vault items, such as those for an individual vault or an Organizational vault. As well as creating individual secure Sends.


On a side note, this is partly why Sends are structured the way they are, so while something like the Send Password option only programmatically prevents someone from viewing the data in a Send it absolutely has nothing to do with the encryption of the Send.
As I understand Bitwarden Sends are stored similar to standard Vault Items completely encrypted end-to-end within the database, and only unencrypted on the client side once opened.

With a Send like
https://send.bitwarden.com/#9b2brHZCfE-wmq_BAOoVBg/YB22EhJX_7jRfU_qB77skw
The first portion is most likely the GUID for the item, with the portion after / being used for the encryption.
One could provide the Send link https://send.bitwarden.com/#9b2brHZCfE-wmq_BAOoVBg without the remaining portion and provide the /YB22EhJX_7jRfU_qB77skw via some out of bands communication, i.e. one via email and the other via verbal or phone communication.
(Though likely for most non-technical users the Password option would suffice, and all this would be over-kill.)

This is also why the Send link
https://send.bitwarden.com/#9b2brHZCfE-wmq_BAOoVBg/YB22EhJX_7jRfU_qB77skw and https://send.bitwarden.com/#9b2brHZCfE-wmq_BAOoVBg/YB22EhJX_7jRfU_qB77skW are not the same, as even removing or changing a single character, such as the “W” does not allow the Send to load properly.

1 Like

@cksapp I have some questions about what you just said:

  • Does this mean that I have to use Bitwarden CLI to create my sends?

  • The url I have to use is https://send.bitwarden.com/?

  • What is the GUID?

For information, I authenticate to the API using the clientId and clientSecret provided by Bitwarden. Here is the code allowing me to authenticate to the API (get the access token):

public function getAccessToken(){
        $curl =curl_init();

        $data = [
            "grant_type" => "client_credentials",
            "scope" => "api.organization",
            "client_id" => $this->client_id,
            "client_secret" => $this->client_secret,
        ];
        $headers = [
            'Content-Type: application/x-www-form-urlencoded',
        ];

        curl_setopt_array($curl, array(
            CURLOPT_URL => 'https://identity.bitwarden.com/connect/token',
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_HTTPHEADER => $headers,
            CURLOPT_POST => true,
            CURLOPT_POSTFIELDS => http_build_query($data),
        ));
        $result = json_decode(curl_exec($curl),true);

        if(!isset($result["access_token"]))
        {
            self::codeTreatment(curl_getinfo($curl,CURLINFO_HTTP_CODE));
        }
        else{
            $this->access_token = $result["access_token"];
            $this->attributes[] = $this->access_token;
            return $this->access_token;
        }
        
    }

Thanks.

Sorry if my tangent was a bit off topic and may have added further confusion.

The GUID is simply an identifier for an encrypted item within Bitwarden’s database. Individual or Organization vault items can use the GUID to link direct to an item for those who have existing access in a Vault for that item.
Being that Sends are public and also short-lived its likely that the process for storing these items on Bitwarden’s side is a bit different, but the security of the E2E encryption is just as strong.


Correct, you can either use the CLI to directly create Sends and have this scripted, or alternatively use the CLI to “host” or serve your own local vault management API endpoint, which you can then make calls against.
A bit more can be found here Bringing a RESTful API to the Bitwarden CLI | Bitwarden Blog

As described once you run the serve command from the Bitwarden CLI, this will provide a local endpoint where you can make REST API calls to. But this can be specified as described.

By default, serve will bind your API web server to localhost however you can specify an alternate hostname with the --hostname option. API requests can only be made from the bound hostname.

Unfortunately I am not super familiar with PHP, so I couldn’t assist much with your code but I hope this information about the individual vault management API and how to get that going can help to get you started.

1 Like

@cksapp I will try to do as you advised. I will not put your message in solution for the moment in the event that I am still blocked. This will avoid recreating a request.

Thank you for your help, I will keep you informed of the results.

(I will be unavailable next week)

Hello,

I have just resumed the development of my project and I am stuck on the authentication with the API keys on CLI. Is it possible to pass the API keys in the login command as arguments?

Example: bw login [client_id] [client_secret] --apikey

I don’t want to have to pass an environment variable or anything else in the command.

In my plugin, the client_id and client_secret are retrieved from a database filled by the user via a web interface.

Thanks

Just getting to this, from my testing it doesn’t appear so. In my brief testing the only way seems to be through the use of an env variable as discussed in Password Manager CLI | Bitwarden Help Center

While not super helpful :face_with_diagonal_mouth:, I am wondering if there would be a method to have your PHP script grab the API credentials from the database and set these environment variables.
Presumably you’ll also need to pass either --passwordenv or --passwordfile to Unlock the CLI or use a session token to fully create a Send

2 Likes

Hello @cksapp ,

If it is not possible to use API keys in this way, I will use email and password based authentication.

Thanks for your help.