SlideShare a Scribd company logo
JSON Web Tokens
Will Improve Your Life
John SJ Anderson • @genehack | Ohio LinuxFest • 30 Sep 2017
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 1
Hi, I’m John.
a/k/a @genehack
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 2
VP, Technology
Infinity
Interactive
iinteractive.com
@iinteractive
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 3
This is
my dog,
Sammy.a/k/a @sammyGenehack
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 4
So, what’s
a JWT?JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 5
jwt.io
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 6
What Does
That Even
Mean?
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 7
Holy RFCs, Batman
— RFC 7519 - JSON Web Token (JWT)
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 8
Holy RFCs, Batman
— RFC 7515 - JSON Web Signature (JWS)
— RFC 7516 - JSON Web Encryption (JWE)
— RFC 7517 - JSON Web Key (JWK)
— RFC 7518 - JSON Web Algorithms (JWA)
— RFC 7519 - JSON Web Token (JWT)
— RFC 7520 - Examples of Protecting Content Using JSON
Object Signing and Encryption (JOSE)
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 9
Sammy found
these RFCs
a bit …dry
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 10
Think of JWTs as…
— A lightweight alternative to cookies (kinda, sorta)
— …that also works with CLI, mobile, or even desktop apps
— An authorization or access control mechanism
— …kinda like OAuth but without losing your will to live
— Cross-domain friendly
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 11
Made of stuff you already know
— Plain ol’ JSON Objects (POJOs)
— Stringified, encoded, and cryptographically signed
— Transmitted over HTTP(S)
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 12
What do they look like?
— dot-delimited string ('.')
— 3 parts
— header
— payload
— signature
— Example: xxx.yyyyy.zzz
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 13
JWT teardown: header
— Plain ole JSON object
— Base64 encoded
— Typically metadata, such as token type and signing
algorithm
{
"alg": "HS256",
"typ": "JWT"
}
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 14
JWT teardown: payload
— Another Base64-encoded POJO
— Contains “claims” – just key-value data
— Types of keys: reserved, public, private
{
"name": "Ohio LinuxFest",
"admin": false,
"iat": 1488562999
}
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 15
JWT teardown: signature
— Encoded header POJO, plus
— Encoded payload POJO, plus
— A secret, plus
— Signing algorithm from header alg key
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 16
A word about my
code samples
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 17
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": "Ohio LinuxFest",
"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! - Ohio LinuxFest 2017 – @genehack 18
Helper function one
function base64EncodeJson (pojo) {
var jsonString = JSON.stringify(pojo);
var encoded = new Buffer(jsonString)
.toString("base64");
return encoded;
}
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 19
Helper function two
function hmacIt (string, secret) {
var hmac = crypto.createHmac("sha256" , secret);
hmac.update(string);
var signature = hmac.digest("hex");
return signature;
}
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 20
The actual data
var header = {
"alg": "HS256",
"typ": "JWT"
};
var payload = {
"name": "Ohio LinuxFest",
"admin": false,
"iat": 1488562999
};
var secret = "be wery, wery qwiet, we're hunting JWTs";
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 21
Actually generating the signature
var encodedHeader = base64EncodeJson(header);
var encodedPayload = base64EncodeJson(payload);
var signature = hmacIt(
encodedHeader + "." + encodedPayload,
secret
);
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 22
Assembling the token
var jwt = encodedHeader
+ "."
+ encodedPayload
+ "."
+ signature;
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 23
Hand-rolled, artisanal JWT
> console.log(jwt.split('.').join('.n'));
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJuYW1lIjoiT2hpbyBMaW51eEZlc3QiLCJhZG1pbiI6ZmFsc2UsImlhdCI6MTQ4ODU2Mjk5OX0=.
f2bdd1f7f0cfc975c0ae6e5c356812af748d6d589d64c9c97f02018166e6ae5e
The critical bit of all of this:
The signature means you can detect any attempts to modify
the header or the payload.
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 24
Validation
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 25
Validation
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 26
Validation
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 27
Validation
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 28
Libraries for DAYS
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 29
Libraries for DAYS
— .NET, Python, Node, Java, Javascript, Ruby, Perl, Go, PHP
— Haskell, Rust, Lua, Scala, Clojure, ObjectiveC, Swift, Delphi
— Support for your favorite language/platform is probably
not an issue
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 30
OK,
you’ve
got my
attentionJWTs WIYL! - Ohio LinuxFest 2017 – @genehack 31
How
do I
actually
use
JWTs?
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 32
Basic authn/authz usage
(image stolen from jwt.io)
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 33
Things to be aware of
— Payload/header NOT encrypted
— …so don’t send anything sensitive!
— Need to control expiration, re-issue, etc.
— Some APIs 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! - Ohio LinuxFest 2017 – @genehack 34
How is it actually transmitted?
— 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 <token>
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 35
How would
you actually
use this
in an app?
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 36
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! - Ohio LinuxFest 2017 – @genehack 37
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! - Ohio LinuxFest 2017 – @genehack 38
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! - Ohio LinuxFest 2017 – @genehack 39
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! - Ohio LinuxFest 2017 – @genehack 40
OK, that’s
how you
make one.
How do you
validate it?
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 41
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
// Bearer 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! - Ohio LinuxFest 2017 – @genehack 42
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! - Ohio LinuxFest 2017 – @genehack 43
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
// Bearer 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! - Ohio LinuxFest 2017 – @genehack 44
Validate with a middleware
.then(function (payload) {
req.jwt = payload;
next();
})
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 45
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! - Ohio LinuxFest 2017 – @genehack 46
That’s cool.
What else
you got?
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 47
Pre-signed
Content
Requests
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 48
Document store application
— Client-side Angular
— Uses JWT for authentication and sessions
— Uses Authorization: Bearer scheme
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 49
PROBLEM!
How do you
display or download
images via browser?
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 50
SOLUTION
JWTs, of course
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 51
(What talk
did you think
this was?)JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 52
Solution
Generate a claim containing document ID:
{
"iat": 1497105117
"exp": 1497105177
"doc": 345345
}
NOTE: short expiration time
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 53
Solution
Sign it and put it in a URL:
/content/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE0OTcxMDUxMTcsImV4cCI
6MTQ5NzEwNTE3NywiZG9jIjozNDUzNDV9.fKKCZgc2ckeONF9ELOcc9EgS-UPrCBX2bwoPmxjStdQ
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 54
Solution
Then on the server side, when you get a request like:
GET /content/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE0OTcxMDUxMTcsImV4cC
I6MTQ5NzEwNTE3Ny wiZG9jIjozNDUzNDV9.fKKCZgc2ckeONF9ELOcc9EgS-UPrCBX2bwoPmxjStdQ
— Extract the token and validate
— Get the document ID from the payload
— Send back that document!
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 55
FAR
OUT
MAAAAANJWTs WIYL! - Ohio LinuxFest 2017 – @genehack 56
Recurring dilemma:
‘lightweight’
access control
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 57
Recurring dilemma: ‘lightweight’ access control
— Option 1: leave it wide open
— a/k/a the MongoDB or WTF,YOLO! pattern
— Option 2: implement full authn/authz subsystem
— …again
— Option 3: OAuth
— !!!!!!
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 58
Where’s the
middle
ground?
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 59
Where’s the
middle
ground?
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 60
A screen
door
for APIs
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 61
Authorization without authentication
— Scenario:
— You have an API
— You don’t want to make anybody authenticate to use it
— You don’t want it wide open to the Internet either
— a/k/a authz without authn
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 62
Solution: JWT with RSA keys
— Another option for secret: RSA key-pair
— Include the public key in the JWT header using JWK
— JSON Web Key, natch
— Allows clients of your API to make tokens
— …which you can verify
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 63
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! - Ohio LinuxFest 2017 – @genehack 64
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! - Ohio LinuxFest 2017 – @genehack 65
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! - Ohio LinuxFest 2017 – @genehack 66
Things to be aware of:
— You still 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 sensitive
stuff in the payload or header
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 67
where’s
the
code?JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 68
Client side
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! - Ohio LinuxFest 2017 – @genehack 69
Client side: load the libraries & keys
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! - Ohio LinuxFest 2017 – @genehack 70
Client side: make the token
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! - Ohio LinuxFest 2017 – @genehack 71
Client side: send the request
HTTP::Request->new(
'POST' => 'https://example.com/endpoint',
['Authorization' => "Bearer $token"],
encode_json({ request => 'body' })
);
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 72
Critical bit:adding the public key to the header
extra_headers => {
jwk => $pub_key->export_key_jwk('public', 1),
},
Pro tip: find an RSA library that supports exporting keys to
JWK format!
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 73
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! - Ohio LinuxFest 2017 – @genehack 74
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! - Ohio LinuxFest 2017 – @genehack 75
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! - Ohio LinuxFest 2017 – @genehack 76
API side: decode the token
— Key in header wrong? FAILS
— Not right algorithm? FAILS
— Doesn’t have iat and exp? FAILS
ALL that validation is happening inside the library, so I don’t
have to worry about it.
— Me? WINS
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 77
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! - Ohio LinuxFest 2017 – @genehack 78
API side: more checks
# 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! - Ohio LinuxFest 2017 – @genehack 79
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 definition, authorized!)
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 80
IMPORTANT NOTE!
This whole scheme does, of course, depend on the client
keeping the private key actually private
…but revocation is as simple as removing the fingerprint from
the whitelist.
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 81
More advanced usage
— Encrypted payloads (JWE)
— Nested JWT
See those RFCs!
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 82
JWT
backlash
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 83
Conclusions
— JWTs solve some really common problems.
— JWTs solve them in a pretty elegant way.
— This is really pretty damn cool.
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 84
Conclusions
— JWTs solve some really common problems.
— JWTs solve them in a pretty elegant way.
— This is really pretty damn cool!!!
— You should think about using JWTs.
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 85
Thanks!
— JWT.io / auth0.com folks
— Ohio LinuxFest organizers
— YOU!
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 86
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 87
Questions?
JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 88

More Related Content

PDF
Javascript Object Signing & Encryption
Aaron Zauner
 
PPTX
JOSE Can You See...
Brian Campbell
 
PDF
JSON Web Tokens Will Improve Your Life
John Anderson
 
PDF
JSON Web Tokens (JWT)
Vladimir Dzhuvinov
 
PPTX
Django cryptography
Erik LaBianca
 
PDF
Riak at The NYC Cloud Computing Meetup Group
siculars
 
PPTX
I Left My JWT in San JOSE
Brian Campbell
 
PDF
JSON Web Tokens Will Improve Your Life
John Anderson
 
Javascript Object Signing & Encryption
Aaron Zauner
 
JOSE Can You See...
Brian Campbell
 
JSON Web Tokens Will Improve Your Life
John Anderson
 
JSON Web Tokens (JWT)
Vladimir Dzhuvinov
 
Django cryptography
Erik LaBianca
 
Riak at The NYC Cloud Computing Meetup Group
siculars
 
I Left My JWT in San JOSE
Brian Campbell
 
JSON Web Tokens Will Improve Your Life
John Anderson
 

What's hot (20)

PPTX
An Introduction to the Emerging JSON-Based Identity and Security Protocols (O...
Brian Campbell
 
PDF
Java 11 - Keeping the Java Release Train on the Right Track
C4Media
 
PDF
CIS14: Developing with OAuth and OIDC Connect
CloudIDSummit
 
PDF
Session 5 - NGSI-LD Advanced Operations | Train the Trainers Program
FIWARE
 
PDF
Symposium on Legal Regulation of Bitcoin, Blockchain & Cryptocurrencies
anupriti
 
PDF
Stateless Microservice Security via JWT and MicroProfile - ES
Otavio Santana
 
PDF
Stateless Microservice Security via JWT and MicroProfile - Mexico
Otávio Santana
 
PDF
Stateless Microservice Security via JWT and MicroProfile - Guatemala
Otávio Santana
 
PDF
JSON Web Tokens Will Improve Your Life
John Anderson
 
PPTX
Blockchain Fundamentals
Bruno Lowagie
 
PDF
Distributed Search in Riak - Integrating Search in a NoSQL Database: Presente...
Lucidworks
 
PDF
Java Persistence Frameworks for MongoDB
MongoDB
 
PDF
Deciphering Explain Output
MongoDB
 
PPTX
Java Persistence Frameworks for MongoDB
Tobias Trelle
 
PDF
Elasticsearch und die Java-Welt
Florian Hopf
 
PDF
Session 2 - NGSI-LD primer & Smart Data Models | Train the Trainers Program
FIWARE
 
PDF
Elastify you application: from SQL to NoSQL in less than one hour!
David Pilato
 
PDF
Node.js 與 google cloud storage
onlinemad
 
PPT
Building Your First App with MongoDB
MongoDB
 
PDF
Blockchain Technologies for Data Science
Bruno Gonçalves
 
An Introduction to the Emerging JSON-Based Identity and Security Protocols (O...
Brian Campbell
 
Java 11 - Keeping the Java Release Train on the Right Track
C4Media
 
CIS14: Developing with OAuth and OIDC Connect
CloudIDSummit
 
Session 5 - NGSI-LD Advanced Operations | Train the Trainers Program
FIWARE
 
Symposium on Legal Regulation of Bitcoin, Blockchain & Cryptocurrencies
anupriti
 
Stateless Microservice Security via JWT and MicroProfile - ES
Otavio Santana
 
Stateless Microservice Security via JWT and MicroProfile - Mexico
Otávio Santana
 
Stateless Microservice Security via JWT and MicroProfile - Guatemala
Otávio Santana
 
JSON Web Tokens Will Improve Your Life
John Anderson
 
Blockchain Fundamentals
Bruno Lowagie
 
Distributed Search in Riak - Integrating Search in a NoSQL Database: Presente...
Lucidworks
 
Java Persistence Frameworks for MongoDB
MongoDB
 
Deciphering Explain Output
MongoDB
 
Java Persistence Frameworks for MongoDB
Tobias Trelle
 
Elasticsearch und die Java-Welt
Florian Hopf
 
Session 2 - NGSI-LD primer & Smart Data Models | Train the Trainers Program
FIWARE
 
Elastify you application: from SQL to NoSQL in less than one hour!
David Pilato
 
Node.js 與 google cloud storage
onlinemad
 
Building Your First App with MongoDB
MongoDB
 
Blockchain Technologies for Data Science
Bruno Gonçalves
 
Ad

Similar to JSON Web Tokens Will Improve Your Life (20)

PDF
JWT! JWT! Let it all out!
John Anderson
 
PDF
Jwt with flask slide deck - alan swenson
Jeffrey Clark
 
PPTX
Pentesting jwt
Jaya Kumar Kondapalli
 
PDF
PHP Experience 2016 - [Palestra] Json Web Token (JWT)
iMasters
 
PDF
Landscape
Amit Gupta
 
PDF
Landscape
Amit Gupta
 
PDF
Using JSON Web Tokens for REST Authentication
Mediacurrent
 
PDF
5 easy steps to understanding json web tokens (jwt)
Amit Gupta
 
PDF
Modern API Security with JSON Web Tokens
Jonathan LeBlanc
 
PDF
[OPD 2019] Attacking JWT tokens
OWASP
 
PDF
JSON Web Tokens
Ivan Rosolen
 
PDF
Autenticação com Json Web Token (JWT)
Ivan Rosolen
 
PDF
JSON WEB TOKEN
Knoldus Inc.
 
PDF
Cracking JWT tokens: a tale of magic, Node.JS and parallel computing - Node.j...
Luciano Mammino
 
PPTX
Json Web Token - JWT
Prashant Walke
 
PDF
Cracking JWT tokens: a tale of magic, Node.js and parallel computing - Code E...
Luciano Mammino
 
PPTX
jwt.pptx
Maleerat Maliyaem
 
PPTX
JsonWebTokens ppt - explains JWT, JWS , JWE Tokens
nagarajapallafl
 
PDF
The Hacker's Guide to JWT Security
Patrycja Wegrzynowicz
 
PDF
Introduction to JWT and How to integrate with Spring Security
Bruno Henrique Rother
 
JWT! JWT! Let it all out!
John Anderson
 
Jwt with flask slide deck - alan swenson
Jeffrey Clark
 
Pentesting jwt
Jaya Kumar Kondapalli
 
PHP Experience 2016 - [Palestra] Json Web Token (JWT)
iMasters
 
Landscape
Amit Gupta
 
Landscape
Amit Gupta
 
Using JSON Web Tokens for REST Authentication
Mediacurrent
 
5 easy steps to understanding json web tokens (jwt)
Amit Gupta
 
Modern API Security with JSON Web Tokens
Jonathan LeBlanc
 
[OPD 2019] Attacking JWT tokens
OWASP
 
JSON Web Tokens
Ivan Rosolen
 
Autenticação com Json Web Token (JWT)
Ivan Rosolen
 
JSON WEB TOKEN
Knoldus Inc.
 
Cracking JWT tokens: a tale of magic, Node.JS and parallel computing - Node.j...
Luciano Mammino
 
Json Web Token - JWT
Prashant Walke
 
Cracking JWT tokens: a tale of magic, Node.js and parallel computing - Code E...
Luciano Mammino
 
JsonWebTokens ppt - explains JWT, JWS , JWE Tokens
nagarajapallafl
 
The Hacker's Guide to JWT Security
Patrycja Wegrzynowicz
 
Introduction to JWT and How to integrate with Spring Security
Bruno Henrique Rother
 
Ad

More from John Anderson (20)

PDF
#speakerlife
John Anderson
 
PDF
Introduction to Git (even for non-developers)
John Anderson
 
PDF
Logs are-magic-devfestweekend2018
John Anderson
 
PDF
Logs Are Magic: Why Git Workflows and Commit Structure Should Matter To You
John Anderson
 
PDF
A static site generator should be your next language learning project
John Anderson
 
PDF
Do you want to be right or do you want to WIN?
John Anderson
 
PDF
An Introduction to Git (even for non-developers)
John Anderson
 
PDF
You got chocolate in my peanut butter! .NET on Mac & Linux
John Anderson
 
PDF
A static site generator should be your next language learning project
John Anderson
 
PDF
Old Dogs & New Tricks: What's New with Perl5 This Century
John Anderson
 
PDF
Introduction to Git (even for non-developers!)
John Anderson
 
PDF
Introduction to Git for Non-Developers
John Anderson
 
PDF
A Modest Introduction To Swift
John Anderson
 
PDF
A static site generator should be your next language learning project
John Anderson
 
PDF
Logs Are Magic: Why Git Workflows and Commit Structure Should Matter To You
John Anderson
 
PDF
Old Dogs & New Tricks: What's New With Perl5 This Century
John Anderson
 
PDF
A Modest Introduction to Swift
John Anderson
 
PDF
Logs Are Magic: Why Git Workflows and Commit Structure Should Matter To You
John Anderson
 
PDF
Friends Don't Let Friends Browse Unencrypted: Running a VPN for friends and f...
John Anderson
 
PDF
A Modest Introduction To Swift
John Anderson
 
#speakerlife
John Anderson
 
Introduction to Git (even for non-developers)
John Anderson
 
Logs are-magic-devfestweekend2018
John Anderson
 
Logs Are Magic: Why Git Workflows and Commit Structure Should Matter To You
John Anderson
 
A static site generator should be your next language learning project
John Anderson
 
Do you want to be right or do you want to WIN?
John Anderson
 
An Introduction to Git (even for non-developers)
John Anderson
 
You got chocolate in my peanut butter! .NET on Mac & Linux
John Anderson
 
A static site generator should be your next language learning project
John Anderson
 
Old Dogs & New Tricks: What's New with Perl5 This Century
John Anderson
 
Introduction to Git (even for non-developers!)
John Anderson
 
Introduction to Git for Non-Developers
John Anderson
 
A Modest Introduction To Swift
John Anderson
 
A static site generator should be your next language learning project
John Anderson
 
Logs Are Magic: Why Git Workflows and Commit Structure Should Matter To You
John Anderson
 
Old Dogs & New Tricks: What's New With Perl5 This Century
John Anderson
 
A Modest Introduction to Swift
John Anderson
 
Logs Are Magic: Why Git Workflows and Commit Structure Should Matter To You
John Anderson
 
Friends Don't Let Friends Browse Unencrypted: Running a VPN for friends and f...
John Anderson
 
A Modest Introduction To Swift
John Anderson
 

Recently uploaded (20)

PDF
Salesforce Implementation Services Provider.pdf
VALiNTRY360
 
PPTX
The-Dawn-of-AI-Reshaping-Our-World.pptxx
parthbhanushali307
 
PDF
MiniTool Power Data Recovery Crack New Pre Activated Version Latest 2025
imang66g
 
PDF
10 posting ideas for community engagement with AI prompts
Pankaj Taneja
 
PDF
Enhancing Healthcare RPM Platforms with Contextual AI Integration
Cadabra Studio
 
PPTX
ConcordeApp: Engineering Global Impact & Unlocking Billions in Event ROI with AI
chastechaste14
 
PPTX
Can You Build Dashboards Using Open Source Visualization Tool.pptx
Varsha Nayak
 
PDF
Immersive experiences: what Pharo users do!
ESUG
 
PPTX
Role Of Python In Programing Language.pptx
jaykoshti048
 
PDF
49784907924775488180_LRN2959_Data_Pump_23ai.pdf
Abilash868456
 
PDF
Summary Of Odoo 18.1 to 18.4 : The Way For Odoo 19
CandidRoot Solutions Private Limited
 
PDF
Applitools Platform Pulse: What's New and What's Coming - July 2025
Applitools
 
PDF
ChatPharo: an Open Architecture for Understanding How to Talk Live to LLMs
ESUG
 
PPTX
Presentation about Database and Database Administrator
abhishekchauhan86963
 
PPTX
Visualising Data with Scatterplots in IBM SPSS Statistics.pptx
Version 1 Analytics
 
PPTX
Maximizing Revenue with Marketo Measure: A Deep Dive into Multi-Touch Attribu...
bbedford2
 
PDF
Protecting the Digital World Cyber Securit
dnthakkar16
 
PPTX
TRAVEL APIs | WHITE LABEL TRAVEL API | TOP TRAVEL APIs
philipnathen82
 
PDF
What to consider before purchasing Microsoft 365 Business Premium_PDF.pdf
Q-Advise
 
PPTX
Presentation about variables and constant.pptx
safalsingh810
 
Salesforce Implementation Services Provider.pdf
VALiNTRY360
 
The-Dawn-of-AI-Reshaping-Our-World.pptxx
parthbhanushali307
 
MiniTool Power Data Recovery Crack New Pre Activated Version Latest 2025
imang66g
 
10 posting ideas for community engagement with AI prompts
Pankaj Taneja
 
Enhancing Healthcare RPM Platforms with Contextual AI Integration
Cadabra Studio
 
ConcordeApp: Engineering Global Impact & Unlocking Billions in Event ROI with AI
chastechaste14
 
Can You Build Dashboards Using Open Source Visualization Tool.pptx
Varsha Nayak
 
Immersive experiences: what Pharo users do!
ESUG
 
Role Of Python In Programing Language.pptx
jaykoshti048
 
49784907924775488180_LRN2959_Data_Pump_23ai.pdf
Abilash868456
 
Summary Of Odoo 18.1 to 18.4 : The Way For Odoo 19
CandidRoot Solutions Private Limited
 
Applitools Platform Pulse: What's New and What's Coming - July 2025
Applitools
 
ChatPharo: an Open Architecture for Understanding How to Talk Live to LLMs
ESUG
 
Presentation about Database and Database Administrator
abhishekchauhan86963
 
Visualising Data with Scatterplots in IBM SPSS Statistics.pptx
Version 1 Analytics
 
Maximizing Revenue with Marketo Measure: A Deep Dive into Multi-Touch Attribu...
bbedford2
 
Protecting the Digital World Cyber Securit
dnthakkar16
 
TRAVEL APIs | WHITE LABEL TRAVEL API | TOP TRAVEL APIs
philipnathen82
 
What to consider before purchasing Microsoft 365 Business Premium_PDF.pdf
Q-Advise
 
Presentation about variables and constant.pptx
safalsingh810
 

JSON Web Tokens Will Improve Your Life

  • 1. JSON Web Tokens Will Improve Your Life John SJ Anderson • @genehack | Ohio LinuxFest • 30 Sep 2017 JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 1
  • 2. Hi, I’m John. a/k/a @genehack JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 2
  • 4. This is my dog, Sammy.a/k/a @sammyGenehack JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 4
  • 5. So, what’s a JWT?JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 5
  • 6. jwt.io JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 6
  • 7. What Does That Even Mean? JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 7
  • 8. Holy RFCs, Batman — RFC 7519 - JSON Web Token (JWT) JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 8
  • 9. Holy RFCs, Batman — RFC 7515 - JSON Web Signature (JWS) — RFC 7516 - JSON Web Encryption (JWE) — RFC 7517 - JSON Web Key (JWK) — RFC 7518 - JSON Web Algorithms (JWA) — RFC 7519 - JSON Web Token (JWT) — RFC 7520 - Examples of Protecting Content Using JSON Object Signing and Encryption (JOSE) JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 9
  • 10. Sammy found these RFCs a bit …dry JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 10
  • 11. Think of JWTs as… — A lightweight alternative to cookies (kinda, sorta) — …that also works with CLI, mobile, or even desktop apps — An authorization or access control mechanism — …kinda like OAuth but without losing your will to live — Cross-domain friendly JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 11
  • 12. Made of stuff you already know — Plain ol’ JSON Objects (POJOs) — Stringified, encoded, and cryptographically signed — Transmitted over HTTP(S) JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 12
  • 13. What do they look like? — dot-delimited string ('.') — 3 parts — header — payload — signature — Example: xxx.yyyyy.zzz JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 13
  • 14. JWT teardown: header — Plain ole JSON object — Base64 encoded — Typically metadata, such as token type and signing algorithm { "alg": "HS256", "typ": "JWT" } JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 14
  • 15. JWT teardown: payload — Another Base64-encoded POJO — Contains “claims” – just key-value data — Types of keys: reserved, public, private { "name": "Ohio LinuxFest", "admin": false, "iat": 1488562999 } JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 15
  • 16. JWT teardown: signature — Encoded header POJO, plus — Encoded payload POJO, plus — A secret, plus — Signing algorithm from header alg key JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 16
  • 17. A word about my code samples JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 17
  • 18. 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": "Ohio LinuxFest", "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! - Ohio LinuxFest 2017 – @genehack 18
  • 19. Helper function one function base64EncodeJson (pojo) { var jsonString = JSON.stringify(pojo); var encoded = new Buffer(jsonString) .toString("base64"); return encoded; } JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 19
  • 20. Helper function two function hmacIt (string, secret) { var hmac = crypto.createHmac("sha256" , secret); hmac.update(string); var signature = hmac.digest("hex"); return signature; } JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 20
  • 21. The actual data var header = { "alg": "HS256", "typ": "JWT" }; var payload = { "name": "Ohio LinuxFest", "admin": false, "iat": 1488562999 }; var secret = "be wery, wery qwiet, we're hunting JWTs"; JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 21
  • 22. Actually generating the signature var encodedHeader = base64EncodeJson(header); var encodedPayload = base64EncodeJson(payload); var signature = hmacIt( encodedHeader + "." + encodedPayload, secret ); JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 22
  • 23. Assembling the token var jwt = encodedHeader + "." + encodedPayload + "." + signature; JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 23
  • 24. Hand-rolled, artisanal JWT > console.log(jwt.split('.').join('.n')); eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. eyJuYW1lIjoiT2hpbyBMaW51eEZlc3QiLCJhZG1pbiI6ZmFsc2UsImlhdCI6MTQ4ODU2Mjk5OX0=. f2bdd1f7f0cfc975c0ae6e5c356812af748d6d589d64c9c97f02018166e6ae5e The critical bit of all of this: The signature means you can detect any attempts to modify the header or the payload. JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 24
  • 25. Validation JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 25
  • 26. Validation JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 26
  • 27. Validation JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 27
  • 28. Validation JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 28
  • 29. Libraries for DAYS JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 29
  • 30. Libraries for DAYS — .NET, Python, Node, Java, Javascript, Ruby, Perl, Go, PHP — Haskell, Rust, Lua, Scala, Clojure, ObjectiveC, Swift, Delphi — Support for your favorite language/platform is probably not an issue JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 30
  • 31. OK, you’ve got my attentionJWTs WIYL! - Ohio LinuxFest 2017 – @genehack 31
  • 32. How do I actually use JWTs? JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 32
  • 33. Basic authn/authz usage (image stolen from jwt.io) JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 33
  • 34. Things to be aware of — Payload/header NOT encrypted — …so don’t send anything sensitive! — Need to control expiration, re-issue, etc. — Some APIs 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! - Ohio LinuxFest 2017 – @genehack 34
  • 35. How is it actually transmitted? — 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 <token> JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 35
  • 36. How would you actually use this in an app? JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 36
  • 37. 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! - Ohio LinuxFest 2017 – @genehack 37
  • 38. 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! - Ohio LinuxFest 2017 – @genehack 38
  • 39. 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! - Ohio LinuxFest 2017 – @genehack 39
  • 40. 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! - Ohio LinuxFest 2017 – @genehack 40
  • 41. OK, that’s how you make one. How do you validate it? JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 41
  • 42. 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 // Bearer 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! - Ohio LinuxFest 2017 – @genehack 42
  • 43. 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! - Ohio LinuxFest 2017 – @genehack 43
  • 44. 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 // Bearer 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! - Ohio LinuxFest 2017 – @genehack 44
  • 45. Validate with a middleware .then(function (payload) { req.jwt = payload; next(); }) JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 45
  • 46. 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! - Ohio LinuxFest 2017 – @genehack 46
  • 47. That’s cool. What else you got? JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 47
  • 48. Pre-signed Content Requests JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 48
  • 49. Document store application — Client-side Angular — Uses JWT for authentication and sessions — Uses Authorization: Bearer scheme JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 49
  • 50. PROBLEM! How do you display or download images via browser? JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 50
  • 51. SOLUTION JWTs, of course JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 51
  • 52. (What talk did you think this was?)JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 52
  • 53. Solution Generate a claim containing document ID: { "iat": 1497105117 "exp": 1497105177 "doc": 345345 } NOTE: short expiration time JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 53
  • 54. Solution Sign it and put it in a URL: /content/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE0OTcxMDUxMTcsImV4cCI 6MTQ5NzEwNTE3NywiZG9jIjozNDUzNDV9.fKKCZgc2ckeONF9ELOcc9EgS-UPrCBX2bwoPmxjStdQ JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 54
  • 55. Solution Then on the server side, when you get a request like: GET /content/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE0OTcxMDUxMTcsImV4cC I6MTQ5NzEwNTE3Ny wiZG9jIjozNDUzNDV9.fKKCZgc2ckeONF9ELOcc9EgS-UPrCBX2bwoPmxjStdQ — Extract the token and validate — Get the document ID from the payload — Send back that document! JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 55
  • 56. FAR OUT MAAAAANJWTs WIYL! - Ohio LinuxFest 2017 – @genehack 56
  • 57. Recurring dilemma: ‘lightweight’ access control JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 57
  • 58. Recurring dilemma: ‘lightweight’ access control — Option 1: leave it wide open — a/k/a the MongoDB or WTF,YOLO! pattern — Option 2: implement full authn/authz subsystem — …again — Option 3: OAuth — !!!!!! JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 58
  • 59. Where’s the middle ground? JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 59
  • 60. Where’s the middle ground? JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 60
  • 61. A screen door for APIs JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 61
  • 62. Authorization without authentication — Scenario: — You have an API — You don’t want to make anybody authenticate to use it — You don’t want it wide open to the Internet either — a/k/a authz without authn JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 62
  • 63. Solution: JWT with RSA keys — Another option for secret: RSA key-pair — Include the public key in the JWT header using JWK — JSON Web Key, natch — Allows clients of your API to make tokens — …which you can verify JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 63
  • 64. 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! - Ohio LinuxFest 2017 – @genehack 64
  • 65. 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! - Ohio LinuxFest 2017 – @genehack 65
  • 66. 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! - Ohio LinuxFest 2017 – @genehack 66
  • 67. Things to be aware of: — You still 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 sensitive stuff in the payload or header JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 67
  • 68. where’s the code?JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 68
  • 69. Client side 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! - Ohio LinuxFest 2017 – @genehack 69
  • 70. Client side: load the libraries & keys 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! - Ohio LinuxFest 2017 – @genehack 70
  • 71. Client side: make the token 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! - Ohio LinuxFest 2017 – @genehack 71
  • 72. Client side: send the request HTTP::Request->new( 'POST' => 'https://example.com/endpoint', ['Authorization' => "Bearer $token"], encode_json({ request => 'body' }) ); JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 72
  • 73. Critical bit:adding the public key to the header extra_headers => { jwk => $pub_key->export_key_jwk('public', 1), }, Pro tip: find an RSA library that supports exporting keys to JWK format! JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 73
  • 74. 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! - Ohio LinuxFest 2017 – @genehack 74
  • 75. 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! - Ohio LinuxFest 2017 – @genehack 75
  • 76. 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! - Ohio LinuxFest 2017 – @genehack 76
  • 77. API side: decode the token — Key in header wrong? FAILS — Not right algorithm? FAILS — Doesn’t have iat and exp? FAILS ALL that validation is happening inside the library, so I don’t have to worry about it. — Me? WINS JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 77
  • 78. 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! - Ohio LinuxFest 2017 – @genehack 78
  • 79. API side: more checks # 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! - Ohio LinuxFest 2017 – @genehack 79
  • 80. 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 definition, authorized!) JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 80
  • 81. IMPORTANT NOTE! This whole scheme does, of course, depend on the client keeping the private key actually private …but revocation is as simple as removing the fingerprint from the whitelist. JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 81
  • 82. More advanced usage — Encrypted payloads (JWE) — Nested JWT See those RFCs! JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 82
  • 83. JWT backlash JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 83
  • 84. Conclusions — JWTs solve some really common problems. — JWTs solve them in a pretty elegant way. — This is really pretty damn cool. JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 84
  • 85. Conclusions — JWTs solve some really common problems. — JWTs solve them in a pretty elegant way. — This is really pretty damn cool!!! — You should think about using JWTs. JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 85
  • 86. Thanks! — JWT.io / auth0.com folks — Ohio LinuxFest organizers — YOU! JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 86
  • 87. JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 87
  • 88. Questions? JWTs WIYL! - Ohio LinuxFest 2017 – @genehack 88