Chapters
33 Chapters
|
3:34:13
|
Login to bookmark this video
-
Course Code
Subscribe to download the code!Compatible PHP versions: ^7.1.3
Subscribe to download the code!Compatible PHP versions: ^7.1.3
-
This Video
Subscribe to download the video!
Subscribe to download the video!
-
Subtitles
Subscribe to download the subtitles!
Subscribe to download the subtitles!
-
Course Script
Subscribe to download the script!
Subscribe to download the script!
24.
API Auth: Do you Need it? And its Parts
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.
What PHP libraries does this tutorial use?
// composer.json
{
"require": {
"php": "^7.1.3",
"ext-iconv": "*",
"composer/package-versions-deprecated": "^1.11", // 1.11.99
"doctrine/annotations": "^1.0", // 1.10.2
"doctrine/doctrine-bundle": "^1.6.10", // 1.10.2
"doctrine/doctrine-migrations-bundle": "^1.3|^2.0", // v2.0.0
"doctrine/orm": "^2.5.11", // v2.7.2
"knplabs/knp-markdown-bundle": "^1.7", // 1.7.0
"knplabs/knp-paginator-bundle": "^2.7", // v2.8.0
"knplabs/knp-time-bundle": "^1.8", // 1.8.0
"nexylan/slack-bundle": "^2.0,<2.2.0", // v2.0.0
"php-http/guzzle6-adapter": "^1.1", // v1.1.1
"phpdocumentor/reflection-docblock": "^3.0|^4.0", // 4.3.0
"sensio/framework-extra-bundle": "^5.1", // v5.2.0
"stof/doctrine-extensions-bundle": "^1.3", // v1.3.0
"symfony/asset": "^4.0", // v4.1.4
"symfony/cache": "^3.3|^4.0", // v4.1.4
"symfony/console": "^4.0", // v4.1.4
"symfony/flex": "^1.0", // v1.21.6
"symfony/framework-bundle": "^4.0", // v4.1.4
"symfony/lts": "^4@dev", // dev-master
"symfony/property-access": "^3.3|^4.0", // v4.1.4
"symfony/property-info": "^3.3|^4.0", // v4.1.4
"symfony/security-bundle": "^4.0", // v4.1.4
"symfony/serializer": "^3.3|^4.0", // v4.1.4
"symfony/twig-bundle": "^4.0", // v4.1.4
"symfony/web-server-bundle": "^4.0", // v4.1.4
"symfony/yaml": "^4.0", // v4.1.4
"twig/extensions": "^1.5" // v1.5.2
},
"require-dev": {
"doctrine/doctrine-fixtures-bundle": "^3.0", // 3.0.2
"easycorp/easy-log-handler": "^1.0.2", // v1.0.7
"fzaninotto/faker": "^1.7", // v1.8.0
"symfony/debug-bundle": "^3.3|^4.0", // v4.1.4
"symfony/dotenv": "^4.0", // v4.1.4
"symfony/maker-bundle": "^1.0", // v1.7.0
"symfony/monolog-bundle": "^3.0", // v3.3.0
"symfony/phpunit-bridge": "^3.3|^4.0", // v4.1.4
"symfony/stopwatch": "^3.3|^4.0", // v4.1.4
"symfony/var-dumper": "^3.3|^4.0", // v4.1.4
"symfony/web-profiler-bundle": "^3.3|^4.0" // v4.1.4
}
}
25 Comments
Is it possible there ever might be a write up on implementing OAuth2 into this or a project similar to this? My issue is I'm a bit confused on if you are intending to implement OAuth2, so for example connecting a facebook account to the site. I don't know if it's going to be possible to implement the site as described throughout this tutorial where you register with a username/password and that creates a new user. If I wanted to implement OAuth2 and allow a user to essentially register an account via facebook, how exactly would that fit into this flow?
Is it possible to implement it to a site built like this tutorial that already has a user registration system? Documentation around the internet about this seems pretty sparse. Tutorials about this are mostly all outdated using symfony 2 and 3. I see there is a package knpuniversity/oauth2-client-bundle, but I'm unsure how I'd go about implementing that into this type of project. Ideally I'd like to allow users to be able to register an account the traditional way via a registration form username/password, or be able to connect their facebook or google account and gain access to the site that way.
Is there anyway a write-up could be done that illustrates how one would go about doing that?
Thank you.
Hey Galen S.!
Yes, I an at least answer some questions about this to get you moving! First, in this chapter, we are implementing token-based authentication, which will look "similar" to how authentication works on a site that implements an "OAuth server". BUT, to make things very clear, if you want to allow your users to login via Facebook, you do NOT need an OAuth server - you just need to create an authenticator that is able to use an "oauth client" to communicate with some other OAuth server (e.g. Facebook).
We do this here on SymfonyCasts, so I can give you some hints :). Basically, allowing login via username/password and also via OAuth (e.g. Facebook) are 2 totally independent "options" for authentication and work quite well together. You will have one authenticator that allows users to login via an email/password and a second authenticator that allows users to "login via Facebook". In https://github.com/knpuniversity/oauth2-client-bundle, we have a spot on the docs that shows what a Guard authenticator setup might look like to login with Facebook. You will follow:
Step 3) Use the Client Service - https://github.com/knpuniversity/oauth2-client-bundle#step-3-use-the-client-service - but leave the the
connectCheckActionmethod blank... because we will do that logic in the authenticator instead.Authenticating with Guard - https://github.com/knpuniversity/oauth2-client-bundle#authenticating-with-guard
The entire flow is this:
A) User clicks "Login with Facebook" on your site to go to
/connect/facebook.B) In that controller, we redirect to Facebook (see the linked docs - we show exactly how to do this). At this point, we have already configured the bundle so that Facebook knows to redirect the user back to
/connect/facebook/checkafter success (theconnect_facebook_check) route).C) After the user approves your app, they are redirected back to
/connect/facebook/check.D) Your authenticator sees this URL, and makes an API request to fetch the User data, and then you either find the existing User in your database and return them from getUser() or create a new User, save them, and then redirect. All of this is shown in the example on that page.
And, it's a bit bigger, but I'd also recommend going through the OAuth tutorial - http://symfonycasts.com/screencast/oauth - then you will really understand how all of this works.
Alternatively, you can use https://github.com/hwi/HWIOAuthBundle. It gives you some more "automatic" features and may be easier to set up, but it's harder to understand what's going on and also to extend (that's why the oauth2 client bundle exists).
Let me know if that helps!
Cheers!
Hi weaverryan,
The scenario you describe with the facebook auth is very close to what I am trying to implement. I am writing a mobile app with a symfony back end that receives JWT tokens which are obtained on the client from Microsoft Identity Provider(Azure AD). The microsoft identity provider is configured to only issue tokens for people who belong to my company(tenant). The Symfony API that I intend to protect by validating these JWT's really has no use for a user class or table, and Microsoft tokens are signed in a way where I can prove they came from Microsoft and were not tampered with using a public key.
I have been able to write a controller that is able to receive one of these tokens in a request header and then verify the jwt header, claims, and signature against the public key, but obviously I need to have this check occur on every call to my API. All over the examples I see use a firewall and all sort of complex stuff around user databases. The reality for me is that I don't need to generate tokens, I don't need to track users(at least at this point), I simply need to serve the data the client asks for if they have a valid token. What is the best approach for that? Do you have any videos that address that use case?
Hey Brandon X!
I totally get it :). You have a "totally not crazy" use-case, but one that is not the "most normal", so you are missing details on it. There are 2 answers: (A) the correct way and (B) the shortcut way
(A) For the correct way, you should setup a firewall and an authenticator that would verify the JWT signature, etc. This would basically be like a normal security system. The key difference is that you would not need a User entity class. But you would still have some User class that implements UserInterface (there are several methods like getPassword() that you could leave blank - check out the
make:usercommand from MakerBundle - it asks you if you need "to persist user data to the database" and if you select no, it gives you a nice setup). That User class would have whatever properties you want on it (probably data that's stored inside the JWT)... and it works like a normal User object, except that it's not saved. It's purely an object that will now be available for the rest of the request... so that other code can read info off of it. And, of course, from a Symfony security perspective, you will look authenticated.(B) The shortcut way is simply to do more-or-less what you're already doing. But instead of having that logic in each controller, you would move it to a listener on kernel.request (renamed to RequestEvent in Symfony 4.4). That code would then run on every request before the controller and you could validate the JWT. That event also has the power to return a response (e.g. an error response). If you need some data from the JWT to be available in the rest of your code, you would set it on some service (e.g. JwtManager) and fetch that where you need it.
The only issue with (B) is that... you're basically reinventing the security system ;). But, as anything goes, if your use-case is simple enough... then that's fine. Btw, if you do (A), add
stateless: trueto your firewall - it'll just make sure that Symfony doesn't try to store the User object in the session.Cheers!
Thank you very much for the very in-depth and through answer! I really appreciate it. This clears up a lot of stuff.
Hey guys!
First, thanks for the great work! I've been learning tons with you.
Now, I have a doubt. You mention "if the only thing that needs to use your API is your own JavaScript". I would like to know if this is a programming methodology or something like that. It would be very helpful if you can point me in the right direction to learn more about that topic.
Thanks again!
Hey Nico,
First of all, thanks for kind words about SymfonyCasts tutorials!
In short, it's about do not complicate things (and your life) when you don't really need it :) Well, if your API endpoints are used by your own JavaScript, i.e. the code that runs on *your* website - you simply just need to require users authenticated via your simple username/password login form. Then, thanks to session all your API requests will work fine. But if your clients might interact with your API headless, e.g. call it via console with curl requests - you need a way to authenticate them properly during those requests, and that's where tokens would help.
Once again, in short, if all you need is to send API requests in your own JS when users browse your website - you don't need that API token auth system.
I hope this is clearer to you now. If it's still difficult to understand - most probably you don't need that complex thing either... probably just try to go with simple session authentication and see if you will stuck at some point when your API requests will fail because of being from non-authenticated users. And then think of a simple way to solve this, probably just show a popup to users asking them to go to your login page and log in again? Will it help? If so, great, you still don't need that complex token auth :)
I hope this helps!
Cheers!
Hi Victor.
Thanks for the quick answer.
Actually, my doubt is related to the creation of API endpoints to be used by my own JavaScript. I'm assuming this is a basic concept, but I'm still learning the basics. I would like to know if this use of API endpoint with my own JavaScript is a methodology or something similar, so I can research more.
Currently, when I create a page I just use the JavaScript with JQuery AJAX and get the data from some backend functions, but I have not been thinking about API endpoints to get the data with JavaScript.
I hope I managed explain my doubt.
Regards :)
Hey Nico,
Fairly speaking I'm not sure I understand now :)
> Currently, when I create a page I just use the JavaScript with JQuery AJAX and get the data from some backend functions
Excellent, that already mean that you have an API endpoint, i.e. the URL you reach with that jQuery AJAX requests is generally speaking is your API endpoint. And if I understand you correctly - you already return some data from it. If so, I don't understand the last part of your the sentence: "... but I have not been thinking about API endpoints to get the data with JavaScript" - jQuery code that make AJAX requests is already your "JavaScript" that returns some data. For me, it's like you said opposite things in that sentence :)
> I would like to know if this use of API endpoint with my own JavaScript is a methodology or something similar, so I can research more.
Well, I suppose you would want to read more about API in general, but that's the concept. If you want learn more about API - please, take a look at our API track here: https://symfonycasts.com/tr...
I hope this helpful!
Cheers!
But... If we do not need the API token authentication, how to deal with CSRF protection in this case?
Hey plashenkov
If you created and rendered your form using the Symfony Form component, then CSRF will be handled automatically but if you did that manually, you may want to check this chapter https://symfonycasts.com/sc...
Cheers!
Hey Diego,
Thank you for your answer. I mean... I am not asking about the login form (I know how to integrate CSRF token into it).
My question is: Ryan says in this video that if we do not need public API and use it just for our site purposes (internally), we do not really need API token auth and can just use the usual login form auth. But when dealing with API in this case — requests to the API will use a cookie, right? So, somebody can send us a link to some method of our API, and if we hit it, it will be processed without any problem, using cookie from our browser (and it can perform something unwanted on the server). Am I right or am I missing something?
So, how to protect our API from CSRF attacks in this case? Not only login form, but our entire API. This is what I am asking about.
Ahh I got it now. In that case you may protect your site from cross origin requests, it should only allow requests coming from your domain. You may want to investigate about CORS. Also, you may want to watch this chapter where Ryan talks about making secure your API https://symfonycasts.com/sc...
Cheers!
Thank you, Diego! This is it.
It appears that "user" is now a reserved word in the entity maker. Disappointing, because user is such an obvious name.
Entity generated! Now let's add some fields!
You can always add more fields later manually or by re-running this command.
New property name (press <return> to stop adding fields):
> user
[ERROR] Name "user" is a reserved word.
New property name (press <return> to stop adding fields):
Hey Tac-Tacelosky!
Sorry for the slow reply! So, what's happening here is that the list of "reserved" words is specific to whatever database platform you're using - https://github.com/symfony/... - so, for example, I don't hit this error locally in MySQL. But, this validation probably shouldn't happen on a relation in any case, is the column name would eventually be user_id. I've created an issue about this https://github.com/symfony/...
Thank for the report!
Hi guys,
just a little question about "Part 2: Creating & Distributing API Tokens". I've created a little script for the second option, ".. could write an API endpoint whose jobs is to create & return tokens."
Though it feels like "bypassing" the whole guard thing. Is there maybe a better solution?
<s>*Phew, sorry this online-code-editor drives me crazy!</s> Finally fixed it ;)
Cheers, Marcus!
Hey Marcus
I think it depends on your situation, if you really need an endpoint for creating new tokens, then I think you can bypass Guard, but you should consider any possible exploits that your endpoint may have. What happens if the user still have a valid token?
Hi Diego,
good point. There has to be some functionality to check if the user still have a valid token.
The basic requirement is as follows: there is a basic website which provides a default login form. There is a protected area which displays some sort of data within tables.
There is also an ios app which could push data via POST into the database. I followed this extremly useful tutorial (Thanks Ryan for this cool tutorial - really appreciate it! ) and did the authentication via token. The app has also a login screen with email and password. I have to send both to the endpoint, check if the user is valid and return a valid token.
Ah ok, so the website users will login in from the default login form and the app will use tokens? If that's your case, then I think what Ryan did on this tutorials is exactly what you need
Hey guys,
thanks for the great overview from "API Auth: Do you Need it? And its Parts"
I just have a few question regarding the "ApiToken Entity" and "Creating & Distributing API Tokens" courses.
- In your implementation each User can have many ApiTokens. Do you have a strategy to limit the maximum token number ? I mean is it "fine" to create a new token each time the previous one is expired and keep them all in the database ?
- I'm working on the internal API for a delivery comparny that wishes a mobile app. The first thing I thought was "I should force user to athenticate themselves before using any endpoints".
In my head the mobile application would first ask a token via a POST request to /api/authenticate (with the crendentials in the body)
And then tje client will consume the endpoints with the given token.
Then I read this part:
"But, if some third-party were building an iPhone app for your site, that app should not use this method. Why? Because it would require the user to enter their email & password directly into the app, so that it could send the info to our API. Ideally, we never want users to give their password to a third-party."
So I'm a bit confused because I read some articles explaining that to store credentials in mobile devices you can use the Keychain Services for iOS and SharedPreferences for Android. So it looks "ok" to store credentials but again I'm not an expert in mobile development.
Hey aguidis!
Yea, this can be a very confusing area :).
> In your implementation each User can have many ApiTokens. Do you have a strategy to limit the maximum token number ? I mean is it "fine" to create a new token each time the previous one is expired and keep them all in the database ?
I don't see any reason to limit the number of tokens - allowing the user to create more tokens isn't really less secure - if a "bad" person can create an access token, then allowing them to create many doesn't really change anything. And so yes, I think it's fine to create a new token each time the previous one expires and keep them all in the database. Once a token expires, it's useless - so you either delete it, or keep it for some sort of logging/legacy purpose. For example, you might decide that you want to log that a specific action was taken by an API token if you ever think that you might need to "track down" which access token was the cause of something that happened in the system. It's up to you.
> So I'm a bit confused because I read some articles explaining that to store credentials in mobile devices you can use the Keychain Services for iOS and SharedPreferences for Android. So it looks "ok" to store credentials but again I'm not an expert in mobile development.
I think your approach of sending a POST with the credentials to your API to get a token (that is then used for all other endpoints) is 100% perfect. The key line is this:
> But, if some third-party were building an iPhone app for your site,
YOUR app is NOT a third-party app :). You own both the API and the app. Specifically, you don't need to worry that the app is, for example, collecting the plain-text passwords and sending them to some bad party. Having an iPhone app where the user types in their email/password so that the app can send to your endpoint is basically the same as allowing people to enter their email/password into a form on YOUR site and hitting submit. That's totally normal :). What is NOT normal would be for me to put a login form on MY site, where people entered their email/pass for YOUR site, and I sent them to your site behind the scenes. That would be a crazy security hole! It's the exact same with apps.
Let me know if that makes sense!
Cheers!
Hey weaverryan, Thank you for your detailed feedback. It totally makes sense !
This is what I was looking for :P
Hey Matt,
Awesome! We glad it helpful for you. Actually, we've got many questions about this lately, so we decided to explain it in this course :)
Cheers!
"Houston: no signs of life"
Start the conversation!