Update authentication logic and SDK fixes

This commit is contained in:
Hamza-Ayed
2026-04-24 15:12:12 +03:00
parent 2745b307a9
commit 4534e8769b
18 changed files with 198 additions and 78 deletions

View File

@@ -20,7 +20,7 @@ class AdminMiddleware
{
public function handle(Request $request, Closure $next)
{
$userType = $request->input('_jwt_user_type');
$userType = $request->attributes->get('_jwt_user_type');
if ($userType !== 'admin') {
return response()->json([

View File

@@ -33,10 +33,69 @@ class HmacAuthMiddleware
public function handle(Request $request, Closure $next)
{
// In V2 transition, we allow requests without HMAC to pass through
// as long as they have a valid JWT (checked by next middleware).
// This maintains compatibility while we update the database schema.
$signature = $request->header('X-Signature');
$timestamp = $request->header('X-Timestamp');
$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);
}
}

View File

@@ -38,12 +38,19 @@ class JwtAuthMiddleware
try {
$decoded = JWT::decode($token, new Key(config('intaleq.jwt_secret'), 'HS256'));
// Attach JWT claims to request
$request->merge([
'_jwt_user_id' => $decoded->user_id ?? null,
'_jwt_user_type' => $decoded->user_type ?? null,
'_jwt_fingerprint' => $decoded->fingerprint ?? null,
]);
// Verify issuer (defense in depth)
$iss = $decoded->iss ?? '';
if (!in_array($iss, ['Tripz', 'Tripz-Wallet'])) {
return response()->json([
'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);