fix(super): accept SMS shared secret as Bearer for kill-switch
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -7,10 +7,9 @@ use Illuminate\Http\Request;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Auth for SuperAdmin → product (kill-switch / super settings):
|
* Auth for SuperAdmin → product (kill-switch / super settings):
|
||||||
* 1) Preferred: shared HMAC (same secret as SMS)
|
* 1) Authorization Bearer matching SUPERADMIN_REMOTE_TOKEN
|
||||||
* X-Abshode-Domain, X-Abshode-Timestamp, X-Abshode-Signature
|
* OR SUPERADMIN_SMS_SHARED_SECRET (CDN-safe; same secret as SMS)
|
||||||
* payload = `{ts}.{METHOD}./api/super/settings.{domain}`
|
* 2) Optional: shared HMAC headers X-Abshode-* (may be stripped by CDN)
|
||||||
* 2) Fallback: Authorization Bearer {SUPERADMIN_REMOTE_TOKEN}
|
|
||||||
*/
|
*/
|
||||||
class EnsureSuperAdminRemoteToken
|
class EnsureSuperAdminRemoteToken
|
||||||
{
|
{
|
||||||
@@ -20,11 +19,11 @@ class EnsureSuperAdminRemoteToken
|
|||||||
|
|
||||||
public function handle(Request $request, Closure $next)
|
public function handle(Request $request, Closure $next)
|
||||||
{
|
{
|
||||||
if ($this->authByHmac($request)) {
|
if ($this->authByBearer($request)) {
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->authByBearer($request)) {
|
if ($this->authByHmac($request)) {
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,8 +61,7 @@ class EnsureSuperAdminRemoteToken
|
|||||||
}
|
}
|
||||||
|
|
||||||
$domain = $this->normalizeHost($domainHeader);
|
$domain = $this->normalizeHost($domainHeader);
|
||||||
$expectedDomain = $this->configuredDomain();
|
if ($domain === '' || ! $this->domainAllowed($domain, $request)) {
|
||||||
if ($domain === '' || $expectedDomain === '' || ! hash_equals($expectedDomain, $domain)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,19 +74,39 @@ class EnsureSuperAdminRemoteToken
|
|||||||
|
|
||||||
private function authByBearer(Request $request): bool
|
private function authByBearer(Request $request): bool
|
||||||
{
|
{
|
||||||
$expected = $this->bearerToken();
|
|
||||||
if ($expected === '') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$auth = (string) $request->header('Authorization', '');
|
$auth = (string) $request->header('Authorization', '');
|
||||||
if (! preg_match('/^\s*Bearer\s+(.+)\s*$/i', $auth, $m)) {
|
if (! preg_match('/^\s*Bearer\s+(.+)\s*$/i', $auth, $m)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$token = trim($m[1]);
|
$token = trim($m[1]);
|
||||||
|
if ($token === '') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return $token !== '' && hash_equals($expected, $token);
|
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
|
private function sharedSecret(): string
|
||||||
|
|||||||
Reference in New Issue
Block a user