Skip to content

Commit

Permalink
feat: improve Exception handling for FirebaseAdapter
Browse files Browse the repository at this point in the history
- add InvalidTokenException and use it
- change Exception classes
- add lang items for error messages
  • Loading branch information
kenjis committed Apr 18, 2023
1 parent 61f06fa commit 2dc1252
Show file tree
Hide file tree
Showing 18 changed files with 110 additions and 15 deletions.
27 changes: 20 additions & 7 deletions src/Authentication/JWT/Adapters/FirebaseAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
}

Expand Down
26 changes: 26 additions & 0 deletions src/Authentication/JWT/Exceptions/InvalidTokenException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace CodeIgniter\Shield\Authentication\JWT\Exceptions;

use CodeIgniter\Shield\Exceptions\ValidationException;
use Exception;

class InvalidTokenException extends ValidationException
{
public static function forInvalidToken(Exception $e): self
{
return new self(lang('Auth.invalidJWT'), 1, $e);
}

public static function forExpiredToken(Exception $e): self
{
return new self(lang('Auth.expiredJWT'), 2, $e);
}

public static function forBeforeValidToken(Exception $e): self
{
return new self(lang('Auth.beforeValidJWT'), 3, $e);
}
}
4 changes: 4 additions & 0 deletions src/Language/de/Auth.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
'unableSendEmailToUser' => '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',
Expand Down
4 changes: 4 additions & 0 deletions src/Language/en/Auth.php
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
4 changes: 4 additions & 0 deletions src/Language/es/Auth.php
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
4 changes: 4 additions & 0 deletions src/Language/fa/Auth.php
Original file line number Diff line number Diff line change
Expand Up @@ -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' => 'نام کاربری',
Expand Down
4 changes: 4 additions & 0 deletions src/Language/fr/Auth.php
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
4 changes: 4 additions & 0 deletions src/Language/id/Auth.php
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
4 changes: 4 additions & 0 deletions src/Language/it/Auth.php
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
4 changes: 4 additions & 0 deletions src/Language/ja/Auth.php
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
4 changes: 4 additions & 0 deletions src/Language/pt-BR/Auth.php
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
4 changes: 4 additions & 0 deletions src/Language/pt/Auth.php
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
4 changes: 4 additions & 0 deletions src/Language/sk/Auth.php
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
4 changes: 4 additions & 0 deletions src/Language/sr/Auth.php
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
4 changes: 4 additions & 0 deletions src/Language/sv-SE/Auth.php
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
4 changes: 4 additions & 0 deletions src/Language/tr/Auth.php
Original file line number Diff line number Diff line change
Expand Up @@ -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ı',
Expand Down
6 changes: 3 additions & 3 deletions tests/Authentication/Authenticators/JWTAuthenticatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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', [
Expand Down
10 changes: 5 additions & 5 deletions tests/Unit/Authentication/JWT/Adapters/FirebaseAdapaterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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();

Expand All @@ -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();

Expand Down

0 comments on commit 2dc1252

Please sign in to comment.