Authentication

We currently support 2 methods of authentication for use with the API.

  • WSSE Authentication
  • JSON Web Tokens

The JWT (JSON web token) method generates a relatively short lived token, so you'd need to refresh it often. For this reason, we recommend using the WSSE method when integrating with Penneo. If you choose to use an SDK, authentication will generally be handled for you. Have a look at the relevant SDK documentation for details.

WSSE Authentication

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.

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($key, $secret)
{
    $nonce   = hash('sha512', uniqid(true));
    $created = date('r');
    $digest  = base64_encode(sha1(base64_decode($nonce) . $created . $secret, true));

    return [
        'Authorization'  => 'WSSE profile="UsernameToken"',
        'X-WSSE'         => "UsernameToken Username=\"{$key}\", PasswordDigest=\"$digest\", Nonce=\"$nonce\", Created=\"$created\"",
        'Accept-charset' => 'utf-8',
        "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')
    });
*/

Helper utilities

You can have a look at this utility that helps generate the wsse header:

http://www.teria.com/~koseki/tools/wssegen/
(Keep in mind that the nonce is base64 encoded in the above utility)

You can even get a WSSE Chrome plugin (here: https://chrome.google.com/webstore/category/extensions )

Note: You should not input production passwords into untrusted websites. Use only when testing on Sandbox.

JSON Web Tokens

This method is mainly used in cases when you ask users to log in each time they want to interact with the Penneo platform. The users login with their credentials which can be classic credentials i.e. username and password, and a Json Web Token (JWT) is generated that can be used as an access token in the subsequent requests.

Consider the example for creating a JWT when using classic credentials i.e. username and password:

https://app.penneo.com/auth/api/v1/token/password
Content-Type: application/json
{
  "username":"<your username here>",
  "password":"<your password here>"
}

You'll get a token back that you can use when building requests.

πŸ“˜

Remember to use the correct authentication headers when using JWT

X-Auth-Token: `<your token here>`
Authorization: JWT
Accept: application/json

Handling expired JWT

JWT have an expiration date which is why they are suitable for use when a user has to manually login. However, if you want to use them for a service e.g. robot user performing tasks on behalf of others, it is probably easiest to rely on WSSE credentials. However, if you prefer using JWTs, you need to create a refresh token which has a longer expiration date. You can use this refresh token to generate a new JWT when it expires.

Considering that you want to generate a refresh token when you are using nemid, you can create a refresh token by calling the following endpoint:

https://app.penneo.com/auth/api/v1/token/nemid
{
  "signature":"<base64 encoded signature>",
  "refresh": true
}

You can use the refresh token to create a new JWT, which you can use in subsequent requests:

https://app.penneo.com/auth/api/v1/token/refresh
Content-Type: application/json
{
  "token": "<refresh_token>"
}

πŸ“˜

Refresh tokens also expire

Refresh tokens can expire if no JWT is generated for a month