Skip to content

Commit

Permalink
[Security] Deprecate current system in favor of a JWTTokenAuthenticat…
Browse files Browse the repository at this point in the history
…or (Guard) (#184)

| Q             | A    |
|---------------|------|
| Bug fix?      | no  |
| New feature?  | yes |
| BC breaks?    | no  |
| Deprecations | yes |
| Fixed tickets | ~ |
| Tests pass?   | yes  |

See #132 discussion.

@slashfan after you started to work on, I made some changes.
I'm really not an expert in the Guard component itself, but it seems make our life easier from all sides.

---

- [x] Add the Guard JWTAuthenticator
- [x] Test it
- [x] Add a full functional test case
- [x] Depreciate old security system
- [x] Update the documentation
chalasr authored Sep 20, 2016
1 parent 2a7dcbe commit dbc0d4c
Showing 55 changed files with 1,879 additions and 221 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -5,6 +5,10 @@ For a diff between two versions https://github.com/lexik/LexikJWTAuthenticationB

## [2.0](https://github.com/lexik/LexikJWTAuthenticationBundle/tree/2.0)

* feature [\#230](https://github.com/lexik/LexikJWTAuthenticationBundle/pull/230) Introduce JWTExpiredEvent ([chalasr](https://github.com/chalasr))

* feature [\#184](https://github.com/lexik/LexikJWTAuthenticationBundle/pull/184) [Security] Deprecate current system in favor of a JWTTokenAuthenticator (Guard) ([chalasr](https://github.com/chalasr))

* feature [\#218](https://github.com/lexik/LexikJWTAuthenticationBundle/pull/218) Add more flexibility in token extractors configuration ([chalasr](https://github.com/chalasr))

* feature [\#217](https://github.com/lexik/LexikJWTAuthenticationBundle/pull/217) Refactor TokenExtractors loading for easy overriding ([chalasr](https://github.com/chalasr))
@@ -25,7 +29,7 @@ For a diff between two versions https://github.com/lexik/LexikJWTAuthenticationB

## [1.7.0](https://github.com/lexik/LexikJWTAuthenticationBundle/tree/v1.7.0) (2016-08-06)

* feature [\#200](https://github.com/lexik/LexikJWTAuthenticationBundle/pull/200) Depreciate injection of Request instances ([chalasr](https://github.com/chalasr))
* feature [\#200](https://github.com/lexik/LexikJWTAuthenticationBundle/pull/200) Deprecate injection of Request instances ([chalasr](https://github.com/chalasr))

## [v1.6.0](https://github.com/lexik/LexikJWTAuthenticationBundle/tree/v1.6.0) (2016-07-07)

14 changes: 8 additions & 6 deletions Encoder/DefaultEncoder.php
Original file line number Diff line number Diff line change
@@ -35,11 +35,11 @@ public function encode(array $payload)
try {
$jws = $this->jwsProvider->create($payload);
} catch (InvalidArgumentException $e) {
throw new JWTEncodeFailureException('An error occurred while trying to encode the JWT token.', $e);
throw new JWTEncodeFailureException(JWTEncodeFailureException::INVALID_CONFIG, 'An error occured while trying to encode the JWT token. Please verify your configuration (private key/passphrase)', $e);
}

if (!$jws->isSigned()) {
throw new JWTEncodeFailureException('Unable to create a signed JWT from the given configuration.');
throw new JWTEncodeFailureException(JWTEncodeFailureException::UNSIGNED_TOKEN, 'Unable to create a signed JWT from the given configuration.');
}

return $jws->getToken();
@@ -53,17 +53,19 @@ public function decode($token)
try {
$jws = $this->jwsProvider->load($token);
} catch (InvalidArgumentException $e) {
throw new JWTDecodeFailureException('Invalid JWT Token', $e);
throw new JWTDecodeFailureException(JWTDecodeFailureException::INVALID_TOKEN, 'Invalid JWT Token', $e);
}

$payload = $jws->getPayload();

if ($jws->isExpired()) {
throw new JWTDecodeFailureException('Expired JWT token');
throw new JWTDecodeFailureException(JWTDecodeFailureException::EXPIRED_TOKEN, 'Expired JWT Token');
}

if (!$jws->isVerified()) {
throw new JWTDecodeFailureException('Unable to verify the given JWT through the given configuration. If the "lexik_jwt_authentication.encoder" encryption options have been changed since your last authentication, please renew the token. If the problem persists, verify that the configured keys/passphrase are valid.');
throw new JWTDecodeFailureException(JWTDecodeFailureException::UNVERIFIED_TOKEN, 'Unable to verify the given JWT through the given configuration. If the "lexik_jwt_authentication.encoder" encryption options have been changed since your last authentication, please renew the token. If the problem persists, verify that the configured keys/passphrase are valid.');
}

return $jws->getPayload();
return $payload;
}
}
8 changes: 5 additions & 3 deletions Encoder/JWTEncoderInterface.php
Original file line number Diff line number Diff line change
@@ -17,16 +17,18 @@ interface JWTEncoderInterface
*
* @return string the encoded token string
*
* @throws JWTEncodeFailureException If an error occurred during the creation of the token (invalid configuration...)
* @throws JWTEncodeFailureException If an error occurred while trying to create
* the token (invalid crypto key, invalid payload...)
*/
public function encode(array $data);

/**
* @param string $token
*
* @return false|array
* @return array
*
* @throws JWTDecodeFailureException If an error occurred during the loading of the token (invalid signature, expired token...)
* @throws JWTDecodeFailureException If an error occurred while trying to load the token
* (invalid signature, invalid crypto key, expired token...)
*/
public function decode($token);
}
12 changes: 12 additions & 0 deletions Event/JWTExpiredEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

namespace Lexik\Bundle\JWTAuthenticationBundle\Event;

/**
* JWTExpiredEvent.
*
* @author Robin Chalas <robin.chalas@gmail.com>
*/
class JWTExpiredEvent extends AuthenticationFailureEvent implements JWTFailureEventInterface
{
}
7 changes: 7 additions & 0 deletions Events.php
Original file line number Diff line number Diff line change
@@ -56,4 +56,11 @@ final class Events
* Hook into this event to set a custom response.
*/
const JWT_NOT_FOUND = 'lexik_jwt_authentication.on_jwt_not_found';

/**
* Dispatched when the token is expired.
* The expired token's payload can be retrieved by hooking into this event, so you can set a different
* response.
*/
const JWT_EXPIRED = 'lexik_jwt_authentication.on_jwt_expired';
}
23 changes: 23 additions & 0 deletions Exception/ExpiredTokenException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace Lexik\Bundle\JWTAuthenticationBundle\Exception;

use Lexik\Bundle\JWTAuthenticationBundle\Security\Guard\JWTTokenAuthenticator;
use Symfony\Component\Security\Core\Exception\AuthenticationException;

/**
* Exception that should be thrown from a {@link JWTTokenAuthenticator} implementation during
* an authentication process.
*
* @author Robin Chalas <robin.chalas@gmail.com>
*/
class ExpiredTokenException extends AuthenticationException
{
/**
* {@inheritdoc}
*/
public function getMessageKey()
{
return 'Expired JWT Token';
}
}
34 changes: 34 additions & 0 deletions Exception/InvalidPayloadException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

namespace Lexik\Bundle\JWTAuthenticationBundle\Exception;

use Symfony\Component\Security\Core\Exception\AuthenticationException;

/**
* Missing key in the token payload during authentication.
*
* @author Robin Chalas <robin.chalas@gmail.com>
*/
class InvalidPayloadException extends AuthenticationException
{
/**
* @var string
*/
private $invalidKey;

/**
* @param string $invalidKey The key that cannot be found in the payload
*/
public function __construct($invalidKey)
{
$this->invalidKey = $invalidKey;
}

/**
* {@inheritdoc}
*/
public function getMessageKey()
{
return sprintf('Unable to find key "%s" in the token payload.', $this->invalidKey);
}
}
21 changes: 21 additions & 0 deletions Exception/InvalidTokenException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace Lexik\Bundle\JWTAuthenticationBundle\Exception;

use Symfony\Component\Security\Core\Exception\AuthenticationException;

/**
* Exception to be thrown in case of invalid token during an authentication process.
*
* @author Robin Chalas <robin.chalas@gmail.com>
*/
class InvalidTokenException extends AuthenticationException
{
/**
* {@inheritdoc}
*/
public function getMessageKey()
{
return 'Invalid JWT Token';
}
}
5 changes: 4 additions & 1 deletion Exception/JWTDecodeFailureException.php
Original file line number Diff line number Diff line change
@@ -3,10 +3,13 @@
namespace Lexik\Bundle\JWTAuthenticationBundle\Exception;

/**
* Base class for exceptions thrown during JWTEncoderInterface::decode().
* JWTDecodeFailureException is thrown if an error occurs in the token decoding process.
*
* @author Robin Chalas <robin.chalas@gmail.com>
*/
class JWTDecodeFailureException extends JWTFailureException
{
const INVALID_TOKEN = 'invalid_token';
const UNVERIFIED_TOKEN = 'unverified_token';
const EXPIRED_TOKEN = 'expired_token';
}
4 changes: 3 additions & 1 deletion Exception/JWTEncodeFailureException.php
Original file line number Diff line number Diff line change
@@ -3,10 +3,12 @@
namespace Lexik\Bundle\JWTAuthenticationBundle\Exception;

/**
* Base class for exceptions thrown during JWTEncoderInterface::encode().
* JWTEncodeFailureException is thrown if an error occurs in the token encoding process.
*
* @author Robin Chalas <robin.chalas@gmail.com>
*/
class JWTEncodeFailureException extends JWTFailureException
{
const INVALID_CONFIG = 'invalid_config';
const UNSIGNED_TOKEN = 'unsigned_token';
}
18 changes: 17 additions & 1 deletion Exception/JWTFailureException.php
Original file line number Diff line number Diff line change
@@ -10,11 +10,27 @@
class JWTFailureException extends \Exception
{
/**
* @var string
*/
private $reason;

/**
* @param string $reason
* @param string $message
* @param \Exception|null $previous
*/
public function __construct($message, \Exception $previous = null)
public function __construct($reason, $message, \Exception $previous = null)
{
$this->reason = $reason;

parent::__construct($message, 0, $previous);
}

/**
* @return string
*/
public function getReason()
{
return $this->reason;
}
}
21 changes: 21 additions & 0 deletions Exception/MissingTokenException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace Lexik\Bundle\JWTAuthenticationBundle\Exception;

use Symfony\Component\Security\Core\Exception\AuthenticationException;

/**
* Exception to be thrown in case of invalid token during an authentication process.
*
* @author Robin Chalas <robin.chalas@gmail.com>
*/
class MissingTokenException extends AuthenticationException
{
/**
* {@inheritdoc}
*/
public function getMessageKey()
{
return 'JWT Token not found';
}
}
41 changes: 41 additions & 0 deletions Exception/UserNotFoundException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

namespace Lexik\Bundle\JWTAuthenticationBundle\Exception;

use Symfony\Component\Security\Core\Exception\AuthenticationException;

/**
* User not found during authentication.
*
* @author Robin Chalas <robin.chalas@gmail.com>
*/
class UserNotFoundException extends AuthenticationException
{
/**
* @var string
*/
private $userIdentityField;

/**
* @var string
*/
private $identity;

/**
* @param string $userIdentityField
* @param string $identity
*/
public function __construct($userIdentityField, $identity)
{
$this->userIdentityField = $userIdentityField;
$this->identity = $identity;
}

/**
* {@inheritdoc}
*/
public function getMessageKey()
{
return sprintf('Unable to load an user with property "%s" = "%s". If the user identity has changed, you must renew the token. Otherwise, verify that the "lexik_jwt_authentication.user_identity_field" config option is correctly set.', $this->userIdentityField, $this->identity);
}
}
2 changes: 2 additions & 0 deletions LexikJWTAuthenticationBundle.php
Original file line number Diff line number Diff line change
@@ -23,6 +23,8 @@ public function build(ContainerBuilder $container)

/** @var SecurityExtension $extension */
$extension = $container->getExtension('security');

// BC 1.x
$extension->addSecurityListenerFactory(new JWTFactory());
}
}
Loading

0 comments on commit dbc0d4c

Please sign in to comment.