If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
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?
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.
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!
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!
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!
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?
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.
Thank you! That was it. I needed to add the firewall configs. I'll check the entry point chapter. Thanks again.
// 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
}
}
Is there going to be a tutorial on how to use this api from another symfony project to authenticate users?