This tutorial has a new version, check it out!

Authenticate a Request with JWT

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $10.00

We already added a denyAccessUnlessGranted() line to ProgrammerController::newAction(). That means this endpoint is broken: we don't have an API authentication system hooked up yet.

Open up ProgrammerControllerTest() and find testPOST(): the test for this endpoint:

... lines 1 - 6
class ProgrammerControllerTest extends ApiTestCase
{
... lines 9 - 15
public function testPOST()
{
... lines 18 - 34
}
... lines 36 - 254
}

Rename this to testPOSTProgrammerWorks() - this will make its name unique enough that we can run it alone:

... lines 1 - 6
class ProgrammerControllerTest extends ApiTestCase
{
... lines 9 - 15
public function testPOSTProgrammerWorks()
{
... lines 18 - 40
}
... lines 42 - 260
}

Copy that name and run it:

./vendor/bin/phpunit --filter testPOSTProgrammerWorks

Instead of the 201, we get a 200 status code after being redirected to /login. I know we don't have our security system hooked up yet, but pretend that it is hooked up and working nicely. How can we update the test to send a token?

Sending a Token in the Test

Well, first, we'll need to create a valid token. Do that the same way we just did in the controller: $token = $this->getService() - which is just a shortcut we made to fetch a service from the container - and grab the lexik_jwt_authentication.encoder service. Finally, call encode() and pass it ['username' => 'weaverryan']:

... lines 1 - 6
class ProgrammerControllerTest extends ApiTestCase
{
... lines 9 - 15
public function testPOSTProgrammerWorks()
{
$data = array(
'nickname' => 'ObjectOrienter',
'avatarNumber' => 5,
'tagLine' => 'a test dev!'
);
$token = $this->getService('lexik_jwt_authentication.encoder')
->encode(['username' => 'weaverryan']);
... lines 26 - 40
}
... lines 42 - 260
}

And we have a token! Now, how do we send it to the server? Well, it's our API, so we can do whatever the heck we want! We can set it as a query string or attach it on a header. The most common way is to set it on a header called Authorization. Add a headers key to the Guzzle call with one header called Authorization. Set its value to the word Bearer, a space, and then the $token.:

... lines 1 - 6
class ProgrammerControllerTest extends ApiTestCase
{
... lines 9 - 15
public function testPOSTProgrammerWorks()
{
... lines 18 - 26
// 1) Create a programmer resource
$response = $this->client->post('/api/programmers', [
'body' => json_encode($data),
'headers' => [
'Authorization' => 'Bearer '.$token
]
]);
... lines 34 - 40
}
... lines 42 - 260
}

Weird as it might look, this is a really standard way to send a token to an API. If we re-run the test now, it of course still fails. But we're finally ready to create an authentication system that looks for this token and authenticates our user.

Leave a comment!

This tutorial uses an older version of Symfony. The concepts of API tokens & JWT are still valid, but integration in newer Symfony versions may be different.

What PHP libraries does this tutorial use?

// 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
    }
}