This tutorial has a new version, check it out!

JSON Web Tokens (are awesome)

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.

Start your All-Access Pass
Buy just this tutorial for $10.00

How does authentication normally work on the web? Usually, after we send our username and password, a cookie is returned to us. Then, on every request after, we send that cookie back to the server: the cookie is delicious, and identifies who we are, it's our key to the app. The server eats that cookie, I mean reads that cookie, and looks it up in some database to figure out who we are.

How all (most) API Authentication Works

Guess what? An API isn't much different. One way or another, an API client will obtain a unique token, which - like the cookie - acts as their key to the API. On every request, the client will send this token and the server will use that token to figure out who the client is and what they're allowed to do.

How does it do that? Typically, the server will have a database of tokens. If I send the token I<3cookies, it can query to see if that's a valid token and to find out what information might be attached to it - like my user id, or even a list of permissions or scopes.

By the way, some of you might be wondering how OAuth fits into all of this. Well, OAuth is basically just a pattern for how your API client gets the token in the first place. And it's not the only method for obtaining tokens - we'll use a simpler method. If OAuth still befuddles you, watch our OAuth2 Tutorial. I'll mention OAuth a few more times, but mostly - stop thinking about it!

Anyways, that's token authentication in a nut shell: you pass around a secret token string instead of your username and password.

A Better way? JSON Web Tokens

But what if we could create a simpler system? What if the API client could simply send us their user ID - like 123 - on each request, instead of a token. Well, that would be awesome! Our application could just read that number, instead of needing to keep a big database of tokens and what they mean.

Alas, we can't do that. Because then anyone could send any user ID and easily authenticate as other users in the system. Right? Actually, no! We can do this.

In your browser, open the main website for JSON web tokens. These are the key to my dream. Basically, a JSON web token is nothing more than a big JSON string that contains whatever data you want to put into it - like a user's id or their favorite color. But then, the JSON is cryptographically signed and encoded to create a new string that doesn't look like JSON at all. This is what a JSON web token actually looks like.

But wait! JSON web tokens are encoded, but anyone can read them: they're easily decoded. This means their information is not private: you would never put something secret inside a JSON web token, like a credit card number - because - it turns out - anyone can read what's inside a JSON web token.

But here's the key: nobody can modify a JSON web token without us knowing. So if I give you a JSON web token that says your user ID is 123, someone else could steal this token and use it to authenticate as you. But, they cannot change the user ID to something else. If they do, we'll know the token has been tampered with.

That's it! JSON web tokens allow us to create tokens that actually contain information, instead of using random strings that require us to store and lookup the meaning of those strings on the server. It makes life simpler.

Oh, and by the way - once you eventually deploy your API, make sure it only works over SSL. No matter how you do authentication, tokens can be stolen. So, use HTTPS!

Now that we know why JSON web tokens - or JWT - rock my world, let's use them!

Leave a comment!

  • 2018-12-11 weaverryan

    Hey Marius Bora!

    Sorry for the slow reply - SymfonyCon was last week and kept us busy! First, what's your motivation for using the JWT for authentication and then a separate access_token after? I'm just making sure I understand what you're trying to do. And second, there is probably not a bundle for what you're thinking of - there is of course LexikJWTAuthenticationBundle for using JWT in general, but for a more specific implementation, you'll probably need to add it yourself. So, you are not missing anything!


  • 2018-12-06 Marius Bora

    I'm thinking of implement a system where the user authenticates with JWT, and then, using the jwt token, the user can request an access_token and that will decide what the user can and cannot do (To keep things small, the symfony app would act as both the authorization server and resource server) . I wonder if there are bundles that already offer this functionality, I've reasearched into a few and I'm planning on finishing some courses on this page about that matter, but before I dig in too deep I need to check I'm not chasing a wilde goose here.

  • 2018-11-19 weaverryan

    Hey Coder!

    > So, if api tokens did not exist, we would be forced to give other apps our password. And then, if we changed our password (because we wanted to deny access to 1 app that we shared it with), we would invalidate access for all of those apps.

    Ah yes, I DO mean that every user would have their own password. Think about Facebook as a good example: imagine I have an account on Facebook (and I of course have my own, unique password). Now, I want to allow some other site - e.g. SymfonyCasts - to access some information on my Facebook account via their API. If API tokens did not exist, then I would need to give SymfonyCasts my Facebook password... so that SymfonyCasts could use it in API calls to Facebook. Sure, there might be the same fancy OAuth flow to "pass" my password to SymfonyCasts. But, the important thing is that, ultimately, SymfonyCasts would have my password. Now, imagine I also want to allow some other site - e.g. GitHub - to be able to access my Facebook account via the API. Now I would *also* need to give GitHub my Facebook password.

    This situations has 2 major issues:
    1) Because SymfonyCasts and GitHub have my password, they can actually do ANYTHING with my Facebook account, including post things to my wall. One of the advantages of API tokens is that (if you need to) you can make an API token only have *some* permissions for an account, instead of ALL permissions.
    2) Imagine that we no longer trust GitHub and we want to "revoke" access - we don't want GitHub to be able to access our Facebook account anymore. The only way to do that is to change my password on Facebook. But, when I do that, I've also (accidentally) revoked access to SymfonyCasts.

    I hope that makes sense! The argument for API tokens is pretty clear. But also, you don't *really* need them unless you want 3rd parties to access your API. If you're building an API ONLY so that your OWN JavaScript can access it, using session cookies is a simpler approach.


  • 2018-11-17 Coder

    Thats a long answer. I think I did not understand most of the things, I need to reread it slower but currently have other stuff to watch. I mark it to come back later. But one thing instatly raises question to me:

    "So, if api tokens did not exist, we would be forced to give other apps our password. And then, if we changed our password (because we wanted to deny access to 1 app that we shared it with), we would invalidate access for all of those apps."

    You mean 1 password for every user? I think every user would have their own pasword and so we just deny access to one user. But since I did not take time to think much, that might be a reason why I did not understand.

  • 2018-10-29 weaverryan

    Hey Coder !

    Really good question. The WHOLE point of "API tokens" is this: to have some "string" that allows access that is different than the user's password. Let me say it a different way. Yes, if you're using SSL, there is no security risk to sending your email/password, credit card information, API token or anything else to the server. So then, why do we have API tokens at all? Why not just have the user *always* send their email/password to the API endpoints?

    Actually, this *is* a valid way to do it. And, like we just talked about, over https, this doesn't really have any security implications.
    The *real* reason for API tokens is that you can create an API token and safely give it to someone else. Then, if you decide that this "other person/app" should not have access to your account anymore, you could (in theory, it depends on how you build your system) invalidate/delete that API token. Heck, you could give out 10 different API tokens to 10 different users. And if you want to deny access to just one of them, you only need to invalidate/delete that API token for 1 app. The other 9 will work, as will your password. Additionally, in a more complex system, you can make some tokens have different permissions than others.

    So, if api tokens did not exist, we would be forced to give other apps our password. And then, if we changed our password (because we wanted to deny access to 1 app that we shared it with), we would invalidate access for all of those apps. This is how, for example, OAuth works with Facebook. If you clicked "Login with Facebook" on SymfonyCasts, you are basically creating & giving SymfonyCasts an API token that gives some access to your Facebook account. SymfonyCasts then sends that token on API requests to Facebook to perform certain actions "as you" (well, we don't do that - we just fetch your email for login purposes, but you get the idea). If you wanted to "invalidate" our access, you could go to Facebook and "revoke our access", which basically makes our API token invalid.

    Phew! The big question with all this API token stuff is: how do you want your users to be able to create API tokens? In this simple app, we created an API endpoint where you can create them. Then, in theory, you could safely give those to someone else. We could also implement an OAuth server (then we would operate a big like Facebook) OR we could create a simple profile section where each user can manually create access tokens (you can do this on GitHub). We actually have an entire short video on this exact topic:

    Let me know if that helps!


  • 2018-10-28 Coder

    I did not get. So the benefit is that we send user id, and we get jwon web token which contains various information? But we first maybe have to still send username and password, so that anyone would not be able to send the user id and get information about the user?

    If that is how I wrote then: why we cannot send credit card information if the user is authenticated and we send that token over SSL? Nobody besides authenticated user will be able to see the credit card information.

  • 2017-11-01 weaverryan

    Hey einue!

    Ah, very interesting question :). I'll explain a bit using the new (version 2) of the though the details will essentially be the same for version 1.4.

    The most important thing to know is that the bundle relies on a *different* library to actually do the key stuff. It either uses or based on your configuration. So let's talk about the first. If you look at their docs, they explain it fairly well: Basically, the private key is used to sign the key, and then the public key is used to verify it.

    This *is* different than asymmetric key encryption. But... it's kind of the same idea in a different direction. I may be off on some finer details (security is always tricky), but basically, if you have a public and private key, you can use it in 2 different ways:

    A) Encryption: Encrypt data with a public key, and decrypt with a private key (like how SSL works)
    B) Signing: Sign data with a private key and "verify" its authenticity with a public key (JWT)

    I hope that helps!


  • 2017-10-30 einue

    Can you point out, when the private key and when the public key is used? I think it is different from an asymmetric key encryption scheme?

  • 2016-10-16 weaverryan

    Just making sure you're aware ;). Thanks for pointing that out - we'll get that fixed and re-export.


  • 2016-10-16 Johan

    The typo at 3:54 "authenticting" triggered me a bit, just a little bit though ;)