Symfony RESTful API: Authentication with JWT (Course 4)

After 3 tutorials, we've got a nice API, But we've been completely ignoring authentication. Thanks to some modern tools, this will be such a treat:

  • Understanding JSON web tokens (JWT)
  • Creating, signing & returning JWT's
  • Using Guard for a custom JWT authentication system
  • Sending tokens on the Authorization header
  • Proper API errors for invalid credentials and missing credentials
  • Choosing to split into 2 firewalls

Your Guides

Ryan Weaver Leanna Pelham

Questions? Conversation?

  • 2016-09-12 weaverryan

    Sweeet! Great job! :)

  • 2016-09-09 fredrsf

    Hi weaverryan ! I build some SPA application with rest api (symfony) + reactjs. This thoughts appeared after I saw this slides http://www.slideshare.net/weav.... Thank your team! And now your answer very helped me, I set stateless on true and used your links for invalidate jwt keys. Thank your!!!

  • 2016-09-01 weaverryan

    Hi fredrsf!

    So, "logging out" with JWT can be tricky. The reason is that - in a true API - your authentication is "stateless" - i.e. there is no "logout" - either you send a valid token in the request to authenticate, or you don't, and you're not authenticated :). If you had an iPhone app that used your API and you wanted to "logout", this could be as simple as making the iPhone app "forget" the token, so that authenticated requests can no longer be made.

    But, there are two problems. By design, JWT's are not meant to be invalidated. So, even if you make your iPhone app (or JavaScript) "forget" the JWT, it's still technically valid until it expires. If you want to actually *invalidate* the JWT, you need to do a bit more work. Here's an interesting SO about this: http://stackoverflow.com/quest....

    The second problem is what you're talking about: it sounds like you're using JWT from JavaScript. And it also sounds like your firewall is "stateful" (the default setting), which means that your session is being stored in a cookie. In this setup, you're AJAX calls are authenticated for *two* reasons: (A) because you're sending a JWT and (B) because after logging in once with your JWT, Symfony "remembers" that you're logged in via a cookie.

    There are two ways to fix this. Since you're using JWT to authenticate, the best solution is to use a separate firewall for your API endpoints and set "stateless: true" on that firewall, so that nothing is stored in the session/cookie. This makes your API authentication stateless, as it should be.

    Otherwise, when you "logout", you'll need to either hit the "/logout" endpoint (e.g. via AJAX) or hit some endpoint and programmatically logout the user (http://stackoverflow.com/quest....

    Let me know if this helps! Often, if I'm making an API *only* so that I can consume it via *my* JavaScript, I won't bother with JWT, and I'll just allow the user to login normally and use sessions. That's another option. But using sessions *and* JWT can making logging out a little weird :)

    Cheers!

  • 2016-08-31 fredrsf

    Hello, how I can to make logout action with JWT? I remove token from localStorage object, but PHPSESSID exists.

  • 2016-05-07 weaverryan

    Thanks Vincent - really happy you enjoyed them! Course 5 is scheduled for May (this month) - it's in the final editing stages (we were aiming for next week, but might not be until the week after).

  • 2016-05-06 Vincent Wong

    Awesome course, I enjoyed them so much, any update on the release schedule for course 5?

  • 2016-04-27 Tahmina Khatoon

    Awesome, finally it has started to publish.

  • 2016-04-27 Chen Dong

    Wow!!!!! It's being so long~ Thanks for the updates!

  • 2016-04-24 weaverryan

    This week :)

  • 2016-04-23 Tahmina Khatoon

    Next approximate date please, I am just waiting for this one from last year ...

  • 2016-01-26 weaverryan

    Episode 3 is in the final stages of editing right now. I have not yet started on this one - so it's likely still 1 month away or longer. You can click at the top of the page and we'll message you when it's ready. We'll do our best to make it sooner! And due to some authentication changes in Symfony (Guard), this will be an *awesome* tutorial :).

    Cheers!

  • 2016-01-22 Rafael

    Any updates?!

  • 2015-10-30 Rasel Khan

    Hey , When i'll got this tuts, I need urgent !!

  • 2015-10-19 weaverryan

    Hey! We'll get this released as soon as possible, but it won't be until at least November! Until then, check out https://knpuniversity.com/scre... and https://knpuniversity.com/scre....

    Cheers!

  • 2015-10-19 Tahmina Khatoon

    October is running out, I am also waiting for this tutorial.

  • 2015-10-11 Marius Puiu

    Daily F5 press :D

  • 2015-08-20 weaverryan

    Hey Manuel!

    Ah, bummer! A few things:

    1) You can email me about the subscription if you want to pause it or cancel it until this is ready - ryan@knpuniversity.com :).

    2) This tutorial will be released in September at the earliest, but more likely October.

    3) Until then, I highly recommend checking out the new Guard library and tutorial we released: https://knpuniversity.com/scre... - with any luck, it'll make adding authentication to your API really easy.

    4) You can also check out the Silex tutorial for all this authentication stuff - there are several chapters on it here: https://knpuniversity.com/scre...

    Cheers!

  • 2015-08-17 Manuel

    oh my god, i just joined the knp university because of this tutorial...
    But its not there... what do you think how much time it takes until release?

  • 2015-07-21 weaverryan

    Hey again!

    If it helps, Guard *is* available now, and would work fine with FOSUserBundle (you could even let FOSUserBundle do the form login stuff, then use Guard only for the API token auth): https://knpuniversity.com/scre...

    Here are some answers assuming you *don't* change to Guard:

    1) I would probably use 1 firewall for this, though you could probably also have 2. With 1 firewall, you're basically saying "I really have 1 security / user system, but I want to make it possible for someone to "login as a user" via an API key"

    2) This part gets more complex. Here's the easiest answer - it's a bit of a "cheat", but will work brilliantly. Don't override or change the FOSUserBundle's user provider at all. So, don't have a custom "provider" key that points to some "api_key_user_provider" - just use the normal FOSUserBundle stuff. Inside your api key authenticator, in authenticateToken(), you won't be able to use the $userProvider argument, because this will be from FOSUserBundle and won't have any custom methods that you need. But that's ok. Instead, inject the EntityManager into your authenticator, lookup your User repository, and query for the user based on the token (your exact logic here may differ). I recommend something very similar to this in Guard in its getUser() method - https://knpuniversity.com/scre....

    3) You're right that you will probably have some endpoint like /api/tokens where you will POST with HTTP BASIC authentication in order to be able to create a token. But now, you should not have another firewall for this - just add the http_basic stuff under your existing firewall. You will now have 1 firewall with *3* different ways to authentication (form_login, simple_preauth and http_basic). That's perfect: those are just three different ways to help a user login to his/her *one* account.

    Cheers!

  • 2015-07-20 Murilo Lobato

    Hey thank you for the reply, actually a month ago I was researching for way to implement it now. And, even after those links helped me a lot, I still don't understand a few things.

    I already have FOSUserBundle installed, and some CRUDs all prefixed by a '/admin' slug. So, my firewall is called "secured_area" is configured accordingly to FOSUserBundle docs.

    My doubts:

    1) Should I have another firewall, called api_secured_area configured like following:


    api_secured_area:
    pattern: ^/api
    stateless: true
    simple_preauth:
    authenticator: apikey_authenticator
    provider: api_key_user_provider


    or I just need to add the “simple_preauth” part to my existing firewall?

    2) Is correct that my “api_key_user_provider” extends the FOS\UserBundle\Security\EmailUserProvider to reuse and centralize the provider logic?

    And now, the worst part at least for me (:

    3) For my mobile apps be able to create a token, I will need to have a "/api/tokens" route (i guess), with HTTP_BASIC auth. The question is: Is correct to have another firewall to define this route as http_basic?

  • 2015-06-17 weaverryan

    Hey Murilo!

    In the mean time - check out http://symfony.com/doc/current.... You may need to setup an OAuth server (there is a bundle for this - FOSOAuthServer), or you might just need a very simple token-based authentication system. We also setup the latter in our REST Silex tutorial - it might help too http://knpuniversity.com/scree.... The Silex tutorial uses a "harder" version of Symfony authentication than the above-linked Symfony docs page (because the simpler way isn't/wasn't supported in Silex), but you might be able to put the ideas together.

    I'll also be releasing a library (should be next week) called Guard that will drastically simplify setting up a custom authentication situation in Symfony. But, The Symfony link above isn't too hard either.

    I hope this helps! This tutorial is probably 2 months away... and I figured you probably can't wait that long ;).

    Cheers!

  • 2015-06-17 Murilo Lobato

    Waiting hard for this episode :0