Menu
Forums
All threads
Latest threads
New posts
Trending threads
New posts
Search forums
Trending
What's new
New posts
New profile posts
Latest activity
Members
Current visitors
New profile posts
Search profile posts
Upgrades
Log in
Register
What's new
Search
Search
Search titles only
By:
All threads
Latest threads
New posts
Trending threads
New posts
Search forums
Menu
Log in
Register
Navigation
Install the app
Install
More options
Contact us
Close Menu
Forums
Software Development
Programming
Programming Q&A
PHP - Need API w/ Authentication
JavaScript is disabled. For a better experience, please enable JavaScript in your browser before proceeding.
You are using an out of date browser. It may not display this or other websites correctly.
You should upgrade or use an
alternative browser
.
Reply to thread
Message
<blockquote data-quote="JayC" data-source="post: 465930" data-attributes="member: 36373"><p>Okay,</p><p></p><p>I am ready for some criticism and feedback - </p><p></p><p>Here is my overall structure:</p><p></p><p>/ api / api.php</p><p>/ api / models / volunteer.php</p><p>/ api / security / authentication.php</p><p></p><p>Here is my Authentication File:</p><p>[CODE]$realm = 'API';</p><p> </p><p> $admins = array('REDACTED' => 'REDACTED', 'REDACTED2' => 'REDACTED2');</p><p></p><p> // function to parse the http auth header</p><p> function http_digest_parse($txt)</p><p> {</p><p> // protect against missing data</p><p> $needed_parts = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1, 'uri'=>1, 'response'=>1);</p><p> $data = array();</p><p> $keys = implode('|', array_keys($needed_parts));</p><p></p><p> preg_match_all('@(' . $keys . ')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@', $txt, $matches, PREG_SET_ORDER);</p><p></p><p> foreach ($matches as $m) {</p><p> $data[$m[1]] = $m[3] ? $m[3] : $m[4];</p><p> unset($needed_parts[$m[1]]);</p><p> }</p><p></p><p> return $needed_parts ? false : $data;</p><p> }</p><p></p><p> if (empty($_SERVER['PHP_AUTH_DIGEST'])) {</p><p> header('HTTP/1.1 401 Unauthorized');</p><p> header('WWW-Authenticate: Digest realm="'.$realm.</p><p> '",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');</p><p></p><p> print "Sorry, you are not authorized to access this area";</p><p> exit;</p><p> }</p><p> </p><p> // analyze the PHP_AUTH_DIGEST variable</p><p> if (!($data = http_digest_parse($_SERVER['PHP_AUTH_DIGEST'])) || !isset($admins[$data['username']]))</p><p> {</p><p> header('HTTP/1.1 401 Unauthorized');</p><p> header('WWW-Authenticate: Digest realm="'.$realm.</p><p> '",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');</p><p> print "Sorry, you are not authorized to access this area";</p><p> exit;</p><p> }</p><p> </p><p> // generate the valid response</p><p> $A1 = md5($data['username'] . ':' . $realm . ':' . $admins[$data['username']]);</p><p> $A2 = md5($_SERVER['REQUEST_METHOD'].':'.$data['uri']);</p><p> $valid_response = md5($A1.':'.$data['nonce'].':'.$data['nc'].':'.$data['cnonce'].':'.$data['qop'].':'.$A2);</p><p> </p><p> if ($data['response'] != $valid_response){</p><p> header('HTTP/1.1 401 Unauthorized');</p><p> header('WWW-Authenticate: Digest realm="'.$realm.</p><p> '",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');</p><p> print "Sorry, you are not authorized to access this area";</p><p> exit;</p><p> }[/CODE]</p><p></p><p>The authentication works great, I am utilizing Postman as [USER=1]@RastaLulz[/USER] mentioned and it really helps to test the system!</p><p></p><p>Now for the API.php file:</p><p>[CODE]<?php</p><p> //Require Authentication</p><p> require_once 'security/authentication.php';</p><p> </p><p> header('Content-Type: application/json'); //Set return type to JSON</p><p> define('IN_INDEX', 1); //Define access to allow global.php to be referenced</p><p> </p><p> //Include Global File</p><p> include_once '../../global.php'; //Access global.php, which will give access to the engine which has the MySQL connection</p><p> </p><p> //Declare Global Variables</p><p> global $engine; //Define engine from the global file</p><p> </p><p> //Include Model Files</p><p> include_once 'models/volunteer.php'; //Include our volunteer model, which will give the blueprint / class for what a volunteer is</p><p> </p><p> //Declare Model Variables</p><p> $volunteer = new Volunteer($engine); //Define an instance of our volunteer</p><p> </p><p> //Get the type of request, current working on just GET requests</p><p> $reqType = '';</p><p></p><p> if(isset($_GET['type'])){</p><p> $reqType = $_GET['type'];</p><p> }else{</p><p> header('HTTP/1.1 400 Bad Request');</p><p> print "Must Define Type Parameter";</p><p> exit;</p><p> }</p><p></p><p> switch(strtoupper($reqType)){</p><p> case "GET":</p><p> {</p><p> HandleGET(); //We established we want to GET, we can call this function to handle our GET options</p><p> }</p><p> break;</p><p> </p><p> case "POST":</p><p> {</p><p> HandlePOST();</p><p> }</p><p> break;</p><p> </p><p> default:</p><p> {</p><p> header('HTTP/1.1 404 Not Found');</p><p> print "The requested Type is not supported";</p><p> exit;</p><p> }</p><p> break;</p><p> }</p><p> </p><p> function HandleGET(){</p><p> global $volunteer; //Get access to our blueprint class</p><p> </p><p> //What is the action we want to GET</p><p> $action = '';</p><p> </p><p> if(isset($_GET['action'])){</p><p> $action = $_GET['action'];</p><p> }else{</p><p> header('HTTP/1.1 400 Bad Request');</p><p> print "Must Define Action Parameter";</p><p> exit;</p><p> }</p><p> </p><p> switch(strtoupper($action)){</p><p> case "VOLUNTEERS": //Sample for returning all of the volunteers in the system</p><p> {</p><p> //Volunteer Query</p><p> $result = $volunteer->read();</p><p> </p><p> $num = mysqli_num_rows($result);</p><p> </p><p> //Check Volunteers Exist</p><p> if($num > 0){</p><p> // Volunteer Array</p><p> $volunteers_arr = array();</p><p> $volunteers_arr['data'] = array();</p><p> </p><p> while($row = mysqli_fetch_array($result)){</p><p> extract($row);</p><p> $volunteer_item = array(</p><p> 'id' => $ID,</p><p> 'fname' => $fname,</p><p> 'lname' => $lname,</p><p> 'email' => $email,</p><p> 'phone' => $phone,</p><p> 'created' => $created</p><p> );</p><p> </p><p> //Push to "data" results</p><p> array_push($volunteers_arr['data'], $volunteer_item);</p><p> }</p><p> </p><p> //Turn result array to JSON</p><p> echo json_encode($volunteers_arr);</p><p> }else{</p><p> //No Posts</p><p> echo json_encode(</p><p> array('message' => 'No Volunteers Found')</p><p> );</p><p> }</p><p> }</p><p> break;</p><p> default:</p><p> {</p><p> header('HTTP/1.1 404 Not Found');</p><p> print "The requested Action is not supported";</p><p> exit;</p><p> }</p><p> break;</p><p> }</p><p> }</p><p> </p><p> function HandlePOST(){</p><p> global $volunteer;</p><p> </p><p> }</p><p>?>[/CODE]</p><p></p><p></p><p>And here is my basic volunteer model class:</p><p>[CODE]<?php</p><p>class Volunteer{</p><p> private $engine;</p><p> private $table = 'volunteers';</p><p> </p><p> //Post Properties</p><p> public $ID;</p><p> public $firstname;</p><p> public $lastname;</p><p> public $email;</p><p> public $phone;</p><p> public $account_create;</p><p> </p><p> // Constructor with DB</p><p> public function __construct($dbEngine){</p><p> $this->engine = $dbEngine;</p><p> }</p><p> </p><p> // Get Volunteers</p><p> public function read(){</p><p> //Create Query</p><p> $query = 'SELECT</p><p> vol.ID as ID,</p><p> vol.firstname as fname,</p><p> vol.lastname as lname,</p><p> vol.email as email,</p><p> vol.phone as phone,</p><p> vol.account_created as created</p><p> FROM</p><p> ' . $this->table . ' vol</p><p> ORDER BY</p><p> vol.account_created DESC';</p><p> </p><p> //Fetch Results</p><p> $results = $this->engine->query($query);</p><p> </p><p> return $results;</p><p> }</p><p>}</p><p>?>[/CODE]</p><p>[automerge]1607790736[/automerge]</p><p>Just going to bump this, as I am on a time frame and want to know if this is following best practices or if I should change how I am implementing this...</p></blockquote><p></p>
[QUOTE="JayC, post: 465930, member: 36373"] Okay, I am ready for some criticism and feedback - Here is my overall structure: / api / api.php / api / models / volunteer.php / api / security / authentication.php Here is my Authentication File: [CODE]$realm = 'API'; $admins = array('REDACTED' => 'REDACTED', 'REDACTED2' => 'REDACTED2'); // function to parse the http auth header function http_digest_parse($txt) { // protect against missing data $needed_parts = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1, 'uri'=>1, 'response'=>1); $data = array(); $keys = implode('|', array_keys($needed_parts)); preg_match_all('@(' . $keys . ')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@', $txt, $matches, PREG_SET_ORDER); foreach ($matches as $m) { $data[$m[1]] = $m[3] ? $m[3] : $m[4]; unset($needed_parts[$m[1]]); } return $needed_parts ? false : $data; } if (empty($_SERVER['PHP_AUTH_DIGEST'])) { header('HTTP/1.1 401 Unauthorized'); header('WWW-Authenticate: Digest realm="'.$realm. '",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"'); print "Sorry, you are not authorized to access this area"; exit; } // analyze the PHP_AUTH_DIGEST variable if (!($data = http_digest_parse($_SERVER['PHP_AUTH_DIGEST'])) || !isset($admins[$data['username']])) { header('HTTP/1.1 401 Unauthorized'); header('WWW-Authenticate: Digest realm="'.$realm. '",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"'); print "Sorry, you are not authorized to access this area"; exit; } // generate the valid response $A1 = md5($data['username'] . ':' . $realm . ':' . $admins[$data['username']]); $A2 = md5($_SERVER['REQUEST_METHOD'].':'.$data['uri']); $valid_response = md5($A1.':'.$data['nonce'].':'.$data['nc'].':'.$data['cnonce'].':'.$data['qop'].':'.$A2); if ($data['response'] != $valid_response){ header('HTTP/1.1 401 Unauthorized'); header('WWW-Authenticate: Digest realm="'.$realm. '",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"'); print "Sorry, you are not authorized to access this area"; exit; }[/CODE] The authentication works great, I am utilizing Postman as [USER=1]@RastaLulz[/USER] mentioned and it really helps to test the system! Now for the API.php file: [CODE]<?php //Require Authentication require_once 'security/authentication.php'; header('Content-Type: application/json'); //Set return type to JSON define('IN_INDEX', 1); //Define access to allow global.php to be referenced //Include Global File include_once '../../global.php'; //Access global.php, which will give access to the engine which has the MySQL connection //Declare Global Variables global $engine; //Define engine from the global file //Include Model Files include_once 'models/volunteer.php'; //Include our volunteer model, which will give the blueprint / class for what a volunteer is //Declare Model Variables $volunteer = new Volunteer($engine); //Define an instance of our volunteer //Get the type of request, current working on just GET requests $reqType = ''; if(isset($_GET['type'])){ $reqType = $_GET['type']; }else{ header('HTTP/1.1 400 Bad Request'); print "Must Define Type Parameter"; exit; } switch(strtoupper($reqType)){ case "GET": { HandleGET(); //We established we want to GET, we can call this function to handle our GET options } break; case "POST": { HandlePOST(); } break; default: { header('HTTP/1.1 404 Not Found'); print "The requested Type is not supported"; exit; } break; } function HandleGET(){ global $volunteer; //Get access to our blueprint class //What is the action we want to GET $action = ''; if(isset($_GET['action'])){ $action = $_GET['action']; }else{ header('HTTP/1.1 400 Bad Request'); print "Must Define Action Parameter"; exit; } switch(strtoupper($action)){ case "VOLUNTEERS": //Sample for returning all of the volunteers in the system { //Volunteer Query $result = $volunteer->read(); $num = mysqli_num_rows($result); //Check Volunteers Exist if($num > 0){ // Volunteer Array $volunteers_arr = array(); $volunteers_arr['data'] = array(); while($row = mysqli_fetch_array($result)){ extract($row); $volunteer_item = array( 'id' => $ID, 'fname' => $fname, 'lname' => $lname, 'email' => $email, 'phone' => $phone, 'created' => $created ); //Push to "data" results array_push($volunteers_arr['data'], $volunteer_item); } //Turn result array to JSON echo json_encode($volunteers_arr); }else{ //No Posts echo json_encode( array('message' => 'No Volunteers Found') ); } } break; default: { header('HTTP/1.1 404 Not Found'); print "The requested Action is not supported"; exit; } break; } } function HandlePOST(){ global $volunteer; } ?>[/CODE] And here is my basic volunteer model class: [CODE]<?php class Volunteer{ private $engine; private $table = 'volunteers'; //Post Properties public $ID; public $firstname; public $lastname; public $email; public $phone; public $account_create; // Constructor with DB public function __construct($dbEngine){ $this->engine = $dbEngine; } // Get Volunteers public function read(){ //Create Query $query = 'SELECT vol.ID as ID, vol.firstname as fname, vol.lastname as lname, vol.email as email, vol.phone as phone, vol.account_created as created FROM ' . $this->table . ' vol ORDER BY vol.account_created DESC'; //Fetch Results $results = $this->engine->query($query); return $results; } } ?>[/CODE] [automerge]1607790736[/automerge] Just going to bump this, as I am on a time frame and want to know if this is following best practices or if I should change how I am implementing this... [/QUOTE]
Insert quotes…
Verification
Post reply
Forums
Software Development
Programming
Programming Q&A
PHP - Need API w/ Authentication
Top