Chapters
26 Chapters
|
2:18:26
|
This course is archived!
This tutorial uses a deprecated micro-framework called Silex. The fundamentals of REST are still ?valid, but the code we use can't be used in a real application.
Login to bookmark this video
-
Course Code
Subscribe to download the code!
Subscribe to download the code!
-
This Video
Subscribe to download the video!
Subscribe to download the video!
-
Course Script
Subscribe to download the script!
Subscribe to download the script!
05.
Authorization via a Token
Scroll down to the script below, click on any sentence (including terminal blocks) to jump to that spot in the video!
Subscribe to jump to this part in the video!
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.
This tutorial uses a deprecated micro-framework called Silex. The fundamentals of REST are still ?valid, but the code we use can't be used in a real application.
What PHP libraries does this tutorial use?
// composer.json
{
"require": {
"silex/silex": "~1.0", // v1.3.2
"symfony/twig-bridge": "~2.1", // v2.7.3
"symfony/security": "~2.4", // v2.7.3
"doctrine/dbal": "^2.5.4", // v2.5.4
"monolog/monolog": "~1.7.0", // 1.7.0
"symfony/validator": "~2.4", // v2.7.3
"symfony/expression-language": "~2.4", // v2.7.3
"jms/serializer": "~0.16", // 0.16.0
"willdurand/hateoas": "~2.3" // v2.3.0
},
"require-dev": {
"behat/mink": "~1.5", // v1.5.0
"behat/mink-goutte-driver": "~1.0.9", // v1.0.9
"behat/mink-selenium2-driver": "~1.1.1", // v1.1.1
"behat/behat": "~2.5", // v2.5.5
"behat/mink-extension": "~1.2.0", // v1.2.0
"phpunit/phpunit": "~5.7.0", // 5.7.27
"guzzle/guzzle": "~3.7" // v3.9.3
}
}
19 Comments
Hi. The authorization test doesn't appear to be working anymore. I'm still not getting the authorization headers in the behat tests even after adding the suggested rewrite rules. The listener is called but authorization is not in the headers. Any ideas?
Nevermind. Was doing this via a Vagrant VM and that seemed to be the issue. Now just running localhost natively.
Same and it appeared to be the rewrite rules causing it. Had to add this to my rewrite conditions in my apache config:
RewriteCond %{HTTP:Authorization} ^(.*)
RewriteRule .* - [e=HTTP_AUTHORIZATION:%1]
Ah, nice debugging! Sometimes, your web server can be configured to "hide" the Authorization header... though I'm not sure why! In case it helps: http://stackoverflow.com/qu...
Cheers!
Hey,
Really like your videos. They are awesome! But they would be even better if you could remove hissing that occurs when you say an "s". Other than that keep up the good work !! :)
well, I made it this far. I'm afraid I'll have to ask... sorry :P
So I brought all the Security part into my project and I am trying authentication for a specific request. If I don't send the authorization header I get the same error you got in the previous chapter, the API returns:
{
"detail": "Authentication Required",
"status": 401,
"type": "http:\/\/localhost:8000\/docs\/errors#authentication_error",
"title": "Invalid or missing authentication"}
which is alright.
However, now I am trying to get the request to execute properly, by adding an Authorization header with the content "token ABCD123" using Postman. I have defined the ApiToken table with the same fields (I just changed userId for user_id in the entire project for consistency with my other classes) and of course added a row with ABCD123 and user_id pointing to the user who should've access.
At this point, I keep getting "Authentication Required". My first question would be, how can I debug from here? I cannot use var_dumps because the api will just return the response. I don't even know if the ApiTokenListener is executing at all. I guess I broke something with the Silex Security. If I get Authentication Required it means that either ApiTokenListener was not executed, or that there was no authentication information on the request (which would mean postman fails) or there's no match in the database (there is!!)... I need to get closer to the problem but I'm unsure on how to debug this with postman alone. How would you proceed? Thanks!
Hi Joan!
Don't worry about asking - it's kind of what I hoped for ;). We don't talk about the security stuff in great detail because it's complicated and implementing it isn't directly related to REST. Anyways, let me see if I can help :).
First, the ApiTokenListener should be called on *every* request to your app. Add a die('fooo'); in the handle() method and make a request to your app. Is this method being called? If you don't see "foo" and just see the response, then this method is *not* being called. You *should* be able to use var_dump (with die) to debug things. If the method is not being called. the first thing to check is that this is all configured in the Application.php file - specifically, there should be a 'api_token' => true, line (not commented) under the "api" firewall in the configureSecurity method. In theory, if that line is there, then your security listener should be called.
You can also take a look at the "finish" code in the code download and see if you spot any differences related to security.
Let me know how it goes!
hey Ryan! thanks for the prompt response, I was about to edit my question hehe, I managed to narrow it down to the fact that apparently Postman isn't setting the Authorization header.
I have:
$request = $event->getRequest();
var_dump($request->headers->has('Authorization'));
// there may not be authentication information on this request
if (!$request->headers->has('Authorization')) {
return;
}
in the handle(), the var_dump returns false. So yeah the security listener is being called but enters in that if returning nothing, and I guess that makes getLoggedInUser to return false and the whole request to return "auth required". Now I am trying to find out what could I be doing wrong, because Postman is relatively simple here, I mean I set the Authorization header to "token ABCD123"... also printing the whole $request crashes the app. Kinda stuck right now but don't really have a question this time :P
apparently it's a symfony/apache issue removing the header: https://github.com/dingo/ap... will update if I make it through
Yep, sorry for spamming here, if someone has issues with not getting the header you should add this to your .htaccess:
RewriteRule ^ - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
Ah, so happy you found it, but that's no fun! And yes, now that I'm looking into this, it looks like a well-known issue. We even have some docs about it for Symfony: http://symfony.com/doc/curr... (scroll near the bottom of this section).
Thanks for posting your solution and now you can keep moving :)
thanks I am getting the proper responses now :)
Adding that RewriteRule to the .htaccess works at the time to pass the Authorization header through. However, now other requests that do not require authentication do get an empty Authorization header that causes the code to throw new BadAuthHeaderFormatException();.
I have modified it the code to look for empty authorization headers too:
// there may not be authentication information on this request
if (!$request->headers->has('Authorization') || $request->headers->get('Authorization') == '') {
return;
}
EDIT:
Added the following condition in the .htaccess:
RewriteCond %{HTTP:Authorization} .+
Am I the only one who is bothered by the inconsistent column names. o.0
Hey Daan Biesterbos
Since you are the first one saying it, I would say, yes :)
But, could you tell us specifically what's bothering you?
Cheers!
Heheh. Well I made it sound more serious than intended I suppose xD I would totally complain in a PR when someone would mix camel case and snake case in the same context ^_^
I'll just make a more conceptual question too: I am developing an API with the idea to be consumed by one js app. The API has some requests that only the app should have access to (and not the users logged in the app). Does it make sense to create an Api Token uniquely for the app itself? I guess that some logic should be done for Silex to give special app credentials to requests using that token and finally, the app would have access to execute certain requests. Or which would be the way to accomplish this?
Hey Joan!
Before I try to give some ideas, let me make a quick point :). It will never be possible to allow JavaScript to be able to execute an endpoint that your logged in users cannot execute. If you think about it, even if you gave your JS some token it could pass back, that token will be exposed to the client (since JS is obviously client-side), and your users could always "steal" that and make those requests directly. And if you *stop* caring about trying to make your JS be able to do something that your logged in user cannot, then you *might* arrive at a very easy solution where you don't use any fancy API token stuff. Instead, you just let your JS use your user's session cookie and be done with it :).
Cheers!
Well, you are so damn right!!
One of the requests that made me ask this was the one about getting stats, which basically returns counts of the database for showing some numbers in the landing page, where noone is logged in. I guess that request can be public. However, what about a request to get all users? I realize that you don't have any UserController for your API and I am not sure if it is because you didn't need to do it for the purpose of this course or you didn't do it because you didn't want to. I have a UserController to create newUsers (when a user registers), to get all the users, etc but now I'm realizing that the app will only need, the POST to create a user, the GET to for a user to see its own info, the PUT in case a user wants to edit the email or password, and the DELETE in case a user wants to delete their account. So the GET all users shouldn't probably exist as it would be giving info of other users away. How do you deal with user registration via API?
"Houston: no signs of life"
Start the conversation!