Update authentication logic and SDK fixes
This commit is contained in:
@@ -579,6 +579,11 @@ class AuthController extends Controller
|
|||||||
return $this->failure('Invalid credentials');
|
return $this->failure('Invalid credentials');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify password if set in DB, otherwise reject for security
|
||||||
|
if (!isset($admin->password) || !password_verify($request->input('password'), $admin->password)) {
|
||||||
|
return $this->failure('Invalid credentials');
|
||||||
|
}
|
||||||
|
|
||||||
$jwt = $this->createJwt((string)$admin->id, 'admin', $request->input('device_number'), 900);
|
$jwt = $this->createJwt((string)$admin->id, 'admin', $request->input('device_number'), 900);
|
||||||
|
|
||||||
return $this->success([
|
return $this->success([
|
||||||
@@ -674,6 +679,8 @@ class AuthController extends Controller
|
|||||||
'promos.promo_code as promo',
|
'promos.promo_code as promo',
|
||||||
'promos.amount as discount',
|
'promos.amount as discount',
|
||||||
'promos.validity_end_date as validity',
|
'promos.validity_end_date as validity',
|
||||||
|
'p.api_key',
|
||||||
|
'p.api_secret',
|
||||||
])
|
])
|
||||||
->selectSub(function ($query) use ($platform, $appName) {
|
->selectSub(function ($query) use ($platform, $appName) {
|
||||||
$query->from('packageInfo')
|
$query->from('packageInfo')
|
||||||
@@ -732,6 +739,7 @@ class AuthController extends Controller
|
|||||||
->table('captain')
|
->table('captain')
|
||||||
->where('email', $encryptedEmail)
|
->where('email', $encryptedEmail)
|
||||||
->where('id', $request->input('id'))
|
->where('id', $request->input('id'))
|
||||||
|
->select('captain.*', 'captain.api_key', 'captain.api_secret')
|
||||||
->first();
|
->first();
|
||||||
|
|
||||||
if (!$driver) {
|
if (!$driver) {
|
||||||
@@ -795,6 +803,7 @@ class AuthController extends Controller
|
|||||||
'iat' => time(),
|
'iat' => time(),
|
||||||
'exp' => time() + $expiry,
|
'exp' => time() + $expiry,
|
||||||
'aud' => $audience,
|
'aud' => $audience,
|
||||||
|
'iss' => 'Tripz',
|
||||||
'jti' => Str::uuid()->toString(),
|
'jti' => Str::uuid()->toString(),
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -826,25 +835,34 @@ class AuthController extends Controller
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$audience = $request->input('aud');
|
$audience = $request->input('aud');
|
||||||
// Validate audience if needed (optional based on audio but good for security)
|
|
||||||
// if (!in_array($audience, config('intaleq.allowed_audiences'))) { ... }
|
|
||||||
|
|
||||||
// The user mentioned using a fixed password like 'passenger' from Flutter
|
|
||||||
// and relying on fingerprint for security.
|
|
||||||
|
|
||||||
// Generate a 24h JWT for the handshake (as requested to be consistent)
|
// Verify the passenger exists
|
||||||
|
$passenger = Passenger::where('id', $request->input('id'))->first();
|
||||||
|
if (!$passenger) {
|
||||||
|
return $this->failure('Invalid credentials');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify fingerprint matches stored device (security)
|
||||||
|
$token = PassengerToken::where('passengerID', $request->input('id'))->first();
|
||||||
|
if (!$token || !hash_equals($token->fingerPrint ?? '', $request->input('fingerPrint'))) {
|
||||||
|
return $this->failure('Device verification failed', 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a 15min JWT for the handshake (security: reduced from 24h)
|
||||||
$jwt = $this->createJwt(
|
$jwt = $this->createJwt(
|
||||||
$request->input('id'),
|
$request->input('id'),
|
||||||
'passenger',
|
'passenger',
|
||||||
$request->input('fingerPrint'),
|
$request->input('fingerPrint'),
|
||||||
86400,
|
900,
|
||||||
$audience
|
$audience
|
||||||
);
|
);
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'status' => 'success',
|
'status' => 'success',
|
||||||
'jwt' => $jwt,
|
'jwt' => $jwt,
|
||||||
'expires_in' => 86400
|
'expires_in' => 900,
|
||||||
|
'api_key' => $passenger->api_key ?? $driver->api_key,
|
||||||
|
'api_secret' => $passenger->api_secret ?? $driver->api_secret,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -861,18 +879,30 @@ class AuthController extends Controller
|
|||||||
'aud' => 'required|string',
|
'aud' => 'required|string',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$driver = Driver::where('id', $request->input('id'))->first();
|
||||||
|
if (!$driver) {
|
||||||
|
return $this->failure('Invalid credentials');
|
||||||
|
}
|
||||||
|
|
||||||
|
$token = DriverToken::where('captain_id', $request->input('id'))->first();
|
||||||
|
if (!$token || !hash_equals($token->fingerPrint ?? '', $request->input('fingerPrint'))) {
|
||||||
|
return $this->failure('Device verification failed', 403);
|
||||||
|
}
|
||||||
|
|
||||||
$jwt = $this->createJwt(
|
$jwt = $this->createJwt(
|
||||||
$request->input('id'),
|
$request->input('id'),
|
||||||
'driver',
|
'driver',
|
||||||
$request->input('fingerPrint'),
|
$request->input('fingerPrint'),
|
||||||
86400,
|
900,
|
||||||
$request->input('aud')
|
$request->input('aud')
|
||||||
);
|
);
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'status' => 'success',
|
'status' => 'success',
|
||||||
'jwt' => $jwt,
|
'jwt' => $jwt,
|
||||||
'expires_in' => 86400
|
'expires_in' => 900,
|
||||||
|
'api_key' => $passenger->api_key ?? $driver->api_key,
|
||||||
|
'api_secret' => $passenger->api_secret ?? $driver->api_secret,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,14 +11,14 @@ class DriverDocController extends Controller
|
|||||||
/** POST /v2/driver/scams */
|
/** POST /v2/driver/scams */
|
||||||
public function reportScam(Request $request): JsonResponse
|
public function reportScam(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
$userId = $request->input('_jwt_user_id');
|
$userId = $request->attributes->get('_jwt_user_id');
|
||||||
return response()->json(['status' => 'success']);
|
return response()->json(['status' => 'success']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** GET /v2/driver/registration-car */
|
/** GET /v2/driver/registration-car */
|
||||||
public function getCarReg(Request $request): JsonResponse
|
public function getCarReg(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
$userId = $request->input('_jwt_user_id');
|
$userId = $request->attributes->get('_jwt_user_id');
|
||||||
$data = DB::connection('primary')->table('RegisrationCar')
|
$data = DB::connection('primary')->table('RegisrationCar')
|
||||||
->where('driverID', $userId)->get();
|
->where('driverID', $userId)->get();
|
||||||
return response()->json(['status' => 'success', 'data' => $data]);
|
return response()->json(['status' => 'success', 'data' => $data]);
|
||||||
@@ -27,7 +27,7 @@ class DriverDocController extends Controller
|
|||||||
/** POST /v2/driver/registration-car */
|
/** POST /v2/driver/registration-car */
|
||||||
public function storeCarReg(Request $request): JsonResponse
|
public function storeCarReg(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
$userId = $request->input('_jwt_user_id');
|
$userId = $request->attributes->get('_jwt_user_id');
|
||||||
// Logic to store...
|
// Logic to store...
|
||||||
return response()->json(['status' => 'success']);
|
return response()->json(['status' => 'success']);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ class InviteController extends Controller
|
|||||||
/** GET /v2/invites/gift */
|
/** GET /v2/invites/gift */
|
||||||
public function checkGift(Request $request): JsonResponse
|
public function checkGift(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
$userId = $request->input('_jwt_user_id');
|
$userId = $request->attributes->get('_jwt_user_id');
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'status' => 'success',
|
'status' => 'success',
|
||||||
'message' => ['gift_available' => true]
|
'message' => ['gift_available' => true]
|
||||||
|
|||||||
@@ -185,7 +185,8 @@ class MiscController extends Controller
|
|||||||
|
|
||||||
return response()->json(['status' => 'success', 'message' => 'Help question saved successfully']);
|
return response()->json(['status' => 'success', 'message' => 'Help question saved successfully']);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
return response()->json(['status' => 'failure', 'message' => 'Database error: ' . $e->getMessage()]);
|
\Log::error('MiscController HelpCenter Error: ' . $e->getMessage());
|
||||||
|
return response()->json(['status' => 'failure', 'message' => 'An error occurred']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,7 +213,8 @@ class MiscController extends Controller
|
|||||||
|
|
||||||
return response()->json(['status' => 'success', 'message' => 'Tip inserted successfully']);
|
return response()->json(['status' => 'success', 'message' => 'Tip inserted successfully']);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
return response()->json(['status' => 'failure', 'message' => 'Database error: ' . $e->getMessage()]);
|
\Log::error('MiscController Tips Error: ' . $e->getMessage());
|
||||||
|
return response()->json(['status' => 'failure', 'message' => 'An error occurred']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,9 +264,10 @@ class MiscController extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
|
\Log::error('MiscController EgyptPhones Error: ' . $e->getMessage());
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'status' => 'failure',
|
'status' => 'failure',
|
||||||
'message' => 'Database error: ' . $e->getMessage()
|
'message' => 'An error occurred'
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ class NotificationController extends Controller
|
|||||||
/** GET /v2/notifications */
|
/** GET /v2/notifications */
|
||||||
public function index(Request $request): JsonResponse
|
public function index(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
$userId = $request->input('_jwt_user_id');
|
$userId = $request->attributes->get('_jwt_user_id');
|
||||||
$userType = $request->input('_jwt_user_type');
|
$userType = $request->attributes->get('_jwt_user_type');
|
||||||
$page = (int) $request->input('page', 1);
|
$page = (int) $request->input('page', 1);
|
||||||
$limit = min((int) $request->input('limit', 20), 50);
|
$limit = min((int) $request->input('limit', 20), 50);
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@ class NotificationController extends Controller
|
|||||||
/** PUT /v2/notifications/{id}/read */
|
/** PUT /v2/notifications/{id}/read */
|
||||||
public function markRead(Request $request, int $id): JsonResponse
|
public function markRead(Request $request, int $id): JsonResponse
|
||||||
{
|
{
|
||||||
$userType = $request->input('_jwt_user_type');
|
$userType = $request->attributes->get('_jwt_user_type');
|
||||||
$table = $userType === 'driver' ? 'notificationCaptain' : 'notifications';
|
$table = $userType === 'driver' ? 'notificationCaptain' : 'notifications';
|
||||||
|
|
||||||
DB::connection('primary')->table($table)
|
DB::connection('primary')->table($table)
|
||||||
@@ -63,8 +63,8 @@ class NotificationController extends Controller
|
|||||||
'fingerPrint' => 'required|string',
|
'fingerPrint' => 'required|string',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$userId = $request->input('_jwt_user_id') ?? $request->input('passengerID');
|
$userId = $request->attributes->get('_jwt_user_id') ?? $request->input('passengerID');
|
||||||
$userType = $request->input('_jwt_user_type') ?? 'passenger';
|
$userType = $request->attributes->get('_jwt_user_type') ?? 'passenger';
|
||||||
|
|
||||||
if (!$userId) {
|
if (!$userId) {
|
||||||
return response()->json(['status' => 'failure', 'message' => 'User ID missing'], 400);
|
return response()->json(['status' => 'failure', 'message' => 'User ID missing'], 400);
|
||||||
@@ -98,8 +98,8 @@ class NotificationController extends Controller
|
|||||||
/** GET /v2/notifications/token */
|
/** GET /v2/notifications/token */
|
||||||
public function getToken(Request $request): JsonResponse
|
public function getToken(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
$userId = $request->input('_jwt_user_id') ?? $request->input('passengerID');
|
$userId = $request->attributes->get('_jwt_user_id') ?? $request->input('passengerID');
|
||||||
$userType = $request->input('_jwt_user_type') ?? 'passenger';
|
$userType = $request->attributes->get('_jwt_user_type') ?? 'passenger';
|
||||||
|
|
||||||
if (!$userId) {
|
if (!$userId) {
|
||||||
return response()->json(['status' => 'failure', 'message' => 'User ID missing'], 400);
|
return response()->json(['status' => 'failure', 'message' => 'User ID missing'], 400);
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ class ProfileController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function passenger(Request $request): JsonResponse
|
public function passenger(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
$id = $request->input('_jwt_user_id');
|
$id = $request->attributes->get('_jwt_user_id');
|
||||||
$passenger = Passenger::active()->find($id);
|
$passenger = Passenger::active()->find($id);
|
||||||
|
|
||||||
if (!$passenger) {
|
if (!$passenger) {
|
||||||
@@ -64,7 +64,7 @@ class ProfileController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function driver(Request $request): JsonResponse
|
public function driver(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
$id = $request->input('_jwt_user_id');
|
$id = $request->attributes->get('_jwt_user_id');
|
||||||
$driver = Driver::active()->byId($id)->first();
|
$driver = Driver::active()->byId($id)->first();
|
||||||
|
|
||||||
if (!$driver) {
|
if (!$driver) {
|
||||||
@@ -101,7 +101,7 @@ class ProfileController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function updatePassenger(Request $request): JsonResponse
|
public function updatePassenger(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
$id = $request->input('_jwt_user_id');
|
$id = $request->attributes->get('_jwt_user_id');
|
||||||
$passenger = Passenger::active()->find($id);
|
$passenger = Passenger::active()->find($id);
|
||||||
|
|
||||||
if (!$passenger) {
|
if (!$passenger) {
|
||||||
@@ -135,7 +135,7 @@ class ProfileController extends Controller
|
|||||||
{
|
{
|
||||||
$request->validate(['email' => 'required|email']);
|
$request->validate(['email' => 'required|email']);
|
||||||
|
|
||||||
$id = $request->input('_jwt_user_id');
|
$id = $request->attributes->get('_jwt_user_id');
|
||||||
$driver = Driver::active()->byId($id)->first();
|
$driver = Driver::active()->byId($id)->first();
|
||||||
|
|
||||||
if (!$driver) {
|
if (!$driver) {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ class PromoController extends Controller
|
|||||||
/** GET /v2/promos */
|
/** GET /v2/promos */
|
||||||
public function index(Request $request): JsonResponse
|
public function index(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
$passengerId = $request->input('_jwt_user_id');
|
$passengerId = $request->attributes->get('_jwt_user_id');
|
||||||
|
|
||||||
$promos = DB::connection('primary')->table('promos')
|
$promos = DB::connection('primary')->table('promos')
|
||||||
->where('passengerID', $passengerId)
|
->where('passengerID', $passengerId)
|
||||||
@@ -70,7 +70,7 @@ class PromoController extends Controller
|
|||||||
'amount' => 'required|string',
|
'amount' => 'required|string',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$passengerId = $request->input('_jwt_user_id');
|
$passengerId = $request->attributes->get('_jwt_user_id');
|
||||||
|
|
||||||
$exists = DB::connection('primary')->table('promos')
|
$exists = DB::connection('primary')->table('promos')
|
||||||
->where('passengerID', $passengerId)->exists();
|
->where('passengerID', $passengerId)->exists();
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class RatingController extends Controller
|
|||||||
'comment' => 'nullable|string|max:500',
|
'comment' => 'nullable|string|max:500',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$passengerId = $request->input('_jwt_user_id');
|
$passengerId = $request->attributes->get('_jwt_user_id');
|
||||||
|
|
||||||
// Prevent duplicate ratings
|
// Prevent duplicate ratings
|
||||||
$exists = DB::connection('primary')->table('ratingDriver')
|
$exists = DB::connection('primary')->table('ratingDriver')
|
||||||
@@ -60,7 +60,7 @@ class RatingController extends Controller
|
|||||||
'comment' => 'nullable|string|max:500',
|
'comment' => 'nullable|string|max:500',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$driverId = $request->input('_jwt_user_id');
|
$driverId = $request->attributes->get('_jwt_user_id');
|
||||||
|
|
||||||
$exists = DB::connection('primary')->table('ratingPassenger')
|
$exists = DB::connection('primary')->table('ratingPassenger')
|
||||||
->where('rideId', $request->input('ride_id'))->exists();
|
->where('rideId', $request->input('ride_id'))->exists();
|
||||||
@@ -88,8 +88,8 @@ class RatingController extends Controller
|
|||||||
'comment' => 'nullable|string|max:300',
|
'comment' => 'nullable|string|max:300',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$userId = $request->input('_jwt_user_id');
|
$userId = $request->attributes->get('_jwt_user_id');
|
||||||
$userType = $request->input('_jwt_user_type');
|
$userType = $request->attributes->get('_jwt_user_type');
|
||||||
|
|
||||||
DB::connection('primary')->table('ratingApp')->insert([
|
DB::connection('primary')->table('ratingApp')->insert([
|
||||||
'name' => $request->input('name', ''),
|
'name' => $request->input('name', ''),
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ class RideController extends Controller
|
|||||||
'passenger_rating' => 'nullable|numeric',
|
'passenger_rating' => 'nullable|numeric',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$passengerId = $request->input('_jwt_user_id');
|
$passengerId = $request->attributes->get('_jwt_user_id');
|
||||||
|
|
||||||
// Prevent duplicate active rides
|
// Prevent duplicate active rides
|
||||||
$activeRide = DB::connection('ride')->table('ride')
|
$activeRide = DB::connection('ride')->table('ride')
|
||||||
@@ -152,7 +152,7 @@ class RideController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function accept(Request $request, int $rideId): JsonResponse
|
public function accept(Request $request, int $rideId): JsonResponse
|
||||||
{
|
{
|
||||||
$driverId = $request->input('_jwt_user_id');
|
$driverId = $request->attributes->get('_jwt_user_id');
|
||||||
|
|
||||||
DB::connection('ride')->beginTransaction();
|
DB::connection('ride')->beginTransaction();
|
||||||
try {
|
try {
|
||||||
@@ -233,7 +233,7 @@ class RideController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function start(Request $request, int $rideId): JsonResponse
|
public function start(Request $request, int $rideId): JsonResponse
|
||||||
{
|
{
|
||||||
$driverId = $request->input('_jwt_user_id');
|
$driverId = $request->attributes->get('_jwt_user_id');
|
||||||
|
|
||||||
$ride = Ride::where('id', $rideId)
|
$ride = Ride::where('id', $rideId)
|
||||||
->where('driver_id', $driverId)
|
->where('driver_id', $driverId)
|
||||||
@@ -280,7 +280,7 @@ class RideController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function arrive(Request $request, int $rideId): JsonResponse
|
public function arrive(Request $request, int $rideId): JsonResponse
|
||||||
{
|
{
|
||||||
$driverId = $request->input('_jwt_user_id');
|
$driverId = $request->attributes->get('_jwt_user_id');
|
||||||
|
|
||||||
$ride = Ride::where('id', $rideId)
|
$ride = Ride::where('id', $rideId)
|
||||||
->where('driver_id', $driverId)
|
->where('driver_id', $driverId)
|
||||||
@@ -316,7 +316,7 @@ class RideController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function finish(Request $request, int $rideId): JsonResponse
|
public function finish(Request $request, int $rideId): JsonResponse
|
||||||
{
|
{
|
||||||
$driverId = $request->input('_jwt_user_id');
|
$driverId = $request->attributes->get('_jwt_user_id');
|
||||||
|
|
||||||
$request->validate([
|
$request->validate([
|
||||||
'price_for_driver' => 'required|numeric',
|
'price_for_driver' => 'required|numeric',
|
||||||
@@ -397,7 +397,7 @@ class RideController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function cancelByPassenger(Request $request, int $rideId): JsonResponse
|
public function cancelByPassenger(Request $request, int $rideId): JsonResponse
|
||||||
{
|
{
|
||||||
$passengerId = $request->input('_jwt_user_id');
|
$passengerId = $request->attributes->get('_jwt_user_id');
|
||||||
|
|
||||||
$ride = Ride::where('id', $rideId)
|
$ride = Ride::where('id', $rideId)
|
||||||
->where('passenger_id', $passengerId)
|
->where('passenger_id', $passengerId)
|
||||||
@@ -452,7 +452,7 @@ class RideController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function cancelByDriver(Request $request, int $rideId): JsonResponse
|
public function cancelByDriver(Request $request, int $rideId): JsonResponse
|
||||||
{
|
{
|
||||||
$driverId = $request->input('_jwt_user_id');
|
$driverId = $request->attributes->get('_jwt_user_id');
|
||||||
|
|
||||||
$ride = Ride::where('id', $rideId)
|
$ride = Ride::where('id', $rideId)
|
||||||
->where('driver_id', $driverId)
|
->where('driver_id', $driverId)
|
||||||
@@ -520,8 +520,8 @@ class RideController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function active(Request $request): JsonResponse
|
public function active(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
$userId = $request->input('_jwt_user_id');
|
$userId = $request->attributes->get('_jwt_user_id');
|
||||||
$userType = $request->input('_jwt_user_type');
|
$userType = $request->attributes->get('_jwt_user_type');
|
||||||
|
|
||||||
$query = Ride::active();
|
$query = Ride::active();
|
||||||
if ($userType === 'driver') {
|
if ($userType === 'driver') {
|
||||||
@@ -544,8 +544,8 @@ class RideController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function index(Request $request): JsonResponse
|
public function index(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
$userId = $request->input('_jwt_user_id');
|
$userId = $request->attributes->get('_jwt_user_id');
|
||||||
$userType = $request->input('_jwt_user_type');
|
$userType = $request->attributes->get('_jwt_user_type');
|
||||||
$page = $request->input('page', 1);
|
$page = $request->input('page', 1);
|
||||||
$limit = min($request->input('limit', 20), 50);
|
$limit = min($request->input('limit', 20), 50);
|
||||||
|
|
||||||
|
|||||||
@@ -196,7 +196,7 @@ class TrackingController extends Controller
|
|||||||
/** GET /v2/tracking/captain-stats */
|
/** GET /v2/tracking/captain-stats */
|
||||||
public function captainStats(Request $request): JsonResponse
|
public function captainStats(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
$driverId = $request->input('_jwt_user_id');
|
$driverId = $request->attributes->get('_jwt_user_id');
|
||||||
|
|
||||||
$totalRides = DB::connection('ride')->table('ride')
|
$totalRides = DB::connection('ride')->table('ride')
|
||||||
->where('driver_id', $driverId)
|
->where('driver_id', $driverId)
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ class UploadController extends Controller
|
|||||||
return response()->json(['status' => 'failure', 'message' => 'File too large'], 400);
|
return response()->json(['status' => 'failure', 'message' => 'File too large'], 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
$userId = $request->input('_jwt_user_id');
|
$userId = $request->attributes->get('_jwt_user_id');
|
||||||
$ext = $file->getClientOriginalExtension() ?: 'mp3';
|
$ext = $file->getClientOriginalExtension() ?: 'mp3';
|
||||||
$filename = 'audio_' . Str::random(24) . '.' . $ext;
|
$filename = 'audio_' . Str::random(24) . '.' . $ext;
|
||||||
|
|
||||||
@@ -130,7 +130,7 @@ class UploadController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generate safe filename
|
// Generate safe filename
|
||||||
$userId = $request->input('_jwt_user_id');
|
$userId = $request->attributes->get('_jwt_user_id');
|
||||||
$ext = match ($mime) {
|
$ext = match ($mime) {
|
||||||
'image/jpeg' => 'jpg',
|
'image/jpeg' => 'jpg',
|
||||||
'image/png' => 'png',
|
'image/png' => 'png',
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class WalletController extends Controller
|
|||||||
/** GET /v2/wallet/passenger */
|
/** GET /v2/wallet/passenger */
|
||||||
public function index(Request $request): JsonResponse
|
public function index(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
$id = $request->input('_jwt_user_id');
|
$id = $request->attributes->get('_jwt_user_id');
|
||||||
$wallet = DB::connection('primary')->table('passengerWallet')
|
$wallet = DB::connection('primary')->table('passengerWallet')
|
||||||
->where('passenger_id', $id)->first();
|
->where('passenger_id', $id)->first();
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ class WalletController extends Controller
|
|||||||
/** GET /v2/wallet/passenger/balance */
|
/** GET /v2/wallet/passenger/balance */
|
||||||
public function balance(Request $request): JsonResponse
|
public function balance(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
$id = $request->input('_jwt_user_id');
|
$id = $request->attributes->get('_jwt_user_id');
|
||||||
$bal = DB::connection('primary')->table('passengerWallet')
|
$bal = DB::connection('primary')->table('passengerWallet')
|
||||||
->where('passenger_id', $id)->value('balance') ?? '0.00';
|
->where('passenger_id', $id)->value('balance') ?? '0.00';
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@ class WalletController extends Controller
|
|||||||
'payment_method' => 'required|string',
|
'payment_method' => 'required|string',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$id = $request->input('_jwt_user_id');
|
$id = $request->attributes->get('_jwt_user_id');
|
||||||
|
|
||||||
DB::connection('primary')->beginTransaction();
|
DB::connection('primary')->beginTransaction();
|
||||||
try {
|
try {
|
||||||
@@ -95,15 +95,22 @@ class WalletController extends Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** PUT /v2/wallet/passenger */
|
/** PUT /v2/wallet/passenger — ADMIN ONLY */
|
||||||
public function update(Request $request): JsonResponse
|
public function update(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
$request->validate(['balance' => 'required|numeric|min:0']);
|
// Only admins can directly set balance
|
||||||
|
$userType = $request->attributes->get('_jwt_user_type');
|
||||||
|
if ($userType !== 'admin') {
|
||||||
|
return response()->json(['status' => 'failure', 'message' => 'Unauthorized'], 403);
|
||||||
|
}
|
||||||
|
|
||||||
$id = $request->input('_jwt_user_id');
|
$request->validate([
|
||||||
|
'balance' => 'required|numeric|min:0',
|
||||||
|
'passenger_id' => 'required|string',
|
||||||
|
]);
|
||||||
|
|
||||||
DB::connection('primary')->table('passengerWallet')
|
DB::connection('primary')->table('passengerWallet')
|
||||||
->where('passenger_id', $id)
|
->where('passenger_id', $request->input('passenger_id'))
|
||||||
->update(['balance' => $request->input('balance')]);
|
->update(['balance' => $request->input('balance')]);
|
||||||
|
|
||||||
return response()->json(['status' => 'success']);
|
return response()->json(['status' => 'success']);
|
||||||
@@ -112,7 +119,7 @@ class WalletController extends Controller
|
|||||||
/** DELETE /v2/wallet/passenger */
|
/** DELETE /v2/wallet/passenger */
|
||||||
public function destroy(Request $request): JsonResponse
|
public function destroy(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
$id = $request->input('_jwt_user_id');
|
$id = $request->attributes->get('_jwt_user_id');
|
||||||
DB::connection('primary')->table('passengerWallet')
|
DB::connection('primary')->table('passengerWallet')
|
||||||
->where('passenger_id', $id)->delete();
|
->where('passenger_id', $id)->delete();
|
||||||
|
|
||||||
@@ -122,7 +129,7 @@ class WalletController extends Controller
|
|||||||
/** GET /v2/wallet/passenger/transactions */
|
/** GET /v2/wallet/passenger/transactions */
|
||||||
public function transactions(Request $request): JsonResponse
|
public function transactions(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
$id = $request->input('_jwt_user_id');
|
$id = $request->attributes->get('_jwt_user_id');
|
||||||
$page = (int) $request->input('page', 1);
|
$page = (int) $request->input('page', 1);
|
||||||
$limit = min((int) $request->input('limit', 20), 50);
|
$limit = min((int) $request->input('limit', 20), 50);
|
||||||
|
|
||||||
@@ -145,7 +152,7 @@ class WalletController extends Controller
|
|||||||
'amount' => 'required|numeric|min:0.01',
|
'amount' => 'required|numeric|min:0.01',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$id = $request->input('_jwt_user_id');
|
$id = $request->attributes->get('_jwt_user_id');
|
||||||
|
|
||||||
DB::connection('primary')->table('payment_tokens_passenger')->insert([
|
DB::connection('primary')->table('payment_tokens_passenger')->insert([
|
||||||
'token' => $request->input('token'),
|
'token' => $request->input('token'),
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ class AdminMiddleware
|
|||||||
{
|
{
|
||||||
public function handle(Request $request, Closure $next)
|
public function handle(Request $request, Closure $next)
|
||||||
{
|
{
|
||||||
$userType = $request->input('_jwt_user_type');
|
$userType = $request->attributes->get('_jwt_user_type');
|
||||||
|
|
||||||
if ($userType !== 'admin') {
|
if ($userType !== 'admin') {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
|
|||||||
@@ -33,10 +33,69 @@ class HmacAuthMiddleware
|
|||||||
|
|
||||||
public function handle(Request $request, Closure $next)
|
public function handle(Request $request, Closure $next)
|
||||||
{
|
{
|
||||||
// In V2 transition, we allow requests without HMAC to pass through
|
$signature = $request->header('X-Signature');
|
||||||
// as long as they have a valid JWT (checked by next middleware).
|
$timestamp = $request->header('X-Timestamp');
|
||||||
// This maintains compatibility while we update the database schema.
|
$nonce = $request->header('X-Nonce');
|
||||||
|
$apiKey = $request->header('X-API-Key');
|
||||||
|
|
||||||
|
// All headers required
|
||||||
|
if (!$signature || !$timestamp || !$nonce || !$apiKey) {
|
||||||
|
return response()->json([
|
||||||
|
'status' => 'failure',
|
||||||
|
'message' => 'Missing security headers'
|
||||||
|
], 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reject if timestamp older than tolerance (replay protection)
|
||||||
|
$tolerance = config('intaleq.hmac_tolerance', 300);
|
||||||
|
if (abs(time() - (int) $timestamp) > $tolerance) {
|
||||||
|
return response()->json([
|
||||||
|
'status' => 'failure',
|
||||||
|
'message' => 'Request expired'
|
||||||
|
], 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nonce replay check (prevent duplicate requests)
|
||||||
|
$nonceKey = 'nonce:' . $nonce;
|
||||||
|
if (Cache::has($nonceKey)) {
|
||||||
|
return response()->json([
|
||||||
|
'status' => 'failure',
|
||||||
|
'message' => 'Duplicate request'
|
||||||
|
], 401);
|
||||||
|
}
|
||||||
|
Cache::put($nonceKey, true, $tolerance);
|
||||||
|
|
||||||
|
// Lookup api_secret by api_key
|
||||||
|
$user = DB::connection('primary')->table('passengers')
|
||||||
|
->where('api_key', $apiKey)->first(['api_secret']);
|
||||||
|
if (!$user) {
|
||||||
|
$user = DB::connection('primary')->table('driver')
|
||||||
|
->where('api_key', $apiKey)->first(['api_secret']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$user) {
|
||||||
|
return response()->json([
|
||||||
|
'status' => 'failure',
|
||||||
|
'message' => 'Invalid API key'
|
||||||
|
], 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute expected signature
|
||||||
|
// METHOD:PATH:TIMESTAMP:NONCE:BODY
|
||||||
|
$method = strtoupper($request->method());
|
||||||
|
$path = $request->path(); // returns path without leading slash (e.g. v2/ride/create)
|
||||||
|
$body = $request->getContent();
|
||||||
|
|
||||||
|
$payload = "$method:$path:$timestamp:$nonce:$body";
|
||||||
|
$expected = hash_hmac(self::ALGORITHM, $payload, $user->api_secret);
|
||||||
|
|
||||||
|
if (!hash_equals($expected, $signature)) {
|
||||||
|
return response()->json([
|
||||||
|
'status' => 'failure',
|
||||||
|
'message' => 'Invalid signature'
|
||||||
|
], 401);
|
||||||
|
}
|
||||||
|
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,12 +38,19 @@ class JwtAuthMiddleware
|
|||||||
try {
|
try {
|
||||||
$decoded = JWT::decode($token, new Key(config('intaleq.jwt_secret'), 'HS256'));
|
$decoded = JWT::decode($token, new Key(config('intaleq.jwt_secret'), 'HS256'));
|
||||||
|
|
||||||
// Attach JWT claims to request
|
// Verify issuer (defense in depth)
|
||||||
$request->merge([
|
$iss = $decoded->iss ?? '';
|
||||||
'_jwt_user_id' => $decoded->user_id ?? null,
|
if (!in_array($iss, ['Tripz', 'Tripz-Wallet'])) {
|
||||||
'_jwt_user_type' => $decoded->user_type ?? null,
|
return response()->json([
|
||||||
'_jwt_fingerprint' => $decoded->fingerprint ?? null,
|
'status' => 'failure',
|
||||||
]);
|
'message' => 'Invalid token issuer'
|
||||||
|
], 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attach JWT claims to request attributes (internal, not spoofable via POST/GET)
|
||||||
|
$request->attributes->set('_jwt_user_id', $decoded->user_id ?? null);
|
||||||
|
$request->attributes->set('_jwt_user_type', $decoded->user_type ?? null);
|
||||||
|
$request->attributes->set('_jwt_fingerprint', $decoded->fingerprint ?? null);
|
||||||
|
|
||||||
return $next($request);
|
return $next($request);
|
||||||
|
|
||||||
|
|||||||
@@ -20,16 +20,30 @@ return Application::configure(basePath: dirname(__DIR__))
|
|||||||
// Global API middleware
|
// Global API middleware
|
||||||
$middleware->api(prepend: [
|
$middleware->api(prepend: [
|
||||||
\Illuminate\Http\Middleware\HandleCors::class,
|
\Illuminate\Http\Middleware\HandleCors::class,
|
||||||
|
'throttle:120,1',
|
||||||
]);
|
]);
|
||||||
})
|
})
|
||||||
->withExceptions(function (Exceptions $exceptions) {
|
->withExceptions(function (Exceptions $exceptions) {
|
||||||
// سنظهر الخطأ الحقيقي لنعرف ماذا يحدث في الـ login-jwt
|
|
||||||
$exceptions->render(function (\Throwable $e) {
|
$exceptions->render(function (\Throwable $e) {
|
||||||
|
\Log::error('Unhandled Exception', [
|
||||||
|
'message' => $e->getMessage(),
|
||||||
|
'file' => $e->getFile(),
|
||||||
|
'line' => $e->getLine(),
|
||||||
|
'trace' => $e->getTraceAsString(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (config('app.debug')) {
|
||||||
|
return response()->json([
|
||||||
|
'status' => 'failure',
|
||||||
|
'message' => 'DEBUG ERROR: ' . $e->getMessage(),
|
||||||
|
'file' => $e->getFile(),
|
||||||
|
'line' => $e->getLine()
|
||||||
|
], 500);
|
||||||
|
}
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'status' => 'failure',
|
'status' => 'failure',
|
||||||
'message' => 'DEBUG ERROR: ' . $e->getMessage(),
|
'message' => 'Internal server error',
|
||||||
'file' => $e->getFile(),
|
|
||||||
'line' => $e->getLine()
|
|
||||||
], 500);
|
], 500);
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -30,12 +30,12 @@ return [
|
|||||||
'port' => env('DB_PRIMARY_PORT', '3306'),
|
'port' => env('DB_PRIMARY_PORT', '3306'),
|
||||||
'database' => env('DB_PRIMARY_NAME_V2', 'intaleqDBV2'),
|
'database' => env('DB_PRIMARY_NAME_V2', 'intaleqDBV2'),
|
||||||
'username' => env('DB_PRIMARY_USER_V2', 'intaleqUserV2'),
|
'username' => env('DB_PRIMARY_USER_V2', 'intaleqUserV2'),
|
||||||
'password' => env('DB_PRIMARY_PASS_V2', 'cqdEGlg'),
|
'password' => env('DB_PRIMARY_PASS_V2', ''),
|
||||||
'unix_socket' => env('DB_SOCKET', ''),
|
'unix_socket' => env('DB_SOCKET', ''),
|
||||||
'charset' => 'utf8mb4',
|
'charset' => 'utf8mb4',
|
||||||
'collation' => 'utf8mb4_general_ci',
|
'collation' => 'utf8mb4_general_ci',
|
||||||
'prefix' => '',
|
'prefix' => '',
|
||||||
'strict' => false,
|
'strict' => true,
|
||||||
'engine' => 'InnoDB',
|
'engine' => 'InnoDB',
|
||||||
'timezone' => '+03:00',
|
'timezone' => '+03:00',
|
||||||
'options' => extension_loaded('pdo_mysql') ? [
|
'options' => extension_loaded('pdo_mysql') ? [
|
||||||
@@ -61,7 +61,7 @@ return [
|
|||||||
'charset' => 'utf8mb4',
|
'charset' => 'utf8mb4',
|
||||||
'collation' => 'utf8mb4_general_ci',
|
'collation' => 'utf8mb4_general_ci',
|
||||||
'prefix' => '',
|
'prefix' => '',
|
||||||
'strict' => false,
|
'strict' => true,
|
||||||
'engine' => 'InnoDB',
|
'engine' => 'InnoDB',
|
||||||
'timezone' => '+03:00',
|
'timezone' => '+03:00',
|
||||||
'options' => extension_loaded('pdo_mysql') ? [
|
'options' => extension_loaded('pdo_mysql') ? [
|
||||||
@@ -86,7 +86,7 @@ return [
|
|||||||
'charset' => 'utf8mb4',
|
'charset' => 'utf8mb4',
|
||||||
'collation' => 'utf8mb4_general_ci',
|
'collation' => 'utf8mb4_general_ci',
|
||||||
'prefix' => '',
|
'prefix' => '',
|
||||||
'strict' => false,
|
'strict' => true,
|
||||||
'engine' => 'InnoDB',
|
'engine' => 'InnoDB',
|
||||||
'timezone' => '+03:00',
|
'timezone' => '+03:00',
|
||||||
'options' => extension_loaded('pdo_mysql') ? [
|
'options' => extension_loaded('pdo_mysql') ? [
|
||||||
|
|||||||
@@ -45,9 +45,9 @@ return [
|
|||||||
// 'internal_socket_key_path' => env('INTERNAL_SOCKET_KEY_PATH', base_path('.internal_socket_key')),
|
// 'internal_socket_key_path' => env('INTERNAL_SOCKET_KEY_PATH', base_path('.internal_socket_key')),
|
||||||
|
|
||||||
// Rate Limiting
|
// Rate Limiting
|
||||||
'rate_limit_login' => (int) env('RATE_LIMIT_LOGIN', 5),
|
'rate_limit_login' => (int) env('RATE_LIMIT_LOGIN', 3),
|
||||||
'rate_limit_login_decay' => (int) env('RATE_LIMIT_LOGIN_DECAY', 60),
|
'rate_limit_login_decay' => (int) env('RATE_LIMIT_LOGIN_DECAY', 60),
|
||||||
'rate_limit_api' => (int) env('RATE_LIMIT_API', 60),
|
'rate_limit_api' => (int) env('RATE_LIMIT_API', 120),
|
||||||
'rate_limit_api_decay' => (int) env('RATE_LIMIT_API_DECAY', 60),
|
'rate_limit_api_decay' => (int) env('RATE_LIMIT_API_DECAY', 60),
|
||||||
|
|
||||||
// Upload
|
// Upload
|
||||||
|
|||||||
Reference in New Issue
Block a user