Auth

This guide walks you through authenticating with our API. Every request must be correctly authenticated with a token, must be safe to resend if a package is dropped, and must be protected against modification and token capture via signatures. Additionally, all requests must use https as the API does not accept plaintext requests over http.

These 3 HTTP headers secure every request to the API:

  1. An Authorization HTTP header containing the value Bearer <access_token> where the <access_token> is acquired through the Login endpoint.

  2. An x-jiko-idempotency HTTP header set to a random uuid for this action, in order to signify a unique request to the API from the Partner’s perspective. This allows you to send the same request multiple times if the first response gets lost in transit.

  3. An x-jiko-signature HTTP header set to a base64 encoded HMAC-SHA256 hash of x-jiko-idempotency+request pathname+body using the shared secret as a key. This shared secret is never sent along with the requests, so even if a token is captured, any malicious requests will be dropped on signature mismatch.

Authorization Header

If you haven't already, reach out to your Jiko point-of-contact to receive a username, password, and shared signing secret. Your company will also be assigned a API URL prefix.

To fetch a token, start by setting your username, password, and signing secret (used later) as environment variables using the following snippet:

export JIKO_USER='your-username';
export JIKO_PASS='your-password';
export JIKO_SIGNING_SECRET='your-signing-secret';
export PREFIX='your-prefix';

Then, send a POST request to Login with the username and password as parameters in the request:

curl -s "https://$PREFIX.sandbox-api.jikoservices.com/api/v1/login/" \
-H 'Content-type: application/json' \
-d '{
 "username": "'$JIKO_USER'",
 "password": "'$JIKO_PASS'"
}'

Your response should look like this:

{
  "token":"your-token",
  "expires":"2012-12-12T12:12:12.946661+00:00"
}

This token will be used in the rest of the tutorial, so save it to your environment:

export TOKEN="your-token"

To do the same through Python, install the requests module with pip install requests and then run this snippet:

import os
import requests

prefix = os.environ["PREFIX"]
root = "https://$PREFIX.sandbox-api.jikoservices.com"
endpoint = "/api/v1/login/"
url = root + endpoint # The endpoint variable is used later when signing the request
body = {"username": os.environ["JIKO_USER"], "password": os.environ["JIKO_PASS"]}
request = requests.post(url, json=body)
response = request.json()

print(response)
token = response["token"]

This token is valid for 1 hour, and the expiration time is also provided in the response. Now that you’ve acquired a token, it can be used to construct the Authorization header like so: Authorization: Bearer <access_token>

Idempotency

A uuid for the idempotency header is easy to come by. In a terminal, run:

uuidgen

to generate a unique uuid for the header. The output should look like this:

0FA3047F-7364-47AF-A679-D391018B79C4

This program is pre-installed on MacOS and most Linux systems, but if it’s missing on your system, install it with sudo apt-get install uuid-runtime

In Python, you can run this snippet:

import uuid

x_jiko_idempotency = str(uuid.uuid4())

print(x_jiko_idempotency)

Now that you’ve acquired a uuid, it can be used to construct the x-jiko-idempotency header like so: x-jiko-idempotency: <uuid>

Request Signing (Signature)

Now that you’ve got an authorized and idempotent request ready to send, the final step is to sign the request using the shared signing secret. To sign a request, we’re going to need a HTTP body and a simple hello-world endpoint to send our first request. In this case, we’ll be fetching the Account data for a dummy customer that is automatically created in the sandbox environment for all partners.

For the terminal, we need to create a few functions to help us:

get_idempotency() { \
  local idempotency = $(uuidgen);
  # On MacOS, uuidgen is uppercase.
  # This line makes it lowercase.
  local idempotency = "$(tr "[A-Z]" "[a-z]" &#x3C;&#x3C;&#x3C; "$idempotency")";
  echo $idempotency;
}

get_signature() { \
  local endpoint = $1;
  local body = $2;
  local idempotency = $3;

  local hash_string = "$idempotency$endpoint$body";
  local signature = $(echo -n $hash_string | openssl sha256 -binary -hmac $JIKO_SIGNING_SECRET | base64);
  echo $signature
}

send_jiko_request() { \
  local method = $1;
  local endpoint = $2;
  local body = ${3:-"{}"};
  idempotency = $(get_idempotency);
  curl --request $method \
  --url https://$PREFIX.sandbox-api.jikoservices.com$endpoint \
  --header "Authorization: Bearer $TOKEN" \
  --header 'Content-Type: application/json' \
  --header "x-jiko-idempotency: $idempotency" \
  --header "x-jiko-signature: $(get_signature $endpoint $body $idempotency)" \
  --data $body \
  | jq
}

Now, we can finally send our first real request:

send_jiko_request GET "/api/v1/agreements/"

To do the same thing in Python, create similar functions and run them with the following code:

import base64
import hashlib
import hmac
import json

def get_idempotency():
  idempotency = str(uuid.uuid4())
  return idempotency

def get_signature(idempotency: str, endpoint: str, body: str):
  hash_string = idempotency + endpoint + body
  hash_input = bytes(hash_string, "utf-8")
  signing_secret = bytes(os.environ["JIKO_SIGNING_SECRET"], "utf-8")
  hashed_signature = hmac.new(
      key=signing_secret, msg=hash_input, digestmod=hashlib.sha256
  ).digest()
  signature = base64.b64encode(hashed_signature)
  return signature

customer_id = "c26ed6d6-cdd0-41a3-ab54-84597309ae3a"
endpoint = "/api/v1/customers/" + customer_id + "/jiko-accounts/"
url = root + endpoint
body = {}
# Omitting the body gives the same result, but this code example
# is general and works both with and without a body

headers = {}
headers["Authorization"] = "Bearer " + token
headers["x-jiko-idempotency"] = get_idempotency()
headers["x-jiko-signature"] = get_signature(
  headers["x-jiko-idempotency"], endpoint, json.dumps(body)
)

resp = requests.get(url, json=body, headers=headers)

print(resp.text)

Congratulations! You’ve taken the first step to offering Jiko money storage and movement to your customers.


Last updated: June 7, 2023