From 37816b8b7ba43e5ca115fc22bf7481ab523b9862 Mon Sep 17 00:00:00 2001 From: mikael-zakaria Date: Wed, 3 Sep 2025 22:45:57 +0700 Subject: [PATCH] Update JWT Success --- app/Config/Filters.php | 10 +- app/Config/Routes.php | 14 ++- app/Controllers/Auth.php | 234 ++++++++++++++++++++----------------- app/Filters/AuthFilter.php | 50 ++++++++ 4 files changed, 191 insertions(+), 117 deletions(-) create mode 100644 app/Filters/AuthFilter.php diff --git a/app/Config/Filters.php b/app/Config/Filters.php index 9f335d2..19c87c6 100644 --- a/app/Config/Filters.php +++ b/app/Config/Filters.php @@ -35,6 +35,7 @@ class Filters extends BaseFilters 'forcehttps' => ForceHTTPS::class, 'pagecache' => PageCache::class, 'performance' => PerformanceMetrics::class, + 'auth' => \App\Filters\AuthFilter::class, ]; /** @@ -70,10 +71,11 @@ class Filters extends BaseFilters */ public array $globals = [ 'before' => [ - 'cors' - // 'honeypot', - // 'csrf', - // 'invalidchars', + // 'auth', + 'cors', + 'honeypot', + 'csrf', + 'invalidchars', ], 'after' => [ // 'honeypot', diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 8bef71b..a5f9802 100644 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -16,11 +16,15 @@ $routes->post('/api/v1/emr/lab/insert', 'NUHATEMP::create'); $routes->post('/api/v1/emr/lab/update-validasi', 'NUHATEMP::update'); $routes->post('/api/v1/emr/lab/detail', 'NUHATEMP::detail'); -$routes->post('/api/auth/login/', 'Auth::login'); -$routes->post('/api/auth/change_pass/', 'Auth::change_pass'); -$routes->post('/api/auth/register/', 'Auth::register'); -$routes->get('/api/auth/check/', 'Auth::checkAuth'); -$routes->post('/api/auth/logout/', 'Auth::logout'); +// $routes->group('api', ['filter' => 'auth'], function($routes) { + $routes->post('/api/coba-auth', 'Auth::coba'); + + $routes->post('/api/auth/login', 'Auth::login'); + $routes->post('/api/auth/change_pass', 'Auth::change_pass'); + $routes->post('/api/auth/register', 'Auth::register'); + $routes->get('/api/auth/check', 'Auth::checkAuth'); + $routes->post('/api/auth/logout', 'Auth::logout'); +// }); $routes->get('/api/patient', 'Patient::index'); $routes->post('/api/patient', 'Patient::create'); diff --git a/app/Controllers/Auth.php b/app/Controllers/Auth.php index 7f04e11..1697704 100644 --- a/app/Controllers/Auth.php +++ b/app/Controllers/Auth.php @@ -4,62 +4,79 @@ namespace App\Controllers; use CodeIgniter\API\ResponseTrait; use CodeIgniter\Controller; -use \Firebase\JWT\JWT; + +use Firebase\JWT\JWT; +use Firebase\JWT\Key; +use Firebase\JWT\ExpiredException; +use Firebase\JWT\SignatureInvalidException; +use Firebase\JWT\BeforeValidException; use CodeIgniter\Cookie\Cookie; class Auth extends Controller { use ResponseTrait; + // ok public function __construct() { $this->db = \Config\Database::connect(); } - // public function login() { - // $username = $this->request->getVar('username'); - // $password = $this->request->getVar('password'); - // $key = getenv('JWT_SECRET'); + // ok + public function checkAuth() { + $token = $this->request->getCookie('token'); + $key = getenv('JWT_SECRET'); - // if (!$username) { - // return $this->fail('Username required.', 400); - // } - - // $sql = "SELECT * FROM users WHERE username=".$this->db->escape($username); - // $query = $this->db->query($sql); - // $row = $query->getRowArray(); + // Jika token FE tidak ada langsung kabarkan failed + if (!$token) { + return $this->respond([ + 'status' => 'failed', + 'message' => 'No token found' + ], 401); + } - // if (!$row) { - // return $this->fail('User not found.', 401); // Use 401 for authentication failures - // } - - // if (!password_verify($password, $row['password'])) { - // return $this->fail('Invalid password.', 401); - // } - - // // JWT payload - // $payload = [ - // 'userid' => $row['id'], - // 'username' => $row['username'], - // 'exp' => time() + 3600 - // ]; + try { + // Decode Token dengan Key yg ada di .env + $decodedPayload = JWT::decode($token, new Key($key, 'HS256')); - // try { - // $jwt = JWT::encode($payload, $key, 'HS256'); - // } catch (Exception $e) { - // return $this->fail('Error generating JWT: ' . $e->getMessage(), 500); - // } + return $this->respond([ + 'status' => 'success', + 'message' => 'Authenticated', + 'data' => $decodedPayload + ], 200); - // // Update last_login - // //$this->userModel->update($user['id'], ['lastlogin' => date('Y-m-d H:i:s')]); + } catch (ExpiredException $e) { + return $this->respond([ + 'status' => 'failed', + 'message' => 'Token expired', + 'data' => [] + ], 401); - // $response = [ - // 'status' => 'success', - // 'message' => 'Login successful', - // 'token' => $jwt, - // ]; - // return $this->respond($response); - // } + } catch (SignatureInvalidException $e) { + return $this->respond([ + 'status' => 'failed', + 'message' => 'Invalid token signature', + 'data' => [] + ], 401); + + } catch (BeforeValidException $e) { + return $this->respond([ + 'status' => 'failed', + 'message' => 'Token not valid yet', + 'data' => [] + ], 401); + + } catch (\Exception $e) { + return $this->respond([ + 'status' => 'failed', + 'message' => 'Invalid token: ' . $e->getMessage(), + 'data' => [] + ], 401); + } + } + + // ok public function login() { + // Ambil dari JSON Form dan Key .env $username = $this->request->getVar('username'); $password = $this->request->getVar('password'); $key = getenv('JWT_SECRET'); @@ -80,104 +97,105 @@ class Auth extends Controller { return $this->fail('Invalid password.', 401); } - // JWT payload + // Buat JWT payload $payload = [ 'userid' => $row['id'], 'username' => $row['username'], - 'exp' => time() + 3600 + 'exp' => time() + 86400 // 1 hari ]; try { + // Melakukan Hash terhadap Payload dengan Kunci .env menggunakan Algortima HMAC + SHA-256 $jwt = JWT::encode($payload, $key, 'HS256'); } catch (Exception $e) { return $this->fail('Error generating JWT: ' . $e->getMessage(), 500); } - // Set cookie (HttpOnly + Secure + SameSite=Strict) + // Kirim Respon ke HttpOnly yg akan disimpan di browser dan tidak akan dapat diakses oleh siapapun $this->response->setCookie([ - 'name' => 'token', - 'value' => $jwt, - 'expire' => 3600, // 1 jam - 'path' => '/', - 'secure' => true, // set true kalau sudah HTTPS - 'httponly' => true, - 'samesite' => Cookie::SAMESITE_NONE // set true kalau sudah HTTPS - // 'samesite' => Cookie::SAMESITE_STRICT + 'name' => 'token', // nama token + 'value' => $jwt, // value dari jwt yg sudah di hash + 'expire' => 86400, // 1 hari + 'path' => '/', // valid untuk semua path + 'secure' => true, // set true kalau sudah HTTPS + 'httponly' => true, // dipakai agar cookie berikut tidak dapat diakses oleh javascript + 'samesite' => Cookie::SAMESITE_NONE ]); // Response tanpa token di body return $this->respond([ 'status' => 'success', 'message' => 'Login successful' - ]); + ], 200); } - public function change_pass() { - $db = \Config\Database::connect(); - $username = $this->request->getJsonVar('username'); - $password = $this->request->getJsonVar('password'); - $password = password_hash($password, PASSWORD_DEFAULT); - - $master = $this->request->getJsonVar('master'); - $masterkey = getenv('masterkey'); + // ok + public function logout() { + // Definisikan ini pada cookies browser, harus sama dengan cookies login + return $this->response->setCookie([ + 'name' => 'token', + 'value' => '', + 'expire' => time() - 3600, + 'path' => '/', + 'secure' => true, + 'httponly' => true, + 'samesite' => Cookie::SAMESITE_NONE - if($master != $masterkey) { - return $this->fail('Invalid master key.', 401); - } - - $sql = "update users set password='$password' where username='$username'"; - $query = $db->query($sql); - $response = [ - 'message' => "Password Changed for $username" - ]; - return $this->respond($response); + ])->setJSON([ + 'status' => 'success', + 'message' => 'Logout successful' + ], 200); } + // ok public function register() { $username = $this->request->getJsonVar('username'); $password = $this->request->getJsonVar('password'); - $password = password_hash($password, PASSWORD_DEFAULT); - // $master = $this->request->getJsonVar('master'); - // $masterkey = getenv('MASTERKEY'); - - // if($master != $masterkey) { - // return $this->fail('Invalid master key.', 401); - // } - - $sql = "INSERT INTO users(username, password) values('$username', '$password')"; - $this->db->query($sql); - $response = [ - 'message' => "User $username created" - ]; - return $this->respondCreated($response); - } - - public function checkAuth() { - $token = $this->request->getCookie('token'); - $key = getenv('JWT_SECRET'); - - if (!$token) { - return $this->fail('No token found', 401); - } - - try { - $decoded = JWT::decode($token, new Key($key, 'HS256')); + // Validasi + if (empty($username) || empty($password)) { return $this->respond([ - 'status' => 'success', - 'message' => 'Authenticated', - 'data' => $decoded - ]); - } catch (\Exception $e) { - return $this->fail('Invalid or expired token: ' . $e->getMessage(), 401); + 'status' => 'failed', + 'message' => 'Username and password are required' + ], 400); // Gunakan 400 Bad Request } + + $password = password_hash($password, PASSWORD_DEFAULT); + $sql = "INSERT INTO users(username, password) values('$username', '$password')"; + + return $this->respond([ + 'status' => 'success', + 'message' => 'User '.$username.' created' + ], 201); } - public function logout() { - return $this->response - ->deleteCookie('token') - ->setJSON(['message' => 'Logout successful']); - } + // public function change_pass() { + // $db = \Config\Database::connect(); + // $username = $this->request->getJsonVar('username'); + // $password = $this->request->getJsonVar('password'); + // $password = password_hash($password, PASSWORD_DEFAULT); -} \ No newline at end of file + // $master = $this->request->getJsonVar('master'); + // $masterkey = getenv('masterkey'); + + // if($master != $masterkey) { + // return $this->fail('Invalid master key.', 401); + // } + + // $sql = "update users set password='$password' where username='$username'"; + // $query = $db->query($sql); + // $response = [ + // 'message' => "Password Changed for $username" + // ]; + // return $this->respond($response); + // } + + public function coba() { + return $this->respond([ + 'status' => 'success', + 'message' => 'Already Login' + ],200); + } + +} diff --git a/app/Filters/AuthFilter.php b/app/Filters/AuthFilter.php new file mode 100644 index 0000000..ad9d297 --- /dev/null +++ b/app/Filters/AuthFilter.php @@ -0,0 +1,50 @@ +getCookie('token'); // ambil dari cookie + + // Kalau tidak ada token + if (!$token) { + return Services::response() + ->setStatusCode(401) + ->setJSON([ + 'status' => 'failed', + 'message' => 'Unauthorized: Token not found' + ]); + } + + try { + // Decode JWT : jika error maka akan mentrigger catch + $decoded = JWT::decode($token, new Key($key, 'HS256')); + + // Kalau mau, bisa inject user info ke request + // $request->userData = $decoded; + + } catch (\Exception $e) { + return Services::response() + ->setStatusCode(401) + ->setJSON([ + 'status' => 'failed', + 'message' => 'Unauthorized: ' . $e->getMessage() + ]); + } + } + + public function after(RequestInterface $request, ResponseInterface $response, $arguments = null) + { + // Tidak perlu apa-apa + } +}