authByBearer($request)) { return $next($request); } if ($this->authByHmac($request)) { return $next($request); } $hasSecret = $this->sharedSecret() !== ''; $hasToken = $this->bearerToken() !== ''; if (! $hasSecret && ! $hasToken) { return response()->json([ 'message' => 'SUPERADMIN_SMS_SHARED_SECRET or SUPERADMIN_REMOTE_TOKEN is not configured', ], 503); } return response()->json(['message' => 'Unauthorized'], 401); } private function authByHmac(Request $request): bool { $secret = $this->sharedSecret(); if ($secret === '') { return false; } $domainHeader = trim((string) $request->header('X-Abshode-Domain', '')); $tsRaw = trim((string) $request->header('X-Abshode-Timestamp', '')); $sigRaw = trim((string) $request->header('X-Abshode-Signature', '')); if ($domainHeader === '' || $tsRaw === '' || $sigRaw === '') { return false; } if (! ctype_digit($tsRaw)) { return false; } $timestamp = (int) $tsRaw; if (abs(time() - $timestamp) > self::MAX_SKEW_SECONDS) { return false; } $domain = $this->normalizeHost($domainHeader); if ($domain === '' || ! $this->domainAllowed($domain, $request)) { return false; } $method = strtoupper($request->getMethod()); $payload = $timestamp.'.'.$method.'.'.self::CANONICAL_PATH.'.'.$domain; $expected = hash_hmac('sha256', $payload, $secret); return hash_equals(strtolower($expected), strtolower($sigRaw)); } private function authByBearer(Request $request): bool { $auth = (string) $request->header('Authorization', ''); if (! preg_match('/^\s*Bearer\s+(.+)\s*$/i', $auth, $m)) { return false; } $token = trim($m[1]); if ($token === '') { return false; } foreach ([$this->bearerToken(), $this->sharedSecret()] as $expected) { if ($expected !== '' && hash_equals($expected, $token)) { return true; } } return false; } private function domainAllowed(string $domain, Request $request): bool { $candidates = [ $this->configuredDomain(), $this->normalizeHost((string) $request->getHost()), ]; foreach ($candidates as $candidate) { if ($candidate !== '' && hash_equals($candidate, $domain)) { return true; } } return false; } private function sharedSecret(): string { return trim((string) config('services.superadmin.sms_shared_secret', '')); } private function bearerToken(): string { return trim((string) config('services.superadmin.remote_token', '')); } private function configuredDomain(): string { $override = trim((string) config('services.superadmin.sms_domain', '')); if ($override !== '') { return $this->normalizeHost($override); } $fromApp = parse_url((string) config('app.url'), PHP_URL_HOST); return $this->normalizeHost(is_string($fromApp) ? $fromApp : ''); } private function normalizeHost(string $value): string { $host = strtolower(trim($value)); $host = preg_replace('#^https?://#', '', $host) ?? $host; $host = explode('/', $host)[0] ?? $host; $host = explode(':', $host)[0] ?? $host; return trim($host); } }