ReportingCloud

Authentication

Topic

Every request to the ReportingCloud Web API carries an Authorization header: an API key as ReportingCloud-APIKey <key>, or the user name and password of the account as Basic <Base64 string>. When the service can't use that header, it doesn't process the request: a request without the header gets status 500 with a message that names no cause, and a key or password the service doesn't know gets 403 Forbidden with a short plain-text message. Start with the test below, then find the response or message you see among the problems that follow.

Test the Credentials on Their Own

A 403 response from a document request doesn't always mean that the credentials are wrong: the Web API also returns it when a quota is used up or too many requests run at the same time. To rule the credentials in or out, send them to GET /v1/account/settings. The endpoint returns the quotas of the account and creates no document:

curl -i -H "Authorization: ReportingCloud-APIKey $REPORTINGCLOUD_API_KEY" \
  https://api.reporting.cloud/v1/account/settings

The option -i prints the status line and the response headers before the body. The status tells you where to go next:

See What the Request Sends

Before you change the credentials, check that the request carries them. The option -v of curl prints every request header on a line that starts with >:

curl -v -H "Authorization: ReportingCloud-APIKey $REPORTINGCLOUD_API_KEY" \
  https://api.reporting.cloud/v1/account/settings
  1. Find the line that starts with > Authorization:. It holds ReportingCloud-APIKey, one space and the key, or Basic, one space and the encoded credentials.

  2. If the line ends after ReportingCloud-APIKey, the variable is empty in the shell that ran curl, and the service answers 403. printenv REPORTINGCLOUD_API_KEY prints the key, or nothing when the variable isn't set.

  3. Set the variable and send the request again:

    export REPORTINGCLOUD_API_KEY='<API key>'
    
  4. When your application sends the request, inspect its headers the same way, for example in a log of the HTTP client. A request without an Authorization header has no credentials; see the next section.

The output of -v contains the key in plain text. Keep it out of tickets, chat messages and logs: anybody who has the key can use the quota of your account.

A Request Without Credentials Returns 500

A request returns status 500 with a short JSON body that doesn't name a cause:

HTTP/2 500
content-type: application/json; charset=utf-8

{"Message":"An error has occurred."}

The request reached the Web API without an Authorization header, with an empty one, or with one that isn't the authorization method, a space and the credentials, such as ReportingCloud-APIKey: followed by the key. A request without credentials gets 500, not 403; see Requests Without Credentials.

  1. Add the header to every request: -H "Authorization: ReportingCloud-APIKey $REPORTINGCLOUD_API_KEY" with curl. The SDKs build it from the credentials you pass to their constructor; Authentication shows both header forms and the SDK calls.
  2. Check with curl -v that the header is sent, as See What the Request Sends describes.
  3. Send the request again. If it now returns 403, the header arrives but the credentials aren't accepted.
  4. If the header reads ReportingCloud-APIKey <key> or Basic <Base64 string> and the request still returns 500, check the request body, which Errors and Status Codes lists among the causes of a 500, and look for a reported incident on the status page, as Check the Service Status describes.

Requests Return 403 Forbidden

Every request, GET /v1/account/settings included, returns 403 with a plain-text body:

HTTP/2 403
content-type: text/plain; charset=utf-8

Invalid username/password combination or invalid APIKey

The Authorization header arrived, but the service doesn't know the key or the user name and password in it. Forbidden describes the response. The common causes are:

  • The header isn't in the expected form. The value is the method name ReportingCloud-APIKey, one space and the key. A key in quotes, another method name or an empty key gets the same 403.
  • The key is incomplete. API keys are random strings of more than 40 characters. A key that lost characters, or gained a line break or a space when it was copied, isn't the same key.
  • The key was deleted. A key removed with Delete Key under Manage Account in the portal, or with DELETE /v1/account/apikey, no longer authorizes requests.
  • The .NET SDK got no key. When the environment variable isn't set, Environment.GetEnvironmentVariable returns null. The ReportingCloud constructor accepts it, and the SDK then sends Basic authentication with an empty user name and password, Authorization: Basic Og==, which gets the same 403.
  • The Basic credentials don't match. See the next section.

To fix it:

  1. Sign in to the portal, open Manage Account and compare your key with the list of API keys.
  2. If the key isn't in the list, click Create Key and store the new key where your application reads it, for example in the environment variable REPORTINGCLOUD_API_KEY. An account has up to 10 API keys; API Keys describes how to manage them.
  3. Send the test request of Test the Credentials on Their Own again.

Basic Authentication Fails

A request with Authorization: Basic ... returns 403 with Invalid username/password combination or invalid APIKey, while the same account works with an API key or used to work with the same code.

Basic authentication sends the user name and password of the ReportingCloud account, so it fails in these situations:

  • The password has changed. Every application that still sends the old password is refused. API keys don't depend on the password.
  • The credentials are encoded incorrectly. The value after Basic is the string username:password encoded with Base64 on a single line, as Authentication shows. With the base64 command of GNU coreutils, two slips change the result: echo adds a line break that is encoded with the credentials, and base64 wraps its output after 76 characters.
  • The .NET SDK changes characters outside ASCII. It encodes the user name and password as ASCII, so it sends every other character, such as ä or €, as ?, and the credentials no longer match.

The difference is visible in the result:

# Encodes the line break that echo adds: dXNlcjpwYXNzCg==
echo 'user:pass' | base64

# Encodes only the credentials, on one line: dXNlcjpwYXNz
printf '%s' 'user:pass' | base64 -w 0

To fix it:

  1. Let curl build the header: the option -u 'username:password' sends Authorization: Basic with the correctly encoded credentials.
  2. When you build the header yourself, encode the credentials without a line break and without wrapping, as the second command above does.
  3. After a password change, update the credentials in every application that uses Basic authentication. Better, replace them with an API key, as API Keys describes. With the .NET SDK and a password that has characters outside ASCII, an API key is the fix.

The PHP SDK Reports Missing Credentials

A PHP script that uses the SDK textcontrol/textcontrol-reportingcloud stops before it sends a request, with this message of TextControl\ReportingCloud\Exception\InvalidArgumentException:

Either the API key, or username and password must be set for authorization

Or it stops when it creates the ReportingCloud object, with a TypeError whose message begins:

TextControl\ReportingCloud\AbstractReportingCloud::setApiKey(): Argument #1 ($apiKey) must be of type string, false given

The SDK builds the Authorization header itself. It throws the first exception when neither an API key nor both a user name and a password are set, for example because the key is an empty string. The TypeError appears when the variable isn't set at all: getenv() then returns false, and the SDK accepts only a string as api_key. Both messages were reproduced with version 4.0.5 of the SDK.

  1. Set the environment variable REPORTINGCLOUD_API_KEY for the process that runs PHP, not only in your shell.

  2. Check the value before you create the object, so that a missing key gives a clear message:

    <?php
    
    declare(strict_types=1);
    
    require_once __DIR__ . '/vendor/autoload.php';
    
    use TextControl\ReportingCloud\ReportingCloud;
    
    $apiKey = getenv('REPORTINGCLOUD_API_KEY');
    
    if (false === $apiKey || '' === $apiKey) {
        exit('Set the environment variable REPORTINGCLOUD_API_KEY.' . PHP_EOL);
    }
    
    $reportingCloud = new ReportingCloud([
        'api_key' => $apiKey,
    ]);
    
  3. Run the script again. Once the key is set, a refused key returns 403 like any other client, as Requests Return 403 Forbidden describes.