diff --git a/src/Authentication/JWT/Adapters/FirebaseAdapter.php b/src/Authentication/JWT/Adapters/FirebaseAdapter.php index 44311281c..27b88fd37 100644 --- a/src/Authentication/JWT/Adapters/FirebaseAdapter.php +++ b/src/Authentication/JWT/Adapters/FirebaseAdapter.php @@ -4,9 +4,11 @@ namespace CodeIgniter\Shield\Authentication\JWT\Adapters; +use CodeIgniter\Shield\Authentication\JWT\Exceptions\InvalidTokenException; use CodeIgniter\Shield\Authentication\JWT\JWSAdapterInterface; use CodeIgniter\Shield\Config\AuthJWT; -use CodeIgniter\Shield\Exceptions\RuntimeException; +use CodeIgniter\Shield\Exceptions\InvalidArgumentException as ShieldInvalidArgumentException; +use CodeIgniter\Shield\Exceptions\LogicException; use DomainException; use Firebase\JWT\BeforeValidException; use Firebase\JWT\ExpiredException; @@ -48,29 +50,40 @@ public static function decode(string $encodedToken, $keyset): stdClass return JWT::decode($encodedToken, $keys); } catch (InvalidArgumentException $e) { // provided key/key-array is empty or malformed. - throw new RuntimeException('Invalid JWT: ' . $e->getMessage(), 0, $e); + throw new ShieldInvalidArgumentException( + 'Invalid Keyset: "' . $keyset . '". ' . $e->getMessage(), + 0, + $e + ); } catch (DomainException $e) { // provided algorithm is unsupported OR // provided key is invalid OR // unknown error thrown in openSSL or libsodium OR // libsodium is required but not available. - throw new RuntimeException('Invalid JWT: ' . $e->getMessage(), 0, $e); + throw new LogicException('Cannot decode JWT: ' . $e->getMessage(), 0, $e); } catch (SignatureInvalidException $e) { // provided JWT signature verification failed. - throw new RuntimeException('Invalid JWT: ' . $e->getMessage(), 0, $e); + throw InvalidTokenException::forInvalidToken($e); } catch (BeforeValidException $e) { // provided JWT is trying to be used before "nbf" claim OR // provided JWT is trying to be used before "iat" claim. - throw new RuntimeException('Expired JWT: ' . $e->getMessage(), 0, $e); + throw InvalidTokenException::forBeforeValidToken($e); } catch (ExpiredException $e) { // provided JWT is trying to be used after "exp" claim. - throw new RuntimeException('Expired JWT: ' . $e->getMessage(), 0, $e); + throw InvalidTokenException::forExpiredToken($e); } catch (UnexpectedValueException $e) { // provided JWT is malformed OR // provided JWT is missing an algorithm / using an unsupported algorithm OR // provided JWT algorithm does not match provided key OR // provided key ID in key/key-array is empty or invalid. - throw new RuntimeException('Invalid JWT: ' . $e->getMessage(), 0, $e); + log_message( + 'error', + '[Shield] ' . class_basename(self::class) . '::' . __FUNCTION__ + . '(' . __LINE__ . ') ' + . get_class($e) . ': ' . $e->getMessage() + ); + + throw InvalidTokenException::forInvalidToken($e); } } diff --git a/src/Authentication/JWT/Exceptions/InvalidTokenException.php b/src/Authentication/JWT/Exceptions/InvalidTokenException.php new file mode 100644 index 000000000..b455dcfd0 --- /dev/null +++ b/src/Authentication/JWT/Exceptions/InvalidTokenException.php @@ -0,0 +1,26 @@ + 'Leider gab es ein Problem beim Senden der E-Mail. Wir konnten keine E-Mail an "{0}" senden.', 'throttled' => 'Es wurden zu viele Anfragen von dieser IP-Adresse gestellt. Sie können es in {0} Sekunden erneut versuchen.', 'notEnoughPrivilege' => 'Sie haben nicht die erforderliche Berechtigung, um den gewünschten Vorgang auszuführen.', + // JWT Exceptions + 'invalidJWT' => '(To be translated) The token is invalid.', + 'expiredJWT' => '(To be translated) The token has expired.', + 'beforeValidJWT' => '(To be translated) The token is not yet available.', 'email' => 'E-Mail-Adresse', 'username' => 'Benutzername', diff --git a/src/Language/en/Auth.php b/src/Language/en/Auth.php index 306c233d5..363fd4af7 100644 --- a/src/Language/en/Auth.php +++ b/src/Language/en/Auth.php @@ -20,6 +20,10 @@ 'unableSendEmailToUser' => 'Sorry, there was a problem sending the email. We could not send an email to "{0}".', 'throttled' => 'Too many requests made from this IP address. You may try again in {0} seconds.', 'notEnoughPrivilege' => 'You do not have the necessary permission to perform the desired operation.', + // JWT Exceptions + 'invalidJWT' => 'The token is invalid.', + 'expiredJWT' => 'The token has expired.', + 'beforeValidJWT' => 'The token is not yet available.', 'email' => 'Email Address', 'username' => 'Username', diff --git a/src/Language/es/Auth.php b/src/Language/es/Auth.php index bd99cb003..2cf2c6211 100644 --- a/src/Language/es/Auth.php +++ b/src/Language/es/Auth.php @@ -20,6 +20,10 @@ 'unableSendEmailToUser' => 'Lo siento, hubo un problema al enviar el correo electrónico. No pudimos enviar un correo electrónico a "{0}".', 'throttled' => 'Se han realizado demasiadas solicitudes desde esta dirección IP. Puedes intentarlo de nuevo en {0} segundos.', 'notEnoughPrivilege' => 'No tienes los permisos necesarios para realizar la operación deseada.', + // JWT Exceptions + 'invalidJWT' => '(To be translated) The token is invalid.', + 'expiredJWT' => '(To be translated) The token has expired.', + 'beforeValidJWT' => '(To be translated) The token is not yet available.', 'email' => 'Correo Electrónico', 'username' => 'Nombre de usuario', diff --git a/src/Language/fa/Auth.php b/src/Language/fa/Auth.php index b3d8e4fe5..ba2fe9c7d 100644 --- a/src/Language/fa/Auth.php +++ b/src/Language/fa/Auth.php @@ -20,6 +20,10 @@ 'unableSendEmailToUser' => 'متاسفانه, در ارسال ایمیل مشکلی پیش آمد. ما نتوانستیم ایمیلی را به "{0}" ارسال کنیم.', 'throttled' => 'درخواست های بسیار زیادی از این آدرس IP انجام شده است. می توانید بعد از {0} ثانیه دوباره امتحان کنید.', 'notEnoughPrivilege' => 'شما مجوز لازم برای انجام عملیات مورد نظر را ندارید.', + // JWT Exceptions + 'invalidJWT' => '(To be translated) The token is invalid.', + 'expiredJWT' => '(To be translated) The token has expired.', + 'beforeValidJWT' => '(To be translated) The token is not yet available.', 'email' => 'آدرس ایمیل', 'username' => 'نام کاربری', diff --git a/src/Language/fr/Auth.php b/src/Language/fr/Auth.php index 50c56881a..b43a354b0 100644 --- a/src/Language/fr/Auth.php +++ b/src/Language/fr/Auth.php @@ -20,6 +20,10 @@ 'unableSendEmailToUser' => 'Désolé, il y a eu un problème lors de l\'envoi de l\'email. Nous ne pouvons pas envoyer un email à "{0}".', 'throttled' => 'Trop de requêtes faites depuis cette adresse IP. Vous pouvez réessayer dans {0} secondes.', 'notEnoughPrivilege' => 'Vous n\'avez pas l\'autorisation nécessaire pour effectuer l\'opération souhaitée.', + // JWT Exceptions + 'invalidJWT' => '(To be translated) The token is invalid.', + 'expiredJWT' => '(To be translated) The token has expired.', + 'beforeValidJWT' => '(To be translated) The token is not yet available.', 'email' => 'Adresse email', 'username' => 'Identifiant', diff --git a/src/Language/id/Auth.php b/src/Language/id/Auth.php index ada97cf22..f2be28a35 100644 --- a/src/Language/id/Auth.php +++ b/src/Language/id/Auth.php @@ -20,6 +20,10 @@ 'unableSendEmailToUser' => 'Maaf, ada masalah saat mengirim email. Kami tidak dapat mengirim email ke "{0}".', 'throttled' => 'Terlalu banyak permintaan yang dibuat dari alamat IP ini. Anda dapat mencoba lagi dalam {0} detik.', 'notEnoughPrivilege' => 'Anda tidak memiliki izin yang diperlukan untuk melakukan operasi yang diinginkan.', + // JWT Exceptions + 'invalidJWT' => '(To be translated) The token is invalid.', + 'expiredJWT' => '(To be translated) The token has expired.', + 'beforeValidJWT' => '(To be translated) The token is not yet available.', 'email' => 'Alamat Email', 'username' => 'Nama Pengguna', diff --git a/src/Language/it/Auth.php b/src/Language/it/Auth.php index f517ac988..af2b41e24 100644 --- a/src/Language/it/Auth.php +++ b/src/Language/it/Auth.php @@ -20,6 +20,10 @@ 'unableSendEmailToUser' => 'Spiacente, c\'è stato un problema inviando l\'email. Non possiamo inviare un\'email a "{0}".', 'throttled' => 'Troppe richieste effettuate da questo indirizzo IP. Potrai riprovare tra {0} secondi.', 'notEnoughPrivilege' => 'Non si dispone dell\'autorizzazione necessaria per eseguire l\'operazione desiderata.', + // JWT Exceptions + 'invalidJWT' => '(To be translated) The token is invalid.', + 'expiredJWT' => '(To be translated) The token has expired.', + 'beforeValidJWT' => '(To be translated) The token is not yet available.', 'email' => 'Indirizzo Email', 'username' => 'Nome Utente', diff --git a/src/Language/ja/Auth.php b/src/Language/ja/Auth.php index e1d4870be..8d1ec4574 100644 --- a/src/Language/ja/Auth.php +++ b/src/Language/ja/Auth.php @@ -20,6 +20,10 @@ 'unableSendEmailToUser' => '申し訳ありませんが、メールの送信に問題がありました。 "{0}"にメールを送信できませんでした。', // 'Sorry, there was a problem sending the email. We could not send an email to "{0}".', 'throttled' => 'このIPアドレスからのリクエストが多すぎます。 {0}秒後に再試行できます。', // Too many requests made from this IP address. You may try again in {0} seconds. 'notEnoughPrivilege' => '目的の操作を実行するために必要な権限がありません。', // You do not have the necessary permission to perform the desired operation. + // JWT Exceptions + 'invalidJWT' => '(To be translated) The token is invalid.', + 'expiredJWT' => '(To be translated) The token has expired.', + 'beforeValidJWT' => '(To be translated) The token is not yet available.', 'email' => 'メールアドレス', // 'Email Address', 'username' => 'ユーザー名', // 'Username', diff --git a/src/Language/pt-BR/Auth.php b/src/Language/pt-BR/Auth.php index 4a9c6cbf4..e98ff1745 100644 --- a/src/Language/pt-BR/Auth.php +++ b/src/Language/pt-BR/Auth.php @@ -20,6 +20,10 @@ 'unableSendEmailToUser' => 'Desculpe, houve um problema ao enviar o email. Não pudemos enviar um email para {0}.', 'throttled' => 'Muitas solicitações feitas a partir deste endereço IP. Você pode tentar novamente em {0} segundos.', 'notEnoughPrivilege' => 'Você não tem a permissão necessária para realizar a operação desejada.', + // JWT Exceptions + 'invalidJWT' => '(To be translated) The token is invalid.', + 'expiredJWT' => '(To be translated) The token has expired.', + 'beforeValidJWT' => '(To be translated) The token is not yet available.', 'email' => 'Endereço de Email', 'username' => 'Nome de usuário', diff --git a/src/Language/pt/Auth.php b/src/Language/pt/Auth.php index 4a9057c3e..a5cfa8492 100644 --- a/src/Language/pt/Auth.php +++ b/src/Language/pt/Auth.php @@ -20,6 +20,10 @@ 'unableSendEmailToUser' => 'Desculpe, houve um problema ao enviar o email. Não pudemos enviar um email para {0}.', 'throttled' => 'Muitas solicitações feitas a partir deste endereço IP. Pode tentar novamente em {0} segundos.', 'notEnoughPrivilege' => 'Não tem a permissão necessária para realizar a operação desejada.', + // JWT Exceptions + 'invalidJWT' => '(To be translated) The token is invalid.', + 'expiredJWT' => '(To be translated) The token has expired.', + 'beforeValidJWT' => '(To be translated) The token is not yet available.', 'email' => 'Endereço de Email', 'username' => 'Nome de utilizador', diff --git a/src/Language/sk/Auth.php b/src/Language/sk/Auth.php index 3617e0560..3424e88c4 100644 --- a/src/Language/sk/Auth.php +++ b/src/Language/sk/Auth.php @@ -20,6 +20,10 @@ 'unableSendEmailToUser' => 'Ľutujeme, pri odosielaní e-mailu sa vyskytol problém. Nepodarilo sa nám odoslať e-mail na adresu „{0}".', 'throttled' => 'Z tejto adresy IP bolo odoslaných príliš veľa žiadostí. Môžete to skúsiť znova o {0} sekúnd.', 'notEnoughPrivilege' => 'Nemáte potrebné povolenie na vykonanie požadovanej operácie.', + // JWT Exceptions + 'invalidJWT' => '(To be translated) The token is invalid.', + 'expiredJWT' => '(To be translated) The token has expired.', + 'beforeValidJWT' => '(To be translated) The token is not yet available.', 'email' => 'Emailová adresa', 'username' => 'Používateľské meno', diff --git a/src/Language/sr/Auth.php b/src/Language/sr/Auth.php index 22b2ecb5d..6c8e71f01 100644 --- a/src/Language/sr/Auth.php +++ b/src/Language/sr/Auth.php @@ -20,6 +20,10 @@ 'unableSendEmailToUser' => 'Žao nam je ali slanje email poruke nije moguće. Nismo u mogućnosti poslati poruku na "{0}".', 'throttled' => 'Preveliki broj zahteva sa vaše IP adrese. Možete pokušati ponovo za {0} secondi.', 'notEnoughPrivilege' => 'Nemate dovoljan nivo autorizacije za zahtevanu akciju.', + // JWT Exceptions + 'invalidJWT' => '(To be translated) The token is invalid.', + 'expiredJWT' => '(To be translated) The token has expired.', + 'beforeValidJWT' => '(To be translated) The token is not yet available.', 'email' => 'E-mail Adresa', 'username' => 'Korisničko ime', diff --git a/src/Language/sv-SE/Auth.php b/src/Language/sv-SE/Auth.php index 7176f650a..a37b09665 100644 --- a/src/Language/sv-SE/Auth.php +++ b/src/Language/sv-SE/Auth.php @@ -20,6 +20,10 @@ 'unableSendEmailToUser' => 'Det var inte möjligt att skicka epost. Det gick inte att skicka till "{0}".', 'throttled' => 'För många anrop från denna IP-adress. Du kan försöka igen om {0} sekunder.', 'notEnoughPrivilege' => 'Du har inte nödvändiga rättigheter för detta kommando.', + // JWT Exceptions + 'invalidJWT' => '(To be translated) The token is invalid.', + 'expiredJWT' => '(To be translated) The token has expired.', + 'beforeValidJWT' => '(To be translated) The token is not yet available.', 'email' => 'Epostadress', 'username' => 'Användarnamn', diff --git a/src/Language/tr/Auth.php b/src/Language/tr/Auth.php index 48c7a247f..57f515e31 100644 --- a/src/Language/tr/Auth.php +++ b/src/Language/tr/Auth.php @@ -20,6 +20,10 @@ 'unableSendEmailToUser' => 'Üzgünüz, e-posta gönderilirken bir sorun oluştu. "{0}" adresine e-posta gönderemedik.', 'throttled' => 'Bu IP adresinden çok fazla istek yapıldı. {0} saniye sonra tekrar deneyebilirsiniz.', 'notEnoughPrivilege' => 'İstediğiniz işlemi gerçekleştirmek için gerekli izne sahip değilsiniz.', + // JWT Exceptions + 'invalidJWT' => '(To be translated) The token is invalid.', + 'expiredJWT' => '(To be translated) The token has expired.', + 'beforeValidJWT' => '(To be translated) The token is not yet available.', 'email' => 'E-posta Adresi', 'username' => 'Kullanıcı Adı', diff --git a/tests/Authentication/Authenticators/JWTAuthenticatorTest.php b/tests/Authentication/Authenticators/JWTAuthenticatorTest.php index 56972f4fb..f9be47533 100644 --- a/tests/Authentication/Authenticators/JWTAuthenticatorTest.php +++ b/tests/Authentication/Authenticators/JWTAuthenticatorTest.php @@ -113,7 +113,7 @@ public function testCheckBadSignatureToken(): void $result = $this->auth->check(['token' => self::BAD_JWT]); $this->assertFalse($result->isOK()); - $this->assertSame('Invalid JWT: Signature verification failed', $result->reason()); + $this->assertSame(lang('Auth.invalidJWT'), $result->reason()); } public function testCheckNoSubToken(): void @@ -140,7 +140,7 @@ public function testCheckOldToken(): void $result = $this->auth->check(['token' => $token]); $this->assertFalse($result->isOK()); - $this->assertSame('Expired JWT: Expired token', $result->reason()); + $this->assertSame(lang('Auth.expiredJWT'), $result->reason()); } public function testCheckNoUserInDatabase(): void @@ -188,7 +188,7 @@ public function testAttemptBadSignatureToken(): void $this->assertInstanceOf(Result::class, $result); $this->assertFalse($result->isOK()); - $this->assertSame('Invalid JWT: Signature verification failed', $result->reason()); + $this->assertSame(lang('Auth.invalidJWT'), $result->reason()); // A login attempt should have always been recorded $this->seeInDatabase('auth_token_logins', [ diff --git a/tests/Unit/Authentication/JWT/Adapters/FirebaseAdapaterTest.php b/tests/Unit/Authentication/JWT/Adapters/FirebaseAdapaterTest.php index 176d3e886..06c8d69d4 100644 --- a/tests/Unit/Authentication/JWT/Adapters/FirebaseAdapaterTest.php +++ b/tests/Unit/Authentication/JWT/Adapters/FirebaseAdapaterTest.php @@ -6,10 +6,10 @@ use CodeIgniter\I18n\Time; use CodeIgniter\Shield\Authentication\JWT\Adapters\FirebaseAdapter; +use CodeIgniter\Shield\Authentication\JWT\Exceptions\InvalidTokenException; use CodeIgniter\Shield\Authentication\JWTManager; use CodeIgniter\Shield\Config\AuthJWT; use CodeIgniter\Shield\Entities\User; -use CodeIgniter\Shield\Exceptions\RuntimeException; use CodeIgniter\Shield\Models\UserModel; use Tests\Support\TestCase; @@ -50,8 +50,8 @@ public static function generateJWT(?Time $clock = null): string public function testDecodeSignatureInvalidException(): void { - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Invalid JWT: Signature verification failed'); + $this->expectException(InvalidTokenException::class); + $this->expectExceptionMessage(lang('Auth.invalidJWT')); $jwtDecoder = new FirebaseAdapter(); @@ -62,8 +62,8 @@ public function testDecodeSignatureInvalidException(): void public function testDecodeExpiredException(): void { - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Expired JWT: Expired token'); + $this->expectException(InvalidTokenException::class); + $this->expectExceptionMessage(lang('Auth.expiredJWT')); $jwtDecoder = new FirebaseAdapter();