Although it's probably be done a hundred times, I'm going to attempt to explain JWT's (JSON web tokens) at a high enough level to make sure the concepts are sticking for me and I can get my head out of language-specific implementations. It's hard to find a broken-down explanation of JWT's that isn't targeted at a framework / language / plugin. Here goes.
First, the alternative
I've been using token-based auth for a while with Angular (1.x and 4.x) for a while now with the help of the really nice Devise Token Auth and the accompanying Angular Token Auth (1.x) and ng2-token (2.x+). Both of these plugins and gems are actually great. I've used them in a couple of production apps with good success. The community is pretty helpful too, considering how small of a plugin it is.
Downsides to regular token auth
- token is stored in the database
- it's a plugin, so you're automatically abstracted from what's happening
Those solutions mentioned above are really good, but they've enabled me to not dig enough to truly understand what's going on.
Plus, as I toy with apps that need to really sit away from their servers (ie, mobile apps using react native), I need something better.
JSON Web Tokens are the easy, relatively simple alternative to all this token madness. It's really not that hard to understand once you sit down and do it. For a more in-depth (rails-specific) example, check out JWT Auth in Rails, From Scratch by Sophie DeBenedetto, it's a really great primer. This will largely be a regurgitation of many of those ideas, but with even more broad strokes.
Step 1. Be able to create a JWT
This part is the only part that is implementation specific. But at a high level you'll need something that is able to encode and decode a JWT. There's no real need to go into frameworks here, you just need something basic.
Step 2. Auth with your DB and send back a JWT
First, we'll assume you have a database with user authentication. It doesn't matter what you use or what framework you're on. Just get an authentication system that has a
/login route. That's all there is to it. Create your login form, POST that username and password to your login route, but instead of sending back your user information, consider packing up a basic piece of user information into a JWT:
Use that payload as the payload portion of the JWT when you encode it, and have your /login route send back the following:
Note: you may want to also consider sending back any permissions/roles the UI needs in order to hide/show buttons. BUT, don't think that'll cover you. Any endpoints you have should also be checking permissions.
Step 3. Save the token locally, use ad nauseam
Once you get this token, save it locally (localStorage is a great place). The great thing is you can save it using anything (even when developing mobile apps), because it's a pretty simple string.
The thing that tripped me up for a bit was trying to figure out how to use the JWT in my UI for checking role, etc. This is not what it's meant for. The JWT is only used for you to tell the server you're already authenticated.
Once you've saved it locally, then attach it to every, yes every request you send to your API. Again, trying to remain front-end framework-agnostic, you'll just add a header to each request like this:
Step 4. Listen for that header, let user in
In your back-end code, setup something that checks for that header, and decodes it to get the ID of the user making the request. Now, not every request should be performed by every user. So let's say a malicious user wanted to request something they were not allowed to see. Well here we can do at least 1 of 2 things:
- Go check roles (on every request, or just on important endpoints) in the DB w/the current user ID passed by the JWT.
- Keep the role and the ID in the jwt
There are obviously pros and cons to both. I'm still fuzzy on making JWT's expire (ie, if a user's role changes), but that seems like a fairly simple thing to do.
Once you've received the token (and checked permissions if need be), you can assume the request is safe (assuming you're over https as well!) and return the info.
Step 5. Logout/delete the token
Probably the best part about JWT's is the ability to automatically know if a user is logged in, and immediately log them out when they're ready. Because no session is stored on the server, all you need to do is populate or clear localStorage and your user is logged out. Piece of cake.
Once you remove state and session from your servers you'll be able to more easily build mobile / disconnected apps, stop worrying about load balancing, etc., all the while keeping your users logged in, and knowing that the right people have access to the right things.
Let me know if anything is missing, or if you've found other nice ways of using JWTs.