If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeTo create our token authentication system, we'll use Guard.
Guard is part of Symfony's core security system and makes setting up custom auth so easy it's actually fun.
In AppBundle
, create a new Security
directory. Inside add a new class: JwtTokenAuthenticator
:
... lines 1 - 2 | |
namespace AppBundle\Security; | |
... lines 4 - 11 | |
use Symfony\Component\Security\Guard\AbstractGuardAuthenticator; | |
class JwtTokenAuthenticator extends AbstractGuardAuthenticator | |
{ | |
... lines 16 - 61 | |
} |
Every authenticator starts the same way: extend AbstractGuardAuthenticator
. Now, all we need to do is fill in the logic for some abstract methods. To get us started quickly, go to the "Code"->"Generate" menu - command
+N
on a Mac - and select "Implement Methods". Select the ones under Guard:
... lines 1 - 7 | |
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; | |
use Symfony\Component\Security\Core\Exception\AuthenticationException; | |
use Symfony\Component\Security\Core\User\UserInterface; | |
use Symfony\Component\Security\Core\User\UserProviderInterface; | |
... lines 12 - 13 | |
class JwtTokenAuthenticator extends AbstractGuardAuthenticator | |
{ | |
public function getCredentials(Request $request) | |
{ | |
... lines 18 - 30 | |
} | |
public function getUser($credentials, UserProviderInterface $userProvider) | |
{ | |
// TODO: Implement getUser() method. | |
} | |
public function checkCredentials($credentials, UserInterface $user) | |
{ | |
// TODO: Implement checkCredentials() method. | |
} | |
public function onAuthenticationFailure(Request $request, AuthenticationException $exception) | |
{ | |
// TODO: Implement onAuthenticationFailure() method. | |
} | |
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey) | |
{ | |
// TODO: Implement onAuthenticationSuccess() method. | |
} | |
public function supportsRememberMe() | |
{ | |
// TODO: Implement supportsRememberMe() method. | |
} | |
... lines 57 - 61 | |
} |
Tip
Version 2 of LexikJWTAuthenticationBundle comes with an authenticator that's based off of the one we're about to build. Feel free to use it instead of building your own... once you learn how it works.
Now, do that one more time and also select the start()
method. That'll put start()
on the bottom, which will be more natural:
... lines 1 - 5 | |
use Symfony\Component\HttpFoundation\Request; | |
... lines 7 - 8 | |
use Symfony\Component\Security\Core\Exception\AuthenticationException; | |
... lines 10 - 13 | |
class JwtTokenAuthenticator extends AbstractGuardAuthenticator | |
{ | |
... lines 16 - 57 | |
public function start(Request $request, AuthenticationException $authException = null) | |
{ | |
// TODO: Implement start() method. | |
} | |
} |
If this is your first Guard authenticator... welcome to party! The process is easy: we'll walk through each method and just fill in the logic. But if you want to know more - check out the Symfony security course.
First: getCredentials()
. Our job is to read the Authorization
header and return the token - if any - that's being passed. To help with this, we can use an object from the JWT bundle we installed earlier: $extractor = new AuthorizationHeaderTokenExtractor()
. Pass it Bearer
- the prefix we're expecting before the actual token - and Authorization
, the header to look on:
... lines 1 - 13 | |
class JwtTokenAuthenticator extends AbstractGuardAuthenticator | |
{ | |
public function getCredentials(Request $request) | |
{ | |
$extractor = new AuthorizationHeaderTokenExtractor( | |
'Bearer', | |
'Authorization' | |
); | |
... lines 22 - 30 | |
} | |
... lines 32 - 61 | |
} |
Grab the token with $token =
$extractor–>extract() and pass it the $request
:
... lines 1 - 17 | |
$extractor = new AuthorizationHeaderTokenExtractor( | |
'Bearer', | |
'Authorization' | |
); | |
$token = $extractor->extract($request); | |
... lines 24 - 63 |
If there is no token, return null
:
... lines 1 - 17 | |
$extractor = new AuthorizationHeaderTokenExtractor( | |
'Bearer', | |
'Authorization' | |
); | |
$token = $extractor->extract($request); | |
if (!$token) { | |
return; | |
} | |
... lines 28 - 63 |
This will cause authentication to stop. Not fail, just stop trying to authenticate the user via this method.
If there is a token, return it!
... lines 1 - 17 | |
$extractor = new AuthorizationHeaderTokenExtractor( | |
'Bearer', | |
'Authorization' | |
); | |
$token = $extractor->extract($request); | |
if (!$token) { | |
return; | |
} | |
return $token; | |
... lines 30 - 63 |
Next, Symfony will call getUser()
and pass this token string as the $credentials
argument. Our job here is to use that token to find the user it relates to.
And this is where JSON web tokens really shine. Because if we simply decode the token, it will contain the username. Then, we can just look it up in the database.
To do this, we'll need two services. On top of the class, add a __construct()
method so we can inject these. First, we need the lexik encoder service. Go back to your terminal and run:
./bin/console debug:container lexik
Select the lexik_jwt_authentication.encoder
service. Ah, this is just an alias for the first service - lexik_jwt_authentication.jwt_encoder
. And this is an instance of JWTEncoder
. Back in the authenticator, use this as the type-hint. Or wait, since it looks like there's an interface this probably implements, you can use JWTEncoderInterface
instead. Give this one more argument: EntityManager $em
:
... lines 1 - 16 | |
class JwtTokenAuthenticator extends AbstractGuardAuthenticator | |
{ | |
... lines 19 - 21 | |
public function __construct(JWTEncoderInterface $jwtEncoder, EntityManager $em) | |
{ | |
... lines 24 - 25 | |
} | |
... lines 27 - 82 | |
} |
I'll use a shortcut - option
+enter
on a Mac - to initialize these fields:
... lines 1 - 16 | |
class JwtTokenAuthenticator extends AbstractGuardAuthenticator | |
{ | |
private $jwtEncoder; | |
private $em; | |
public function __construct(JWTEncoderInterface $jwtEncoder, EntityManager $em) | |
{ | |
$this->jwtEncoder = $jwtEncoder; | |
$this->em = $em; | |
} | |
... lines 27 - 82 | |
} |
This created the two properties and set them for me. Nice!
Head back down to getUser()
. First: decode the token. To do that, $data = $this–>jwtEncoder->decode()
and pass it $credentials
- that's our token string:
... lines 1 - 16 | |
class JwtTokenAuthenticator extends AbstractGuardAuthenticator | |
{ | |
... lines 19 - 43 | |
public function getUser($credentials, UserProviderInterface $userProvider) | |
{ | |
$data = $this->jwtEncoder->decode($credentials); | |
... lines 47 - 56 | |
} | |
... lines 58 - 82 | |
} |
That's it! $data
is now an array of whatever information we originally put into the token. Fundamentally, this works just like a normal json_decode
, except that the library is also checking to make sure that the contents of our token weren't changed. It does this by using our private key.
Tip
The private key is only used in the case of symmetric signing. In the much more common case of asymmetric signing, the private key is used to sign the token, and the public key is used to verify the signature.
This guarantees that nobody has changed the username to some other username because they're a jerk. Encryption is amazing.
It also checks the token's expiration: our tokens last 1 hour because that's what we setup in config.yml
:
... lines 1 - 72 | |
lexik_jwt_authentication: | |
... lines 74 - 76 | |
token_ttl: 3600 |
So, if ($data === false)
, then we know that there's a problem with the token. If there is, throw a new CustomUserMessageAuthenticationException()
with Invalid token
:
... lines 1 - 45 | |
$data = $this->jwtEncoder->decode($credentials); | |
if ($data === false) { | |
throw new CustomUserMessageAuthenticationException('Invalid Token'); | |
} | |
... lines 51 - 84 |
Tip
In version 2 of the bundle, you should instead use a try-catch around this line:
use Lexik\Bundle\JWTAuthenticationBundle\Exception\JWTDecodeFailureException;
// ...
public function getUser($credentials, UserProviderInterface $userProvider)
{
try {
$data = $this->jwtEncoder->decode($credentials);
} catch (JWTDecodeFailureException $e) {
// if you want to, use can use $e->getReason() to find out which of the 3 possible things went wrong
// and tweak the message accordingly
// https://github.com/lexik/LexikJWTAuthenticationBundle/blob/05e15967f4dab94c8a75b275692d928a2fbf6d18/Exception/JWTDecodeFailureException.php
throw new CustomUserMessageAuthenticationException('Invalid Token');
}
// ...
}
We'll talk about what that does in a second.
But if everything is good, get the username with $username = $data['username']
:
... lines 1 - 45 | |
$data = $this->jwtEncoder->decode($credentials); | |
if ($data === false) { | |
throw new CustomUserMessageAuthenticationException('Invalid Token'); | |
} | |
$username = $data['username']; | |
... lines 53 - 84 |
Then, query for and return the user with return
$this–>em–>getRepository('AppBundle:User')–>findOneBy() passing username
set to $username
:
... lines 1 - 45 | |
$data = $this->jwtEncoder->decode($credentials); | |
if ($data === false) { | |
throw new CustomUserMessageAuthenticationException('Invalid Token'); | |
} | |
$username = $data['username']; | |
return $this->em | |
->getRepository('AppBundle:User') | |
->findOneBy(['username' => $username]); | |
... lines 57 - 84 |
If the user is not found, this will return null
and authentication will fail. But if a user is found, then Symfony finally calls checkCredentials()
. Just return true
:
... lines 1 - 16 | |
class JwtTokenAuthenticator extends AbstractGuardAuthenticator | |
{ | |
... lines 19 - 58 | |
public function checkCredentials($credentials, UserInterface $user) | |
{ | |
return true; | |
} | |
... lines 63 - 82 | |
} |
There's no password or anything else we need to check at this point.
And that's it for the important stuff!
Skip onAuthenticationFailure()
for now. And for onAuthenticationSuccess()
, purposefully do nothing:
... lines 1 - 16 | |
class JwtTokenAuthenticator extends AbstractGuardAuthenticator | |
{ | |
... lines 19 - 63 | |
public function onAuthenticationFailure(Request $request, AuthenticationException $exception) | |
{ | |
} | |
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey) | |
{ | |
// do nothing - let the controller be called | |
} | |
... lines 73 - 82 | |
} |
We want the authenticated request to continue to the controller so we can do our normal work.
In supportsRememberMe()
- this doesn't apply to us - so return false
:
... lines 1 - 16 | |
class JwtTokenAuthenticator extends AbstractGuardAuthenticator | |
{ | |
... lines 19 - 73 | |
public function supportsRememberMe() | |
{ | |
return false; | |
} | |
... lines 78 - 82 | |
} |
And keep start()
blank for another minute. With just getCredentials()
and getUser()
filled in, our authenticator is ready to go. Let's hook it up!
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!
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 !
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.!
I obtained token in json_login, it works.
Nice job!
In our current application we use session-based authentication. However, its not convenient as client is a separate react application ,
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?
also we want in the future we want to have different client app which will be connecting to Symfony API
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.
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?
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 AuthenticationEntryPointInterface
and 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, set entry_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_created
event (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. For onAuthenticationSuccess
, 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 your User
object 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!
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!
// 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
}
}
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 !