diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 531d5836..dfa43a0a 100755 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -600,6 +600,8 @@ $routes->group("employeeRest", ['filter' => ['appSignature'] ], function ($route $routes->group("employeeRest", ["filter" => ['appSignature' , 'authJWT']], function ($routes) { + $routes->post('logout', 'RestAuthenticationController::logout'); + $routes->post("ecardRequest", "ApiServiceController::ecardRequest"); $routes->get("getWellnessURL", "ApiServiceController::getWellnessURL"); @@ -754,7 +756,7 @@ $routes->post("retrieveWebhookDataClaim","ClientWebHooksController::pullData_cla //Third party ICICILombard Api Call -$routes->get('generateAuthToken','ICICILombardController::generateAuthToken'); +$routes->get('generateAuthToken','FhplApiController::generateAuthToken'); $routes->get('createEnrollmentBatch','ICICILombardController::createEnrollmentBatch'); $routes->get('getEnrollmentBatchStatus','ICICILombardController::getEnrollmentBatchStatus'); $routes->get('fetchUHIDDetails','ICICILombardController::fetchUHIDDetails'); diff --git a/app/Controllers/ClaimsUploadController.php b/app/Controllers/ClaimsUploadController.php new file mode 100644 index 00000000..02c9a187 --- /dev/null +++ b/app/Controllers/ClaimsUploadController.php @@ -0,0 +1,48 @@ +db = \Config\Database::connect(); + } + + public function uploadDump() + { + $file = $this->request->getFile('file'); + + if (!$file || !$file->isValid()) { + return $this->respond([ + 'status' => 'failed', + 'message' => 'Invalid file' + ], 400); + } + + $data = [ + 'client_id' => $this->request->getPost('client_id'), + 'tpa_id' => $this->request->getPost('tpa_id'), + 'client_policy_id' => $this->request->getPost('client_policy_id'), + 'from_date' => $this->request->getPost('from_date'), + 'to_date' => $this->request->getPost('to_date'), + 'upload_file' => $file->getRandomName(), + // 'uploaded_by' => user_id() + ]; + + $file->move(WRITEPATH . 'uploads/claims_dump', $data['upload_file']); + + $this->db->table('claims_dump_uploads')->insert($data); + + return $this->respond([ + 'status' => 'success', + 'message' => 'Claims dump uploaded' + ]); + } +} diff --git a/app/Controllers/FhplApiController.php b/app/Controllers/FhplApiController.php new file mode 100644 index 00000000..33037652 --- /dev/null +++ b/app/Controllers/FhplApiController.php @@ -0,0 +1,73 @@ +db = \Config\Database::connect(); + $this->fhplTpaId = getenv('FHPL_PRIMARY_KEY_CONSTANT'); + } + + public function generateAuthToken() + { + $url = env('FHPL_TOKEN_URL'); // example: https://uat.fhpl.net/token + + // x-www-form-urlencoded body + $postData = http_build_query([ + 'UserName' => 'TestApi@fhpl', + 'Password' => 'Fhpl@12345', + 'grant_type' => 'password', + ]); + + $ch = curl_init(); + + curl_setopt_array($ch, [ + CURLOPT_URL => $url, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_CUSTOMREQUEST => 'GET', // SAME AS POSTMAN + CURLOPT_POSTFIELDS => $postData, + CURLOPT_HTTPHEADER => [ + 'Content-Type: application/x-www-form-urlencoded', + 'Accept: application/json', + ], + CURLOPT_TIMEOUT => 30, + ]); + + $response = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + + if (curl_errno($ch)) { + return $this->response->setJSON([ + 'status' => false, + 'error' => curl_error($ch), + ]); + } + + curl_close($ch); + + return $this->response->setJSON([ + 'status' => $httpCode === 200, + 'http_code' => $httpCode, + 'response' => json_decode($response, true), + ]); + } + + +} diff --git a/app/Controllers/RestAuthenticationController.php b/app/Controllers/RestAuthenticationController.php index bad20540..50dbd80f 100755 --- a/app/Controllers/RestAuthenticationController.php +++ b/app/Controllers/RestAuthenticationController.php @@ -110,6 +110,13 @@ class RestAuthenticationController extends AdminController if (isset($employeeData['employee_id'])) { + + //check the resend otp (with in 60 seconds don't allow another otp to send) + $check = canSendOtp($employeeData); + if (!$check['allowed']) { + return $this->respond(['status' => false,'message' => 'OTP already sent. Please wait before retrying.','retry_after_seconds' => $check['retry_after'] ])->setStatusCode(429); // Too Many Requests + } + $this->myLogger->logme("error", "REST-AUTH-CONTROLLER - verifyEmployeeWithMobileNumber: Employee verified with ID = " . $employeeData['employee_id']); log_message('error', ' '); log_message('error', '************************ POST END ********************************'); @@ -217,6 +224,12 @@ class RestAuthenticationController extends AdminController if (isset($employeeData['employee_id'])) { + //check the resend otp (with in 60 seconds don't allow another otp to send) + $check = canSendOtp($employeeData); + if (!$check['allowed']) { + return $this->respond(['status' => false,'message' => 'OTP already sent. Please wait before retrying.','retry_after_seconds' => $check['retry_after'] ])->setStatusCode(429); // Too Many Requests + } + $builder = $this->employeeModel ->where('email_corporate', $email) @@ -518,6 +531,12 @@ class RestAuthenticationController extends AdminController if ($HrData) { + + //check the resend otp (with in 60 seconds don't allow another otp to send) + $check = canSendOtp($HrData); + if (!$check['allowed']) { + return $this->respond(['status' => false,'message' => 'OTP already sent. Please wait before retrying.','retry_after_seconds' => $check['retry_after'] ])->setStatusCode(429); // Too Many Requests + } $sql = "UPDATE level_contacts SET otp = ? WHERE mobile = ? AND contact_type = 'client' AND is_active = 1"; @@ -569,6 +588,12 @@ class RestAuthenticationController extends AdminController if ($HrData) { + //check the resend otp (with in 60 seconds don't allow another otp to send) + $check = canSendOtp($HrData); + if (!$check['allowed']) { + return $this->respond(['status' => false,'message' => 'OTP already sent. Please wait before retrying.','retry_after_seconds' => $check['retry_after'] ])->setStatusCode(429); // Too Many Requests + } + $sql = " UPDATE level_contacts SET otp = ? @@ -2289,4 +2314,53 @@ class RestAuthenticationController extends AdminController } } + public function logout() + { + $authHeader = $this->request->getHeaderLine('Authorization'); + + if (!$authHeader) { + return $this->respond([ + 'status' => false, + 'message' => 'Authorization token missing' + ], 401); + } + + // Validate JWT (your hardened function) + $result = JWTToken::validateJWT($authHeader); + + if ($result['status'] !== true) { + return $this->respond([ + 'status' => false, + 'message' => 'Invalid or expired token' + ], 401); + } + + $decoded = $result['decoded']; + $userId = $decoded['id'] ?? null; + + if (!$userId) { + return $this->respond([ + 'status' => false, + 'message' => 'Invalid token payload' + ], 401); + } + + // Identify user type + if (isset($decoded['emp_code'])) { + $model = new EmployeeModel(); + } else { + $model = new LevelContactModel(); + } + + // Invalidate token server-side + $model->update($userId, [ + 'token_time_out' => null + ]); + + return $this->respond([ + 'status' => true, + 'message' => 'Logged out successfully' + ], 200); + } + } \ No newline at end of file diff --git a/app/Filters/AuthJWT.php b/app/Filters/AuthJWT.php index 484372a7..63ecf655 100755 --- a/app/Filters/AuthJWT.php +++ b/app/Filters/AuthJWT.php @@ -18,67 +18,119 @@ use App\Models\LevelContactModel; class AuthJWT implements FilterInterface { + // public function before(RequestInterface $request, $arguments = null) + // { + // $jwt = $request->getHeader('Authorization'); + + // if ($jwt) { + // if (JWTToken::validateJWT($jwt)) { + // $data = JWTToken::validateJWT($jwt); + // $data = json_decode($data); + + // $id = $data->decoded->id; + // if(isset($data->decoded->emp_code)){ + // $model = new EmployeeModel(); + // $user_data = $model->where('id', $id)->first(); + + // if($user_data['token_time_out'] > time()){ + // $data =["token_time_out" => time() + getenv('TOKENTIMEOUT') ]; + // $model->update($id, $data); + // return true; + // }else{ + + // // if($user_data['token_time_out'] != "" && $user_data['token_time_out'] != NULL) + + // $data =["token_time_out" => '']; + // $model->update($id, $data); + // header('Content-Type: application/json'); + // http_response_code(401); + // // $error = json_encode(["status" => 401, "message" => $data->message]); + // $error = json_encode(["status" => 401, "message" => "Token is Invalid"]); + // echo $error; + // exit; + // } + // }else{ + // $model = new LevelContactModel(); + // $hr_data = $model->where('id', $id)->first(); + + // if($hr_data['token_time_out'] > time()){ + // $data =["token_time_out" => time() + getenv('TOKENTIMEOUT') ]; + // $model->update($id, $data); + // return true; + // }else{ + // $data =["token_time_out" => '']; + // $model->update($id, $data); + // header('Content-Type: application/json'); + // http_response_code(401); + // // $error = json_encode(["status" => 401, "message" => $data->message]); + // $error = json_encode(["status" => 401, "message" => "Token is Invalid"]); + // echo $error; + // exit; + // } + // } + + // } + // } else { + // header('Content-Type: application/json'); + // http_response_code(403); + // $error = json_encode(["status" => 403, "message" => "Access Forbidden!"]); + // echo $error; + // exit(); + // } + // } + public function before(RequestInterface $request, $arguments = null) { - $jwt = $request->getHeader('Authorization'); + $authHeader = $request->getHeaderLine('Authorization'); - if ($jwt) { - if (JWTToken::validateJWT($jwt)) { - $data = JWTToken::validateJWT($jwt); - $data = json_decode($data); - - $id = $data->decoded->id; - if(isset($data->decoded->emp_code)){ - $model = new EmployeeModel(); - $user_data = $model->where('id', $id)->first(); - - if($user_data['token_time_out'] > time()){ - $data =["token_time_out" => time() + getenv('TOKENTIMEOUT') ]; - $model->update($id, $data); - return true; - }else{ - - // if($user_data['token_time_out'] != "" && $user_data['token_time_out'] != NULL) - - $data =["token_time_out" => '']; - $model->update($id, $data); - header('Content-Type: application/json'); - http_response_code(401); - // $error = json_encode(["status" => 401, "message" => $data->message]); - $error = json_encode(["status" => 401, "message" => "Token is Invalid"]); - echo $error; - exit; - } - }else{ - $model = new LevelContactModel(); - $hr_data = $model->where('id', $id)->first(); - - if($hr_data['token_time_out'] > time()){ - $data =["token_time_out" => time() + getenv('TOKENTIMEOUT') ]; - $model->update($id, $data); - return true; - }else{ - $data =["token_time_out" => '']; - $model->update($id, $data); - header('Content-Type: application/json'); - http_response_code(401); - // $error = json_encode(["status" => 401, "message" => $data->message]); - $error = json_encode(["status" => 401, "message" => "Token is Invalid"]); - echo $error; - exit; - } - } - - } - } else { - header('Content-Type: application/json'); - http_response_code(403); - $error = json_encode(["status" => 403, "message" => "Access Forbidden!"]); - echo $error; - exit(); + if (!$authHeader) { + return $this->reject(403, 'Access Forbidden'); } + + $result = JWTToken::validateJWT($authHeader); + + if ($result['status'] !== true) { + return $this->reject(401, $result['message']); + } + + $decoded = $result['decoded']; + $id = $decoded['id'] ?? null; + + if (!$id) { + return $this->reject(401, 'Invalid token payload'); + } + + if (isset($decoded['emp_code'])) { + $model = new EmployeeModel(); + } else { + $model = new LevelContactModel(); + } + + $user = $model->find($id); + + if (!$user || $user['token_time_out'] <= time()) { + $model->update($id, ['token_time_out' => null]); + return $this->reject(401, 'Token expired'); + } + + // Refresh sliding expiration + $model->update($id, [ + 'token_time_out' => time() + getenv('TOKENTIMEOUT') + ]); + + return true; } + private function reject(int $code, string $message) + { + return service('response') + ->setStatusCode($code) + ->setJSON(['status' => $code, 'message' => $message]) + ->send(); + } + + + public function after(RequestInterface $request, ResponseInterface $response, $arguments = null) { // Do something here after the response is sent diff --git a/app/Helpers/JWTToken.php b/app/Helpers/JWTToken.php index 41d76b5c..73978ddb 100755 --- a/app/Helpers/JWTToken.php +++ b/app/Helpers/JWTToken.php @@ -18,9 +18,12 @@ use App\Models\LevelContactModel; class JWTToken { + + private const ALLOWED_ALG = 'HS512'; + public static function encode($data =null) { - $secret_Key ="secret"; + $secret_Key = env('JWT_SECRET'); $request_data = (array)$data; @@ -47,30 +50,79 @@ class JWTToken } } - public static function validateJWT($jwt) + // public static function validateJWT($jwt) + // { + // $jwtParts = explode(' ', $jwt); + + // // print_r($jwtParts); + // if (count($jwtParts) != 2 || $jwtParts[0] == 'Bearer') { + // return false; + // } + + // $token = $jwtParts[1]; + + // try { + // $decoded = JWT::decode($token, new Key(env('JWT_SECRET'), 'HS512')); + // return json_encode(['status' => true, 'message' => 'Token is valid', 'decoded' => (array) $decoded]); + // } catch (ExpiredException $e) { + // return json_encode(['status' => false, 'message' => 'Token has expired']); + // } catch (BeforeValidException $e) { + // return json_encode(['status' => false, 'message' => 'Token is not yet valid']); + // } catch (SignatureInvalidException $e) { + // return json_encode(['status' => false, 'message' => 'Token signature is invalid']); + // } catch (\Exception $e) { + // return json_encode(['status' => false, 'message' => 'An error occurred while decoding the token']); + // } + + // } + + public static function validateJWT(string $authHeader) { - $jwtParts = explode(' ', $jwt); + // 1️⃣ Validate Authorization header + if (!preg_match('/^Bearer\s(\S+)$/', $authHeader, $matches)) { + return ['status' => false, 'message' => 'Invalid Authorization header']; + } - // print_r($jwtParts); - if (count($jwtParts) != 2 || $jwtParts[0] == 'Bearer') { - return false; - } + $token = $matches[1]; - $token = $jwtParts[1]; + // 2️⃣ Decode JWT header manually + $jwtParts = explode('.', $token); + if (count($jwtParts) !== 3) { + return ['status' => false, 'message' => 'Malformed JWT']; + } - try { - $decoded = JWT::decode($token, new Key("secret", 'HS512')); - return json_encode(['status' => true, 'message' => 'Token is valid', 'decoded' => (array) $decoded]); - } catch (ExpiredException $e) { - return json_encode(['status' => false, 'message' => 'Token has expired']); - } catch (BeforeValidException $e) { - return json_encode(['status' => false, 'message' => 'Token is not yet valid']); - } catch (SignatureInvalidException $e) { - return json_encode(['status' => false, 'message' => 'Token signature is invalid']); - } catch (\Exception $e) { - return json_encode(['status' => false, 'message' => 'An error occurred while decoding the token']); - } + $header = json_decode(base64_decode(strtr($jwtParts[0], '-_', '+/')), true); + // 3️⃣ Reject missing or NONE algorithm + if ( + empty($header['alg']) || + $header['alg'] === 'none' || + $header['alg'] !== self::ALLOWED_ALG + ) { + return ['status' => false, 'message' => 'Invalid or unsupported JWT algorithm']; + } + + // 4️⃣ Enforce signature validation + try { + $decoded = JWT::decode( + $token, + new Key(env('JWT_SECRET'), self::ALLOWED_ALG) + ); + + return [ + 'status' => true, + 'decoded' => (array) $decoded + ]; + + } catch (ExpiredException $e) { + return ['status' => false, 'message' => 'Token expired']; + } catch (BeforeValidException $e) { + return ['status' => false, 'message' => 'Token not yet valid']; + } catch (SignatureInvalidException $e) { + return ['status' => false, 'message' => 'Invalid token signature']; + } catch (\Exception $e) { + return ['status' => false, 'message' => 'Token validation failed']; + } } diff --git a/app/Helpers/utility_helper.php b/app/Helpers/utility_helper.php index 1585b803..fe7898e3 100755 --- a/app/Helpers/utility_helper.php +++ b/app/Helpers/utility_helper.php @@ -978,3 +978,29 @@ if (!function_exists('generate_ecard_download_link_based_on_tpa')) { } } +if (!function_exists('canSendOtp')) { + function canSendOtp(array $row, int $limitSeconds = 60): array + { + // If OTP does not exist → allow + if (empty($row['otp']) || empty($row['updated_at'])) { + return ['allowed' => true]; + } + + $lastUpdated = strtotime($row['updated_at']); + $currentTime = time(); + + // Calculate expiry time + $allowedAfter = $lastUpdated + $limitSeconds; + + // If still within limit → block + if ($currentTime < $allowedAfter) { + return [ + 'allowed' => false, + 'retry_after' => $allowedAfter - $currentTime + ]; + } + + return ['allowed' => true]; + } +} +