Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine
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!

8
Login or Register to join the conversation

Is there going to be a tutorial on how to use this api from another symfony project to authenticate users?

Reply

Hey Michael,

Probably our "OAuth2 in 8 steps" could be interesting for you: https://knpuniversity.com/s... . But for now, we do not plan any API courses, you can spy on your upcoming tutorials on this page: https://knpuniversity.com/c...

Cheers!

Reply

Hey Nicholas!

Hmm, are you sure? Since we're using the Guzzle client in this tutorial, we should be able to send the raw HTTP header names (i.e. Authorization) and not need to prefix them. But, something may have changed. Are you possibly using the Symfony built-in Client instead? I'm trying to figure out what the difference is :).

Cheers!

Reply
Default user avatar

Where is the /login redirection covered? How does it happen?
Thanks

Reply

Hi Vlad!

That's explained a few chapters from here - it's caused by what's called an "entry point": https://knpuniversity.com/s...

Hope that helps!

Reply
Default user avatar

I'm following this tutorial and working on parallel project and for some reason I don't get the /login redirection. What am I missing?

Reply

Just saw this message :). What behavior *are* you getting? Ultimately, the /login redirection is caused by the "form_login" system in the firewall. It's possible that you don't have that (or have some other behavior). Check out the entry point chapter, but let me know if you have questions - the "entry point" idea can be tricky.

Reply
Default user avatar

Thank you! That was it. I needed to add the firewall configs. I'll check the entry point chapter. Thanks again.

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

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