In this talk, the attendees will learn about OAuth, JWTs and OpenID Connect. By understanding how to use those flows, it will help developers make application more secure and save significant development time. By using simple examples, the speaker tries to make this talk both informative and entertaining.
- OAuth
- What is OAuth
- The access code grant
- The implicit grant
- JWTs
- What is a token
- Anatomy of a JWT
- What is a refresh token
- Simple OAuth server code samples and demo
- Open ID Connect
- General flow
- OIDC demo
I Don't Care About Security (And Neither Should You)
1. I Don’t Care About
Security
(And Neither Should
You)
Joel
Lord
Summer. Sea. JavaScript.
2. I DON’T CARE ABOUT SECURITY
(AND NEITHER SHOULD YOU)
3.
4. I DON’T CARE ABOUT SECURITY
(AND NEITHER SHOULD YOU)
5. @joel__lord #OdessaJS
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
About Me
➤ Author @ Udemy, Egghead
➤ Co-organizer @ NomadJS
➤ Developer Advocate @ Red Hat
OpenShift
@joel__lord
joellord
25. @joel__lord #OdessaJS
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
TRADITIONAL
APPLICATIONS
▸ Browser requests a login page
26. @joel__lord #OdessaJS
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
TRADITIONAL
APPLICATIONS
▸ Browser requests a login page
27. @joel__lord #OdessaJS
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
TRADITIONAL
APPLICATIONS
▸ Browser requests a login page
28. @joel__lord #OdessaJS
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
TRADITIONAL
APPLICATIONS
▸ Browser requests a login page
▸ Server validates on the database
29. @joel__lord #OdessaJS
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
TRADITIONAL
APPLICATIONS
▸ Browser requests a login page
▸ Server validates on the database
��
30. @joel__lord #OdessaJS
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
TRADITIONAL
APPLICATIONS
▸ Browser requests a login page
▸ Server validates on the database
▸ It creates a session and sends back
a cookie identifier
��
32. @joel__lord #OdessaJS
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
WHAT’S WRONG WITH
TRADITIONAL AUTHENTICATION?
▸ Multiple platforms connecting to
your application
33. @joel__lord #OdessaJS
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
WHAT’S WRONG WITH
TRADITIONAL AUTHENTICATION?
▸ Multiple platforms connecting to
your application
▸ Tightly coupled
34. @joel__lord #OdessaJS
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
WHAT’S WRONG WITH
TRADITIONAL AUTHENTICATION?
▸ Multiple platforms connecting to
your application
▸ Tightly coupled
▸ Sharing credentials to connect to a
third party API
35. @joel__lord #OdessaJS
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
WHAT’S WRONG WITH
TRADITIONAL AUTHENTICATION?
▸ Multiple platforms connecting to
your application
▸ Tightly coupled
▸ Sharing credentials to connect to a
third party API
▸ Users have a gazillions accounts
already, which leads to password
reuse and lower conversion rates
61. @joel__lord #OdessaJS
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
TOKENS
▸ Access Tokens
▸ Gives you access to a resource
▸ Controls access to your API
▸ Short Lived
▸ Refresh Tokens
▸ Enable you to get a new
access token
▸ Longed lived
▸ Can be revoked
62. @joel__lord #OdessaJS
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
TOKENS
▸ Refresh Tokens
▸ Enable you to get a new
access token
▸ Longed lived
▸ Can be revoked
63. @joel__lord #OdessaJS
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
TOKENS
▸ Refresh Tokens
▸ Enable you to get a new
access token
▸ Longed lived
▸ Can be revoked
64. @joel__lord #OdessaJS
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
TOKENS
▸ WS Federated
▸ SAML
▸ Custom stuff
▸ …
65. @joel__lord #OdessaJS
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
TOKENS
▸ WS Federated
▸ SAML
▸ Custom stuff
▸ JWT
66. @joel__lord #OdessaJS
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
JSON WEB TOKENS (JWTS, NOT JOTS)
eyJhbGciOiJIUzI1NiIsInR5cCI
6IkpXVCJ9.eyJzdWIiOiIxMjM
0NTY3ODkwIiwibmFtZSI6Ikp
vZWwgTG9yZCIsImFkbWluIj
p0cnVlLCJzY29wZSI6InBvc3R
zOnJlYWQgcG9zdHM6d3Jp
dGUifQ.XesR-pKdlscHfUwoK
vHnACqfpe2ywJ6t1BJKsq9rE
cg
67. @joel__lord #OdessaJS
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
JSON WEB TOKENS (JWTS, NOT JOTS)
▸ Header
▸ Payload
▸ Signature
eyJhbGciOiJIUzI1NiIsInR5cCI
6IkpXVCJ9.eyJzdWIiOiIxMjM
0NTY3ODkwIiwibmFtZSI6Ikp
vZWwgTG9yZCIsImFkbWluIj
p0cnVlLCJzY29wZSI6InBvc3R
zOnJlYWQgcG9zdHM6d3Jp
dGUifQ.XesR-pKdlscHfUwoK
vHnACqfpe2ywJ6t1BJKsq9rE
cg
68. @joel__lord #OdessaJS
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
JSON WEB TOKENS (JWTS, NOT JOTS)
▸ Header eyJhbGciOiJIUzI1NiIsInR5cCI
6IkpXVCJ9.eyJzdWIiOiIxMjM
0NTY3ODkwIiwibmFtZSI6Ikp
vZWwgTG9yZCIsImFkbWluIj
p0cnVlLCJzY29wZSI6InBvc3R
zOnJlYWQgcG9zdHM6d3Jp
dGUifQ.XesR-pKdlscHfUwoK
vHnACqfpe2ywJ6t1BJKsq9rE
cg
69. @joel__lord #OdessaJS
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
JSON WEB TOKENS (JWTS, NOT JOTS)
▸ Header eyJhbGciOiJIUzI1NiIsInR5cCI
6IkpXVCJ9
70. @joel__lord #OdessaJS
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
JSON WEB TOKENS (JWTS, NOT JOTS)
▸ Header atob(‘eyJhbGciOiJIUzI1NiIsIn
R5cCI6IkpXVCJ9’);
71. @joel__lord #OdessaJS
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
JSON WEB TOKENS (JWTS, NOT JOTS)
▸ Header {
"alg": "HS256",
"typ": "JWT"
}
72. @joel__lord #OdessaJS
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
JSON WEB TOKENS (JWTS, NOT JOTS)
▸ Payload eyJhbGciOiJIUzI1NiIsInR5cCI
6IkpXVCJ9.eyJzdWIiOiIxMjM
0NTY3ODkwIiwibmFtZSI6Ikp
vZWwgTG9yZCIsImFkbWluIj
p0cnVlLCJzY29wZSI6InBvc3R
zOnJlYWQgcG9zdHM6d3Jp
dGUifQ.XesR-pKdlscHfUwoK
vHnACqfpe2ywJ6t1BJKsq9rE
cg
73. @joel__lord #OdessaJS
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
JSON WEB TOKENS (JWTS, NOT JOTS)
▸ Payload eyJzdWIiOiIxMjM0NTY3ODk
wIiwibmFtZSI6IkpvZWwgTG9
yZCIsImFkbWluIjp0cnVlLCJzY
29wZSI6InBvc3RzOnJlYWQg
cG9zdHM6d3JpdGUifQ
74. @joel__lord #OdessaJS
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
JSON WEB TOKENS (JWTS, NOT JOTS)
▸ Payload atob(‘eyJzdWIiOiIxMjM0NTY
3ODkwIiwibmFtZSI6IkpvZWw
gTG9yZCIsImFkbWluIjp0cnVl
LCJzY29wZSI6InBvc3RzOnJlY
WQgcG9zdHM6d3JpdGUifQ
’);
75. @joel__lord #OdessaJS
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
JSON WEB TOKENS (JWTS, NOT JOTS)
▸ Payload {
"sub": "1234567890",
"name": "Joel Lord",
"admin": true,
"scope": "posts:read
posts:write"
}
76. @joel__lord #OdessaJS
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
JSON WEB TOKENS (JWTS, NOT JOTS)
▸ Signature XesR-pKdlscHfUwoKvHnACqf
pe2ywJ6t1BJKsq9rEcg
77. @joel__lord #OdessaJS
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
JSON WEB TOKENS (JWTS, NOT JOTS)
▸ Signature hmacsha256(
`${header}.${payload}`,
SECRET_KEY
);
78. @joel__lord #OdessaJS
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
JSON WEB TOKENS (JWTS, NOT JOTS)
Image: https://jwt.io
80. Auth Server API
var express = require('express');
var Webtask = require('webtask-tools');
var bodyParser = require('body-parser');
var randopeep = require("randopeep");
var jwt = require("jsonwebtoken");
var app = express();
var users = [
{id: 1, username: "joellord", password: "joellord"},
{id: 2, username: "guest", password: "guest"}
];
app.use(bodyParser.json());
app.post("/login", function(req, res) {
if (!req.body.username || !req.body.password) return res.status(400).send("Need username
and password");
var user = users.find(function(u) {
return u.username === req.body.username && u.password === req.body.password;
});
if (!user) return res.status(401).send("User not found");
var token = jwt.sign({
sub: user.id,
scope: "api:read",
username: user.username
}, "mysupersecret", {expiresIn: "10 minutes"});
res.status(200).send({token: token});
});
app.get('*', function (req, res) {
res.sendStatus(404);
});
module.exports = Webtask.fromExpress(app);
var express = require('express');
var Webtask = require('webtask-tools');
var bodyParser = require('body-parser');
var jwt = require("jsonwebtoken");
var app = express();
var users = [
{id: 1, username: "joellord", password: "joellord"},
{id: 2, username: "guest", password: "guest"}
];
app.use(bodyParser.urlencoded());
app.get("/login", function(req, res) {
var loginForm = "<form method='post'><input type=hidden name=callback value='" +
req.query.callback + "'><input type=text name=username /><input type=text name=password
/><input type=submit></form>";
res.status(200).send(loginForm);
});
app.post("/login", function(req, res) {
if (!req.body.username || !req.body.password) return res.status(400).send("Need username
and password");
var user = users.find(function(u) {
return u.username === req.body.username && u.password === req.body.password;
});
if (!user) return res.status(401).send("User not found");
var token = jwt.sign({
sub: user.id,
scope: "api:read",
username: user.username
}, "mysupersecret", {expiresIn: "10 minutes"});
res.redirect(req.body.callback + "#access_token=" + token);
});
81. Auth Server
var express = require('express');
var bodyParser = require('body-parser');
var jwt = require("jsonwebtoken");
var app = express();
// ...
82. Auth Server
var express = require('express');
var bodyParser = require('body-parser');
var jwt = require("jsonwebtoken");
var app = express();
// ...
83. Auth Server
var express = require('express');
var bodyParser = require('body-parser');
var jwt = require("jsonwebtoken");
var app = express();
// ...
84. Auth Server
var express = require('express');
var bodyParser = require('body-parser');
var jwt = require("jsonwebtoken");
var app = express();
// ...
86. Auth Server
// Requires ...
var users = [...];
app.use(bodyParser.urlencoded());
app.post("/login", function(req, res) {
// POST for login
});
app.get('*', function (req, res) {
res.sendStatus(404);
});
87. Auth Server
// Requires ...
var users = [...];
app.use(bodyParser.urlencoded());
app.post("/login", function(req, res) {
// POST for login
});
app.get('*', function (req, res) {
res.sendStatus(404);
});
88. Auth Server
app.post("/login", function(req, res) {
// POST for login
if (!req.body.username || !req.body.password)
return res.status(400).send("Need username and password");
var user = users.find(function(u) {
return u.username === req.body.username && u.password === req.body.password;
});
if (!user) return res.status(401).send("User not found");
var token = jwt.sign({
sub: user.id,
scope: "api:read",
username: user.username
}, "mysupersecret", {expiresIn: "10 minutes"});
res.redirect(req.body.callback + "#access_token=" + token);
});
89. Auth Server
app.post("/login", function(req, res) {
// POST for login
if (!req.body.username || !req.body.password)
return res.status(400).send("Need username and password");
var user = users.find(function(u) {
return u.username === req.body.username && u.password === req.body.password;
});
if (!user) return res.status(401).send("User not found");
var token = jwt.sign({
sub: user.id,
scope: "api:read",
username: user.username
}, "mysupersecret", {expiresIn: "10 minutes"});
res.redirect(req.body.callback + "#access_token=" + token);
});
90. Auth Server
app.post("/login", function(req, res) {
// POST for login
if (!req.body.username || !req.body.password)
return res.status(400).send("Need username and password");
var user = users.find(function(u) {
return u.username === req.body.username && u.password === req.body.password;
});
if (!user) return res.status(401).send("User not found");
var token = jwt.sign({
sub: user.id,
scope: "api:read",
username: user.username
}, "mysupersecret", {expiresIn: "10 minutes"});
res.redirect(req.body.callback + "#access_token=" + token);
});
91. Auth Server
app.post("/login", function(req, res) {
// POST for login
if (!req.body.username || !req.body.password)
return res.status(400).send("Need username and password");
var user = users.find(function(u) {
return u.username === req.body.username && u.password === req.body.password;
});
if (!user) return res.status(401).send("User not found");
var token = jwt.sign({
sub: user.id,
scope: "api:read",
username: user.username
}, "mysupersecret", {expiresIn: "10 minutes"});
res.redirect(req.body.callback + "#access_token=" + token);
});
92. Auth Server
// Requires ...
var users = [...];
app.use(bodyParser.urlencoded());
app.post("/login", function(req, res) {
// POST for login
});
app.get('*', function (req, res) {
res.sendStatus(404);
});
app.listen(8080, () => console.log("Auth server running on 8080"));}
93. API
var express = require('express');
var bodyParser = require('body-parser');
var randopeep = require("randopeep");
var expressjwt = require("express-jwt");
var app = express();
94. API
var express = require('express');
var bodyParser = require('body-parser');
var randopeep = require("randopeep");
var expressjwt = require("express-jwt");
var app = express();
95. API
var express = require('express');
var bodyParser = require('body-parser');
var randopeep = require("randopeep");
var expressjwt = require("express-jwt");
var app = express();
96. API
var express = require('express');
var bodyParser = require('body-parser');
var randopeep = require("randopeep");
var expressjwt = require("express-jwt");
var app = express();
97. API
var express = require('express');
var bodyParser = require('body-parser');
var randopeep = require("randopeep");
var expressjwt = require("express-jwt");
var app = express();
103. API
// Requires and config ...
app.get("/headline", function(req, res) {
// Unprotected
});
app.get("/protected/headline", jwtCheck, function(req, res) {
// Protected
});
app.get('*', function (req, res) {
res.sendStatus(404);
});
app.listen(8888, () => console.log("API listening on 8888"));
104.
105. @joel__lord #OdessaJS
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
DEAR DEMO GODS, PLEASE
LET THIS WORK
▸ https://github.com/joellord/secure-s
pa-auth0
115. @joel__lord #OdessaJS
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
OPEN ID CONNECT
▸ Built on top of OAuth 2.0
▸ OpenID Connect is to OpenId what JavaScript is to Java
▸ Provides Identity Tokens in JWT format
▸ Uses a /userinfo endpoint to provide this info
116. @joel__lord #OdessaJS
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
OPEN ID CONNECT
▸ Uses the “scope” to fetch info
▸ openid
▸ Profile
▸ email
▸ address
▸ phone
125. @joel__lord #OdessaJS
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
OPEN ID CONNECT
Image: https://openidconnect.net
126. @joel__lord #OdessaJS
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
DEAR DEMO GODS, PLEASE
LET THIS WORK
▸ https://github.com/joellord/secure-s
pa-auth0
129. @joel__lord #OdessaJS
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
RESOURCES
▸ General JWT resource
▸ https://jwt.io/
▸ Learn how OIDC works
▸ https://openidconnect.net/
▸ Code samples
▸ http://bit.ly/idontcareaboutsecurity