If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
The authenticator class is done - well done enough to see it working. Next, we
need to register it as a service. Open up app/config/services.yml
to add it:
call it jwt_token_authenticator
. Set its class to AppBundle\Security\JwtTokenAuthenticator
:
... lines 1 - 5 | |
services: | |
... lines 7 - 35 | |
jwt_token_authenticator: | |
class: AppBundle\Security\JwtTokenAuthenticator | |
autowire: true |
And instead of adding an arguments
key: here's your permission to be lazy! Set autowire
to true
to make Symfony guess the arguments for us.
Finally, copy the service name and head into security.yml
. Under the firewall,
add a guard
key, add authenticators
below that and paste the service name:
security: | |
... lines 2 - 8 | |
firewalls: | |
main: | |
... lines 11 - 20 | |
guard: | |
authenticators: | |
- 'jwt_token_authenticator' | |
... lines 24 - 32 |
As soon as you do that, Symfony will call getCredentials()
on the authenticator
on every request. If we send a request that has an Authorization
header, it should
work its magic.
Let's try it! Run our original testPOSTProgrammerWorks()
test: this is sending
a valid JSON web token.
./vendor/bin/phpunit --filter testPOSTProgrammerWorks
And this time... it passes!
Hold on, that's pretty amazing! The authenticator automatically decodes the token
and authenticates the user. By the time ProgrammerController
is executed, our user
is logged in. In fact, there's one other spot we can finally fix.
Down on line 37, we originally had to make it look like every programmer was being
created by weaverryan
:
... lines 1 - 18 | |
class ProgrammerController extends BaseController | |
{ | |
... lines 21 - 24 | |
public function newAction(Request $request) | |
{ | |
... lines 27 - 36 | |
$programmer->setUser($this->findUserByUsername('weaverryan')); | |
... lines 38 - 50 | |
} | |
... lines 52 - 191 | |
} |
Without authentication, we didn't know who was actually making the API requests, and since every Programmer needs an owner, this hack was born.
Replace this with $this->getUser()
:
... lines 1 - 18 | |
class ProgrammerController extends BaseController | |
{ | |
... lines 21 - 24 | |
public function newAction(Request $request) | |
{ | |
... lines 27 - 36 | |
$programmer->setUser($this->getUser()); | |
... lines 38 - 50 | |
} | |
... lines 52 - 191 | |
} |
That's it.
Our controller doesn't know or care how we were authenticated: it just cares that
$this->getUser()
returns the correct user object.
Run the test again.
./vendor/bin/phpunit --filter testPOSTProgrammerWorks
It still passes! Welcome to our beautiful JWT authentication system. Now, time to lock down every endpoint: I don't want other users messing with my code battlers.
// composer.json
{
"require": {
"php": ">=5.5.9",
"symfony/symfony": "3.0.*", // v3.0.3
"doctrine/orm": "^2.5", // v2.5.4
"doctrine/doctrine-bundle": "^1.6", // 1.6.2
"doctrine/doctrine-cache-bundle": "^1.2", // 1.3.0
"symfony/swiftmailer-bundle": "^2.3", // v2.3.11
"symfony/monolog-bundle": "^2.8", // v2.10.0
"sensio/distribution-bundle": "^5.0", // v5.0.4
"sensio/framework-extra-bundle": "^3.0.2", // v3.0.14
"incenteev/composer-parameter-handler": "~2.0", // v2.1.2
"jms/serializer-bundle": "^1.1.0", // 1.1.0
"white-october/pagerfanta-bundle": "^1.0", // v1.0.5
"lexik/jwt-authentication-bundle": "^1.4" // v1.4.3
},
"require-dev": {
"sensio/generator-bundle": "^3.0", // v3.0.6
"symfony/phpunit-bridge": "^3.0", // v3.0.3
"behat/behat": "~3.1@dev", // dev-master
"behat/mink-extension": "~2.2.0", // v2.2
"behat/mink-goutte-driver": "~1.2.0", // v1.2.1
"behat/mink-selenium2-driver": "~1.3.0", // v1.3.1
"phpunit/phpunit": "~4.6.0", // 4.6.10
"doctrine/doctrine-fixtures-bundle": "^2.3" // 2.3.0
}
}