From 7836b72d6d33379f6ba70aaff9700eb15f3b27c7 Mon Sep 17 00:00:00 2001 From: ykiakao Date: Thu, 21 May 2026 13:27:35 -0500 Subject: [PATCH] Normalize JWT public key PEM --- app/Http/Middleware/JwtAuthMiddleware.php | 31 ++++++++++++++++++++++- routes/api.php | 21 +++++++++++++-- routes/web.php | 21 +++++++++++++-- 3 files changed, 68 insertions(+), 5 deletions(-) diff --git a/app/Http/Middleware/JwtAuthMiddleware.php b/app/Http/Middleware/JwtAuthMiddleware.php index dac309f..b5d9f7f 100644 --- a/app/Http/Middleware/JwtAuthMiddleware.php +++ b/app/Http/Middleware/JwtAuthMiddleware.php @@ -103,7 +103,7 @@ class JwtAuthMiddleware private function signatureIsValid(string $token, string $signature): bool { [$header, $payload] = explode('.', $token, 3); - $publicKey = str_replace('\\n', "\n", (string) config('jwt.public_key')); + $publicKey = $this->normalizePublicKey((string) config('jwt.public_key')); if ($publicKey === '') { throw new \RuntimeException('JWT public key is empty'); @@ -129,6 +129,35 @@ class JwtAuthMiddleware return $result === 1; } + private function normalizePublicKey(string $publicKey): string + { + $publicKey = trim(str_replace(['\\r\\n', '\\n', '\\r', "\r\n", "\r"], "\n", $publicKey)); + + if ($publicKey === '') { + return ''; + } + + if ( + preg_match( + '/-----BEGIN PUBLIC KEY-----(.*?)-----END PUBLIC KEY-----/s', + $publicKey, + $matches + ) + ) { + $body = preg_replace('/\s+/', '', $matches[1]); + + if ($body === '') { + return ''; + } + + return "-----BEGIN PUBLIC KEY-----\n" + . chunk_split($body, 64, "\n") + . "-----END PUBLIC KEY-----\n"; + } + + return $publicKey; + } + private function tokenIsExpired(array $payload): bool { if (!isset($payload['exp']) || !is_numeric($payload['exp'])) { diff --git a/routes/api.php b/routes/api.php index 6300234..88ade54 100644 --- a/routes/api.php +++ b/routes/api.php @@ -30,12 +30,29 @@ Route::get('/health', function () { Route::get('/health-check-key', function () { $rawPublicKey = (string) config('jwt.public_key'); - $formattedPublicKey = str_replace('\\n', "\n", $rawPublicKey); + $formattedPublicKey = trim(str_replace(['\\r\\n', '\\n', '\\r', "\r\n", "\r"], "\n", $rawPublicKey)); + + if ( + preg_match( + '/-----BEGIN PUBLIC KEY-----(.*?)-----END PUBLIC KEY-----/s', + $formattedPublicKey, + $matches + ) + ) { + $body = preg_replace('/\s+/', '', $matches[1]); + $formattedPublicKey = "-----BEGIN PUBLIC KEY-----\n" + . chunk_split($body, 64, "\n") + . "-----END PUBLIC KEY-----\n"; + } + $publicKeyResource = openssl_pkey_get_public($formattedPublicKey); return response()->json([ 'raw_key_empty' => $rawPublicKey === '', - 'key_length' => strlen($formattedPublicKey), + 'raw_key_length' => strlen($rawPublicKey), + 'formatted_key_length' => strlen($formattedPublicKey), + 'has_begin_marker' => str_contains($rawPublicKey, '-----BEGIN PUBLIC KEY-----'), + 'has_end_marker' => str_contains($rawPublicKey, '-----END PUBLIC KEY-----'), 'openssl_accepted' => $publicKeyResource !== false, 'openssl_error' => openssl_error_string(), ]); diff --git a/routes/web.php b/routes/web.php index f260cea..5b24608 100644 --- a/routes/web.php +++ b/routes/web.php @@ -17,12 +17,29 @@ Route::get('/health', function () { Route::get('/health-check-key', function () { $rawPublicKey = (string) config('jwt.public_key'); - $formattedPublicKey = str_replace('\\n', "\n", $rawPublicKey); + $formattedPublicKey = trim(str_replace(['\\r\\n', '\\n', '\\r', "\r\n", "\r"], "\n", $rawPublicKey)); + + if ( + preg_match( + '/-----BEGIN PUBLIC KEY-----(.*?)-----END PUBLIC KEY-----/s', + $formattedPublicKey, + $matches + ) + ) { + $body = preg_replace('/\s+/', '', $matches[1]); + $formattedPublicKey = "-----BEGIN PUBLIC KEY-----\n" + . chunk_split($body, 64, "\n") + . "-----END PUBLIC KEY-----\n"; + } + $publicKeyResource = openssl_pkey_get_public($formattedPublicKey); return response()->json([ 'raw_key_empty' => $rawPublicKey === '', - 'key_length' => strlen($formattedPublicKey), + 'raw_key_length' => strlen($rawPublicKey), + 'formatted_key_length' => strlen($formattedPublicKey), + 'has_begin_marker' => str_contains($rawPublicKey, '-----BEGIN PUBLIC KEY-----'), + 'has_end_marker' => str_contains($rawPublicKey, '-----END PUBLIC KEY-----'), 'openssl_accepted' => $publicKeyResource !== false, 'openssl_error' => openssl_error_string(), ]);