BillRun supports OAuth2 standard since version 5.13.
For creating an OAuth2 client id & secret, you can go to General Settings→Security tab and create client.
Once you received id & secret, you can now populate temporary token and use it for API authentication.
URL: http://<HOST>/oauth2/token
Parameters can be under header user & password or request client_id & client_secret, accordingly to Name and Secret Key in the UI.
In addition, need to add the request grant_type variable with a value of client_credentials.
Examples as follow:
$ curl -X POST -u crm:3b3a53c8ab1f7de6256706ad0106d455 -d "grant_type=client_credentials" "http://billrun/oauth2/token"
or http request style:
$ curl -X POST -d "grant_type=client_credentials&client_id=crm&client_secret=3b3a53c8ab1f7de6256706ad0106d455" "http://billrun/oauth2/token"
Response should be in the following format:
{"access_token":"1abe535b260d170f24eb06f08e56cb7d639d3208","expires_in":3600,"token_type":"Bearer","scope":null}
Once you've received the token, you also received the expiration period under expires_in field.
For using the token, you can put in API request header or in the API request data. See example as follow:
$ curl --header 'Authorization: Bearer 1abe535b260d170f24eb06f08e56cb7d639d3208' "http://billrun/billapi/rates/uniqueget?query=%5B%5D"
or http request style:
$ curl "http://billrun/billapi/rates/uniqueget?query=%5B%5D&access_token=1abe535b260d170f24eb06f08e56cb7d639d3208"
The token will be expired in 1 hour, and the API response will be with failure and header 401 Unauthorized (instead of 200 OK). By this header response client can trigger re-generation of new token.
Client examples:
Old API authentication is not recommended if you're using 5.13 and above.
This mechanism is deprecated and will be removed on version 6; instead, please use OAuth2 mechanism
This mechanism signed the http request using the HMAC algorithm to prevent non-genuine API requests.
BillRun adds a layer of security to the BillRun API’s.
The added layer of security protects the integrity of the data received by the BillRun API.
Just like you would sign a receipt at the supermarket to prove that you have made the purchase, BillRun asks its vendors to sign their API calls so we can protect the integrity of our vendors data.
In order to digitally sign data you need to be in possession of a ‘secret’.
The initial secret is available after registration under "Settings --> General Settings --> Security". Multiple secrets can be created and managed.
Before sending data to the BillRun API, prepare a JSON representation of your request. Note that all the JSON values have to be of “string” type. The vendor also needs to add the unix timestamp of the current date under the field name _t_ in the request data.
After inserting the new value to the request, the signature value is calculated using the current BillRun signature method (currently HMAC), and stores the resulting value under the field name _sig_ in the request body.
The current method of signing is HMAC.
To use the HMAC algorithm you need a secret (key), the data to be signed (JSON representation of the request) and a hash algorithm.
BillRun API’s use sha512 as the hashing algorithm.
Instead of the signature that uses an asymmetric encryption, HMAC is a symmetric algorithm which uses a shared secret.
The shared secret is kept safely both by the tenant (Business Customers), and in the BillRun server.
The request received for the API stores the signature under the field name _sig_, and the epoch timestamp value of ‘now’, under the field name ‘_t_’.
The epoch time stamp makes it impossible for API requests to be recorded and retransmitted by a malicious attacker.
After validation has succeeded, the _sig_ and t values are removed from the API request parameters.
You can read more here:
https://en.wikipedia.org/wiki/Hash-based_message_authentication_code
Now = time_now();
Request[‘_t_’] = Now; (variable must be string! Please casting if this is unix timestamp)
Sig = binary_to_hex(Sign(secret, Request, “sha512”)); (results in a string containing 128 characters)
Request[‘_sig_’] = Sig;
$request_data = array(
"Date" => "2016-12-08 11:56:01",
"Call_Extension" => "1111",
"Call_Agent" =>"JOHN BAR",
"Call_Phonenumber" => "123456789",
"Call_Status" => "ANSWERED",
"Billsec" => "15",
"Call_Type" => "Outgoing Call"
);
$realtime_request = array(
"file_type" => "Some_input_processor_name",
"request" => json_encode($request_data)
);
$realtime_request["_t_"] = (string) time();
// Note at this stage $realtime_request has 3 attributes
$realtime_request["_sig_"] = hash_hmac("sha512", json_encode($realtime_request), $secret);
// Note at this stage $realtime_request has 4 attributes
send the http request to next url =>
"https://<BILLRUN HOST>/realtime?" . http_build_query($realtime_request)
event = {
"Date" => "2016-12-08 11:56:01",
"Call_Extension" => "1111",
"Call_Agent" =>"JOHN BAR",
"Call_Phonenumber" => "123456789",
"Call_Status" => "ANSWERED",
"Billsec" => "15",
"Call_Type" => "Outgoing Call"
}
_t = Time.now.to_i.to_s
event_json = event.to_json
realtime_request = {}
realtime_request['file_type'] = 'your_input_processor_name'
realtime_request['request'] = event_json
realtime_request['_t_'] = _t
digest = OpenSSL::Digest.new('sha512')
sig = OpenSSL::HMAC.hexdigest(digest, secret, realtime_request.to_json)
@url = "https://<BILLRUN_HOST>/realtime?file_type=your_input_processor_name&request=#{event_json}&_t_=#{_t}&_sig_=#{sig}"
import requests
import hashlib
import hmac
import json
import time
secret = b'secretkey'
timestamp = str(int(time.time()))
page = '0';
size = '5';
params = '{"_t_":"' + timestamp + '","page":"' + page + '","size":"' + size + '"}'
message = str.encode(params)
signing = hmac.new(secret, message, hashlib.sha512)
sig = signing.hexdigest()
getParams = '_t_=' + timestamp + '&page=' + page + '&size=' + size + '&_sig_=' + sig
url = 'http://<billrun_host>/billapi/plans/get?' + getParams
r = requests.get(url)
import { createHmac } from 'crypto';
body = Object.assign(body, { _t_: Date.now().toString() });
const secret = credentials.secret as string;
body = Object.assign(body, { _sig_: createHmac('sha512', secret).update(JSON.stringify(body)).digest('hex') });
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.Date;
public class HttpRequest {
private final String USER_AGENT = "Mozilla/5.0";
public static void main(String[] args) throws Exception {
Mac sha512_HMAC = null;
String result = null;
String key = "secretsecret1234";
Date date = new Date();
long num = date.getTime();
String timestamp = Long.toString(num);
String size = "100";
String page = "0";
JSONObject query = new JSONObject();
query.put("name", "ALL");
JSONObject message = new JSONObject();
message.put("size", size);
message.put("query", query.toString());
message.put("_t_", timestamp);
message.put("page", page);
byte[] byteKey = key.getBytes("ASCII");
final String HMAC_SHA256 = "HmacSHA512";
sha512_HMAC = Mac.getInstance(HMAC_SHA256);
SecretKeySpec keySpec = new SecretKeySpec(byteKey, HMAC_SHA256);
sha512_HMAC.init(keySpec);
byte[] mac_data = sha512_HMAC.doFinal(message.toString().getBytes("ASCII"));
result = bytesToHex(mac_data).toLowerCase();
message.put("_sig_", result);
System.out.println(message.toString());
HttpRequest http = new HttpRequest();
System.out.println("Testing 1 - Send Http GET request");
http.sendGet(message);
}
// HTTP GET request
// shared secret = secretsecret1234
private void sendGet(JSONObject message) throws Exception {
String params = httpBuildQuery(message);
String url = "http://billrun/billapi/plans/get?" .concat(params);
URL obj = new URL(url);
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
// optional default is GET
con.setRequestMethod("GET");
// add request header
con.setRequestProperty("User-Agent", USER_AGENT);
int responseCode = con.getResponseCode();
System.out.println("\nSending 'GET' request to URL : " + url);
System.out.println("Response Code : " + responseCode);
BufferedReader in = new BufferedReader(
new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
// print result
System.out.println(response.toString());
}
public static String bytesToHex(byte[] bytes) {
final char[] hexArray = "0123456789ABCDEF".toCharArray();
char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
public static String httpBuildQuery(JSONObject message) throws JSONException {
JSONArray keys = message.names();
String httpBuildQuery = "";
for (int i = 0; i < keys.length(); ++i) {
String key = keys.getString(i); // Here's your key
String value = message.getString(key);
httpBuildQuery = httpBuildQuery.concat(key).concat("=").concat(value);
if (i + 1 < keys.length()) {
httpBuildQuery = httpBuildQuery.concat("&");
}
}
return httpBuildQuery;
}
}
Here are sample usages of HMAC in different languages:
To securely use the API we added a digital signature on each API call.
A request should have the values:
The signature validation process occurs as follow: