Skip to content

Commit

Permalink
Introduce JWTGuardAuthenticator
Browse files Browse the repository at this point in the history
Retrieve user from the dynamic userProvider
Catch possible encode/decode failure exceptions (better after #162)
Handle token extractors configuration
Split configuration for readability
Require browser-kit for tests
Refactor functional tests structure
Add more functional tests
Depreciate current system classes
Tests subscribing to authentication events
Update CHANGELOG according to these changes
Document config-related changes in UPGRADE.md
Deprecate JWTManagerInterface in favor of JWTTokenManagerInterface
Improve functional tests by adding CallableEventSubscriber
Avoid calling onAuthenticationFailure() from start()
Grammar
Scrutinizer fixes
  • Loading branch information
chalasr committed Aug 28, 2016
1 parent 228d090 commit 73ebbac
Show file tree
Hide file tree
Showing 41 changed files with 1,409 additions and 188 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ For a diff between two versions https://github.com/lexik/LexikJWTAuthenticationB

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

* 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))
Expand All @@ -25,7 +27,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)

Expand Down
2 changes: 1 addition & 1 deletion Encoder/DefaultEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public function decode($token)
}

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

if (!$jws->isVerified()) {
Expand Down
2 changes: 1 addition & 1 deletion Encoder/JWTEncoderInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ 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...)
*/
Expand Down
69 changes: 69 additions & 0 deletions Exception/JWTAuthenticationException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

namespace Lexik\Bundle\JWTAuthenticationBundle\Exception;

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

/**
* Exception to be used during a failed JWT authentication process.
*
* @author Robin Chalas <robin.chalas@gmail.com>
*/
class JWTAuthenticationException extends AuthenticationException
{
/**
* Returns an AuthenticationException in case of invalid token.
*
* To be used if the token cannot be properly decoded.
*
* @param JWTDecodeFailureException|null $previous
*
* @return JWTAuthenticationException
*/
public static function invalidToken(JWTDecodeFailureException $previous = null)
{
return new self($previous ? $previous->getMessage() : 'Invalid JWT Token', 0, $previous);
}

/**
* Returns an AuthenticationException in case of token not found.
*
* @param string $message
*
* @return JWTAuthenticationException
*/
public static function tokenNotFound($message = 'JWT Token not found')
{
return new self($message);
}

/**
* Returns an AuthenticationException in case of invalid user.
*
* To be used if no user can be loaded from the identity retrieved from
* the decoded token's payload.
*
* @param string $identity
* @param string $identityField
*
* @return JWTAuthenticationException
*/
public static function invalidUser($identity, $identityField)
{
return new self(sprintf('Unable to load a valid user with property "%s" = "%s". If the user identity has been changed, you must renew the token. Otherwise, verify that the "lexik_jwt_authentication.user_identity_field" config option is correctly set.', $identityField, $identity));
}

/**
* Returns an AuthenticationException in case of invalid payload.
*
* To be used if a key in missing in the payload or contains an unexpected value.
*
* @param string $message
*
* @return JWTAuthenticationException
*/
public static function invalidPayload($message = 'Invalid payload')
{
return new self($message);
}
}
36 changes: 26 additions & 10 deletions Resources/config/services.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,23 @@
<argument>%lexik_jwt_authentication.user_identity_field%</argument>
</call>
</service>
<!-- JWS Provider -->
<service id="lexik_jwt_authentication.jws_provider" class="Lexik\Bundle\JWTAuthenticationBundle\Services\JWSProvider" public="false">
<argument type="service" id="lexik_jwt_authentication.key_loader"/>
<argument>%lexik_jwt_authentication.encoder.encryption_engine%</argument>
<argument>%lexik_jwt_authentication.encoder.signature_algorithm%</argument>
</service>

<!-- Abstract JWT Token Authenticator / Guard implementation -->
<service id="lexik_jwt_authentication.security.guard.jwt_token_authenticator" class="Lexik\Bundle\JWTAuthenticationBundle\Security\Guard\JWTTokenAuthenticator" abstract="true">
<argument type="service" id="lexik_jwt_authentication.jwt_manager"/>
<argument type="service" id="event_dispatcher"/>
<argument type="service" id="lexik_jwt_authentication.extractor.chain_extractor"/>
<argument type="string" /> <!-- Custom user identity field -->
</service>
<!-- Default JWT Token Authenticator -->
<service id="lexik_jwt_authentication.jwt_token_authenticator" parent="lexik_jwt_authentication.security.guard.jwt_token_authenticator" />

<!-- JWT Authentication response interceptor -->
<service id="lexik_jwt_authentication.handler.authentication_success" class="Lexik\Bundle\JWTAuthenticationBundle\Security\Http\Authentication\AuthenticationSuccessHandler">
<argument type="service" id="lexik_jwt_authentication.jwt_manager"/>
Expand All @@ -28,6 +45,7 @@
<tag name="monolog.logger" channel="security" />
<argument type="service" id="event_dispatcher"/>
</service>

<!-- OpenSSL Key Loader -->
<service id="lexik_jwt_authentication.key_loader.openssl" class="Lexik\Bundle\JWTAuthenticationBundle\Services\KeyLoader\OpenSSLKeyLoader" public="false">
<argument>%lexik_jwt_authentication.private_key_path%</argument>
Expand All @@ -40,24 +58,30 @@
<argument>%lexik_jwt_authentication.public_key_path%</argument>
<argument>%lexik_jwt_authentication.pass_phrase%</argument>
</service>
<!-- JWT Security Authentication Provider -->

<!-- JWT Security Authentication Provider (Deprecated in 2.0) -->
<service id="lexik_jwt_authentication.security.authentication.provider" class="Lexik\Bundle\JWTAuthenticationBundle\Security\Authentication\Provider\JWTProvider" public="false">
<argument /> <!-- User Provider -->
<argument type="service" id="lexik_jwt_authentication.jwt_manager" />
<call method="setUserIdentityField">
<argument>%lexik_jwt_authentication.user_identity_field%</argument>
</call>
<argument type="service" id="event_dispatcher"/>
<deprecated>The "%service_id%" service is deprecated since LexikJWTAuthenticationBundle version 2.0 and will be removed in 3.0</deprecated>
</service>
<!-- JWT Security Authentication Listener -->
<!-- JWT Security Authentication Listener (Deprecated in 2.0) -->
<service id="lexik_jwt_authentication.security.authentication.listener" class="Lexik\Bundle\JWTAuthenticationBundle\Security\Firewall\JWTListener" public="false">
<argument type="service" id="security.token_storage"/>
<argument type="service" id="security.authentication.manager"/>
<argument /> <!-- Options -->
<call method="setDispatcher">
<argument type="service" id="event_dispatcher"/>
</call>
<deprecated>The "%service_id%" service is deprecated since LexikJWTAuthenticationBundle version 2.0 and will be removed in 3.0</deprecated>
</service>
<!-- JWT Security Authentication Entry Point (Deprecated in 2.0) -->
<service id="lexik_jwt_authentication.security.authentication.entry_point" class="Lexik\Bundle\JWTAuthenticationBundle\Security\Http\EntryPoint\JWTEntryPoint" public="false" />

<!-- Token Extractor Map -->
<service id="lexik_jwt_authentication.token_extractor_map" class="Lexik\Bundle\JWTAuthenticationBundle\Services\TokenExtractorMap" public="false">
<argument type="collection" /> <!-- The extractor services -->
Expand All @@ -82,14 +106,6 @@
<service id="lexik_jwt_authentication.extractor.cookie_extractor" class="Lexik\Bundle\JWTAuthenticationBundle\TokenExtractor\CookieTokenExtractor">
<argument /> <!-- Name -->
</service>
<!-- JWT Security Authentication Entry Point -->
<service id="lexik_jwt_authentication.security.authentication.entry_point" class="Lexik\Bundle\JWTAuthenticationBundle\Security\Http\EntryPoint\JWTEntryPoint" public="false" />
<!-- JWS Provider -->
<service id="lexik_jwt_authentication.jws_provider" class="Lexik\Bundle\JWTAuthenticationBundle\Services\JWSProvider" public="false">
<argument type="service" id="lexik_jwt_authentication.key_loader"/>
<argument>%lexik_jwt_authentication.encoder.encryption_engine%</argument>
<argument>%lexik_jwt_authentication.encoder.signature_algorithm%</argument>
</service>
</services>

</container>
78 changes: 36 additions & 42 deletions Resources/doc/1-configuration-reference.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
Configuration reference
=======================

Configuration reference
------------------------
Bundle configuration
---------------------

### Minimal configuration

### Full default configuration

Expand All @@ -11,38 +13,52 @@ Configuration reference
# ...
lexik_jwt_authentication:
# ssh private key path
private_key_path: %kernel.root_dir%/var/jwt/private.pem
private_key_path: '%kernel.root_dir%/var/jwt/private.pem'
# ssh public key path
public_key_path: %kernel.root_dir%/var/jwt/public.pem
public_key_path: '%kernel.root_dir%/var/jwt/public.pem'
# ssh key pass phrase
pass_phrase: ''
# token ttl
token_ttl: 3600
# key under which the user identity will be stored in the token payload
user_identity_field: username

# token encoding/decoding settings
encoder:
# token encoder/decoder service - default implementation based on the namshi/jose library
service: lexik_jwt_authentication.encoder.default
service: lexik_jwt_authentication.encoder.default
# encryption engine used by the encoder service
encryption_engine: openssl
encryption_engine: openssl
# encryption algorithm used by the encoder service
signature_algorithm: RS256
signature_algorithm: RS256

# token extraction settings
token_extractors:
authorization_header: # look for a token as Authorization Header
enabled: true
prefix: Bearer
name: Authorization
cookie: # check token in a cookie
enabled: false
name: BEARER
query_parameter: # check token in query string parameter
enabled: false
name: bearer
```
### Encoder configuration
#### Encoder configuration
#### service
##### service
Default based on the [Namshi/JOSE](https://github.com/namshi/jose) library.
To create your own encoder service, see the [JWT encoder service customization chapter](5-encoder-service.md).
#### encryption_engine
##### encryption_engine
One of `openssl` and `phpseclib`, the encryption engines supported by the default token encoder service.
See the [OpenSSL](https://github.com/openssl/openssl) and [phpseclib](https://github.com/phpseclib/phpseclib) documentations for more information.

#### signature_algorithm
##### signature_algorithm

One of the algorithms supported by the default encoder for the configured [encryption engine](#encryption_engine).

Expand All @@ -54,44 +70,22 @@ __Supported algorithms for OpenSSL:__
__Supported algorithms for phpseclib:__
- RS256, RS384, RS512 (RSA)

Security reference
-------------------

### Simplest configuration

``` yaml
# app/config/security.yml
# ...
firewalls:
# ...
api:
# ...
lexik_jwt: ~ # check token in Authorization Header, with a value prefix of 'Bearer'
```
Security configuration
-----------------------

### Full default configuration

``` yaml
```yaml
# app/config/security.yml
# ...
firewalls:
# ...
api:
# ...
# advanced configuration
lexik_jwt:
authorization_header: # check token in Authorization Header
enabled: true
prefix: Bearer
name: Authorization
cookie: # check token in a cookie
enabled: false
name: BEARER
query_parameter: # check token in query string parameter
enabled: false
name: bearer
throw_exceptions: false # When an authentication failure occurs, return a 401 response immediately
create_entry_point: true # When no authentication details are provided, create a default entry point that returns a 401 response
authentication_provider: lexik_jwt_authentication.security.authentication.provider
authentication_listener: lexik_jwt_authentication.security.authentication.listener
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
```

For more details about the `lexik_jwt_authentication.jwt_token_authenticator` service and how to
customize it, see ["Extending the Guard JWTTokenAuthenticator"](6-extending-jwt-authenticator.md)
Empty file.
6 changes: 6 additions & 0 deletions Security/Authentication/Provider/JWTProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Lexik\Bundle\JWTAuthenticationBundle\Events;
use Lexik\Bundle\JWTAuthenticationBundle\Exception\JWTDecodeFailureException;
use Lexik\Bundle\JWTAuthenticationBundle\Security\Authentication\Token\JWTUserToken;
use Lexik\Bundle\JWTAuthenticationBundle\Security\Guard\JWTTokenAuthenticator;
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTManagerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface;
Expand All @@ -17,6 +18,9 @@
* JWTProvider.
*
* @author Nicolas Cabot <n.cabot@lexik.fr>
*
* @deprecated since 2.0, will be removed in 3.0. See
* {@link JWTTokenAuthenticator} instead
*/
class JWTProvider implements AuthenticationProviderInterface
{
Expand Down Expand Up @@ -50,6 +54,8 @@ public function __construct(
JWTManagerInterface $jwtManager,
EventDispatcherInterface $dispatcher
) {
@trigger_error(sprintf('The "%s" class is deprecated since version 2.0 and will be removed in 3.0. See "%s" instead.', __CLASS__, JWTTokenAuthenticator::class), E_USER_DEPRECATED);

$this->userProvider = $userProvider;
$this->jwtManager = $jwtManager;
$this->dispatcher = $dispatcher;
Expand Down
55 changes: 55 additions & 0 deletions Security/Authentication/Token/PreAuthenticationJWTUserToken.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

namespace Lexik\Bundle\JWTAuthenticationBundle\Security\Authentication\Token;

use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken;

/**
* PreAuthenticationJWTUserToken.
*
* @author Robin Chalas <robin.chalas@gmail.com>
*/
final class PreAuthenticationJWTUserToken extends PreAuthenticationGuardToken
{
/**
* @var string
*/
private $rawToken;

/**
* @var array
*/
private $payload;

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

/**
* {@inheritdoc}
*/
public function getCredentials()
{
return $this->rawToken;
}

/**
* {@inheritdoc}
*/
public function setPayload(array $payload)
{
$this->payload = $payload;
}

/**
* {@inheritdoc}
*/
public function getPayload()
{
return $this->payload;
}
}
Loading

0 comments on commit 73ebbac

Please sign in to comment.