07.
JWT Guard Authenticator (Part 1)
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.
If you liked what you've learned so far, dive in! Subscribe to get access to this tutorial plus video, code and script downloads.
Whoops, an error! Please, try again later.
32 Comments
Hey, If we want to perform traditional validation of username( character count and valid email) and password (character count) before authentication is performed.
The users are in database and we don't want to hit the database before this validation gives a go. How the solution gonna be !
Hey Ahmed,
So, if you don't want to hit DB data if validation is failed - it probably makes sense to do in getCredentials() that has access to Request object. And if data is invalid - just return empty credentials that will be passed to getUser() where first of all you can check if credentials are empty and if so - immediately return null or throw an CustomUserMessageAuthenticationException. Or you know what, you even can throw this exception from the getCredentials() - just saw it in docs: https://symfony.com/doc/cur.... So, if credentials invalid - throw this exception and that's it.
Cheers!
Thank you for your reply i really appreciate it.
Cheers!
Hey Ahmed,
You're welcome! I'm glad if it helped you ;)
Cheers!
I'm trying to understing how to make an authentificaion system to my API
so i installed lexik auth bundle, then for next steps i find my slef blocked cause i need some responses:
1)why we need to use an authenticator ? and then why Guard ? what's your logic when you created Guard system?
2)what is the relation between firewall, autheticator and guard ?
3)Does we really to implement a provider and then point it under security.yml/providers ? in wish case of exemple authentication, we need to create providers ?
[important!]What is the main reason of firewall, autheticator, guard and providers ?
THANK YOU CHEERS
Hey again ahmedbhs!
Let me see if I can help :)
1) When a request comes into your app, you will want to write/run some code that checks to see if that request contains some authentication information (e.g. an API token) and then authenticates the user based on that information. Especially in an API, you'll want this code to execute on every request and run *before* your controller (so that by the time your controller is executed, you're already authenticated). The Guard system is an easy way for you to add this code: create one class, fill in a few methods, and BAM! You've got an authentication system that you fully control. The lexik auth bundle *could* have done this work for you, but, if I remember correctly, they tell *you* to create the Guard authenticator so that you ultimately have full control over your API (including the response you send on auth error, auth success, etc).
2) Another great question! Your "firewall" *is* your security system, and you typically only have one. For each firewall, you could configure multiple "authenticators". For example, suppose you have one firewall with 3 authenticators. At the beginning of each request, all 3 authenticators are called. This means that each authenticator has the opportunity to look for authentication information and (potentially) authenticate the user. One, somewhat real example would be to have 2 authenticators: (A) an JWT token authenticator that looks for a token on a X-API-Token header, (B) a http basic authenticator that looks for HTTP basic username+password to exist and authenticates based on that. Basically, you will have 1 authenticator per *method* of authentication that you want to offer your users. Many time, you will have just one, but you could have 10! And "Guard" is just the fancy name we gave to the "authenticator" system - don't give that term any special thought.
3) This is the BEST question :). Yes, you need to create a provider. But, this is not really because you *need* it or will use it. Instead, it's more because Symfony's security system requires this to exist. For a traditional HTML app, you *do* need it (for some session-based logic). But, for an API, you really should *not* need it. So, create it (because it's required), but then stop thinking about it :).
> [important!]What is the main reason of firewall, autheticator, guard and providers ?
* Firewall = your entire security system. You will typically only have 1. But if you had 2, as the request enters your app, only 1 firewall is activated (based on the URL string). A firewall is typically not an important concept unless you have multiple (btw, the "dev" firewall does NOT count as a second firewall - ignore it - you really only have 1 real firewall in a normal Symfony app).
* Authenticator: classes that run at the beginning of each request and attempt to authenticate the user based on different logic. All authenticators for the "active" firewall are called one every request.
* Guard: the fancy word we gave to the authenticator system
* providers: an unfortunately "required" thing you should just create, then forget about. Someday, we will hopefully make this optional!
Cheers!
thank you I really appreciate your help, your time, you made my day, you just give me a global easy and simple image, to understand :)
1) I know that the lexical bundle takes care of validating the token, but here,do you think that is recommended to store the api key into the database for some reasons?
2) Does the first Ajax request {username, password} is sent in clear?
3) after getting the token in my front app, is it possible to decode it?
Hello,
Is it possible to use the rememberMe function when using jwt or do I have to use the built in version from Symfony2 ?
How would you go about it ?
[EDIT] Well angular as a module to remember the jwt, i'm gonna check this out héhé
Hey @noograss!
I hope that angular module helped you! On the Symfony-side, hmmm, yea, I suppose you could use the remember_me with JWT :). The remember_me key under your firewall simply sets a remember me cookie. From an Angular front-end, if, for some reason, your AJAX requests are suddenly not authenticated (e.g. you no longer have the token or the token has expired), then naturally the remember me cookie should cause your requests to continue to be authenticated.
Cheers!
Hi
It was a mistake in my firewall configurarion. Its now working fine :)
A big thanks for this course, learned a lot !
Woohooo! Cheers!
Hi! I am using Symfony 4.2 and in a class which extends AbstractGuardAuthenticator should implement method "supports", which is called on every request to decide if this authenticator should be used for the request. Returning false will cause this authenticator to be skipped. What should I include here? Checking the header for containing "Authorisation"? I though it is checked in "getCredentials" method?
You can check things like the request method type, the route being accessed, and probably an special header that helps you out determine the login mechanism
Cheers!
Thanks for quick reply. I am little confused of how the client obtains the token? Which login route it should access? Currently my React app sends credentials to /api/json route which is json_login method, provided by symfony. But now with Jwt autheticator, should the client send request to /api/tokens to login and obtain token?
Hey Assel N.!
I think I can understand the confusion :). There are 2 parts to the process:
1) Somehow, the client needs to GET a token - like send an email+password to some endpoint and get a JWT back. That can be the job of (for example) json_login (if you configure its success handler to send back a JWT).
2) Once you have a token, you need an authenticator that reads the token on EVERY request and "authenticates" the user using it. THAT is really what we're building in this chapter: the thing that READS the token and authenticates you.
So, what you should use for step 1? You can use json_login - you just need to configure it to send back a JWT when it finishes - the LexikJWTAuthenticationBundle has some code to help with this - you can see it in this section https://github.com/lexik/Le...
OR, you can create a normal route & controller that reads the POST'ed email+pass and returns the JWT. That's what we do (a bit earlier) in this tutorial: https://symfonycasts.com/sc...
Does that help? Also, as a disclaimer, I ALWAYS have to say that if you're building an API *only* for your own JavaScript to consume (nobody else, external will read it), you should probably NOT have a token-based system. You should instead rely on session authentication. It's much simpler. In that situation, you could use json_login and... be done! You would send it the email/password, and when that endpoint is successful, it will set a session cookie. Every AJAX request after will automatically be authenticated using that session cookie. Simple, easy and secure.
Cheers!
Hi @weaverryan! Thank you very much for your reply! I obtained token in json_login, it works.
In our current application we use session-based authentication. However, its not convenient as client is a separate react application , also we want in the future we want to have different client app which will be connecting to Symfony API. So thats why I thought to go with API tokens.
I have another problem Following this tutorial all routes to /api/ now goes through new JWTTokenAuthenticator, which is great, however now i cannot see the response on the browser (http://localhost:8000/api/module/1 for example). When I go to the link on the browser it calls the "Start" method in JWTTokenAuthenticator and requires authentification. What I want to do is, when you access api route through URL (on browser), I want it to use the normal authenticator (LoginFormAuthenticator), so that I can see json response, but if the request is XMLHttpRequest (which means its Ajax), then it uses JWTokenAuthenticator. How to do that?
Thank you for your help, it is highly appreciated
Hey Assel N.!
Nice job!
Why isn't it convenient? It should be the most convenient, as you don't need to do anything in React - the session cookie is automatically set by Symfony and then sent on your AJAX requests. Is there some factor that makes this difficult in your app?
THAT is indeed the reason to use API tokens :). The tricky thing with those (in JavaScript) is that there really isn't a great (safe) place to store the tokens. Http-only cookies were made for this use-case - not readable by JavaScript, but are sent with request back to your server to authenticate.
Excellent question! Each firewall has exactly one "entry point" - that's the "start" method on your authenticator. But, when you have multiple authentication systems under one firewall, this gets kinda "weird". When you go to /api, should Symfony call the "start" method on theJWTTokenAuthenticator or the start() method on YOUR authenticator? How would it know? In truth, Symfony "kind of" just guesses and uses one (or, in this case, you may be choosing one - there are ways to configure which to use - but I won't go into that).
Short answer is this: create a NEW class that implements
AuthenticationEntryPointInterfaceand put whatever logic you want in it - you can check if the request is AJAX, or whatever. Return different Responses in different situations. Then, under your firewall, setentry_point: App\Your\New\EntryPointClass. Now, IT will be used.Let me know how it goes!
Cheers!
I'm confused about what is where.
Is this part supposed to be in API application or in application that connects to API?
I guess that second option, similar to Facebook or Google guard.
But do I need to install JWT also in client application, with all this keys generating? I just need to connect to API so it seems kind of overkill.
Yo @Radosław!
It's the first - and API application itself that someone - maybe your own JavaScript - will connect to. If you're creating an API so that your own JAvaScript can talk to it (and nobody else will use your API), then, indeed, a much easier option than dealing with API tokens is simply to create a login form in Symfony and make the user login like normally with that login form. That will establish a session cookie and... boom! All your API/AJAX requests will instantly be authenticated. If you even want to make that login form happen via AJAX, just make your login form "success" return JSON instead of redirecting. Then, send an AJAX request to the login URL and it will also set the session cookie like normal.
So yes, in a lot of cases, needing to creating an token-based authentication system for your API is overkill. Let me know if this clarifies!
Cheers!
Hi there, how and where can I add additional information like first name, last name etc. to the token?
Thank you in advance!
Hey Leon M. !
Sorry for my slow reply! The answer depends on how you're using this library. In this tutorial, we're creating the token all by ourselves - so you can do it right inside that endpoint - https://symfonycasts.com/screencast/symfony-rest4/create-json-web-token#codeblock-5b2a065f04
However, if you're using the bundle in a more traditional way - by using json_login and their success_handler (https://github.com/lexik/LexikJWTAuthenticationBundle/blob/master/Resources/doc/index.md#configuration), then you'll need to create a Symfony event subscriber and listen to the
lexik_jwt_authentication.on_jwt_createdevent (https://github.com/lexik/LexikJWTAuthenticationBundle/blob/14262e890833e55d7218ea8c16f114582771a494/Events.php#L34), which allows you to add more "payload" to the token.Let me know if this helps!
Cheers!
Hey guys, can you please tell me if it's possible to have a custom Authenticator that doesn't implement all 8 methods nor follow the Authenticators' flow specified in here? I don't need to fetch the user object in my API (aka run the getUser method) because, to my knowelge, The reason why we use JWT is to reduce database queries but if I secure my API with an Authenticator that extends AbstractGuardAuthenticator, then that just means that there'll be at least one on every request. I also don't need to redirect the user (start method). I really want to know this even if there's a another way of doing it within the framework :) Thanks.
Hey JoeRaid!
Excellent question. You do need to implement all 8 methods. But those methods are super flexible - you can do whatever you need. For example:
A) In getUser(), you need to return a User object. But that doesn't mean you need a database query. If you're using JWT, then the token contains some sort of user information. Often, people will just embed the id of the user then query for a User object from the database using that id. But, that's not the only way. You could also embed all the data that your API needs in the JWT - e.g. email, roles, etc (could be anything). In that case, you should create a User class (that implements UserInterface) and has these fields. This would not be a Doctrine entity. Inside getUser(), you would use the information from the JWT to create, populate and return that User object. There's no database query - the User class is basically an object-oriented representation of your JWT. The cool thing is that, in the rest of your app, you can now fetch the JWT information very easily (via this nice User class you created) instead of needing to fetch the JWT directly and decode it. That happens just once in your authenticator.
B) For
start(), if you're building an API, you can leave this blank - that's totally allowed. But it should really return a JSON response with a 401 status code. That is the proper way to communicate to your API clients that this endpoint requires authentication and they didn't pass it. ForonAuthenticationSuccess, you totally can (and should) leave this method blank. That's not a hack - for API's, on success, you want to do nothing.I hope this helps! The authenticator is infinitely flexible :).
Cheers!
Thank you so much for your time. Yes, I was afraid that relying on Authenticators while changing too much compared to the documentations and your examples would be considered a hack. Thanks for the confirmation.
Cheers!
If you have this error like:
PHP Fatal error: Class AppBundle\Security\JwtTokenAuthenticator contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface::start) in src/AppBundle/Security/JwtTokenAuthenticator.php on line 16
It's only because, GuardAuthenticatorInterface extends with another AuthenticationEntryPointInterface, so just implement the missing method called `start` (\Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface::start)
If you don't care about this, just do a `return;`
For more informations, reads commentary about this method:
This is called when an anonymous request accesses a resource that requires authentication, when the user is not authenticated at all.
It returns generally a new Response.
It's for helping the user to connect to the website (so redirect to a form or use a header with an API for example)
Bye!
Thanks for the example, it definitely clarified a few questions I had as far as how guard was working.
Since we are utilizing jwt, is there really any benefit with a trip back to the db to verify the user since its already in the jwt claim? Couldn’t we just verify the claim was OK by calling $token->verify($signer, ‘secret') and just letting the request flow though. Is there another class (instead of AbstractGuardAuthenticator) that would be better reflect said example. I’m still very new to Silex (using the following symfony components: guard/security/doctrine) and am struggling to find a solution to my example.
Also I just wanted to point out that the following sentence is incorrect: “This guarantees that nobody has changed the username to some other username because they're a jerk. Encryption is amazing” I think what you meant to say was “Signing is amazing”. The data is technically not encrypted since we can still read it, however it is signed so we can determine if the contents have been tampered with on the client side or in transit.
Hey Binarymayhem!
You are definitely understanding the JWT philosophy correctly! So yes, we don't technically need to hit the database to verify the user, we're doing it for a different reason :). In this example, all we store in the token is the username. But, for Symfony's security to work, we need the full user object. The database call is just to take that username and go query for all of the fresh User data (i.e. so we get the full user object).
In other situations, it might make more sense for you to include more than the username in the JWT - e.g. you might include their email, roles, etc. Then, you could totally use that information to create an entire User object without querying the database. It just depends on your scenario :). Most of the time, people store their Users in the database and expect that when they say
$this->getUser()to fetch the User object, that it is their User entity. But in a more sophisticated setup, you could make yourUserobject just a normal class (not a Doctrine entity), and then create the User object from the JWT as described. This would allow you to have very fine-grained controls in your JWT (for example, you could create different tokens with different roles).And you're right about the sentence - this is definitely signing! I was a bit lazy on my words there, but I hope the correct sentiment comes through.
Cheers!
I am getting this error
The AppBundle\Security\JwtTokenAuthenticator::getUser() method must return a UserInterface. You returned AppBundle\Entity\User.
Hey Rajesh,
Do you implement UserInterface in your AppBundle\Entity\User entity? Because looks like you don't regarding this error. Or, maybe you just forget to use namespace for UserInterface. Make sure you have "use Symfony\Component\Security\Core\User\UserInterface" namespace in JwtTokenAuthenticator.
Cheers!
thanks...now its working
In Symfony 3.1.5 autowiring does not work. It's not sure which service to pick: the default encoder or the lcobucci encoder (whatever that is...).
I fixed it though by just manually wiring the arguments :)
Hey Johan!
Good fix! The new version of the LexikJWTAuthenticationBundle (released just a couple of days ago) should fix this problem (another awesome user reported it here, and the bundle maintainer fixed it quickly). Yes open source!
A few other minor things may be different if you use the LexikJWTAuthenticationBundle 2.* (we use 1.* in the tutorial) - I have it on my list to add any notes necessary to our tutorial to mention those. So far, the only other issue I know of is talked about here: https://knpuniversity.com/s...
Cheers!
"Houston: no signs of life"
Start the conversation!