Using WSSE

You can get your integration credentials from your profile in Penneo here:


Once you have them, you're almost ready to start sending requests.
Before you do, you have to generate a new set of authentication headers for every API request that you make.

The header looks like this:

X-WSSE: UsernameToken Username="_your_key_", PasswordDigest="_digest_", Nonce="_nonce_", Created="2015-10-19T10:22:35Z"

It contains your username, a calculated Password Digest, a nonce, and the timestamp at which the digest was created.

🚧

Created is a timestamp that should be recent, as we allow a skew of +-60s of the real time. Any Created that is outside of the +-60s time-frame will automatically be rejected. Given this limitation, we recommend consuming the generated Nonce, Created and Digest right after their generation, without delay. Please make sure the real time on your machine is up to date. The format for Created should be ISO 8601 format (e.g., 2024-01-15T12:30:00.000Z).

❗️

The same Created can be used for one API call. Replaying the same timestamp with the same any other Nonce and Digest, will result in rejection of the request.

Requirements for the nonce
You'll need to calculate the value used for PasswordDigest. In order to do that, you'll need to create a nonce. The nonce can be any random string with the following requirements:

  • It should be unique for every request
  • It needs to be a string not longer than 64 chars

Generating the headers

Here are some examples of how you can generate the authentication headers using WSSE:

function generateHeaders($api_key, $api_secret) {
    $rawNonce = random_bytes(8);
    $nonce = base64_encode($rawNonce);

    $created = gmdate("Y-m-d\\TH:i:s.v\\Z");

    $digest = base64_encode(
        sha1($rawNonce . $created . $api_secret, true)
    );

    return [
        "Authorization: WSSE profile=\"UsernameToken\"",
        "X-WSSE: UsernameToken Username=\"{$api_key}\", PasswordDigest=\"{$digest}\", Nonce=\"{$nonce}\", Created=\"{$created}\"",
        "Accept: application/json",
        "Content-Type: application/json",
    ];
}
def generate_headers(key, secret):
    timestamp = datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%SZ")
    nonce = generate_nonce()
    digest = '{}{}{}'.format(base64.b64decode(nonce).decode('utf-8'), timestamp, secret)
    hashed_digest = hashlib.sha1(bytes(digest, 'utf-8')).digest()
    encoded_hashed_digest = base64.b64encode(hashed_digest).decode('utf-8')

    username_token = 'UsernameToken Username="{}", PasswordDigest="{}", Nonce="{}", Created="{}"'.format(key, encoded_hashed_digest, nonce, timestamp)

    headers = {
        'Authorization': 'WSSE profile="UsernameToken"',
        'X-WSSE': username_token,
        'Accept-charset': 'utf-8',
        'Accept': 'application/json',
        'Content-Type': 'application/json'
    }

    return headers
// Have look here:
// https://github.com/Penneo/sdk-net/blob/df7e1d523570b189147039e6555044a64826ed58/Src/Penneo/Connector/WSSEAuthenticator.cs#L49
// @see: https://www.npmjs.com/package/wsse
const { UsernameToken } = require('wsse');

// Generates headers to be used in every authenticated request
function generateHeaders(key, secret) {
    const token = new UsernameToken({
        username: key,
        password: secret
    });
    return {
        'Authorization': 'WSSE profile="UsernameToken"',
        'X-WSSE': 'UsernameToken ' + token.getWSSEHeader({nonceBase64: true}),
        'Accept-charset':  'utf-8',
        "Accept":  'application/json',
        'Content-Type': 'application/json'
    };
};

/*
    Example

    request.get('api/v1/casefiles', {
        headers: generateHeaders('myAPIKey', 'myAPISecret')
    });
*/