refactor(social): single source of truth for token redaction
Same shape of bug as the refreshToken duplication: the regex that strips access_token / Bearer headers from logged HTTP bodies existed in three near-identical copies (HasSocialHttpClient trait, TokenRefreshClient, SocialPublishException), drifting subtly — SocialPublishException was missing the JSON "token" pattern. Extracts a TokenRedactor::redact(string) helper and routes all callers through it. Adding a new token format now means one regex in one file.
This commit is contained in:
parent
fcb70b3599
commit
cf80e1fcae
4 changed files with 38 additions and 58 deletions
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
namespace App\Exceptions\Social;
|
||||
|
||||
use App\Services\Social\TokenRedactor;
|
||||
use RuntimeException;
|
||||
|
||||
abstract class SocialPublishException extends RuntimeException
|
||||
|
|
@ -27,32 +28,10 @@ public function context(): array
|
|||
'category' => $this->category->value,
|
||||
'platform_error_code' => $this->platformErrorCode,
|
||||
'user_message' => $this->userMessage,
|
||||
'raw_response' => $this->redactTokens($this->rawResponse),
|
||||
'raw_response' => $this->rawResponse !== null ? TokenRedactor::redact($this->rawResponse) : null,
|
||||
];
|
||||
}
|
||||
|
||||
private function redactTokens(?string $text): ?string
|
||||
{
|
||||
if ($text === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Redact common token patterns from API error responses
|
||||
return preg_replace(
|
||||
[
|
||||
'/access_token=([^&"\s]+)/',
|
||||
'/"access_token"\s*:\s*"([^"]+)"/',
|
||||
'/Bearer\s+\S+/',
|
||||
],
|
||||
[
|
||||
'access_token=[REDACTED]',
|
||||
'"access_token":"[REDACTED]"',
|
||||
'Bearer [REDACTED]',
|
||||
],
|
||||
$text
|
||||
);
|
||||
}
|
||||
|
||||
abstract public static function fromApiResponse(mixed $response): static;
|
||||
|
||||
abstract public function platform(): string;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
namespace App\Services\Social\Concerns;
|
||||
|
||||
use App\Models\PostPlatform;
|
||||
use App\Services\Social\TokenRedactor;
|
||||
use Illuminate\Http\Client\PendingRequest;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
|
|
@ -38,20 +39,6 @@ protected function socialHttp(): PendingRequest
|
|||
|
||||
protected function redactResponseBody(string $body): string
|
||||
{
|
||||
return preg_replace(
|
||||
[
|
||||
'/access_token=([^&"\s]+)/',
|
||||
'/"access_token"\s*:\s*"([^"]+)"/',
|
||||
'/Bearer\s+\S+/',
|
||||
'/"token"\s*:\s*"([^"]+)"/',
|
||||
],
|
||||
[
|
||||
'access_token=[REDACTED]',
|
||||
'"access_token":"[REDACTED]"',
|
||||
'Bearer [REDACTED]',
|
||||
'"token":"[REDACTED]"',
|
||||
],
|
||||
$body
|
||||
);
|
||||
return TokenRedactor::redact($body);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
33
app/Services/Social/TokenRedactor.php
Normal file
33
app/Services/Social/TokenRedactor.php
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Social;
|
||||
|
||||
/**
|
||||
* Strips OAuth tokens from raw HTTP bodies before they hit logs or
|
||||
* exception messages. Centralizes the regex patterns so they evolve in
|
||||
* one place — adding a new token format (e.g. provider-specific) means
|
||||
* extending this list, not hunting through the codebase.
|
||||
*/
|
||||
class TokenRedactor
|
||||
{
|
||||
public static function redact(string $body): string
|
||||
{
|
||||
return preg_replace(
|
||||
[
|
||||
'/access_token=([^&"\s]+)/',
|
||||
'/"access_token"\s*:\s*"([^"]+)"/',
|
||||
'/Bearer\s+\S+/',
|
||||
'/"token"\s*:\s*"([^"]+)"/',
|
||||
],
|
||||
[
|
||||
'access_token=[REDACTED]',
|
||||
'"access_token":"[REDACTED]"',
|
||||
'Bearer [REDACTED]',
|
||||
'"token":"[REDACTED]"',
|
||||
],
|
||||
$body
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -58,7 +58,7 @@ public function send(Closure $request): Response
|
|||
|
||||
if ($response->failed()) {
|
||||
Log::error("TokenRefreshClient: {$name} token refresh failed", [
|
||||
'body' => $this->redactBody($response->body()),
|
||||
'body' => TokenRedactor::redact($response->body()),
|
||||
]);
|
||||
|
||||
$body = $response->json();
|
||||
|
|
@ -71,23 +71,4 @@ public function send(Closure $request): Response
|
|||
|
||||
return $response;
|
||||
}
|
||||
|
||||
private function redactBody(string $body): string
|
||||
{
|
||||
return preg_replace(
|
||||
[
|
||||
'/access_token=([^&"\s]+)/',
|
||||
'/"access_token"\s*:\s*"([^"]+)"/',
|
||||
'/Bearer\s+\S+/',
|
||||
'/"token"\s*:\s*"([^"]+)"/',
|
||||
],
|
||||
[
|
||||
'access_token=[REDACTED]',
|
||||
'"access_token":"[REDACTED]"',
|
||||
'Bearer [REDACTED]',
|
||||
'"token":"[REDACTED]"',
|
||||
],
|
||||
$body
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue