Portable Sessions with JSON Web Tokens
CC Attribution - ShareAlike 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this
Ivy, Lance
Ever wonder why applications use sessions and APIs use tokens? Must there really be a difference? JSON Web Tokens are an emerging standard for portable secure messages. We'll talk briefly about how they're built and how they earn your trust, then dig into some practical examples you can take back and apply to your own majestic monolith or serious services.
 All right, good morning. I'm going to get started. People can come in from the hallway if they're still out. My name is Lance. If you're here, you probably want to learn about JSON Web tokens, so you're in the right place. We're going to talk a little about what they are, why they exist, and maybe who cares. So they're tokens. They use JSON and they're built for the web. Never mind, scratch that. Now that you're all in the room, I'd like to offer you a special invite, the ground floor of mine. So I've got this idea. It's got the application. You're going to log in and you're going to see a face. Probably your face. It's going to fit somewhere in this like faces as a service vertical. I think it's going to be pretty big. I mean the face is going to be pretty big, clearly. I'm calling it face page. So welcome. You're all hired. You didn't know this was an interview. I mean you passed the code challenge when you registered for the conference, right? So we're good. And you all have faces, so the qualifications are there. So let's go. Oh, I suppose you're wondering who you'll be working with. I'm Lance Ivy. I'm based out of Portland, Oregon. I've seen little out of sorts because I'm trying to get used to the bright solar radiation. I started professionally tinkering on the web as an unpaid developer in 2003, joined some friends and colleagues from college on this startup idea in a small town of like 30,000 people. And started in front end and the era of Netscape had just wrapped up. So I like opened it once, took a picture of a horribly broken rendering and never went back. And made some nice tables after that. Some things happened in 2006, 2008. I became a paid Rails developer. I found my way to working on Kickstarter. Made my own share of mistakes, learned from most of them, picked up a bunch of stories. Currently I'm working with EmpatiCo and also building an open source project called Carrington Off-N, which is an authentication server, you can imagine, devised, if it were rebuilt today as a standalone service. And this is the primary learning opportunity for the talk. So let's get back to FacePage. Obviously we're going to run FacePage as a standard Rails application. So here we go. We have a few meetings. We pivot. We sprint. We change task management systems. We iterate on faces and what they mean to our users and how they're going to change the world and then we deploy it. It's out in the wild. Users love it. There's just one problem. People like seeing their faces on the go and they're looking for us in the native app stores. So easy, right? We make an API. We build out our ILS, Android apps. And maybe the app and the API are intertwined. JSON endpoints the same controller. Or maybe they're namespace, but they're still in the same app. It's a majestic model. Or maybe it's a standalone deployment, but that doesn't matter. What matters is it's up in the cloud, right? It's there. It's running. It works. But something along the way has happened. We couldn't use cookies for our API. So we implemented a quick token authentication system. So now we've got cookies over here and tokens over there and this uneasy feeling that there's something similar, but we can't quite put our finger on it. And we just keep going. But it turns out that we've actually implemented two authentication systems and they're separate. So this is technical debt, but we figure we can manage. It's working. We keep moving. And just suppress those uneasy feelings into a tight little ball. And someone on high maybe makes the decision to move to services, which I don't know. We try it out and we build this API gateway. But is it written in Rails? Does it speak cookies or tokens or both? Do we have to retrofit our authentication schemes into it? Are we on our own? Do we have help? Do we move to something else? I mean, what happens now? Well, I want to take a step back and consider the problem that these cookies and tokens are actually trying to solve. How do we even get here? It's time to ask what even is logging in? What happens? What are the cookies and tokens doing? Let's dig in. We can say it starts here with a username and password. These days it could also be Facebook or GitHub or Google or some other auth provider, but probably still at least passwords. Turns out that's not really the part we care about. What we care about is how we keep track. So here's the classic way of keeping track. Browser logs in. We send back a cookie. And browsers know about cookies. They know to include them on every request back to our domain. So every request for a face on a page is logged in. And we can show your face back. I mean, the requests that want JavaScript and CSS and images are also carrying these cookies, but that's a different talk, right? The simplest explanation for cookies is a header protocol. When the server responds with a set cookie header, the browser knows to include it back in the cookie header. Every future request. But I think it's time to consider the elephant in the room here. What kinds of cookies are these, really? I mean, when we diagram HTTP cookies, they usually look like this. Chocolate chip or raisin, maybe. Oh, what's for lunch? But this is how we should think of them as fortune cookies with a message inside. So Rails uses these to store bits of data in the browser, and we can crack one open and see what's inside. Here's one that you might actually see in an actual Rails application. It's an encoded message with a signature, and you can split it apart on the double hyphens right there. Here's what's inside. There's a user ID. There's a CSRF token, because with cookie authentication, you need that kind of thing. There's a signature that we can use to just make sure that it's legit. So there we go. Cookies are headers on HTTP responses and requests that do things like transport a user ID back and forth. And that's the login story we care about right now. So what's happening with the tokens on the API side of things? Well, one common convention might look like this. The server responds to a login request with some random string in the JSON body, let's say. And again, the device sends it back on future requests, but this time in the authorization header. Now these tokens are opaque. They're random strings. They have no meaning until we use them to find something more interesting like the user ID. And this is good, but it's not great. On the upside, we could delete these tokens to revoke access at any point. We have some control over that. But on the downside, every API query now involves a database query. This is, by the way, how Rails sessions used to work before switching to cookies. And it was a performance problem. The browser submitted a session ID, and you use that to actually find their session from the database. All right, so let's put together what we've learned. The Rails session cookie uses a cookie header, but our API tokens use the authorization header. The Rails session cookie is structured data, but the API is just an opaque random string. Now the Rails session cookie can be verified with cryptography, and API token is security through queries. So can you imagine the best of both worlds? Chase on web tokens. It's signed structured data. This is rather similar to Rails signed cookies. We've added a third segment, the header, and this just describes the format of the token. We can decode the message. What you're looking at here are called the token claims. Now they're called claims to give you a bit of skepticism, because you actually have to assert that these claims are true before you can trust it. Here's a list of common claims. You can see that they're heavily abbreviated. And the reason is because Chase on web tokens are designed to fit in headers and other character limited small spaces. So they have to save on bytes for your more interesting content. All the claims in the left are what I consider metadata. Their claims necessary to verify that a token may be used appropriately in different situations. I'll just go through these. The issuer describes the party that generated and signed the token. Audience describes the party that the message is intended for. These might be the same thing. They might not. Issued at is when the token was created. An expiration is when the token should be ignored. They can have a lifetime. The claims in the right are what I consider the payload. And this is the information that you probably want to extract for your business logic. Now you can put anything you want in here as long as the issuer and the audience agree on what it means. But the common one that a lot of issuers and audiences agree on is the subject. And this is meant to identify the party that the message is about or the person who owns the token, the person who has it. And this is where we put the user ID. So the Chase on web token standard is a pretty generic thing. It's just a spec for sending secure messages. But one of its primary uses is identity. It actually evolved in the context of OAuth and OpenID Connect. And you can see a lot of that in the claims that are built into it. So we can actually imagine it kind of like Rails Cookies and API tokens. Think of it as an ID card. It's like an ID card that makes a number of claims and it contains some security features. This ID card has an issuer. It's from the internet. It has a subject, name, has expiration and issued dates. It has this pretty sweet little official stamp. Security. But it's actually up to you to check the card and detect forgeries to make sure that you can actually take this identification. So here's how you do it. One, is it from someone that we recognize as an authority? Check the issuer. Two, was it intended for me? Check the audience. Has it expired? Check the expiration. Is it a forgery? Check the signature. Can you recreate the signature based on those values? And last but not least, was it generated before or after that time we had to change our secret because we published it on GitHub? And you can check issued at for that. If you can answer those five questions, you're in pretty good shape. And the good news is that you can get a library to just do this for you. Okay. So we've learned that JSON Web tokens are secure messages, like a Rails signed session cookie. We've learned that they contain claims that we need to verify, and we've learned about the most important claims and what they represent. So let's talk about what we can do with them. We've already mentioned identity tokens, so let's continue from there. One of the problems we had in our Face Page app was the duplicate authentication system. So let's see how JSON Web tokens can help. First let's add JSON Web tokens to our comparison table. So while the Rails cookies are tied to the cookie header and the API tokens use the authorization header, our JSON Web tokens are ready for either. They don't care. The Rails cookies were structured data signed with cryptography, and that's actually pretty good. So our JSON Web tokens share those characteristics. So if we use an identity, JSON Web token, for login, it might look like this. So submit the login, Rails responds with a JSON Web token in a cookie. The JWT contains the user ID as the subject. Now in future requests, the browser just sends it back in the cookie. And this looks pretty familiar. And that's a good thing. We actually haven't changed our headers or the relationship between the browser and the server. We're still using cookies. We've just changed the format of the message inside the cookie. And on the API, we can drop it in here as well. There's no change to the client. They're still just sending a string back and forth. But now the string is a JSON Web token. It has structured data. It has meaning. It's not random. And the server can do something with it. So this is the JSON Web token solution. One token, two headers, one authentication system. It doesn't matter whether the server finds the token inside a cookie or inside an authorization header, it can still handle that value exactly the same and set the current user for the duration of the request. Problem number two in our Face Page app. Previously the API had to execute a query on every request just to discover who was making it. And now our API can verify the JWT with the claims and the cryptography. So this replaces a network-bound database bottleneck process with a straightforward CPU-bound calculation. This will perform faster. This will scale better. This will introduce less variation into response times and generally just have fewer failure modes. Problem number three, our cookies were implemented for Rails, by Rails. Now don't get me wrong, the default Rails session store is wonderful. It's, it works. It's hidden. It is secure. It is very well designed. And it does the job it needs to do. But it is tightly coupled to Rails. It is tightly coupled to cookies. And it's kind of tightly coupled to majestic monoliths. So if any of those things don't work for you, you have to ask yourself what's next. JSON WebToken libraries are implemented in at least 20 languages. They're decoupled from the cookies. And they contain claims that you can use to build any kind of distributed architecture. So they're, they're more flexible. It's a more general purpose solution. Problem number four, in a distributed architecture, you might find yourself sharing secrets so that when you sign a message with one system, you can verify it on another. And this involves trusted back channels like copy and paste. Or you know, configuration management systems. And now that the secret exists in more places, it's a bigger attack surface. If any one place is compromised, that secret can be used to attack all the other places as well. So what do JSON WebTokens offer? They support asymmetric key algorithms, like RSA. Now the signature process used by Rails to sign a cookie is called HMAC. You give it a salt, it hashes the cookie. You take that salt, you rehash it later, and it verifies. Now the required setup for RSA is a little bit different. The server signing the key, signing the token, needs a special RSA key, not just a random salt. It uses the private key to sign the token. But then it can actually publish the public key on a free and open HTTP endpoint using a spec like JSON Web Keys. When some audience, some other server receives the message, what it can do is go fetch the public key, use that to verify, and then cache that forever. So one HTTP call automatically shares the secret. But it's not a secret, it's the public key. So this investment means that you don't need to share secrets. It's a little bit more upfront than the operational cost is lower. This means there's no copy and paste between the systems, you just fetch the key over HTTP. There's no super coordinated lockstep deploy process when you need to change it on both places at the same time without dropping messages in the middle. This also reduces the attack surface. A lost secret can only attack the service that leaked it. This can actually even prepare you for some really nifty, like automatic key rotation stuff. I'm not going to get into that right now, but if you're curious how that works, I'm happy to talk about it, come find me around the conference. Problem number five, you thought you knew about how password resets worked. You generate and send a token, it's a random string. You verify it in your controller, and then you regenerate a new token to expire the old ones that you sent out and make sure that people can't hack the system. And when you think about it, this is actually a third authentication system. It works a lot like the opaque API tokens. And again, it's implemented as a one off. So here's how I build a password reset JSON web token. First start with a standard identity token. This contains the metadata claims and the subject. Then I add a scope claim. Now I looked around, this one doesn't seem to be standard, and I couldn't find a better one, so I'm just calling it scope. The idea here is that I'll configure my passwords controller to accept tokens with this scope. And I'll configure the rest of my application to reject tokens with this scope or any other scope that doesn't seem appropriate for them. Then I add an optimistic lock. Now an optimistic lock is where you keep some kind of version. And every time a field updates, you increment that version. Anyone who wants to make a change has to tell you what the current version is. This way you make sure that they don't overwrite something that changed without their knowledge. And you can achieve this with just using a time stamp. So that's what I've done here. I'm maintaining a user's password changed at field, which is also good for other features, and then verify it against the token. If it matches, it's a go. And this effectively has the same, this effectively also expires the old reset tokens as soon as the password changes. So here we go. Once again, we can upgrade opaque tokens into structured signed data. This is one less field in the user's table. This is one less index for your queries. But even better is that we've absorbed a third authentication system by teaching our JSN WebToken backend about scopes and by teaching our password controller about optimistic locks. Problem number six, suppose that you're sending emails with survey links or some other kind of strong call to action where you need the user to click and you need to know who is making that click. And if you don't help them at all, they're going to hit a login wall, and maybe they're on their phone so they don't want to type it in, and they just come back later or they don't, and your conversion drops. So maybe you implement random strings, opaque tokens, and you connect them back to the user just like the API system. I mean, this is sounding pretty familiar, right? So we can just generalize the password reset solution. All we need is a scope claim. If it sounds like I'm suggesting that we send user sessions through email, yeah, actually. That's exactly what I'm suggesting. I mean, that's basically what the randomly generated one-off random strings are doing. They're giving a login session by email. But this one is built into our authentication system. It's not a one-off implementation that you're going to forget about. All right. And number seven, your application is already doing lots of stuff. And you're including a lot of this common standard authentication feature stuff as if it's somehow unique. They run from the same database. They can be affected by every deploy, every upgrade. And all this complexity is in one spot, which makes it a lot harder to audit your attack surface and understand where you need to remain secure. And let's not forget the always-present user God model. And JSON WebFocans can help. So they were born ready for this. This is why the issuer and audience claims exist, so they can be different things. And the issuer, let's imagine, takes responsibility for the account. And the account might be the user name and the password. And the last time the password was changed and how many times they failed their login or any of that stuff. And this leaves your application responsible for a simpler user's model. It just needs to relate the user with an account. And this is actually what I learned firsthand while working on Keratin Authent, which is kind of what you get if device was rebuilt as a standalone authentication service. It removes the complexity from your app, relies heavily on JSON WebFocans, uses every trick I've mentioned here, and then some. The core tech is pretty stable, I think. And I've got some ideas for some advanced features. So if this is interesting to anybody, I'd love to chat about it. All right. In conclusion, these are the things I hope you take away from the presentation. One, you can use JSON WebFocans right now. It doesn't matter if you have a monolith or services, you probably recognized one or more of these problems. Two, JSON WebFocans have a low learning curve and a high skill ceiling. There's a lot more that you can do with them as you gain confidence. But all you need to do is start somewhere. So you're probably going to go home from this conference with a head full of ideas for things that you wish you could use at your day job and all this cool stuff that you want to try. And maybe JSON WebFocans is one of those. Pick something. Learn it by doing it. Try it out and make your knowledge real. All right. And we do have time for questions. So the question is, if the user has been disabled by an admin since they logged in, how can you immediately log them out and make sure that they don't come back? You can create, for example, a blacklist and keep this temporary cache of invalidated tokens. If you choose to for critical actions, implement a revalidation. But yeah, the trade-off is real. The trade-off is real. If you keep authentication in a token that lives for 30 minutes or something, that's the other thing. I recommend that these tokens have a short life and that you regenerate them frequently. So the time window for that kind of problem is small. Yeah. So, again, the comment is that the checking this blacklist takes time. So I recommend deciding which endpoints are critical and revalidating for just those endpoints. There are probably a lot of things where it's okay if they can continue to read data for a few more minutes or maybe what you want to do is protect that data and you can choose to revalidate where necessary. Well you can try JSON Web Tokens in an existing app by finding any kind of opaque token and seeing how you can replace it. So that's one way to try it out. If you want to try an authentication server, then it helps to have an app with some kind of login scheme where you just swap out where the form submits. So the form might submit to your back end, now the form submits to a different back end. Those are two ideas. Is there a drop-in replacement? There's a library called knock and knock is like devised with JWT but it's built into the monolith. So an authentication server is going to run as a separate deployment but knock will use JWT as part of the same monolith. Refreshing the tokens with more tokens. So the way that I've seen is where you maintain what's called an access token and a refresh token. And the access token is what you're sending to the API. The refresh token is more secure because it's not used for very much. The one thing it's used for is getting new access tokens. So let's say that your access token is good for an hour. And let's say that you decide every 30 minutes, way before that's going to expire, you use the refresh token to get a new one. What you can do is build revocation into that refresh system. So the refresh maybe actually does a Redis query or a MySQL query or something to make sure that it's still okay to generate new access tokens. The dual token system means that one can have different security properties and the other is used for your really chatty protocols because it's lightweight and doesn't require queries. And they're kept in different places with different security properties. And so the commenter, the idea was that if you attempt to use a token and the error is that it's expired, you might decide it's time to get a new one. And that's definitely like the fail-safe. I think that aggressively refreshing them before that happens is a cleaner experience. And if you refresh well enough, you may or may not need that kind of last moment it's expired. Let's get a new one thing. Yeah, that's the issued at check. So in that kind of dire situation, which we all hope isn't happening, there's really no going back. You generate a new key and start using it. And then what you do is you roll out this idea of an epoch and you say, any token generated before this point in time was using an old key that we don't trust anymore. So if the issued at is before last Friday when this happened, then just throw it away. And that means people are logged out. Oh, so the comment was that you might have multiple keys and so you need an epoch per key. JSON Web Tokens has a claim called key ID. So you can embed within each JWT a signature of the key that was being used. And that will let you, if you have per key revocation, tell exactly which one was being used and whether it's still trustworthy. For simple login for sessions, it's just what you saw. It's the user ID. If you choose to put them in a cookie, then you need to also figure out CSRF. And you can put that CSRF token in the JWT because it's open. You can add more stuff into it and solve those same problems. I'm actually not a big fan of keeping a lot of data in sessions anyway. So I want to say on the slide it was like two or three lines. It was maybe 50% longer than a Rails equivalent cookie. Some people advocate for keeping more user information in there. Like are they an admin? What permissions do they have? What's their name and email? So you can save more queries that way. The downside is, as mentioned earlier, you have to decide how much you care about that information getting stale. All right. I think we're good. Thank you.
 All right, good morning.  I'm going to get started.  People can come in from the hallway if they're still out.  My name is Lance.  If you're here, you probably want to learn about JSON Web tokens, so you're in the right  place.  We're going to talk a little about what they are, why they exist, and maybe who cares.  So they're tokens.  They use JSON and they're built for the web.  Never mind, scratch that.  Now that you're all in the room, I'd like to offer you a special invite, the ground  floor of mine.  So I've got this idea.  It's got the application.  You're going to log in and you're going to see a face.  Probably your face.  It's going to fit somewhere in this like faces as a service vertical.  I think it's going to be pretty big.  I mean the face is going to be pretty big, clearly.  I'm calling it face page.  So welcome.  You're all hired.  You didn't know this was an interview.  I mean you passed the code challenge when you registered for the conference, right?  So we're good.  And you all have faces, so the qualifications are there.  So let's go.  Oh, I suppose you're wondering who you'll be working with.  I'm Lance Ivy.  I'm based out of Portland, Oregon.  I've seen little out of sorts because I'm trying to get used to the bright solar radiation.  I started professionally tinkering on the web as an unpaid developer in 2003, joined  some friends and colleagues from college on this startup idea in a small town of like  30,000 people.  And started in front end and the era of Netscape had just wrapped up.  So I like opened it once, took a picture of a horribly broken rendering and never went  back.  And made some nice tables after that.  Some things happened in 2006, 2008.  I became a paid Rails developer.  I found my way to working on Kickstarter.  Made my own share of mistakes, learned from most of them, picked up a bunch of stories.  Currently I'm working with EmpatiCo and also building an open source project called Carrington  Off-N, which is an authentication server, you can imagine, devised, if it were rebuilt  today as a standalone service.  And this is the primary learning opportunity for the talk.  So let's get back to FacePage.  Obviously we're going to run FacePage as a standard Rails application.  So here we go.  We have a few meetings.  We pivot.  We sprint.  We change task management systems.  We iterate on faces and what they mean to our users and how they're going to change the  world and then we deploy it.  It's out in the wild.  Users love it.  There's just one problem.  People like seeing their faces on the go and they're looking for us in the native app  stores.  So easy, right?  We make an API.  We build out our ILS, Android apps.  And maybe the app and the API are intertwined.  JSON endpoints the same controller.  Or maybe they're namespace, but they're still in the same app.  It's a majestic model.  Or maybe it's a standalone deployment, but that doesn't matter.  What matters is it's up in the cloud, right?  It's there.  It's running.  It works.  But something along the way has happened.  We couldn't use cookies for our API.  So we implemented a quick token authentication system.  So now we've got cookies over here and tokens over there and this uneasy feeling that there's  something similar, but we can't quite put our finger on it.  And we just keep going.  But it turns out that we've actually implemented two authentication systems and they're separate.  So this is technical debt, but we figure we can manage.  It's working.  We keep moving.  And just suppress those uneasy feelings into a tight little ball.  And someone on high maybe makes the decision to move to services, which I don't know.  We try it out and we build this API gateway.  But is it written in Rails?  Does it speak cookies or tokens or both?  Do we have to retrofit our authentication schemes into it?  Are we on our own?  Do we have help?  Do we move to something else?  I mean, what happens now?  Well, I want to take a step back and consider the problem that these cookies and tokens  are actually trying to solve.  How do we even get here?  It's time to ask what even is logging in?  What happens?  What are the cookies and tokens doing?  Let's dig in.  We can say it starts here with a username and password.  These days it could also be Facebook or GitHub or Google or some other auth provider, but  probably still at least passwords.  Turns out that's not really the part we care about.  What we care about is how we keep track.  So here's the classic way of keeping track.  Browser logs in.  We send back a cookie.  And browsers know about cookies.  They know to include them on every request back to our domain.  So every request for a face on a page is logged in.  And we can show your face back.  I mean, the requests that want JavaScript and CSS and images are also carrying these  cookies, but that's a different talk, right?  The simplest explanation for cookies is a header protocol.  When the server responds with a set cookie header, the browser knows to include it back  in the cookie header.  Every future request.  But I think it's time to consider the elephant in the room here.  What kinds of cookies are these, really?  I mean, when we diagram HTTP cookies, they usually look like this.  Chocolate chip or raisin, maybe.  Oh, what's for lunch?  But this is how we should think of them as fortune cookies with a message inside.  So Rails uses these to store bits of data in the browser, and we can crack one open and  see what's inside.  Here's one that you might actually see in an actual Rails application.  It's an encoded message with a signature, and you can split it apart on the double hyphens  right there.  Here's what's inside.  There's a user ID.  There's a CSRF token, because with cookie authentication, you need that kind of thing.  There's a signature that we can use to just make sure that it's legit.  So there we go.  Cookies are headers on HTTP responses and requests that do things like transport a user  ID back and forth.  And that's the login story we care about right now.  So what's happening with the tokens on the API side of things?  Well, one common convention might look like this.  The server responds to a login request with some random string in the JSON body, let's  say.  And again, the device sends it back on future requests, but this time in the authorization  header.  Now these tokens are opaque.  They're random strings.  They have no meaning until we use them to find something more interesting like the user  ID.  And this is good, but it's not great.  On the upside, we could delete these tokens to revoke access at any point.  We have some control over that.  But on the downside, every API query now involves a database query.  This is, by the way, how Rails sessions used to work before switching to cookies.  And it was a performance problem.  The browser submitted a session ID, and you use that to actually find their session from  the database.  All right, so let's put together what we've learned.  The Rails session cookie uses a cookie header, but our API tokens use the authorization header.  The Rails session cookie is structured data, but the API is just an opaque random string.  Now the Rails session cookie can be verified with cryptography, and API token is security  through queries.  So can you imagine the best of both worlds?  Chase on web tokens.  It's signed structured data.  This is rather similar to Rails signed cookies.  We've added a third segment, the header, and this just describes the format of the token.  We can decode the message.  What you're looking at here are called the token claims.  Now they're called claims to give you a bit of skepticism, because you actually have to  assert that these claims are true before you can trust it.  Here's a list of common claims.  You can see that they're heavily abbreviated.  And the reason is because Chase on web tokens are designed to fit in headers and other character  limited small spaces.  So they have to save on bytes for your more interesting content.  All the claims in the left are what I consider metadata.  Their claims necessary to verify that a token may be used appropriately in different situations.  I'll just go through these.  The issuer describes the party that generated and signed the token.  Audience describes the party that the message is intended for.  These might be the same thing.  They might not.  Issued at is when the token was created.  An expiration is when the token should be ignored.  They can have a lifetime.  The claims in the right are what I consider the payload.  And this is the information that you probably want to extract for your business logic.  Now you can put anything you want in here as long as the issuer and the audience agree  on what it means.  But the common one that a lot of issuers and audiences agree on is the subject.  And this is meant to identify the party that the message is about or the person who owns  the token, the person who has it.  And this is where we put the user ID.  So the Chase on web token standard is a pretty generic thing.  It's just a spec for sending secure messages.  But one of its primary uses is identity.  It actually evolved in the context of OAuth and OpenID Connect.  And you can see a lot of that in the claims that are built into it.  So we can actually imagine it kind of like Rails Cookies and API tokens.  Think of it as an ID card.  It's like an ID card that makes a number of claims and it contains some security features.  This ID card has an issuer.  It's from the internet.  It has a subject, name, has expiration and issued dates.  It has this pretty sweet little official stamp.  Security.  But it's actually up to you to check the card and detect forgeries to make sure that you  can actually take this identification.  So here's how you do it.  One, is it from someone that we recognize as an authority?  Check the issuer.  Two, was it intended for me?  Check the audience.  Has it expired?  Check the expiration.  Is it a forgery?  Check the signature.  Can you recreate the signature based on those values?  And last but not least, was it generated before or after that time we had to change our secret  because we published it on GitHub?  And you can check issued at for that.  If you can answer those five questions, you're in pretty good shape.  And the good news is that you can get a library to just do this for you.  Okay.  So we've learned that JSON Web tokens are secure messages, like a Rails signed session  cookie.  We've learned that they contain claims that we need to verify, and we've learned about  the most important claims and what they represent.  So let's talk about what we can do with them.  We've already mentioned identity tokens, so let's continue from there.  One of the problems we had in our Face Page app was the duplicate authentication system.  So let's see how JSON Web tokens can help.  First let's add JSON Web tokens to our comparison table.  So while the Rails cookies are tied to the cookie header and the API tokens use the authorization  header, our JSON Web tokens are ready for either.  They don't care.  The Rails cookies were structured data signed with cryptography, and that's actually pretty  good.  So our JSON Web tokens share those characteristics.  So if we use an identity, JSON Web token, for login, it might look like this.  So submit the login, Rails responds with a JSON Web token in a cookie.  The JWT contains the user ID as the subject.  Now in future requests, the browser just sends it back in the cookie.  And this looks pretty familiar.  And that's a good thing.  We actually haven't changed our headers or the relationship between the browser and the  server.  We're still using cookies.  We've just changed the format of the message inside the cookie.  And on the API, we can drop it in here as well.  There's no change to the client.  They're still just sending a string back and forth.  But now the string is a JSON Web token.  It has structured data.  It has meaning.  It's not random.  And the server can do something with it.  So this is the JSON Web token solution.  One token, two headers, one authentication system.  It doesn't matter whether the server finds the token inside a cookie or inside an authorization  header, it can still handle that value exactly the same and set the current user for the  duration of the request.  Problem number two in our Face Page app.  Previously the API had to execute a query on every request just to discover who was  making it.  And now our API can verify the JWT with the claims and the cryptography.  So this replaces a network-bound database bottleneck process with a straightforward CPU-bound calculation.  This will perform faster.  This will scale better.  This will introduce less variation into response times and generally just have fewer failure  modes.  Problem number three, our cookies were implemented for Rails, by Rails.  Now don't get me wrong, the default Rails session store is wonderful.  It's, it works.  It's hidden.  It is secure.  It is very well designed.  And it does the job it needs to do.  But it is tightly coupled to Rails.  It is tightly coupled to cookies.  And it's kind of tightly coupled to majestic monoliths.  So if any of those things don't work for you, you have to ask yourself what's next.  JSON WebToken libraries are implemented in at least 20 languages.  They're decoupled from the cookies.  And they contain claims that you can use to build any kind of distributed architecture.  So they're, they're more flexible.  It's a more general purpose solution.  Problem number four, in a distributed architecture, you might find yourself sharing secrets so  that when you sign a message with one system, you can verify it on another.  And this involves trusted back channels like copy and paste.  Or you know, configuration management systems.  And now that the secret exists in more places, it's a bigger attack surface.  If any one place is compromised, that secret can be used to attack all the other places  as well.  So what do JSON WebTokens offer?  They support asymmetric key algorithms, like RSA.  Now the signature process used by Rails to sign a cookie is called HMAC.  You give it a salt, it hashes the cookie.  You take that salt, you rehash it later, and it verifies.  Now the required setup for RSA is a little bit different.  The server signing the key, signing the token, needs a special RSA key, not just a random  salt.  It uses the private key to sign the token.  But then it can actually publish the public key on a free and open HTTP endpoint using  a spec like JSON Web Keys.  When some audience, some other server receives the message, what it can do is go fetch the  public key, use that to verify, and then cache that forever.  So one HTTP call automatically shares the secret.  But it's not a secret, it's the public key.  So this investment means that you don't need to share secrets.  It's a little bit more upfront than the operational cost is lower.  This means there's no copy and paste between the systems, you just fetch the key over HTTP.  There's no super coordinated lockstep deploy process when you need to change it on both  places at the same time without dropping messages in the middle.  This also reduces the attack surface.  A lost secret can only attack the service that leaked it.  This can actually even prepare you for some really nifty, like automatic key rotation  stuff.  I'm not going to get into that right now, but if you're curious how that works, I'm  happy to talk about it, come find me around the conference.  Problem number five, you thought you knew about how password resets worked.  You generate and send a token, it's a random string.  You verify it in your controller, and then you regenerate a new token to expire the old  ones that you sent out and make sure that people can't hack the system.  And when you think about it, this is actually a third authentication system.  It works a lot like the opaque API tokens.  And again, it's implemented as a one off.  So here's how I build a password reset JSON web token.  First start with a standard identity token.  This contains the metadata claims and the subject.  Then I add a scope claim.  Now I looked around, this one doesn't seem to be standard, and I couldn't find a better  one, so I'm just calling it scope.  The idea here is that I'll configure my passwords controller to accept tokens with this scope.  And I'll configure the rest of my application to reject tokens with this scope or any other  scope that doesn't seem appropriate for them.  Then I add an optimistic lock.  Now an optimistic lock is where you keep some kind of version.  And every time a field updates, you increment that version.  Anyone who wants to make a change has to tell you what the current version is.  This way you make sure that they don't overwrite something that changed without their knowledge.  And you can achieve this with just using a time stamp.  So that's what I've done here.  I'm maintaining a user's password changed at field, which is also good for other features,  and then verify it against the token.  If it matches, it's a go.  And this effectively has the same, this effectively also expires the old reset tokens as soon  as the password changes.  So here we go.  Once again, we can upgrade opaque tokens into structured signed data.  This is one less field in the user's table.  This is one less index for your queries.  But even better is that we've absorbed a third authentication system by teaching our  JSN WebToken backend about scopes and by teaching our password controller about optimistic locks.  Problem number six, suppose that you're sending emails with survey links or some other kind  of strong call to action where you need the user to click and you need to know who is  making that click.  And if you don't help them at all, they're going to hit a login wall, and maybe they're  on their phone so they don't want to type it in, and they just come back later or they  don't, and your conversion drops.  So maybe you implement random strings, opaque tokens, and you connect them back to the user  just like the API system.  I mean, this is sounding pretty familiar, right?  So we can just generalize the password reset solution.  All we need is a scope claim.  If it sounds like I'm suggesting that we send user sessions through email, yeah, actually.  That's exactly what I'm suggesting.  I mean, that's basically what the randomly generated one-off random strings are doing.  They're giving a login session by email.  But this one is built into our authentication system.  It's not a one-off implementation that you're going to forget about.  All right.  And number seven, your application is already doing lots of stuff.  And you're including a lot of this common standard authentication feature stuff as if  it's somehow unique.  They run from the same database.  They can be affected by every deploy, every upgrade.  And all this complexity is in one spot, which makes it a lot harder to audit your attack  surface and understand where you need to remain secure.  And let's not forget the always-present user God model.  And JSON WebFocans can help.  So they were born ready for this.  This is why the issuer and audience claims exist, so they can be different things.  And the issuer, let's imagine, takes responsibility for the account.  And the account might be the user name and the password.  And the last time the password was changed and how many times they failed their login  or any of that stuff.  And this leaves your application responsible for a simpler user's model.  It just needs to relate the user with an account.  And this is actually what I learned firsthand while working on Keratin Authent, which is  kind of what you get if device was rebuilt as a standalone authentication service.  It removes the complexity from your app, relies heavily on JSON WebFocans, uses every trick  I've mentioned here, and then some.  The core tech is pretty stable, I think.  And I've got some ideas for some advanced features.  So if this is interesting to anybody, I'd love to chat about it.  All right.  In conclusion, these are the things I hope you take away from the presentation.  One, you can use JSON WebFocans right now.  It doesn't matter if you have a monolith or services, you probably recognized one or more  of these problems.  Two, JSON WebFocans have a low learning curve and a high skill ceiling.  There's a lot more that you can do with them as you gain confidence.  But all you need to do is start somewhere.  So you're probably going to go home from this conference with a head full of ideas for things  that you wish you could use at your day job and all this cool stuff that you want to try.  And maybe JSON WebFocans is one of those.  Pick something.  Learn it by doing it.  Try it out and make your knowledge real.  All right.  And we do have time for questions.  So the question is, if the user has been disabled by an admin since they logged in, how can  you immediately log them out and make sure that they don't come back?  You can create, for example, a blacklist and keep this temporary cache of invalidated tokens.  If you choose to for critical actions, implement a revalidation.  But yeah, the trade-off is real.  The trade-off is real.  If you keep authentication in a token that lives for 30 minutes or something, that's the  other thing.  I recommend that these tokens have a short life and that you regenerate them frequently.  So the time window for that kind of problem is small.  Yeah.  So, again, the comment is that the checking this blacklist takes time.  So I recommend deciding which endpoints are critical and revalidating for just those endpoints.  There are probably a lot of things where it's okay if they can continue to read data for  a few more minutes or maybe what you want to do is protect that data and you can choose  to revalidate where necessary.  Well you can try JSON Web Tokens in an existing app by finding any kind of opaque token and  seeing how you can replace it.  So that's one way to try it out.  If you want to try an authentication server, then it helps to have an app with some kind  of login scheme where you just swap out where the form submits.  So the form might submit to your back end, now the form submits to a different back end.  Those are two ideas.  Is there a drop-in replacement?  There's a library called knock and knock is like devised with JWT but it's built into  the monolith.  So an authentication server is going to run as a separate deployment but knock will use  JWT as part of the same monolith.  Refreshing the tokens with more tokens.  So the way that I've seen is where you maintain what's called an access token and a refresh  token.  And the access token is what you're sending to the API.  The refresh token is more secure because it's not used for very much.  The one thing it's used for is getting new access tokens.  So let's say that your access token is good for an hour.  And let's say that you decide every 30 minutes, way before that's going to expire, you use  the refresh token to get a new one.  What you can do is build revocation into that refresh system.  So the refresh maybe actually does a Redis query or a MySQL query or something to make  sure that it's still okay to generate new access tokens.  The dual token system means that one can have different security properties and the other  is used for your really chatty protocols because it's lightweight and doesn't require queries.  And they're kept in different places with different security properties.  And so the commenter, the idea was that if you attempt to use a token and the error is  that it's expired, you might decide it's time to get a new one.  And that's definitely like the fail-safe.  I think that aggressively refreshing them before that happens is a cleaner experience.  And if you refresh well enough, you may or may not need that kind of last moment it's  expired.  Let's get a new one thing.  Yeah, that's the issued at check.  So in that kind of dire situation, which we all hope isn't happening, there's really  no going back.  You generate a new key and start using it.  And then what you do is you roll out this idea of an epoch and you say, any token generated  before this point in time was using an old key that we don't trust anymore.  So if the issued at is before last Friday when this happened, then just throw it away.  And that means people are logged out.  Oh, so the comment was that you might have multiple keys and so you need an epoch per  key.  JSON Web Tokens has a claim called key ID.  So you can embed within each JWT a signature of the key that was being used.  And that will let you, if you have per key revocation, tell exactly which one was being  used and whether it's still trustworthy.  For simple login for sessions, it's just what you saw.  It's the user ID.  If you choose to put them in a cookie, then you need to also figure out CSRF.  And you can put that CSRF token in the JWT because it's open.  You can add more stuff into it and solve those same problems.  I'm actually not a big fan of keeping a lot of data in sessions anyway.  So I want to say on the slide it was like two or three lines.  It was maybe 50% longer than a Rails equivalent cookie.  Some people advocate for keeping more user information in there.  Like are they an admin?  What permissions do they have?  What's their name and email?  So you can save more queries that way.  The downside is, as mentioned earlier, you have to decide how much you care about that  information getting stale.  All right.  I think we're good.  Thank you.
Portable_Sessions_with_JSON_Web_Tokens_2_0_1019.png Portable_Sessions_with_JSON_Web_Tokens_2_1020_1451.png Portable_Sessions_with_JSON_Web_Tokens_2_1452_1607.png Portable_Sessions_with_JSON_Web_Tokens_2_1608_1991.png Portable_Sessions_with_JSON_Web_Tokens_2_1992_2123.png Portable_Sessions_with_JSON_Web_Tokens_2_2124_2315.png Portable_Sessions_with_JSON_Web_Tokens_2_2316_2447.png Portable_Sessions_with_JSON_Web_Tokens_2_2448_2543.png Portable_Sessions_with_JSON_Web_Tokens_2_2544_2651.png Portable_Sessions_with_JSON_Web_Tokens_2_2652_3227.png Portable_Sessions_with_JSON_Web_Tokens_2_3228_4247.png Portable_Sessions_with_JSON_Web_Tokens_2_4248_4451.png Portable_Sessions_with_JSON_Web_Tokens_2_4452_7499.png Portable_Sessions_with_JSON_Web_Tokens_2_7500_7763.png Portable_Sessions_with_JSON_Web_Tokens_2_7764_8195.png Portable_Sessions_with_JSON_Web_Tokens_2_8196_9395.png Portable_Sessions_with_JSON_Web_Tokens_2_9396_9467.png Portable_Sessions_with_JSON_Web_Tokens_2_9468_9743.png Portable_Sessions_with_JSON_Web_Tokens_2_9744_9851.png Portable_Sessions_with_JSON_Web_Tokens_2_9852_9983.png Portable_Sessions_with_JSON_Web_Tokens_2_9984_10739.png Portable_Sessions_with_JSON_Web_Tokens_2_10740_12767.png Portable_Sessions_with_JSON_Web_Tokens_2_12768_13019.png Portable_Sessions_with_JSON_Web_Tokens_2_13020_13463.png Portable_Sessions_with_JSON_Web_Tokens_2_13464_13559.png Portable_Sessions_with_JSON_Web_Tokens_2_13560_15275.png Portable_Sessions_with_JSON_Web_Tokens_2_15276_15875.png Portable_Sessions_with_JSON_Web_Tokens_2_15876_16955.png Portable_Sessions_with_JSON_Web_Tokens_2_16956_17819.png Portable_Sessions_with_JSON_Web_Tokens_2_17820_19139.png Portable_Sessions_with_JSON_Web_Tokens_2_19140_19871.png Portable_Sessions_with_JSON_Web_Tokens_2_19872_20867.png Portable_Sessions_with_JSON_Web_Tokens_2_20868_22199.png Portable_Sessions_with_JSON_Web_Tokens_2_22200_29435.png Portable_Sessions_with_JSON_Web_Tokens_2_29436_29771.png Portable_Sessions_with_JSON_Web_Tokens_2_29772_30119.png Portable_Sessions_with_JSON_Web_Tokens_2_30120_30443.png Portable_Sessions_with_JSON_Web_Tokens_2_30444_31547.png Portable_Sessions_with_JSON_Web_Tokens_2_31548_35831.png Portable_Sessions_with_JSON_Web_Tokens_2_35832_36779.png Portable_Sessions_with_JSON_Web_Tokens_2_36780_38003.png Portable_Sessions_with_JSON_Web_Tokens_2_38004_47555.png Portable_Sessions_with_JSON_Web_Tokens_2_47556_47723.png Portable_Sessions_with_JSON_Web_Tokens_2_47724_47746.png