Slide 1

Slide 1 text

JSON Web Tokens Will Improve Your Life John SJ Anderson | @genehack | Beer City Code | 10 Jun 2017 JWTs WIYL! - BeerCityCode 2017 – @genehack 1

Slide 2

Slide 2 text

Hi, I'm John. a/k/a @genehack JWTs WIYL! - BeerCityCode 2017 – @genehack 2

Slide 3

Slide 3 text

VP, Tech Infinity Interac,ve JWTs WIYL! - BeerCityCode 2017 – @genehack 3

Slide 4

Slide 4 text

Now that I've been promoted into a management position, I don't get to do all that much coding anymore. When I do end up with a coding project, Sammy helps me out from her cushion under my desk This is my dog, Sammy a/k/a @sammyGenehack JWTs WIYL! - BeerCityCode 2017 – @genehack 4

Slide 5

Slide 5 text

who's heard of JWTs before this talk? who's using JWTs? So, what's a JWT? JWTs WIYL! - BeerCityCode 2017 – @genehack 5

Slide 6

Slide 6 text

jwt.io JWTs WIYL! - BeerCityCode 2017 – @genehack 6

Slide 7

Slide 7 text

Sadly, Sammy suffers from the horrible disease RSF -- Resting Stoned Face What Does That Even Mean JWTs WIYL! - BeerCityCode 2017 – @genehack 7

Slide 8

Slide 8 text

Holy RFCs, Batman • RFC 7519 - JSON Web Token (JWT) JWTs WIYL! - BeerCityCode 2017 – @genehack 8

Slide 9

Slide 9 text

It turns out, it's not just RFC7519 you need to worry about... Holy RFCs, Batman • RFC 7515 - JSON Web Signature (JWS) • RFC 7516 - JSON Web Encryp?on (JWE) • RFC 7517 - JSON Web Key (JWK) • RFC 7518 - JSON Web Algorithms (JWA) • RFC 7519 - JSON Web Token (JWT) • RFC 7520 - Examples of Protec?ng Content Using JSON Object Signing and Encryp?on (JOSE) JWTs WIYL! - BeerCityCode 2017 – @genehack 9

Slide 10

Slide 10 text

Sammy found these RFCs a bit …dry JWTs WIYL! - BeerCityCode 2017 – @genehack 10

Slide 11

Slide 11 text

Think of JWTs as… • A lightweight alterna.ve to cookies (kinda, sorta) • ...that also works with CLI, mobile, or even desktop apps • An authoriza.on or access control mechanism • ...kinda like OAuth but without losing your will to live • Cross-domain friendly JWTs WIYL! - BeerCityCode 2017 – @genehack 11

Slide 12

Slide 12 text

Made of stuff you already know • Plain ol' JSON Objects (POJOs) • Stringified, encoded, and cryptographically signed • Transmi@ed over HTTP(S) JWTs WIYL! - BeerCityCode 2017 – @genehack 12

Slide 13

Slide 13 text

Dot-delim Three parts... Let's look at each of these three parts in a bit more detail What do they look like? • dot-delimited string ('.') • 3 parts • header • payload • signature • Example: xxx.yyyyy.zzz JWTs WIYL! - BeerCityCode 2017 – @genehack 13

Slide 14

Slide 14 text

JWT teardown: header • Plain ole JSON object • Base64 encoded • Typically metadata, such as token type and signing algorithm { "alg": "HS256", "typ": "JWT" } JWTs WIYL! - BeerCityCode 2017 – @genehack 14

Slide 15

Slide 15 text

JWT teardown: payload • Another Base64-encoded POJO • Contains "claims" – just key-value data • Types of keys: reserved, public, private { "name": "Beer City Code", "admin": false, "iat": 1488562999 } JWTs WIYL! - BeerCityCode 2017 – @genehack 15

Slide 16

Slide 16 text

JWT teardown: signature • Encoded header POJO, plus • Encoded payload POJO, plus • A secret, plus • Signing algorithm from header alg key JWTs WIYL! - BeerCityCode 2017 – @genehack 16

Slide 17

Slide 17 text

I'm going to be showing a fair amount of code in this talk -- maybe a bit too much code -- but I really wanted to drive home how simple and elegant JWTs are, and showing how they actually work on a code level is the best way to do that, IMO In real practice, you'd almost certainly be using a library for most of the code I'm showing, but since the point is how uncomplicated most of these operations are, I wanted to give you some idea of what was happening inside that code For each code sample, I'm going to show the whole code sample -- and it's going to be way too small to read. I'm doing that just so you can see, it's really not that much code. We'll then step through each one in much smaller 3 or 4 line chunks. A word about my code samples JWTs WIYL! - BeerCityCode 2017 – @genehack 17

Slide 18

Slide 18 text

Making a JWT function base64EncodeJson (pojo) { var jsonString = JSON.stringify(pojo); var encoded = new Buffer(jsonString).toString("base64"); return encoded; } function hmacIt (string, secret) { var hmac = crypto.createHmac("sha256" , secret); hmac.update(string); var signature = hmac.digest("hex"); return signature; } var header = { "alg": "HS256", "typ": "JWT" }; var payload = { "name": "Beer City Code", "admin": false, "iat": 1488562999 }; var secret = "be wery, wery qwiet, we're hunting JWTs"; var encodedHeader = base64EncodeJson(header); var encodedPayload = base64EncodeJson(payload); var signature = hmacIt(encodedHeader + "." + encodedPayload, secret); var jwt = encodedHeader + "." + encodedPayload + "." + signature; JWTs WIYL! - BeerCityCode 2017 – @genehack 18

Slide 19

Slide 19 text

Helper func+ons function base64EncodeJson (pojo) { var jsonString = JSON.stringify(pojo); var encoded = new Buffer(jsonString) .toString("base64"); return encoded; } JWTs WIYL! - BeerCityCode 2017 – @genehack 19

Slide 20

Slide 20 text

Helper func+ons function hmacIt (string, secret) { var hmac = crypto.createHmac("sha256" , secret); hmac.update(string); var signature = hmac.digest("hex"); return signature; } JWTs WIYL! - BeerCityCode 2017 – @genehack 20

Slide 21

Slide 21 text

The actual data var header = { "alg": "HS256", "typ": "JWT" }; var payload = { "name": "Beer City Code", "admin": false, "iat": 1488562999 }; var secret = "be wery, wery qwiet, we're hunting JWTs"; JWTs WIYL! - BeerCityCode 2017 – @genehack 21

Slide 22

Slide 22 text

Really genera*ng the signature var encodedHeader = base64EncodeJson(header); var encodedPayload = base64EncodeJson(payload); var signature = hmacIt( encodedHeader + "." + encodedPayload, secret ); var jwt = encodedHeader + "." + encodedPayload + "." + signature; JWTs WIYL! - BeerCityCode 2017 – @genehack 22

Slide 23

Slide 23 text

Hand-rolled, ar,sanal JWT console.log(jwt); 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. eyJuYW1lIjoiQmVlciBDaXR5IENvZGUiLCJhZG1pbiI6ZmFsc2UsImlhdCI6MTQ4ODU2Mjk5OX0=. 3044732035245ffd52869d1695510e9d16d8f28355b64ad8acd91c6a635463cb' Key bit: The signature means you can detect any a2empts to modify the header or the payload. JWTs WIYL! - BeerCityCode 2017 – @genehack 23

Slide 24

Slide 24 text

Valida&on JWTs WIYL! - BeerCityCode 2017 – @genehack 24

Slide 25

Slide 25 text

Valida&on JWTs WIYL! - BeerCityCode 2017 – @genehack 25

Slide 26

Slide 26 text

Valida&on JWTs WIYL! - BeerCityCode 2017 – @genehack 26

Slide 27

Slide 27 text

Valida&on JWTs WIYL! - BeerCityCode 2017 – @genehack 27

Slide 28

Slide 28 text

Libraries for DAYS JWTs WIYL! - BeerCityCode 2017 – @genehack 28

Slide 29

Slide 29 text

Frequently more than 1 library for a given platform, with varying degrees of support Libraries for DAYS • .NET, Python, Node, Java, Javascript, Ruby, Perl, Go, PHP • Haskell, Rust, Lua, Scala, Clojure, ObjecDveC, SwiF, Delphi • Support for your favorite language/pla3orm is probably not an issue JWTs WIYL! - BeerCityCode 2017 – @genehack 29

Slide 30

Slide 30 text

At this point, Sammy perked back up a little bit. Now that we were past the RFC reading stage, and she'd seen how simple and elegant JWTs were conceptually, she starting asking... OK, you've got my a"en%on JWTs WIYL! - BeerCityCode 2017 – @genehack 30

Slide 31

Slide 31 text

How do I actually use JWTs? JWTs WIYL! - BeerCityCode 2017 – @genehack 31

Slide 32

Slide 32 text

there are couple of different ways, because JWTs are intentionally pretty flexible. but one way you'll probably end up using them is as part of a fairly standard authentication/ authorization type flow Basic auth/authz usage (image stolen from jwt.io) JWTs WIYL! - BeerCityCode 2017 – @genehack 32

Slide 33

Slide 33 text

Things to be aware of • Payload/header NOT encrypted • …don't send anything sensi6ve! • Need to control expira6on, re-issue, etc. • Some APIs will send a fresh JWT to the client per-request • Sites other than issuing site can receive JWT • …but they must share the secret to validate JWTs WIYL! - BeerCityCode 2017 – @genehack 33

Slide 34

Slide 34 text

How is it actually transmi0ed? • Up to you! Various methods: • As part of the URL in a GET • In a POST body • In the Authorization header using Bearer scheme: Authorization: Bearer JWTs WIYL! - BeerCityCode 2017 – @genehack 34

Slide 35

Slide 35 text

Sammy isn't very big on theory. She likes to see the actual implementation so she can really understand what's going on... How would you actually use this in an app? JWTs WIYL! - BeerCityCode 2017 – @genehack 35

Slide 36

Slide 36 text

Node code for generating a token after a successful login Generate a token on login app.post('/user/login', app.wrap(user_login)); var jwt = require('jwt'); // helper wrapper around 'jsonwebtoken' function * user_login (req, res) { if (! (req.body.email && req.body.password)) { res.status(400); res.json({message: 'invalid request'}); return; } var user = yield _fetch_user_by_email(req.body.email); var claims; if (_pw_validate(user.password, req.body.password)) { claims = { user_id: user.id }; } else { res.status(401); res.header('WWW-Authenticate', 'Bearer realm=myapp'); res.json({ message: 'authorization required' }); return; } // sign the claim set and return the token in a header var token = jwt.sign(claims); res.append('X-MyApp-Token', token); res.status(200); } JWTs WIYL! - BeerCityCode 2017 – @genehack 36

Slide 37

Slide 37 text

This code is from an express app, so first we have to declare a route Then we import a helper library that's just a thin wrapper around the jsonwebtoken NPM library. Generate a token on login app.post('/user/login', app.wrap(user_login)); var jwt = require('jwt'); // helper wrapper around 'jsonwebtoken' function * user_login (req, res) { if (! (req.body.email && req.body.password)) { res.status(400); res.json({message: 'invalid request'}); return; } JWTs WIYL! - BeerCityCode 2017 – @genehack 37

Slide 38

Slide 38 text

Generate a token on login var user = yield _fetch_user_by_email(req.body.email); var claims; if (_pw_validate(user.password, req.body.password)) { claims = { user_id: user.id }; } else { res.status(401); res.header('WWW-Authenticate', 'Bearer realm=myapp'); res.json({ message: 'authorization required' }); return; } JWTs WIYL! - BeerCityCode 2017 – @genehack 38

Slide 39

Slide 39 text

Generate a token on login // sign the claim set and return the token in a header var token = jwt.sign(claims); res.append('X-MyApp-Token', token); res.status(200); } JWTs WIYL! - BeerCityCode 2017 – @genehack 39

Slide 40

Slide 40 text

OK, that's how you make one. How do you validate it? JWTs WIYL! - BeerCityCode 2017 – @genehack 40

Slide 41

Slide 41 text

Again, because this is Express, we can do validation in a middleware. That'll make sure it happens on every request. Validate with a middleware // enable JWT-verification middleware var jwt = require('jwt'); // helper wrapper around 'jsonwebtoken' app.use(function (req, res, next) { // initialize the jwt object req.jwt = {}; // now parse the Authorization header if it exists Promise.resolve(req.headers.authorization).then(function (auth) { // If the Authorization header is present and employs the correct // Bearar scheme, extract the token and attempt to verify it. if (auth) { var scheme = auth.split(' ')[0]; var token = auth.split(' ')[1]; if (scheme == 'Bearer') { return jwt.verify(token).catch(function (error) { throw new Error('failed to verify claim'); }); } } throw new Error('authorization not attempted'); }) .then(function (payload) { req.jwt = payload; next(); }) .catch(function (error) { // Allow login without JWT if (req.path == '/user/login' && req.method == 'POST') { return next(); } res.status(401); res.header('WWW-Authenticate', 'Bearer realm=myapp'); res.json({ message: 'authorization required' }); }); }); JWTs WIYL! - BeerCityCode 2017 – @genehack 41

Slide 42

Slide 42 text

Validate with a middleware // enable JWT-verification middleware var jwt = require('jwt'); // helper wrapper around 'jsonwebtoken' app.use(function (req, res, next) { // initialize the jwt object req.jwt = {}; JWTs WIYL! - BeerCityCode 2017 – @genehack 42

Slide 43

Slide 43 text

Validate with a middleware // now parse the Authorization header if it exists Promise.resolve(req.headers.authorization).then(function (auth) { // If the Authorization header is present and employs the correct // Bearar scheme, extract the token and attempt to verify it. if (auth) { var scheme = auth.split(' ')[0]; var token = auth.split(' ')[1]; if (scheme === 'Bearer') { return jwt.verify(token).catch(function (error) { throw new Error('failed to verify claim'); }); } } throw new Error('authorization not attempted'); }) JWTs WIYL! - BeerCityCode 2017 – @genehack 43

Slide 44

Slide 44 text

Validate with a middleware .then(function (payload) { req.jwt = payload; next(); }) JWTs WIYL! - BeerCityCode 2017 – @genehack 44

Slide 45

Slide 45 text

Validate with a middleware .catch(function (error) { // Allow login without JWT if (req.path == '/user/login' && req.method == 'POST') { return next(); } res.status(401); res.header('WWW-Authenticate', 'Bearer realm=myapp'); res.json({ message: 'authorization required' }); }); }); JWTs WIYL! - BeerCityCode 2017 – @genehack 45

Slide 46

Slide 46 text

At this point, Sammy was pretty impressed and happy, and was making plans to use JWTs in all her future projects. But she wondered if there was anything else JWTs could do for her... That's cool. What else you got? JWTs WIYL! - BeerCityCode 2017 – @genehack 46

Slide 47

Slide 47 text

Pre-signed Content Requests JWTs WIYL! - BeerCityCode 2017 – @genehack 47

Slide 48

Slide 48 text

Document store applica0on • Client-side Angular • Uses JWT for authen8ca8on and sessions • Uses Authorization: Bearer scheme JWTs WIYL! - BeerCityCode 2017 – @genehack 48

Slide 49

Slide 49 text

PROBLEM! How do you display or download images via browser? JWTs WIYL! - BeerCityCode 2017 – @genehack 49

Slide 50

Slide 50 text

SOLUTION JWTs WIYL! - BeerCityCode 2017 – @genehack 50

Slide 51

Slide 51 text

JWTs, of course JWTs WIYL! - BeerCityCode 2017 – @genehack 51

Slide 52

Slide 52 text

What talk did you think this was? JWTs WIYL! - BeerCityCode 2017 – @genehack 52

Slide 53

Slide 53 text

Solu%on • Generate a claim containing document ID: { "iat": 1497105117 "exp": 1497105177 "doc": 345345 } • NOTE: short expira-on -me JWTs WIYL! - BeerCityCode 2017 – @genehack 53

Slide 54

Slide 54 text

Solu%on • Sign it and put it in a URL: /content/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. eyJpYXQiOjE0OTcxMDUxMTcsImV4cCI6MTQ5NzEwNTE3Ny wiZG9jIjozNDUzNDV9.fKKCZgc2ckeONF9ELOcc9EgS-UPr CBX2bwoPmxjStdQ JWTs WIYL! - BeerCityCode 2017 – @genehack 54

Slide 55

Slide 55 text

Solu%on • Then on the server side, when you get a request that looks like: GET /content/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. eyJpYXQiOjE0OTcxMDUxMTcsImV4cCI6MTQ5NzEwNTE3Ny wiZG9jIjozNDUzNDV9.fKKCZgc2ckeONF9ELOcc9EgS-UPr CBX2bwoPmxjStdQ • Extract the token and validate • Get the document ID from the payload • Send back that document! JWTs WIYL! - BeerCityCode 2017 – @genehack 55

Slide 56

Slide 56 text

FAR OUT MAAAAAN JWTs WIYL! - BeerCityCode 2017 – @genehack 56

Slide 57

Slide 57 text

This is actually the feature of JWTs that inspired me to give this talk, because JWTs provide an easy solution for a problem that I feel like I've run into time and time again in my coding career Recurring dilemma: 'lightweight' access control JWTs WIYL! - BeerCityCode 2017 – @genehack 57

Slide 58

Slide 58 text

leave it wide open or implement full login ...and then user management, admin screens, etc. or oauth -- but i've never had a good experience using oauth. anybody here like oauth? Recurring dilemma: 'lightweight' access control • Op$on 1: leave it wide open • a/k/a the MongoDB or WTF,YOLO! paAern • Op$on 2: implement full authn/authz subsystem • …again • Op$on 3: OAuth !!!!!! JWTs WIYL! - BeerCityCode 2017 – @genehack 58

Slide 59

Slide 59 text

photo credits * open = https://www.flickr.com/photos/ keoni101/5356662124/ Where's the middle ground? JWTs WIYL! - BeerCityCode 2017 – @genehack 59

Slide 60

Slide 60 text

photo credits * vault = https://www.flickr.com/photos/ goodfeeling/24796188573/ Where's the middle ground? JWTs WIYL! - BeerCityCode 2017 – @genehack 60

Slide 61

Slide 61 text

photo credit = https://www.flickr.com/photos/slemmon/ 4938498564 A screen door for APIs JWTs WIYL! - BeerCityCode 2017 – @genehack 61

Slide 62

Slide 62 text

Authoriza*on without authen*ca*on • Scenario: • You have an API • You don't want to make anybody authen;cate to use it • You don't want it wide open to the Internet either • a/k/a authz without authn JWTs WIYL! - BeerCityCode 2017 – @genehack 62

Slide 63

Slide 63 text

Solu%on: JWT with RSA keys • Alterna)ve to secret in previous scenario: RSA key-pair • Can include the public key in the JWT header using JWK • JSON Web Key, natch • Allows API client to produce claims in a verifiable way JWTs WIYL! - BeerCityCode 2017 – @genehack 63

Slide 64

Slide 64 text

To set it up: • Give authorized API client an RSA key-pair • Record the fingerprint of the public key (important later!) • You can even let the client generate the key-pair • You just need the public key fingerprint JWTs WIYL! - BeerCityCode 2017 – @genehack 64

Slide 65

Slide 65 text

On the client side: • They make a JWT, using the private key to sign • They include the public key in the header • Include iat (issued-at) and exp (expires) claims • Send JWT in with API request JWTs WIYL! - BeerCityCode 2017 – @genehack 65

Slide 66

Slide 66 text

On the API side: • Get the public key out of the header • Validate the signature using the public key • Validate that public key fingerprint is white-listed • Signature produced with private key • Public key is white-listed • Therefore we know JWT is valid! JWTs WIYL! - BeerCityCode 2017 – @genehack 66

Slide 67

Slide 67 text

Things to be aware of: • You s'll want to validate iat and exp and any other rules • Your library should probably do that stuff for you, mostly • Again, nothing is encrypted, so don't plan on sensi've stuff in the payload or header JWTs WIYL! - BeerCityCode 2017 – @genehack 67

Slide 68

Slide 68 text

Client side code use Crypt::JWT qw(encode_jwt); use Crypt::PK::RSA; use HTTP::Request; # generate a JWT and POST a request my $pri_key = Crypt::PK::RSA->new('./key.pri'); my $pub_key = Crypt::PK::RSA->new('./key.pub'); my $token = encode_jwt( alg => 'RS512', extra_headers => { jwk => $pub_key->export_key_jwk('public', 1), nonce => undef , }, key => $pri_key , payload => { iat => time() }, relative_exp => 1800, ); HTTP::Request->new( 'POST' => 'https://example.com/endpoint', ['Authorization' => "Bearer $token"], encode_json({ request => 'body' }) ); JWTs WIYL! - BeerCityCode 2017 – @genehack 68

Slide 69

Slide 69 text

maybe don't store your keys in files in the same directory as your code... Client side code use Crypt::JWT qw(encode_jwt); use Crypt::PK::RSA; use HTTP::Request; my $pri_key = Crypt::PK::RSA->new('./key.pri'); my $pub_key = Crypt::PK::RSA->new('./key.pub'); JWTs WIYL! - BeerCityCode 2017 – @genehack 69

Slide 70

Slide 70 text

Client side code my $token = encode_jwt( alg => 'RS512', extra_headers => { jwk => $pub_key->export_key_jwk('public', 1), nonce => undef , }, key => $pri_key , payload => { iat => time() }, relative_exp => 1800, ); JWTs WIYL! - BeerCityCode 2017 – @genehack 70

Slide 71

Slide 71 text

Client side code HTTP::Request->new( 'POST' => 'https://example.com/endpoint', ['Authorization' => "Bearer $token"], encode_json({ request => 'body' }) ); JWTs WIYL! - BeerCityCode 2017 – @genehack 71

Slide 72

Slide 72 text

Cri$cal bit: adding the public key to the header extra_headers => { jwk => $pub_key->export_key_jwk('public', 1), }, Key: find an RSA library that supports export to JWK format! JWTs WIYL! - BeerCityCode 2017 – @genehack 72

Slide 73

Slide 73 text

API side use Crypt::JWT qw(decode_jwt); use Crypt::PK::RSA; use Dancer; use Try::Tiny; my $auth_header = request_header 'Authorization' ; my $token; status_401 unless ( $token ) = $auth_header =~ /^Bearer (.*)$/; # try to decode it and confirm valid sig, # and valid iat and exp claims my( $header, $payload ); try { ( $header, $payload ) = decode_jwt( token => $token , decode_header => 1 , accepted_alg => 'RS512' , verify_iat => 1 , verify_exp => 1 ); }; # no catch block, just drop the error, we're out of here in that case status_401 unless $header and $payload; # check that expiration time is less than one hour status_401 unless $payload->{exp} - $payload->{iat} < 3600; # check that the included public key is on the whitelist my $pk = Crypt::PK::RSA->new; $pk->import_key($header->{jwk}); my $thumbprint = $pk->export_key_jwk_thumbprint; status_401 unless config->{whitelist}{$thumbprint}; # if we get here, we're all good! ... JWTs WIYL! - BeerCityCode 2017 – @genehack 73

Slide 74

Slide 74 text

API side: get the token use Crypt::JWT qw(decode_jwt); use Crypt::PK::RSA; use Dancer; use Try::Tiny; my $auth_header = request_header 'Authorization' ; my $token; status_401 unless ( $token ) = $auth_header =~ /^Bearer (.*)$/; JWTs WIYL! - BeerCityCode 2017 – @genehack 74

Slide 75

Slide 75 text

API side: decode the token # try to decode it and confirm valid sig, # and valid iat and exp claims my( $header, $payload ); try { ( $header, $payload ) = decode_jwt( token => $token , decode_header => 1 , accepted_alg => 'RS512' , verify_iat => 1 , verify_exp => 1 ); }; # no catch block, just drop the error, we're out of here in that case status_401 unless $header and $payload; JWTs WIYL! - BeerCityCode 2017 – @genehack 75

Slide 76

Slide 76 text

API side: decode the token • Key in header wrong? FAILS • Not right algorithm? FAILS • Doesn't have iat and exp? FAILS ALL that valida)on is happening inside the library, so I don't have to worry about it. • Me? WINS JWTs WIYL! - BeerCityCode 2017 – @genehack 76

Slide 77

Slide 77 text

API side: more checks • We specify in the API docs that tokens can only be valid for one hour • Have to check that ourselves • Also need to make sure this isn't some random RSA keypair • Need to make sure we know this public key JWTs WIYL! - BeerCityCode 2017 – @genehack 77

Slide 78

Slide 78 text

our api has a rule that the token can't have an expires time more than 1 hour into the future. we also need to make sure the public key fingerprint is on the allowed list one weakness with this scheme is, if a valid token leaks, that allows access to the API until it expires -- so make sure you chose an allowable access window based on an evaluation of that potential impact API side: more valida0on # check that expiration time is less than one hour status_401 unless $payload->{exp} - $payload->{iat} < 3600; # check that the included public key is on the whitelist my $pk = Crypt::PK::RSA->new; $pk->import_key($header->{jwk}); my $thumbprint = $pk->export_key_jwk_thumbprint; status_401 unless config->{whitelist}{$thumbprint}; JWTs WIYL! - BeerCityCode 2017 – @genehack 78

Slide 79

Slide 79 text

API side: THAT'S ALL FOLKS # if we get here, we're all good! • We know the public key in the header by its fingerprint, • so we know the private key was used to sign the JWT • (or it wouldn't validate) • and therefore the JWT is from the private key holder • (who is, by definiAon, authorized!) JWTs WIYL! - BeerCityCode 2017 – @genehack 79

Slide 80

Slide 80 text

IMPORTANT NOTE! This does, of course, depend on the client keeping the private key actually private …but revoca,on is as simple as removing the fingerprint from the whitelist. JWTs WIYL! - BeerCityCode 2017 – @genehack 80

Slide 81

Slide 81 text

More advanced usage • Encrypted payloads (JWE) • Nested JWT See those RFCs! JWTs WIYL! - BeerCityCode 2017 – @genehack 81

Slide 82

Slide 82 text

JWT backlash JWTs WIYL! - BeerCityCode 2017 – @genehack 82

Slide 83

Slide 83 text

Conclusions • JWTs solve some really common problems. • JWTs solve them in a pre7y elegant way. • This is really pre7y damn cool. JWTs WIYL! - BeerCityCode 2017 – @genehack 83

Slide 84

Slide 84 text

Conclusions • JWTs solve some really common problems. • JWTs solve them in a pre7y elegant way. • This is really pre7y damn cool!!! • You should think about using JWTs. JWTs WIYL! - BeerCityCode 2017 – @genehack 84

Slide 85

Slide 85 text

Thanks! • JWT.io / auth0.com folks • Beer City Code organizers • YOU! JWTs WIYL! - BeerCityCode 2017 – @genehack 85

Slide 86

Slide 86 text

JWTs WIYL! - BeerCityCode 2017 – @genehack 86

Slide 87

Slide 87 text

Ques%ons? JWTs WIYL! - BeerCityCode 2017 – @genehack 87