From c2298a81f901d7110a8b1f9927d62e653a6b59c8 Mon Sep 17 00:00:00 2001
From: Sibidharan Nandhakumar <hello@sibidharan.me>
Date: Sun, 16 May 2021 16:52:50 +0530
Subject: [PATCH] Auth done

---
 api/apis/auth/current.php | 26 ++++++++++++
 api/apis/auth/login.php   |  7 ++++
 api/apis/auth/refresh.php | 11 +++--
 api/index.php             | 80 +++++++++++++++++++++++++++++-------
 api/lib/Auth.class.php    | 24 +++++++++--
 api/lib/OAuth.class.php   | 85 +++++++++++++++++++++++++++++++++------
 call.php                  |  7 +++-
 7 files changed, 203 insertions(+), 37 deletions(-)
 create mode 100644 api/apis/auth/current.php

diff --git a/api/apis/auth/current.php b/api/apis/auth/current.php
new file mode 100644
index 0000000..a53c0fa
--- /dev/null
+++ b/api/apis/auth/current.php
@@ -0,0 +1,26 @@
+<?php
+
+${basename(__FILE__, '.php')} = function(){
+    if($this->get_request_method() == "POST" and $this->isAuthenticated()){
+        try{
+            $data = [
+                "username" => $this->getUsername(),
+            ];
+            $data = $this->json($data);
+            $this->response($data, 200);
+        } catch(Exception $e){
+            $data = [
+                "error" => $e->getMessage()
+            ];
+            $data = $this->json($data);
+            $this->response($data, 403);
+        }
+
+    } else {
+        $data = [
+            "error" => "Bad request"
+        ];
+        $data = $this->json($data);
+        $this->response($data, 400);
+    }
+};
\ No newline at end of file
diff --git a/api/apis/auth/login.php b/api/apis/auth/login.php
index 7a4e345..4d4ea49 100644
--- a/api/apis/auth/login.php
+++ b/api/apis/auth/login.php
@@ -1,6 +1,13 @@
 <?php
 
 ${basename(__FILE__, '.php')} = function(){
+    if($this->isAuthenticated()){
+        $data = [
+            "error" => "Already logged in"
+        ];
+        $data = $this->json($data);
+        $this->response($data, 400);
+    }
     if($this->get_request_method() == "POST" and isset($this->_request['username']) and isset($this->_request['password'])){
         $username = $this->_request['username'];
         $password = $this->_request['password'];
diff --git a/api/apis/auth/refresh.php b/api/apis/auth/refresh.php
index 7a4e345..78ed561 100644
--- a/api/apis/auth/refresh.php
+++ b/api/apis/auth/refresh.php
@@ -1,14 +1,13 @@
 <?php
 
 ${basename(__FILE__, '.php')} = function(){
-    if($this->get_request_method() == "POST" and isset($this->_request['username']) and isset($this->_request['password'])){
-        $username = $this->_request['username'];
-        $password = $this->_request['password'];
+    if($this->get_request_method() == "POST" and isset($this->_request['refresh_token'])){
+        $refresh_token = $this->_request['refresh_token'];
         try {
-            $auth = new Auth($username, $password);
+            $auth = new OAuth($refresh_token);
             $data = [
-                "message" => "Login success",
-                "tokens" => $auth->getAuthTokens()
+                "message" => "Refresh Success",
+                "tokens" => $auth->refreshAccess()
             ];
             $data = $this->json($data);
             $this->response($data, 200);
diff --git a/api/index.php b/api/index.php
index 124689a..2990de0 100644
--- a/api/index.php
+++ b/api/index.php
@@ -12,6 +12,7 @@ class API extends REST {
     
     private $db = NULL;
     private $current_call;
+    private $auth = null;
     
     public function __construct(){
         parent::__construct();                  // Init parent contructor
@@ -31,27 +32,70 @@ class API extends REST {
         else {
             if(isset($_GET['namespace'])){
                 $dir = $_SERVER['DOCUMENT_ROOT'].'/api/apis/'.$_GET['namespace'];
-                $methods = scandir($dir);
-                //var_dump($methods);
-                foreach($methods as $m){
-                    if($m == "." or $m == ".."){
-                        continue;
-                    }
-                    $basem = basename($m, '.php');
-                    //echo "Trying to call $basem() for $func()\n";
-                    if($basem == $func){
-                        include $dir."/".$m;
-                        $this->current_call = Closure::bind(${$basem}, $this, get_class());
-                        $this->$basem();
-                    }
+                $file = $dir.'/'.$func.'.php';
+                if(file_exists($file)){
+                    include $file;
+                    $this->current_call = Closure::bind(${$func}, $this, get_class());
+                    $this->$func();
+                } else {
+                    $this->response($this->json(['error'=>'method_not_found']),404);
                 }
+
+                /** 
+                 * Use the following snippet if you want to include multiple files
+                 */
+                // $methods = scandir($dir);
+                // //var_dump($methods);
+                // foreach($methods as $m){
+                //     if($m == "." or $m == ".."){
+                //         continue;
+                //     }
+                //     $basem = basename($m, '.php');
+                //     //echo "Trying to call $basem() for $func()\n";
+                //     if($basem == $func){
+                //         include $dir."/".$m;
+                //         $this->current_call = Closure::bind(${$basem}, $this, get_class());
+                //         $this->$basem();
+                //     }
+                // }
             } else {
                 //we can even process functions without namespace here.
-                $this->response($this->json(['error'=>'methood_not_found']),404);
+                $this->response($this->json(['error'=>'method_not_found']),404);
             }
         }
     }
 
+    public function auth(){
+        $headers = getallheaders();
+        if(isset($headers['Authorization'])){
+            $token = explode(' ', $headers['Authorization']);
+            $this->auth = new Auth($token[1]);
+        }
+    }
+
+    public function isAuthenticated(){
+        if($this->auth == null){
+            return false;
+        }
+        if($this->auth->getOAuth()->authenticate() and isset($_SESSION['username'])){
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    public function getUsername(){
+        return $_SESSION['username'];
+    }
+
+    public function die($e){
+        $data = [
+            "error" => $e->getMessage()
+        ];
+        $data = $this->json($data);
+        $this->response($data,400);
+    }
+
     public function __call($method, $args){
         if(is_callable($this->current_call)){
             return call_user_func_array($this->current_call, $args);
@@ -131,5 +175,11 @@ class API extends REST {
 // Initiiate Library
 
 $api = new API;
-$api->processApi();
+try {
+    $api->auth();
+    $api->processApi();
+} catch (Exception $e){
+    $api->die($e);
+}
+
 ?>
\ No newline at end of file
diff --git a/api/lib/Auth.class.php b/api/lib/Auth.class.php
index 6c964c2..84ed180 100644
--- a/api/lib/Auth.class.php
+++ b/api/lib/Auth.class.php
@@ -10,6 +10,7 @@ class Auth {
     private $db;
     private $isTokenAuth = false;
     private $loginTokens = null;
+    private $oauth;
 
     public function __construct($username, $password = NULL){
         $this->db = Database::getConnection();
@@ -24,7 +25,8 @@ class Auth {
         }
 
         if($this->isTokenAuth){
-            throw new Exception("Not Implemented");
+            $this->oauth = new OAuth($this->token);
+            $this->oauth->authenticate();
         } else {
             $user = new User($this->username);
             $hash = $user->getPasswordHash();
@@ -33,19 +35,35 @@ class Auth {
                 if(!$user->isActive()){
                     throw new Exception("Please check your email and activate your account.");
                 }
-                $this->loginTokens = $this->addSession();
+                $this->loginTokens = $this->addSession(7200);
             } else {
                 throw new Exception("Password Mismatch");
             }
         }
     }
 
+    /**
+     * Returns the username of authenticated user
+     */
+    public function getUsername(){
+        if($this->oauth->authenticate()){
+            return $this->oauth->getUsername();
+        } else {
+            return "a";
+        }
+    }
+
+    public function getOAuth(){
+        return $this->oauth;
+    }
+
     public function getAuthTokens(){
         return $this->loginTokens;
     }
 
     private function addSession(){
-        $oauth = new OAuth($this->username);
+        $oauth = new OAuth();
+        $oauth->setUsername($this->username);
         $session = $oauth->newSession();
         return $session;
     }
diff --git a/api/lib/OAuth.class.php b/api/lib/OAuth.class.php
index 856bbc3..3bc8cc0 100644
--- a/api/lib/OAuth.class.php
+++ b/api/lib/OAuth.class.php
@@ -6,29 +6,82 @@ require_once($_SERVER['DOCUMENT_ROOT'].'/api/lib/User.class.php');
 
 class OAuth {
     private $db;
-    private $refresh_token;
-    private $access_token;
+    private $refresh_token = null;
+    private $access_token = null;
     private $valid_for = 7200;
     private $username;
+    private $user;
 
-    public function __construct($username, $refresh_token = NULL){
-        $this->refresh_token = $refresh_token;
+    /**
+     * Can construct without refresh token for new session
+     * Can construct with refresh token for refresh session
+     */
+    public function __construct($token = NULL){
         $this->db = Database::getConnection();
+        if($token != NULL){
+            if($this->startsWith($token, 'a.')){
+                $this->access_token = $token;
+            } else if($this->startsWith($token, 'r.')){
+                $this->refresh_token = $token;
+            } else {
+                $this->setUsername($token);
+            }
+        }
+    }
+
+    public function setUsername($username){
         $this->username = $username;
-        $u = new User($this->username);
+        $this->user = new User($this->username);
+    }
+
+    public function getUsername(){
+        return $this->username;
     }
 
-    public function newSession($valid_for = 7200){
+    public function authenticate(){
+        if($this->access_token != null){
+            $query = "SELECT * FROM apis.session WHERE access_token = '$this->access_token';";
+            $result = mysqli_query($this->db, $query);
+            if($result){
+                $data = mysqli_fetch_assoc($result);
+                $created_at = strtotime($data['created_at']);
+                $expires_at = $created_at + $data['valid_for'];
+
+                if(time() <= $expires_at){
+                    if (session_status() === PHP_SESSION_NONE) {
+                        session_start();
+                    }
+                    $this->username = $_SESSION['username'] = $data['username'];
+                    $_SESSION['token'] = $this->access_token;
+                    return true;
+                } else {
+                   throw new Exception("Expired token");
+                }
+            } else {
+                throw new Exception(mysqli_error($this->db));
+            }
+        }
+    }
+
+    public function newSession($valid_for = 7200, $reference_token = 'auth_grant'){
+        if($this->username == NULL){
+            throw new Exception("Username not set for OAuth");
+        }
         $this->valid_for = $valid_for;
-        $this->access_token = Auth::generateRandomHash(32);
-        $this->refresh_token = Auth::generateRandomHash(32);
+        $this->access_token = 'a.'.Auth::generateRandomHash(32);
+        if($reference_token == 'auth_grant'){
+            $this->refresh_token = 'r.'.Auth::generateRandomHash(32);
+        } else {
+            $this->refresh_token = 'd.'.Auth::generateRandomHash(16);
+        }
         $query = "INSERT INTO `apis`.`session` (`username`, `access_token`, `refresh_token`, `valid_for`, `reference_token`) 
-                                        VALUES ('$this->username', '$this->access_token', '$this->refresh_token', $this->valid_for, 'auth_grant');";
+                                        VALUES ('$this->username', '$this->access_token', '$this->refresh_token', $this->valid_for, '$reference_token');";
         if(mysqli_query($this->db, $query)){
             return array(
                 "access_token" => $this->access_token,
                 "valid_for" => $this->valid_for,
                 "refresh_token" => $this->refresh_token,
+                "reference_token" => $reference_token,
                 "type" => 'api'
             );
         } else {
@@ -37,19 +90,27 @@ class OAuth {
     }
 
     public function refreshAccess(){
-        if($this->refresh_token){
+        if($this->refresh_token != NULL and !$this->startsWith($this->refresh_token, 'd.')){
             $query = "SELECT * FROM apis.session WHERE refresh_token = '$this->refresh_token';";
             $result = mysqli_query($this->db, $query);
             if($result){
                 $data = mysqli_fetch_assoc($result);
+                $this->username = $data['username'];
                 if($data['valid'] == 1){
-
+                    return $this->newSession(7200, $this->refresh_token);
                 } else {
                     throw new Exception("Expired token");
                 }
             } else {
-                throw new Exception("Invalid request");
+                throw new Exception("Error: "+mysqli_error($this->db));
             }
+        } else {
+            throw new Exception("Invalid request");
         }
     }
+
+    private function startsWith ($string, $startString){
+        $len = strlen($startString);
+        return (substr($string, 0, $len) === $startString);
+    }
 }
\ No newline at end of file
diff --git a/call.php b/call.php
index 0a9707b..160a46c 100644
--- a/call.php
+++ b/call.php
@@ -46,10 +46,15 @@ class Superhero {
 }
 
 $hero = new Superhero("Batman");
+echo date_default_timezone_get();
+echo date("Y-m-d h:i:s a", time());
 echo $hero->getName()."\n";
 echo $hero->get_powers();
+//session_start();
+$_SESSION['hello'] = 'world';
+print_r($_SESSION);
 
-var_dump($_SERVER);
+//var_dump($_SERVER);
 
 
 ?>
-- 
GitLab