JWT: Other Things to Think about

Mostly, that's it! JWT authentication is pretty cool: create an endpoint to fetch a token and an authenticator to check if that token is valid. With the error handling we added, this is a really robust system.

But, there are a few other things I want you to think about: things that you may want to consider for your situation.

Adding Scopes/Roles to the Token

First, when we created our JWT, we put the username inside of it:

41 lines src/AppBundle/Controller/Api/TokenController.php
... lines 1 - 12
class TokenController extends BaseController
... lines 15 - 18
public function newTokenAction(Request $request)
$user = $this->getDoctrine()
->findOneBy(['username' => $request->getUser()]);
if (!$user) {
throw $this->createNotFoundException();
$isValid = $this->get('security.password_encoder')
->isPasswordValid($user, $request->getPassword());
if (!$isValid) {
throw new BadCredentialsException();
$token = $this->get('lexik_jwt_authentication.encoder')
->encode(['username' => $user->getUsername()]);
return new JsonResponse(['token' => $token]);

Later, we used that to query for the User object:

101 lines src/AppBundle/Security/JwtTokenAuthenticator.php
... lines 1 - 19
class JwtTokenAuthenticator extends AbstractGuardAuthenticator
... lines 22 - 48
public function getUser($credentials, UserProviderInterface $userProvider)
$data = $this->jwtEncoder->decode($credentials);
if ($data === false) {
throw new CustomUserMessageAuthenticationException('Invalid Token');
$username = $data['username'];
return $this->em
->findOneBy(['username' => $username]);
... lines 63 - 99

But, you can put any information in your token. In fact, you could also include "scopes" - or "roles" to use a more Symfony-ish word - inside your token. Also, nobody is forcing your authenticator to load a user from the database. To get really crazy, you could decode the token and create some new, non-entity User object, and populate it entirely from the information inside of that token.

And really, not everyone issues tokens that are related to a specific user in their system. Sometimes, tokens are more like a package of permissions that describe what an API client can and can't do. This is a powerful idea.

OAuth versus JWT

And what about OAuth? If you've watched our OAuth tutorial, then you remember that OAuth is just a mechanism for securely delivering a token to an API client. You may or may not need OAuth for your app, but if you do use it, you still have the option to use JSON web tokens as your bearer, or access tokens. It's not an OAuth versus JWT thing: each accomplishes different goals.

Refresh Tokens

Finally, let's talk refresh tokens. In our app, we gave our tokens a lifetime of 1 hour:

77 lines app/config/config.yml
... lines 1 - 72
... lines 74 - 76
token_ttl: 3600

You see, JWT's aren't supposed to last forever. If you need them to, you might choose to issue a refresh token along with your normal access token. Then later, an API client could send the refresh token to the server and exchange it for a new JWT access token. Implementing this is pretty easy: it involves creating an extra token and an endpoint for exchanging it later. Auth0 - a leader in JWT - has a nice blog post about it.

Ok! If you have any questions, let me know. I know this stuff can be crazy confusing! Do your best to not overcomplicate things.

And as always, I'll see you guys next time.

Leave a comment!

  • 2017-11-07 weaverryan

    Hey Ahmed Bhs!

    Excellent question! Actually, you have a 2 main options:

    1) If you want, you can create a nice authentication system with NO API token system at all. Instead, you can create a traditional form login system and submit the POST request. You'll just need to make sure your form login system does not redirect (like a normal form system does), and instead returns some nice JSON response (the JSON response can be anything, but maybe it will be useful for you to return some user data - but that doesn't really matter). In this system, Symfony will set a session cookie just like with a normal, non-AJAX login. All future AJAX requests will automatically use this cookie to authenticate. I recommend this approach in a lot of cases... because it's really simple. If your API exists *only* so that your own JavaScript frontend can talk to it, then this is great.

    2) If you do need/want to use API tokens for some reason, then the "flow" would look something like this:

    A) When the user logs in, send a POST request with the username & password to some endpoint that will check the validity and return an access token. We do this exactly here:

    B) Store the token somewhere - I believe a cookie is the best place. Check out this article:

    C) On each future AJAX request, yes, you'll need to read the token and send it on a header.

    I hope this helps!

  • 2017-11-05 Ahmed Bhs

    I really need some help!! Any idea how to deal with authentication via ajax in front side ?
    I'm not sure maybe:
    1) send username+password json format ?no need to encrypt the json data before sending ?
    2) get the tocken from the api, and set it into local storage ?
    3) for every request do we need to send the token ?
    And what about the data base what should contain ?
    I will be so happy if you provide a jquery example.

  • 2017-01-09 alsbury

    +1 For refresh token tutorial

  • 2016-11-07 Ruslan

    Thanks Ryan. I've preferred to use the $request to store the data.

  • 2016-11-07 weaverryan

    Hey Ruslan!

    Yep, you can definitely do this :). With our setup, we didn't make any easy way, but effectively, you simply need to decode the JWT in the same way that we decode it in the authenticator. Basically, you can repeat the things we do here: - grab the token from the header, and then decode it. Of course, it would be even better to take this code and put it into a new service class. Then, you could call that from your authenticator or from your controller. And alternative method would be to update your authenticator to "put" the decoded JWT "somewhere" - e.g. set it on a service... or even put it into the request object $request->attributes->set('jwt_data', $data) (where $data is the decoded JWT that we have in getUser() of the authenticator).

    Let me know if this helps! Cheers!

  • 2016-11-06 Ruslan


    Is there a correct way to get some data from jwt token in a controller? For example, if I add a company id to my token, how can I then get that id in my controller?

  • 2016-06-24 Vlad

    Thank you, Ryan! This totally makes sense.

  • 2016-06-24 weaverryan

    Hi Vlad!

    You're absolutely right. But their very nature, JWT's are not meant to expire - though you *could* make this happen, but storing them on your server and checking their status. There's actually a really good article about this:

    But, it does kind of defeat the purpose, since the beauty of JWT is that they are standalone and don't need to be stored or checked anywhere. But, you need to do what you need to do :).


  • 2016-06-23 Vlad

    Hi Ryan and Victor,

    I've done some research on this. Correct me, if I'm wrong, but I believe, it is impossible to invalidate a token to force a logout.
    The token isn't stored anywhere on the server, but simply contains the information necessary for its validation, such as the username, expiration time.

    I haven't looked into OAuth, but I think, it might have something to accomplish this functionality.

    Thank you for your suggestions.

  • 2016-06-23 Vlad

    Hi Ryan,
    I want to also add some sort of a logout action to let the user logout, and not simply let the token expire.
    I was thinking of a route: /api/logout for that.
    How would I accomplish it? Is there a way to expire the token upon request?

  • 2016-06-13 Vlad

    Thank you, Ryan!

  • 2016-06-13 Vlad

    Thank you, Ryan!

  • 2016-06-11 weaverryan

    Hey Vlad!

    This is less related to the firewall (which is mostly about "authentication" - identifying who you are) and more about authorization: denying access. We'll talk about this (next week actually) in the Symfony security tutorial:

    But basically, IS_AUTHENTICATED_ANONYMOUSLY is a way to guarantee that non-authenticated users have access to the regex path. For example, Obviously, we want users to be able to access /register and /login without needing to be logged in :).

    However, like routing, access controls match from top to bottom, and *stop* as soon as they match *one* line. This is important for the last 2 lines:

    1) ^/api if the URL has not matched any of the previous lines, then guarantee anonymous access to /api*. We do this just because we decided that we will deny access in the controller, not here.

    2) ^/ This matches *all* URLs. But, if the current URL already matched one of the above access controls, it will not reach this access control. This means that - for example - /login*, /register* and /api* will all be public (unless access is denied in the controller). But *every* other page will require you to be logged in.


  • 2016-06-11 weaverryan

    Hi Vlad!

    Yes, JWT is sufficient for simple API authentication. And the short answer between JTW and OAuth2 is that the are different things: JWT is a type of token string (one that contains information). OAuth2 describes a process for *obtaining* a token. In fact, if you setup an OAuth2 server, you can actually issue JWT's as your access token :).

    OAuth2 is great - but it's also a bit complex if you're not an expert in it. I generally tell people not to worry about using OAuth2 for their app, unless they will have third-parties integrating with their API who will need to obtain access tokens for specific users (e.g. Facebook - many sites/apps need to obtain access tokens for different users on the site). This is the most common place you see OAuth.

    In case you haven't checked it out yet, we have an awesome tutorial explaining OAuth2 as well :)


  • 2016-06-10 Vlad

    Hi Ryan,
    In security.yml, I noticed additional configuration:

    - { path: ^/_wdt|_profiler, roles: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/register, roles: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
    # allow anonymous API - if auth is needed, it's handled in the controller
    - { path: ^/api, roles: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/, roles: IS_AUTHENTICATED_FULLY }

    Is this configuration related to the firewall?

  • 2016-06-10 Vlad

    Hi Ryan,
    Thank you for the great tutorial.
    Is JWT Authentication sufficient for simple API authentication?
    What are the disadvantages of JWT over OAuth2?

  • 2016-06-09 weaverryan

    I think so too - I've added it to our idea list!

  • 2016-06-09 Roberto Briones Arg├╝elles

    I think that the topic of "Refresh token" is basic , It would be cool If you guys make a tutorial for this :D