HMAC Base64 Authentication? - php

I have no clue what's even going on in this but I am attempting to use an API and they have yet another different authentication standard called HMAC with Sha384 to base64.
This is the example provided:
class ICObenchAPI {
private $privateKey = 'private-key';
private $publicKey = 'public-key';
private $apiUrl = 'https://icobench.com/api/v1/';
public $result;
public function getICOs($type = 'all', $data = ''){
return $this->send('icos/' . $type, $data);
}
public function getICO($icoId, $data = ''){
return $this->send('ico/' . $icoId, $data);
}
public function getOther($type){
return $this->send('other/' . $type, '');
}
private function send($action, $data){
$dataJson = json_encode($data);
$sig = base64_encode(hash_hmac('sha384', $dataJson, $this->privateKey, true));
$ch = curl_init($this->apiUrl . $action);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POSTFIELDS, $dataJson);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json',
'Content-Length: ' . strlen($dataJson),
'X-ICObench-Key: ' . $this->publicKey,
'X-ICObench-Sig: ' . $sig)
);
$reply = curl_exec($ch);
$ff = $reply;
$reply = json_decode($reply,true);
if(isset($reply['error'])){
$this->result = $reply['error'];
return false;
}else if(isset($reply['message'])){
$this->result = $reply['message'];
return true;
}else if(isset($reply)){
$this->result = json_encode($reply);
return true;
}else{
$this->result = htmlspecialchars($ff);
return false;
}
}
public function result(){
return $this->result;
}
}
I'm looking to take the PHP example provided and turn it into a nodeJS script, just really don't know where to start. I've looked at crypto-js and others but just don't comperhend what specifically is happening in the request to make since of what i'm even writing

Crypto-js is the good way to do.
You need to first encrypt your data and then Base64 it to create a signature used in header
let dataJSON = JSON.stringify(data);
let sign = CryptoJS.HmacSHA384(dataJSON, this.privateKey);
sign = CryptoJS.enc.Base64.stringify(sign);
I pushed on github a working example : ICObenchAPI.js

I wrote a Node js wrapper library called node-icobench. You are welcome to use it.
npm install node-icobench
Here is a sneak peek to the HMAC part with a few alterations for the sake of this example:
const crypto = require('crypto');
// Stringify POST data
let jsonData = JSON.stringify(data);
// Create HMAC based on algo and private key
let hmac = crypto.createHmac('sha384', privateKey);
// Create HMAC Digest of json data
hmac.update(jsonData);
// return Base64 encoding of HMAC
let signedData = hmac.digest('base64');

Related

CURL Post Request to API Looping Through Database

I've been trying to select values (students data) from mysql database table and looping through database to send to an API using PHP CURL Post request but it's not working.
This is the API body:
{
"students":[
{
"admissionNumber": "2010",
"class":"js one"
},
{
"admissionNumber": "2020",
"class":"ss one"
}
],
"appDomain":"www.schooldomain.com"
}
Parameters I want to send are "admissionNumber" and "class" parameters while "appDomain" is same for all. Here's my code:
if(isset($_POST['submit'])){
$body = "success";
$info = "yes";
class SendDATA
{
private $url = 'https://url-of-the-endpoint';
private $username = '';
private $appDomain = 'http://schooldomain.com/';
// public function to commit the send
public function send($admNo,$class)
{
$url_array= array('admissionNumber'=>$admNo,'class'=>$class,'appDomain'=>$this-> appDomain);
$url_string = $data = http_build_query($url_array);
// using the curl library to make the request
$curlHandle = curl_init();
curl_setopt($curlHandle, CURLOPT_URL, $this->url);
curl_setopt($curlHandle, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curlHandle, CURLOPT_POSTFIELDS, $url_string);
curl_setopt($curlHandle, CURLOPT_POST, 1);
$responseBody = curl_exec($curlHandle);
$responseInfo = curl_getinfo($curlHandle);
curl_close($curlHandle);
return $this->handleResponse($responseBody,$responseInfo);
}
private function handleResponse($body,$info)
{
if ($info['http_code']==200){ // successful submission
$xml_obj = simplexml_load_string($body);
// extract
return true;
}
else{
// error handling
return false;
}
}
}
$sms = new SendDATA();
$result = mysqli_query( $mysqli, "SELECT * FROM school_kids");
while ($row = mysqli_fetch_array($result)) {
$admNo = $row['admNo'];
$class = $row['class'];
$sms->send($admNo,$class,"header");
echo $admNo. " ".$class;
}
}
The question is rather unclear; when you say "this is the API body", I presume this JSON fragment is what the REST API at https://url-of-the-endpoint expects. If so, you are building your request body wrong. http_build_query creates an URL-encoded form data block (like key=value&anotherKey=another_value), not a JSON. For a JSON, here's what you want:
$data = array('students' => array
(
array('admissionNumber' => $admNo, 'class' => $class)
),
'appDomain':$this->appDomain
);
$url_string = $data = json_encode($data);
Also, you probably want to remove the HTTP headers from the response:
curl_setopt($curlHandle, CURLOPT_HEADER, false);

Using Postmark with PHP

Hi I'm new to php and postmark and am trying to get a form submission set to my email. I have the email working however I can't get it to show the header("Location: thanks.php) page. Any help would be greatly appreciated. Thanks.
require("postmark.php");
$postmark = new Postmark("API KEY","calvin.hemington#example.com","$email");
if($postmark->to("calvin.hemington#example.com")->subject("Mission Woodshop | " . $name)->plain_message($email_body)->send()){
exit;
}
header("Location: thanks.php");
exit;
<?php
/**
* This is a simple library for sending emails with Postmark created by Matthew Loberg (http://mloberg.com)
*/
class Postmark{
private $api_key;
private $data = array();
function __construct($apikey,$from,$reply=""){
$this->api_key = $apikey;
$this->data["From"] = $from;
$this->data["ReplyTo"] = $reply;
}
function send(){
$headers = array(
"Accept: application/json",
"Content-Type: application/json",
"X-Postmark-Server-Token: {$this->api_key}"
);
$data = $this->data;
$ch = curl_init('http://api.postmarkapp.com/email');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$return = curl_exec($ch);
$curl_error = curl_error($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
// do some checking to make sure it sent
if($http_code !== 200){
return false;
}else{
return true;
}
}
function to($to){
$this->data["To"] = $to;
return $this;
}
function subject($subject){
$this->data["subject"] = $subject;
return $this;
}
function html_message($body){
$this->data["HtmlBody"] = "<html><body>{$body}</body></html>";
return $this;
}
function plain_message($msg){
$this->data["TextBody"] = $msg;
return $this;
}
function tag($tag){
$this->data["Tag"] = $tag;
return $this;
}
}
Presumably $postmark->send() returns true when it works. Your if/then statement says 'exit when the send succeeds'.
If you move the header() call into the if/then it should work as expected. You'll also want to handle the case where the $postmark->to call fails, possibly redirect to error page at that point.
It may be easier to use our new officially supported lib which gives full details on responses for API calls. http://developer.postmarkapp.com/developer-official-libs.html#php

How to send and receive data via HTTP header using cURL?

I am trying to build a simple API to allow a client to send me data over HTTPS.
I created a class that will take a username/password then it does a database look up. If the user is found then it issues a token. Then the token will be send back to the requester via HTTP header.
Once a username, password and a token sent back then the script reads the data sent from the client via $_POST request and processes it.
The challenge that I am having is sending the token to the requester via cURL and receiving the USERNAME, PASSWORD & TOKEN from the HTTP header correctly.
My question is how can I correctly send the token via HTTP header in the generateToken() method? Also how can I read the HTTP headers once the request is made?
Below is my class: api.php file
<?php
require('../classes/connection.php');
class api {
private $user_name;
private $user_password;
private $user_token;
private $db;
private $keepAlive = 120; //2 minutes = 120 seconds
private $authorizes = false;
private $token = '';
private $ch;
private $user_ready = false;
function api($database, $server){
//establish a database connection
$this->db = new connection($database, $server);
$this->ch = curl_init();
//read user_name, password, token from the header and set it
if(isset($_SERVER['API-User-Name']))
$this->user_name = $_SERVER['API-User-Name'];
if(isset($_SERVER['API-User-Password']))
$this->user_password = $_SERVER['API-User-Password'];
if(isset($_SERVER['API-User-Token']))
$this->user_token = $_SERVER['API-User-Token'];
//check if the user is allowed
if( $this->authenticateAccess() === true ){
$this->authorizes = true;
//ensure the token is valid otherwise generate a new token
if( $this->isValidToken() )
$this->user_ready = true;
else
$this->generateToken();
}
}
//return weather to process the send data
public function isUserReady(){
return $this->user_ready;
}
//return weather the user is authorized
private function isAutherized(){
return $this->authorizes;
}
//return the set token
private function getToken(){
return $this->token;
}
//check if the requester is authorized to access the system
private function authenticateAccess(){
//unauthorized old session
$this->unautherizeExpiredTokens();
if( $this->ch === false)
return false;
if( empty($this->user_name) || empty($this->user_password) )
return false;
//ensure HTTPS is used
if( !isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != 'on')
return false;
//read the user information
$get_user = $this->db->getDataSet('SELECT ip_addreses, user_password, token_expires_on, current_token
FROM api_users
WHERE user_name = ?
LIMIT 1', array($this->user_name));
if( count($get_user) != 1)
return false;
$data = $get_user[0];
//remove bad values if any
$ip_addreses = preg_replace("/[^0-9,.]/", "", $data['ip_addreses']);
$allowed_ips = explode(',', $ip_addreses);
//ensure the IP address is allowed
if( !isset($_SERVER['REMOTE_ADDR']) || !in_array($_SERVER['REMOTE_ADDR'], $allowed_ips) )
return false;
//check if the password is valid
if( password_verify($this->password, $data['user_password'] ) )
return true;
else
return false;
}
//check if the token is valid
private function isValidToken(){
if( !$this->isAutherized() )
return false;
//unauthorized old session
$this->unautherizeExpiredTokens();
if( empty($this->user_token) )
return false;
$get_user = $this->db->getDataSet('SELECT token_expires_on, current_token
FROM api_users
WHERE user_name = ? AND current_token = ?
LIMIT 1', array($this->user_name, $this->user_token ));
if( count($get_user) != 1)
return false;
$data = $get_user[0];
if( empty($data['token_expires_on']) || $data['current_token'] != $this->user_token )
return false;
//make sure that the token is not expired
if( !empty($data['token_expires_on']) && time() > $data['token_expires_on'])
return false;
}
//generate a new token
private function generateToken(){
//generate a token
$token = md5(uniqid(mt_rand(), true));
//set expiration date for this token
$expire_on = time() + $this->keepAlive;
//Save the new token in the database with expiration time = $this->keepAlive seconds
$update = $this->db->processQuery('UPDATE api_users
SET current_ip = ?,
current_token = ?,
token_expites_on = ?
WHERE user_name = ?', array($_SERVER['REMOTE_ADDR'], $token, $expire_on ));
//if the token is saved in the database then send the new token via cURL header.
if($update){
//set the token as a header value and then sent it back to the requester.
$this->token = $token;
$curl_header = array();
$curl_header[] = 'API-User-Token: ' . $token;
curl_setopt($this->ch, CURLOPT_HEADER, true);
curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($this->ch, CURLOPT_HTTPHEADER, $curl_header);
return $token;
} else
return false;
}
//remove old tokens
private function unautherizeExpiredTokens(){
$this->db->processQuery('UPDATE api_users
SET current_ip = NULL,
current_token = NULL,
token_expites_on = NULL
WHERE token_expites_on IS NOT NULL AND token_expites_on <= ?', array( time() ) );
}
}
?>
And to use this class I would do the following form the API access link. Once I figure out how to ready the http data then there will be no need to pass the $username and $password to the class instead it will be ready in the class from the header.
Therefore, the access.php file will look like the following
include('api.php');
$request = new api('database_name','serverIPaddress');
if( $request->isUserReady() ){
//process transaction all transactions
$_POST['notes']; //// take the data validated it and then insert into the database
echo 'Bingo!';
} else {
echo 'You are not authorized to use this API';
}
?>
To use this API the client will have to call it like so client.php file will looks like this:
<?php
$curl_header = array();
$curl_header[] = 'API-User-Name: test';
$curl_header[] = 'API-User-Password: password';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://mydomainname.com/api/access.php");
//curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, $curl_header);
$result = curl_exec($ch);
print_r($result);
curl_close($ch);
?>
Just use CURLOPT_HTTPHEADER:
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'X-THING_ONE: abcdefghijklmnopqrstuvwxyz',
'X-THING_TWO: 12345678910'
));
I prefer to set the header as an array outside of the curl_setopt like this:
$curl_headers = array();
$curl_headers[] = 'X-THING_ONE: abcdefghijklmnopqrstuvwxyz';
$curl_headers[] = 'X-THING_TWO: 12345678910';
curl_setopt($ch, CURLOPT_HTTPHEADER, $curl_headers);
EDIT: Okay, it looks like you know how CURLOPT_HEADER works. But looking at your code there seems to be a typo right here.
curl_setopt($this->ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($this->ch, CURLOPT_HTTPHEADER, array('API-Token: ' . $this->getToken() ));
Why do you have $this->ch in CURLOPT_HEADER & CURLOPT_HTTPHEADER but just $ch for CURLOPT_RETURNTRANSFER? Shouldn’t that be like this?
curl_setopt($this->ch, CURLOPT_HEADER, true);
curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($this->ch, CURLOPT_HTTPHEADER, array('API-Token: ' . $this->getToken() ));
EDIT: In addition to that typo, it seems that the original poster needs to know how to get the parameters on the receiving side:
Also how can I read the HTTP headers once the request is made?
Easy. They are accessed via $_SERVER predefined variable in PHP. So you would grab them like this:
$_SERVER['X-API-User-Name'];
$_SERVER['X-API-User-Password'];
$_SERVER['X-API-User-Token'];
And you can check what is passed while debugging by doing this:
echo '<pre>';
print_r($_SERVER);
echo '</pre>';

No response from twitter api with request_token

I have been trying to follow the steps laid out in the docs for twitter sign in here: https://dev.twitter.com/docs/auth/implementing-sign-twitter
My Code:
$oauth_consumer_secret = '***';
$access_token_secret = '***';
$oauth_consumer_key = '***';
$oauth_nonce = createNonce();
$oauth_signature_method = 'HMAC-SHA1';
$oauth_time = time();
$oauth_token = '***';
$oauth_version = '1.0';
$oauth = array(
'oauth_callback' => '***',
'oauth_consumer_key'=>$oauth_consumer_key,
'oauth_nonce'=>$oauth_nonce,
'oauth_signature_method'=>$oauth_signature_method,
'oauth_timestamp'=>$oauth_time,
'oauth_token'=>$oauth_token,
'oauth_version'=>$oauth_version
);
$baseURI = 'https://api.twitter.com/1.1/oauth/request_token';
$baseString = buildBaseString($baseURI,$oauth);
$compositeKey = getCompositeKey($oauth_consumer_secret,null);
$oauth_signature = base64_encode(hash_hmac('sha1', $baseString, $compositeKey, true));
$oauth['oauth_signature'] = $oauth_signature; //add the signature to our oauth array
$header = array(buildAuthorizationHeader($oauth));
$login = loginUser($baseURI,$header);
echo $login;
function loginUser($baseURI,$header){
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $baseURI);
curl_setopt($ch, CURLOPT_HEADER, TRUE);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
$output = curl_exec($ch);
curl_close($ch);
if ($output!=''){
return $output;
} else {
return 'fail';
};
};
function buildBaseString($baseURI,$params){
$r = array(); // temp array
ksort($params); // sorts params alphabetically by key
foreach($params as $key=>$value){
$r[] = '$key='.rawurlencode($value);
};
return 'POST&'.rawurlencode($baseURI).'&'.rawurlencode(implode('&', $r)); // returns complete base string
};
// Create composite key
function getCompositeKey($consumerSecret,$requestToken){
return rawurlencode($consumerSecret) . '&' . rawurlencode($requestToken);
};
function createNonce(){
$characters = 'abcdefghijklmnopqrstuvwxyz0123456789';
$string = '';
for ($i=0; $i<32; $i++) {
$string .= $characters[rand(0, strlen($characters) - 1)];
};
return $string;
};
function buildAuthorizationHeader($oauth){
$r = 'Authorization: OAuth '; //header prefix
$values = array(); //temporary key=value array
foreach($oauth as $key=>$value)
$values[] = "$key=\"" . rawurlencode($value) . "\""; //encode key=value string
$r .= implode(', ', $values); //reassemble
return $r; //return full authorization header
};
The Problem I am having is that I am getting no response what so ever! So the login function just keeps returning 'fail'.
When I change curlopt_ssl_verifypeer to false I get a HTTP/1.1 401 Unauthorized error.
Any help or clues would be appreciated.
The SSL and OAuth issues are most likely separate.
As for SSL, your certificate authoririty (CA) bundle is most likely out of date. You can either tell curl to not verify the peer CURLOPT_SSL_VERIFYPEER = 0 or update the CA bundle. You can download a current CA bundle here. Most people (myself included) just turn off VERIFYPEER. Although this practice should be discouraged, it's a common solution.
When generating the request token, you do not need oauth_token in your oauth parameters. You are asking for a request token, you don't have one yet. Not sure if this matters, but only use ',' as the delimiter, not ', ' as in $r .= implode(', ', $values); //reassemble
I looked through the rest of your implementation and it looks right. Having written my own, I can appreciate the difficulty here.

Gotomeeting php api(oauth) implementation

I am trying to create a php gotomeating api implementation. I successfully got the access_token but for any other requests I get error responses. This is my code:
<?php
session_start();
$key = '#';
$secret = '#';
$domain = $_SERVER['HTTP_HOST'];
$base = "/oauth/index.php";
$base_url = urlencode("http://$domain$base");
$OAuth_url = "https://api.citrixonline.com/oauth/authorize?client_id=$key&redirect_uri=$base_url";
$OAuth_exchange_keys_url = "http://api.citrixonline.com/oauth/access_token?grant_type=authorization_code&code={responseKey}&client_id=$key";
if($_SESSION['access_token']) CreateForm();else
if($_GET['send']) OAuth_Authentication($OAuth_url);
elseif($_GET['code']) OAuth_Exchanging_Response_Key($_GET['code'],$OAuth_exchange_keys_url);
function OAuth_Authentication ($url){
$_SESSION['access_token'] = false;
header("Location: $url");
}
function CreateForm(){
$data = getURL('https://api.citrixonline.com/G2M/rest/meetings?oauth_token='.$_SESSION['access_token'],false);
}
function OAuth_Exchanging_Response_Key($code,$url){
if($_SESSION['access_token']){
CreateForm();
return true;
}
$data = getURL(str_replace('{responseKey}',$code,$url));
if(IsJsonString($data)){
$data = json_decode($data);
$_SESSION['access_token'] = $data->access_token;
CreateForm();
}else{
echo 'error';
}
}
/*
* Helper functions
*/
/*
* checks if a string is json
*/
function IsJsonString($str){
try{
$jObject = json_decode($str);
}catch(Exception $e){
return false;
}
return (is_object($jObject)) ? true : false;
}
/*
* CURL function to get url
*/
function getURL($url,$auth_token = false,$data=false){
// Initialize session and set URL.
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
// Set so curl_exec returns the result instead of outputting it.
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
if($auth_token){
curl_setopt($curl, CURLOPT_HTTPHEADER, array('Authorization: OAuth oauth_token='.$auth_token));
}
if($data){
curl_setopt($ch, CURLOPT_POST,true);
$d = json_encode('{ "subject":"test", "starttime":"2011-12-01T09:00:00Z", "endtime":"2011-12-01T10:00:00Z", "passwordrequired":false, "conferencecallinfo":"test", "timezonekey":"", "meetingtype":"Scheduled" }');
echo implode('&', array_map('urlify',array_keys($data),$data));
echo ';';
curl_setopt($ch, CURLOPT_POSTFIELDS,
implode('&', array_map('urlify',array_keys($data),$data))
);
}
// Get the response and close the channel.
$response = curl_exec($ch);
/*
* if redirect, redirect
*/
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($code == 301 || $code == 302) {
preg_match('/<a href="(.*?)">/', $response, $matches);
$newurl = str_replace('&','&',trim(array_pop($matches)));
$response = getURL($newurl);
} else {
$code = 0;
}
curl_close($ch);
return $response;
}
function urlify($key, $val) {
return urlencode($key).'='.urlencode($val);
}
to start the connect process you need to make a request to the php file fith send=1. I tryed diffrent atempts to get the list of meetings but could not get a good response.
Did anybody had prev problems with this or know of a solution for this?
Edit:
This is not a curl error, the server responds with error messages, in the forums from citrix they say it should work, no further details on why it dosen't work, if I have a problem with the way I implemented the oauth or the request code. The most comon error I get is: "error code:31305" that is not documented on the forum.
[I also posted this on the Citrix Developer Forums, but for completeness will mention it here as well.]
We are still finalizing the documentation for these interfaces and some parameters which are written as optional are actually required.
Compared to your example above, changes needed are:
set timezonekey to 67 (Pacific time)
set passwordrequired to false
set conferencecallinfo to Hybrid (meaning: both PSTN and VOIP will be provided)
Taking those changes into account, your sample data would look more like the following:
{"subject":"test meeting", "starttime":"2012-02-01T08:00:00",
"endtime":"2012-02-01T09:00:00", "timezonekey":"67",
"meetingtype":"Scheduled", "passwordrequired":"false",
"conferencecallinfo":"Hybrid"}
You can also check out a working PHP sample app I created: http://pastebin.com/zE77qzAz

Categories