mnavarrocarter / auth-bundle
Installs: 1
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 1
Forks: 1
Open Issues: 0
Type:symfony-bundle
Requires
- php: ^7.1.3
- lcobucci/jwt: ^3.2
- symfony/config: ^4.0
- symfony/dependency-injection: ^4.0
- symfony/http-foundation: ^4.0
- symfony/security: ^4.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^2.12
- symfony/phpunit-bridge: ^4.0
This package is auto-updated.
Last update: 2019-08-18 18:29:24 UTC
README
Introduction
This bundle provides JWT Authentication for your Symfony Project.
Installation
Simply run composer require mnavarrocarter/auth-bundle
Before that, make sure you have Symfony's security component (the bundle) in your
project, using: composer require security
(Flex only)
Configuration
In order for this Bundle to work properly with your application, you need to define
a series of rules in your security.yaml
. The most important ones are the the ones
defined in the two firewalls, auth
and main
.
The auth firewall is where the user authenticates with username and email and
as a result, a token is returned to him. The token is created in the
mnc_auth.login_handler.default
service. You can replace and create your own. Just
use the original one as an example.
In the main firewall, the authentication method is the token itself. This is processed
by the mnc_auth.guard_authenticator.default
service. If you want to customize
the behaviour of this class (success and failure responses, or SymfonyToken creation)
the best way is to extend MNC\Bundle\AuthBundle\Security\Guard\JwtGuardAuthenticator
and override the methods that interest you. Then define it as a service and place it
in your authenticator.
security: firewalls: # ... other firewalls auth: pattern: ^/auth stateless: false anonymous: true form_login: check_path: /auth/token # This habdler creates the token and returns it in a response # You can also create your own. # See: success_handler: mnc_auth.login_handler.default # The failure handler just returns an error response failure_handler: mnc_auth.login_handler.default # This only changes the default params username_parameter: email password_parameter: password main: pattern: ^/ anonymous: false # Important if you want to do token session invalidation stateless: false guard: # This authenticator does all the work when a request carries a token. authenticators: - mnc_auth.guard_authenticator.default
Also, you need to add this route in your routes.yaml
auth_token: path: /auth/token
How it works
Using the awesome lcobucci/jwt
library, this bundle creates a JWT encoded with
Hmac Sha 512 using your kernel secret as your secret key. The claims are:
- sub (subject): the value from
UserInterface::getUsername()
- exp (expiration): the token creation time + 3600 seconds
- jti (json token id): an autogenerated id for the token
However, you can implement custom behaviour beyond these defaults. Keep reading to know how.
The TokenService
Token creation and verification is centralized in the TokenService. This services has two required dependencies and a third optional one.
The first dependency is a class implementing TokenBuilderInterface. It contains the instructions for token creation and validation. You can create your own implementation that uses different signing algorithms and that sets different claims and validation rules. Then, you can make the TokenService use your implementation by modifying the config.
The second dependency is a class implementing the TokenIdentifierGenerator interface, that generates ids for your JWT's. The default one uses PHP's uniqid function, but you can implement your own using other method. Then, you can make the TokenService use your implementation by modifying the config.
The third dependency is TokenStoreInterface. This bundle does not provide any implementation of that interface. That's up to you. By default, the TokenService does not use if it's not defined. For more info on how this can be of some use for you, refer to using the TokenStoreInterface below.
If you want to sign a token using Rsa, you have to implement your own token builder following TokenBuilderInterface contract.
Modifiying the default TokenBuilder
If the hashing algorithm (Hmac Sha 512) of the default token builder suits you, but you want
to change the secret or the token lifetime, you can do so by modifying the config in the
node mnc_auth.default_builder
. Refer to the default config reference
for more info.
Using the TokenStoreInterface
As it is, this bundles works and serves the basic purposes of authentication. However, there are more complex scenarios where you may need some extra functionality. For example, ¿what about being able to detect when a machine uses a token requested in another machine? That means that someone has either shared their token or stole it. ¿What if you want to revoke a token? JWT are non-persisted, that means, they are not stored in the db. So you don't have a way to "know" a token.
This is TokenStoreInterface to the rescue. The interface defines a minimal contract for token id persistence. You can associate that to a cookie for example, and to a user. And easily give the change to the user to revoke tokens in other sessions. The possibilities are basically up to you.
This bundle does not have a default TokenStore service, because that's an implementation detail. We don't know what persistence engine you are using. However, you can look at this example that implements the TokenStoreInterface in a DoctrineRepository, and invalidates tokens that do not belong to the same session id.
<?php namespace App\Repository; use App\Entity\Token; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use MNC\Bundle\AuthBundle\Token\Store\TokenStoreInterface; use Symfony\Bridge\Doctrine\RegistryInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Session\SessionInterface; /** * @method Token|null find($id, $lockMode = null, $lockVersion = null) * @method Token|null findOneBy(array $criteria, array $orderBy = null) * @method Token[] findAll() * @method Token[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) */ class TokenRepository extends ServiceEntityRepository implements TokenStoreInterface { /** * @var RequestStack */ private $requestStack; public function __construct(RegistryInterface $registry, RequestStack $requestStack) { parent::__construct($registry, Token::class); $this->requestStack = $requestStack; } public function store($tokenId): void { if ($this->getSession()->has('toid')) { $toid = $this->getSession()->get('toid'); } else { $toid = md5($this->getSession()->getId()); $this->getSession()->set('toid', $toid); } $token = $this->findOneBy(['sessionId' => $toid]); if (null === $token) { $token = new Token($tokenId, $toid); } else { $token->updateTokenId($tokenId); } $manager = $this->getEntityManager(); $manager->persist($token); $manager->flush(); } public function revoke($tokenId): void { $token = $this->find($tokenId); if (null !== $token) { $token->revoke(); $manager = $this->getEntityManager(); $manager->persist($token); $manager->flush(); } } public function isTokenValid($tokenId): bool { $token = $this->find($tokenId); if (null !== $token) { return $token->getSessionId() === $this->getSession()->get('toid'); } return false; } public function isTokenRevoked($tokenId): bool { $token = $this->find($tokenId); if (null !== $token) { return $token->isRevoked(); } return true; } public function getRequest(): Request { return $this->requestStack->getMasterRequest(); } public function getSession(): SessionInterface { return $this->getRequest()->getSession(); } }
Default Config Reference
mnc_auth: token_service: # You can define a custom token builder changing this value token_builder: mnc_auth.token_builder.default # You can define a custom token id generator changing this value token_id_generator: mnc_auth.identifier_generator.default # You can define your implementation of token store here. token_store: ~ default_builder: # This allows you to modify the secret for the default token builder secret: '%kernel.secret%' # This allows you to modify the expiration time for the default token builder. ttl: 3600 # Token extractors configuration. token_extractors: cookie: key_name: token query_param: key_name: token header: header_name: Authorization prefix: Bearer