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);
?>