# 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 ` where the `` is acquired through the [Login endpoint](/products/partner-api/reference/security/login_api_v1_login__post). 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: ```bash 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](/products/partner-api/reference/security/login_api_v1_login__post) with the username and password as parameters in the request: ```bash 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: ```json { "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: ```bash export TOKEN="your-token" ``` To do the same through Python, install the requests module with `pip install requests` and then run this snippet: ```python 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 ` ## Idempotency A uuid for the idempotency header is easy to come by. In a terminal, run: ```bash uuidgen ``` to generate a unique uuid for the header. The output should look like this: ```bash 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: ```python 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: ` ## 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 [Pocket data](/products/partner-api/reference/pockets/list-pockets-v2) 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: ```bash get_idempotency() { \ local idempotency = $(uuidgen); # On MacOS, uuidgen is uppercase. # This line makes it lowercase. local idempotency = "$(tr "[A-Z]" "[a-z]" <<< "$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: ```bash send_jiko_request GET "/api/v1/agreements/" ``` To do the same thing in Python, create similar functions and run them with the following code: ```python 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. ### Postman request signing When using [Postman](https://getpostman.com) to test requests against the sandbox Partner API, request signatures can be automated using a pre-request script. The following script dynamically generates the required headers for request signing. To use this script, ensure that the `secret` environment variable is set to the shared secret provided by the Jiko team. ```js // fetch the shared secret from the currently active Postman environment const secret = pm.environment.get('secret') const enc = new TextEncoder('utf-8') let idempotencyKey = crypto.randomUUID() let signature = await createSignature(idempotencyKey) let headers = [ ['x-jiko-idempotency', idempotencyKey], ['x-jiko-signature', signature], ] headers.forEach(([key, value]) => pm.request.addHeader({ key, value })) /** * Utility functions below */ async function createSignature(idempotency) { let path = pm.variables.replaceIn(pm.request.url).getPath() let body = pm.variables.replaceIn(pm.request.body).toString() if (pm.request.body.mode === 'raw') { pm.request.body.raw = body } let hmacKey = await createHmac(secret) return await digestMessage(hmacKey, idempotency + path + body) } async function createHmac(secret) { return await crypto.subtle.importKey( 'raw', enc.encode(secret), { name: 'HMAC', hash: { name: 'SHA-256' }, }, false, ['sign', 'verify'] ) } async function digestMessage(key, message) { const hashBuffer = await crypto.subtle.sign('HMAC', key, enc.encode(message)) const hashArray = new Uint8Array(hashBuffer) return btoa(String.fromCharCode.apply(null, hashArray)) } ```