I Don't Care About Security (And Neither Should You)

J
I DON’T CARE ABOUT SECURITY
(AND NEITHER SHOULD YOU)
I Don't Care About Security (And Neither Should You)
I DON’T CARE ABOUT SECURITY
(AND NEITHER SHOULD YOU)
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
ABOUT ME
▸ Passionate about tech
▸ Technical Evangelist at Auth0,
Author at Udemy, Egghead
@joel__lord
I Don't Care About Security (And Neither Should You)
I Don't Care About Security (And Neither Should You)
I Don't Care About Security (And Neither Should You)
I Don't Care About Security (And Neither Should You)
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
OAUTH FLOWS
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
OAUTH FLOWS
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
OAUTH FLOWS
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
OAUTH FLOWS
INTRODUCING OAUTH!
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
OAUTH FLOWS
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
OAUTH FLOWS
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
OAUTH FLOWS
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
OAUTH FLOWS
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
OAUTH FLOWS
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
OAUTH FLOWS
BUT WHY ?
DELEGATION!
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
TRADITIONAL APPLICATIONS
▸ Browser requests a login page
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
TRADITIONAL APPLICATIONS
▸ Browser requests a login page
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
TRADITIONAL APPLICATIONS
▸ Browser requests a login page
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
TRADITIONAL APPLICATIONS
▸ Browser requests a login page
▸ Server validates on the database
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
TRADITIONAL APPLICATIONS
▸ Browser requests a login page
▸ Server validates on the database
👍
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
👍
"
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
WHAT’S WRONG WITH TRADITIONAL AUTHENTICATION?
▸ Multiple platforms connecting to
your application
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
WHAT’S WRONG WITH TRADITIONAL AUTHENTICATION?
▸ Multiple platforms connecting to
your application
▸ Tightly coupled
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
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
I Don't Care About Security (And Neither Should You)
OAUTH GRANTS
I DON’T CARE ABOUT SECURITY (AND
NEITHER SHOULD YOU)
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
AUTHORIZATION CODE
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
AUTHORIZATION CODE
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
AUTHORIZATION CODE
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
AUTHORIZATION CODE
👍
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
AUTHORIZATION CODE
👍
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
AUTHORIZATION CODE
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
AUTHORIZATION CODE
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
AUTHORIZATION CODE
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
AUTHORIZATION CODE
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
AUTHORIZATION CODE
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
AUTHORIZATION CODE
⛔
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
AUTHORIZATION CODE
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
AUTHORIZATION CODE
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
IMPLICIT GRANT
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
IMPLICIT GRANT
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
IMPLICIT GRANT
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
IMPLICIT GRANT
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
IMPLICIT GRANT
👍
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
IMPLICIT GRANT
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
IMPLICIT GRANT
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
IMPLICIT GRANT
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
IMPLICIT GRANT
👍
TOKENS 101
I DON’T CARE ABOUT SECURITY (AND
NEITHER SHOULD YOU)
@joel__lord
#PyConSK
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
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
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
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
TOKENS
▸ WS Federated
▸ SAML
▸ Custom stuff
▸ …
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
TOKENS
▸ WS Federated
▸ SAML
▸ Custom stuff
▸ JWT
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
JSON WEB TOKENS (JWTS, NOT JOTS)
eyJhbGciOiJIUzI1NiIsInR5cC
I6IkpXVCJ9.eyJzdWIiOiIxMj
M0NTY3ODkwIiwibmFtZSI6I
kpvZWwgTG9yZCIsImFkbWl
uIjp0cnVlLCJzY29wZSI6InBv
c3RzOnJlYWQgcG9zdHM6
d3JpdGUifQ.XesR-
pKdlscHfUwoKvHnACqfpe2
ywJ6t1BJKsq9rEcg
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
JSON WEB TOKENS (JWTS, NOT JOTS)
▸ Header
▸ Payload
▸ Signature
eyJhbGciOiJIUzI1NiIsInR5cC
I6IkpXVCJ9.eyJzdWIiOiIxMj
M0NTY3ODkwIiwibmFtZSI6I
kpvZWwgTG9yZCIsImFkbWl
uIjp0cnVlLCJzY29wZSI6InBv
c3RzOnJlYWQgcG9zdHM6
d3JpdGUifQ.XesR-
pKdlscHfUwoKvHnACqfpe2
ywJ6t1BJKsq9rEcg
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
JSON WEB TOKENS (JWTS, NOT JOTS)
▸ Header eyJhbGciOiJIUzI1NiIsInR5cC
I6IkpXVCJ9.eyJzdWIiOiIxMj
M0NTY3ODkwIiwibmFtZSI6I
kpvZWwgTG9yZCIsImFkbWl
uIjp0cnVlLCJzY29wZSI6InBv
c3RzOnJlYWQgcG9zdHM6
d3JpdGUifQ.XesR-
pKdlscHfUwoKvHnACqfpe2
ywJ6t1BJKsq9rEcg
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
JSON WEB TOKENS (JWTS, NOT JOTS)
▸ Header eyJhbGciOiJIUzI1NiIsInR5cC
I6IkpXVCJ9
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
JSON WEB TOKENS (JWTS, NOT JOTS)
▸ Header atob(‘eyJhbGciOiJIUzI1NiIsI
nR5cCI6IkpXVCJ9’);
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
JSON WEB TOKENS (JWTS, NOT JOTS)
▸ Header {
"alg": "HS256",
"typ": "JWT"
}
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
JSON WEB TOKENS (JWTS, NOT JOTS)
▸ Payload eyJhbGciOiJIUzI1NiIsInR5cC
I6IkpXVCJ9.eyJzdWIiOiIxMj
M0NTY3ODkwIiwibmFtZSI6I
kpvZWwgTG9yZCIsImFkbWl
uIjp0cnVlLCJzY29wZSI6InBv
c3RzOnJlYWQgcG9zdHM6
d3JpdGUifQ.XesR-
pKdlscHfUwoKvHnACqfpe2
ywJ6t1BJKsq9rEcg
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
JSON WEB TOKENS (JWTS, NOT JOTS)
▸ Payload eyJzdWIiOiIxMjM0NTY3OD
kwIiwibmFtZSI6IkpvZWwgT
G9yZCIsImFkbWluIjp0cnVlL
CJzY29wZSI6InBvc3RzOnJl
YWQgcG9zdHM6d3JpdGUi
fQ
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
JSON WEB TOKENS (JWTS, NOT JOTS)
▸ Payload atob(‘eyJzdWIiOiIxMjM0NT
Y3ODkwIiwibmFtZSI6IkpvZ
WwgTG9yZCIsImFkbWluIjp
0cnVlLCJzY29wZSI6InBvc3R
zOnJlYWQgcG9zdHM6d3J
pdGUifQ’);
@joel__lord
#PyConSK
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"
}
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
JSON WEB TOKENS (JWTS, NOT JOTS)
▸ Signature XesR-
pKdlscHfUwoKvHnACqfpe2
ywJ6t1BJKsq9rEcg
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
JSON WEB TOKENS (JWTS, NOT JOTS)
▸ Signature hmacsha256(
`${header}.${payload}`,
SECRET_KEY
);
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
JSON WEB TOKENS (JWTS, NOT JOTS)
Image: https://jwt.io
CODE TIME!
Auth Server API
from flask import Flask, jsonify
import os
from api_auth_header import AuthError, requires_auth
from flask_cors import CORS
from randopeep import clickbait
app = Flask(__name__)
CORS(app)
@app.errorhandler(AuthError)
def handle_auth_error(ex):
response = jsonify(ex.error)
response.status_code = ex.status_code
return response
@app.route("/headline")
def headline():
return clickbait.headline()
@app.route("/protected/headline")
@requires_auth
def protected_headline():
return clickbait.headline("Joel Lord")
if __name__ == '__main__':
port = int(os.environ.get('PORT', 8888))
print("Auth server started on port %d " % port)
app.run(host='0.0.0.0', port=port, debug=True)
import os
from flask import Flask, request, abort, redirect
from jose import jwt
app = Flask(__name__)
users = [{"id": 1, "username": "joellord", "password": "joellord"}]
@app.route("/login", methods=["GET"])
def login_form():
print("Serve form")
callback_url = request.args.get("callback")
return "<form method='post'>...</form>"
@app.route("/login", methods=["POST"])
def process_login():
username = request.form.get("username")
password = request.form.get("password")
callback = request.form.get("callback")
if (username is "" or password is ""):
abort(400)
user = next((user for user in users if user["username"] ==
username and user["password"] == password), False)
if (user == False):
abort(401)
print(user)
token = jwt.encode({
"sub": str(user["id"]),
"username": user["username"],
"scope": "dostuff",
"aud": "my-random-clickbait-api",
"iss": "my-small-auth-server"
}, "my-super-secret-key", algorithm='HS256')
redirect_url = callback + "#access_token=" + token
return redirect(redirect_url, code=302)
if __name__ == '__main__':
port = int(os.environ.get('PORT', 8080))
print("Auth server started on port %d " % port)
app.run(host='0.0.0.0', port=port, debug=True)
●
@joel__lord
#PyConSK
Auth Server
import os
from flask import Flask, request, abort, redirect
from jose import jwt
app = Flask(__name__)
// ...
@joel__lord
#PyConSK
Auth Server
import os
from flask import Flask, request, abort, redirect
from jose import jwt
app = Flask(__name__)
// ...
@joel__lord
#PyConSK
Auth Server
import os
from flask import Flask, request, abort, redirect
from jose import jwt
app = Flask(__name__)
// ...
@joel__lord
#PyConSK
Auth Server
import os
from flask import Flask, request, abort, redirect
from jose import jwt
app = Flask(__name__)
// ...
@joel__lord
#PyConSK
Auth Server
# imports ...
users = [
{
"id": 1,
"username": "joellord",
"password": "joellord"
},
{
"id": 2,
"username": "guest",
"password": "guest"
}
]
@joel__lord
#PyConSK
Auth Server
# imports ...
users = [...]
@app.route("/login", methods=["GET"])
def login_form():
# login form
@app.route("/login", methods=["POST"])
def process_login():
# handle request
@joel__lord
#PyConSK
Auth Server
# imports ...
users = [...]
@app.route("/login", methods=["GET"])
def login_form():
# login form
@app.route("/login", methods=["POST"])
def process_login():
# handle request
@joel__lord
#PyConSK
Auth Server
@app.route("/login", methods=["GET"])
def login_form():
print("Serve form")
callback_url = request.args.get("callback")
return "<form method='post'><input type=hidden
name=callback value='" + callback_url + "'><input
type=text name=username /><input type=text
name=password /><input type=submit></form>"
@joel__lord
#PyConSK
Auth Server
@app.route("/login", methods=["GET"])
def login_form():
print("Serve form")
callback_url = request.args.get("callback")
return "<form method='post'><input type=hidden
name=callback value='" + callback_url + "'><input
type=text name=username /><input type=text
name=password /><input type=submit></form>"
@joel__lord
#PyConSK
Auth Server
@app.route("/login", methods=["POST"])
def process_login():
username = request.form.get("username")
password = request.form.get("password")
callback = request.form.get("callback")
if (username is "" or password is ""):
abort(400)
user = next((user for user in users if user["username"] == username
and user["password"] == password), False)
if (user == False):
abort(401)
token = jwt.encode({
"sub": str(user["id"]),
"username": user["username"],
"scope": "dostuff",
"aud": "my-random-clickbait-api",
"iss": "my-small-auth-server"
}, "my-super-secret-key", algorithm=‘HS256')
redirect_url = callback + "#access_token=" + token
return redirect(redirect_url, code=302)
@joel__lord
#PyConSK
Auth Server
@app.route("/login", methods=["POST"])
def process_login():
username = request.form.get("username")
password = request.form.get("password")
callback = request.form.get("callback")
if (username is "" or password is ""):
abort(400)
user = next((user for user in users if user["username"] == username
and user["password"] == password), False)
if (user == False):
abort(401)
token = jwt.encode({
"sub": str(user["id"]),
"username": user["username"],
"scope": "dostuff",
"aud": "my-random-clickbait-api",
"iss": "my-small-auth-server"
}, "my-super-secret-key", algorithm=‘HS256')
redirect_url = callback + "#access_token=" + token
return redirect(redirect_url, code=302)
@joel__lord
#PyConSK
Auth Server
@app.route("/login", methods=["POST"])
def process_login():
username = request.form.get("username")
password = request.form.get("password")
callback = request.form.get("callback")
if (username is "" or password is ""):
abort(400)
user = next((user for user in users if user["username"] == username
and user["password"] == password), False)
if (user == False):
abort(401)
token = jwt.encode({
"sub": str(user["id"]),
"username": user["username"],
"scope": "dostuff",
"aud": "my-random-clickbait-api",
"iss": "my-small-auth-server"
}, "my-super-secret-key", algorithm=‘HS256')
redirect_url = callback + "#access_token=" + token
return redirect(redirect_url, code=302)
@joel__lord
#PyConSK
Auth Server
@app.route("/login", methods=["POST"])
def process_login():
username = request.form.get("username")
password = request.form.get("password")
callback = request.form.get("callback")
if (username is "" or password is ""):
abort(400)
user = next((user for user in users if user["username"] == username
and user["password"] == password), False)
if (user == False):
abort(401)
token = jwt.encode({
"sub": str(user["id"]),
"username": user["username"],
"scope": "dostuff",
"aud": "my-random-clickbait-api",
"iss": "my-small-auth-server"
}, "my-super-secret-key", algorithm=‘HS256')
redirect_url = callback + "#access_token=" + token
return redirect(redirect_url, code=302)
@joel__lord
#PyConSK
Auth Server
@app.route("/login", methods=["POST"])
def process_login():
username = request.form.get("username")
password = request.form.get("password")
callback = request.form.get("callback")
if (username is "" or password is ""):
abort(400)
user = next((user for user in users if user["username"] == username
and user["password"] == password), False)
if (user == False):
abort(401)
token = jwt.encode({
"sub": str(user["id"]),
"username": user["username"],
"scope": "dostuff",
"aud": "my-random-clickbait-api",
"iss": "my-small-auth-server"
}, "my-super-secret-key", algorithm=‘HS256')
redirect_url = callback + "#access_token=" + token
return redirect(redirect_url, code=302)
@joel__lord
#PyConSK
Auth Server
@app.route("/login", methods=["POST"])
def process_login():
username = request.form.get("username")
password = request.form.get("password")
callback = request.form.get("callback")
if (username is "" or password is ""):
abort(400)
user = next((user for user in users if user["username"] == username
and user["password"] == password), False)
if (user == False):
abort(401)
token = jwt.encode({
"sub": str(user["id"]),
"username": user["username"],
"scope": "dostuff",
"aud": "my-random-clickbait-api",
"iss": "my-small-auth-server"
}, "my-super-secret-key", algorithm=‘HS256')
redirect_url = callback + "#access_token=" + token
return redirect(redirect_url, code=302)
@joel__lord
#PyConSK
Auth Server
# imports ...
users = [...]
@app.route("/login", methods=["GET"])
# ...
@app.route("/login", methods=["POST"])
# ...
if __name__ == '__main__':
port = int(os.environ.get('PORT', 8080))
print("Auth server started on port %d " % port)
app.run(host='0.0.0.0', port=port, debug=True)
@joel__lord
#PyConSK
API
from flask import Flask, jsonify
import os
from api_auth_header import AuthError, requires_auth
from flask_cors import CORS
from randopeep import clickbait
app = Flask(__name__)
CORS(app)
@joel__lord
#PyConSK
API
# imports
@app.route("/headline")
def headline():
return clickbait.headline()
@app.route("/protected/headline")
@requires_auth
def protected_headline():
return clickbait.headline("Joel Lord")
@joel__lord
#PyConSK
API
# imports
@app.route("/headline")
def headline():
return clickbait.headline()
@app.route("/protected/headline")
@requires_auth
def protected_headline():
return clickbait.headline("Joel Lord")
@joel__lord
#PyConSK
API
# imports
@app.route("/headline")
def headline():
return clickbait.headline()
@app.route("/protected/headline")
@requires_auth
def protected_headline():
return clickbait.headline("Joel Lord")
@joel__lord
#PyConSK
API
# imports …
@app.route("/headline")
def headline():
# …
@app.route("/protected/headline")
@requires_auth
def protected_headline():
# …
if __name__ == '__main__':
port = int(os.environ.get('PORT', 8888))
print("Auth server started on port %d " % port)
app.run(host='0.0.0.0', port=port, debug=True)
@joel__lord
#PyConSK
API (@requires_authfrom functools import wraps
from jose import jwt
from flask import _request_ctx_stack, request
class AuthError(Exception):
def __init__(self, error, status_code):
self.error = error
self.status_code = status_code
def get_token_auth_header():
# Get and validate the token...
return token
def requires_auth(f):
@wraps(f)
def decorated(*args, **kwargs):
token = get_token_auth_header()
try:
payload = jwt.decode(token, "my-super-secret-key", algorithms="HS256")
except jwt.ExpiredSignatureError:
raise AuthError({"code": "token_expired", "description": "Token is expired"}, 401)
# Also check claims and headers
_request_ctx_stack.top.current_user = payload
return f(*args, **kwargs)
return decorated
@joel__lord
#PyConSK
API (@requires_authfrom functools import wraps
from jose import jwt
from flask import _request_ctx_stack, request
class AuthError(Exception):
def __init__(self, error, status_code):
self.error = error
self.status_code = status_code
def get_token_auth_header():
# Get and validate the token...
return token
def requires_auth(f):
@wraps(f)
def decorated(*args, **kwargs):
token = get_token_auth_header()
try:
payload = jwt.decode(token, "my-super-secret-key", algorithms="HS256")
except jwt.ExpiredSignatureError:
raise AuthError({"code": "token_expired", "description": "Token is expired"}, 401)
# Also check claims and headers
_request_ctx_stack.top.current_user = payload
return f(*args, **kwargs)
return decorated
@joel__lord
#PyConSK
API (@requires_authfrom functools import wraps
from jose import jwt
from flask import _request_ctx_stack, request
class AuthError(Exception):
def __init__(self, error, status_code):
self.error = error
self.status_code = status_code
def get_token_auth_header():
# Get and validate the token...
return token
def requires_auth(f):
@wraps(f)
def decorated(*args, **kwargs):
token = get_token_auth_header()
try:
payload = jwt.decode(token, "my-super-secret-key", algorithms="HS256")
except jwt.ExpiredSignatureError:
raise AuthError({"code": "token_expired", "description": "Token is expired"}, 401)
# Also check claims and headers
_request_ctx_stack.top.current_user = payload
return f(*args, **kwargs)
return decorated
@joel__lord
#PyConSK
API (@requires_authfrom functools import wraps
from jose import jwt
from flask import _request_ctx_stack, request
class AuthError(Exception):
def __init__(self, error, status_code):
self.error = error
self.status_code = status_code
def get_token_auth_header():
# Get and validate the token...
return token
def requires_auth(f):
@wraps(f)
def decorated(*args, **kwargs):
token = get_token_auth_header()
try:
payload = jwt.decode(token, "my-super-secret-key", algorithms="HS256")
except jwt.ExpiredSignatureError:
raise AuthError({"code": "token_expired", "description": "Token is expired"}, 401)
# Also check claims and headers
_request_ctx_stack.top.current_user = payload
return f(*args, **kwargs)
return decorated
I Don't Care About Security (And Neither Should You)
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
DEAR DEMO GODS, PLEASE LET THIS WORK
▸ https://github.com/joellord/
secure-spa-auth0
DELEGATION!
I Don't Care About Security (And Neither Should You)
I Don't Care About Security (And Neither Should You)
I Don't Care About Security (And Neither Should You)
I Don't Care About Security (And Neither Should You)
I Don't Care About Security (And Neither Should You)
I Don't Care About Security (And Neither Should You)
I Don't Care About Security (And Neither Should You)
INTRODUCING OPEN ID CONNECT!
@joel__lord
#PyConSK
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
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
OPEN ID CONNECT
▸ Uses the “scope” to fetch info
▸ openid
▸ Profile
▸ email
▸ address
▸ phone
@joel__lord
#PyConSK
I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
OPEN ID CONNECT
Image: https://openidconnect.net
DELEGATION!
@joel__lord
#PyConSK
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
@joel__lord
joellord
I DON’T CARE ABOUT SECURITY
(AND NEITHER SHOULD YOU)
PyCon SK
March 23rd, 2019
THANK YOU !
TEXT
TEXT
1 of 121

Recommended

I Don't Care About Security (And Neither Should You) by Joel lord by
I Don't Care About Security (And Neither Should You) by Joel lordI Don't Care About Security (And Neither Should You) by Joel lord
I Don't Care About Security (And Neither Should You) by Joel lordOdessaJS Conf
3.1K views129 slides
I Don't Care About Security (And Neither Should You) by
I Don't Care About Security (And Neither Should You)I Don't Care About Security (And Neither Should You)
I Don't Care About Security (And Neither Should You)Joel Lord
670 views131 slides
CITEC #CON2-Dirty Attack with Google Hacking by
CITEC #CON2-Dirty Attack with Google HackingCITEC #CON2-Dirty Attack with Google Hacking
CITEC #CON2-Dirty Attack with Google HackingPrathan Phongthiproek
53.8K views66 slides
صناعة التأثير by
صناعة التأثيرصناعة التأثير
صناعة التأثيرHani Al-Menaii
16.2K views237 slides
Magazine draft by
Magazine draftMagazine draft
Magazine draftAreYouNormalBass
196 views4 slides
I Don't Care About Security (And Neither Should You) by
I Don't Care About Security (And Neither Should You)I Don't Care About Security (And Neither Should You)
I Don't Care About Security (And Neither Should You)Joel Lord
153 views126 slides

More Related Content

More from Joel Lord

From Ceasar Cipher To Quantum Cryptography by
From Ceasar Cipher To Quantum CryptographyFrom Ceasar Cipher To Quantum Cryptography
From Ceasar Cipher To Quantum CryptographyJoel Lord
405 views185 slides
Forgot Password? Yes I Did! by
Forgot Password? Yes I Did!Forgot Password? Yes I Did!
Forgot Password? Yes I Did!Joel Lord
432 views61 slides
Mot de passe oublié? Absolument! by
Mot de passe oublié? Absolument!Mot de passe oublié? Absolument!
Mot de passe oublié? Absolument!Joel Lord
193 views69 slides
Asynchronicity: concurrency. A tale of by
Asynchronicity: concurrency. A tale ofAsynchronicity: concurrency. A tale of
Asynchronicity: concurrency. A tale ofJoel Lord
283 views176 slides
Learning Machine Learning by
Learning Machine LearningLearning Machine Learning
Learning Machine LearningJoel Lord
452 views45 slides
Forgot Password? Yes I Did! by
Forgot Password? Yes I Did!Forgot Password? Yes I Did!
Forgot Password? Yes I Did!Joel Lord
295 views66 slides

More from Joel Lord(20)

From Ceasar Cipher To Quantum Cryptography by Joel Lord
From Ceasar Cipher To Quantum CryptographyFrom Ceasar Cipher To Quantum Cryptography
From Ceasar Cipher To Quantum Cryptography
Joel Lord405 views
Forgot Password? Yes I Did! by Joel Lord
Forgot Password? Yes I Did!Forgot Password? Yes I Did!
Forgot Password? Yes I Did!
Joel Lord432 views
Mot de passe oublié? Absolument! by Joel Lord
Mot de passe oublié? Absolument!Mot de passe oublié? Absolument!
Mot de passe oublié? Absolument!
Joel Lord193 views
Asynchronicity: concurrency. A tale of by Joel Lord
Asynchronicity: concurrency. A tale ofAsynchronicity: concurrency. A tale of
Asynchronicity: concurrency. A tale of
Joel Lord283 views
Learning Machine Learning by Joel Lord
Learning Machine LearningLearning Machine Learning
Learning Machine Learning
Joel Lord452 views
Forgot Password? Yes I Did! by Joel Lord
Forgot Password? Yes I Did!Forgot Password? Yes I Did!
Forgot Password? Yes I Did!
Joel Lord295 views
WTH is a JWT by Joel Lord
WTH is a JWTWTH is a JWT
WTH is a JWT
Joel Lord909 views
I Don't Care About Security (And Neither Should You) by Joel Lord
I Don't Care About Security (And Neither Should You)I Don't Care About Security (And Neither Should You)
I Don't Care About Security (And Neither Should You)
Joel Lord125 views
Forgot Password? Yes I Did! by Joel Lord
Forgot Password? Yes I Did!Forgot Password? Yes I Did!
Forgot Password? Yes I Did!
Joel Lord115 views
I Don't Care About Security (And Neither Should You) by Joel Lord
I Don't Care About Security (And Neither Should You)I Don't Care About Security (And Neither Should You)
I Don't Care About Security (And Neither Should You)
Joel Lord195 views
WTH is a JWT by Joel Lord
WTH is a JWTWTH is a JWT
WTH is a JWT
Joel Lord311 views
Asynchonicity: concurrency. A tale of by Joel Lord
Asynchonicity: concurrency. A tale ofAsynchonicity: concurrency. A tale of
Asynchonicity: concurrency. A tale of
Joel Lord360 views
I Don't Care About Security by Joel Lord
I Don't Care About Security I Don't Care About Security
I Don't Care About Security
Joel Lord247 views
I Don't Care About Security (And Neither Should You) by Joel Lord
I Don't Care About Security (And Neither Should You)I Don't Care About Security (And Neither Should You)
I Don't Care About Security (And Neither Should You)
Joel Lord209 views
I Don't Care About Security (And Neither Should You) by Joel Lord
I Don't Care About Security (And Neither Should You)I Don't Care About Security (And Neither Should You)
I Don't Care About Security (And Neither Should You)
Joel Lord233 views
Secure your SPA with Auth0 by Joel Lord
Secure your SPA with Auth0Secure your SPA with Auth0
Secure your SPA with Auth0
Joel Lord341 views
Learning Machine Learning by Joel Lord
Learning Machine LearningLearning Machine Learning
Learning Machine Learning
Joel Lord343 views
Learning Machine Learning by Joel Lord
Learning Machine LearningLearning Machine Learning
Learning Machine Learning
Joel Lord241 views
Rise of the Nodebots by Joel Lord
Rise of the NodebotsRise of the Nodebots
Rise of the Nodebots
Joel Lord262 views
Let's Get Physical by Joel Lord
Let's Get PhysicalLet's Get Physical
Let's Get Physical
Joel Lord279 views

Recently uploaded

information by
informationinformation
informationkhelgishekhar
10 views4 slides
The Dark Web : Hidden Services by
The Dark Web : Hidden ServicesThe Dark Web : Hidden Services
The Dark Web : Hidden ServicesAnshu Singh
5 views24 slides
IETF 118: Starlink Protocol Performance by
IETF 118: Starlink Protocol PerformanceIETF 118: Starlink Protocol Performance
IETF 118: Starlink Protocol PerformanceAPNIC
394 views22 slides
PORTFOLIO 1 (Bret Michael Pepito).pdf by
PORTFOLIO 1 (Bret Michael Pepito).pdfPORTFOLIO 1 (Bret Michael Pepito).pdf
PORTFOLIO 1 (Bret Michael Pepito).pdfbrejess0410
9 views6 slides
WEB 2.O TOOLS: Empowering education.pptx by
WEB 2.O TOOLS: Empowering education.pptxWEB 2.O TOOLS: Empowering education.pptx
WEB 2.O TOOLS: Empowering education.pptxnarmadhamanohar21
16 views16 slides
Marketing and Community Building in Web3 by
Marketing and Community Building in Web3Marketing and Community Building in Web3
Marketing and Community Building in Web3Federico Ast
14 views64 slides

Recently uploaded(10)

The Dark Web : Hidden Services by Anshu Singh
The Dark Web : Hidden ServicesThe Dark Web : Hidden Services
The Dark Web : Hidden Services
Anshu Singh5 views
IETF 118: Starlink Protocol Performance by APNIC
IETF 118: Starlink Protocol PerformanceIETF 118: Starlink Protocol Performance
IETF 118: Starlink Protocol Performance
APNIC394 views
PORTFOLIO 1 (Bret Michael Pepito).pdf by brejess0410
PORTFOLIO 1 (Bret Michael Pepito).pdfPORTFOLIO 1 (Bret Michael Pepito).pdf
PORTFOLIO 1 (Bret Michael Pepito).pdf
brejess04109 views
Marketing and Community Building in Web3 by Federico Ast
Marketing and Community Building in Web3Marketing and Community Building in Web3
Marketing and Community Building in Web3
Federico Ast14 views
How to think like a threat actor for Kubernetes.pptx by LibbySchulze1
How to think like a threat actor for Kubernetes.pptxHow to think like a threat actor for Kubernetes.pptx
How to think like a threat actor for Kubernetes.pptx
LibbySchulze15 views
Building trust in our information ecosystem: who do we trust in an emergency by Tina Purnat
Building trust in our information ecosystem: who do we trust in an emergencyBuilding trust in our information ecosystem: who do we trust in an emergency
Building trust in our information ecosystem: who do we trust in an emergency
Tina Purnat109 views

I Don't Care About Security (And Neither Should You)

  • 1. I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
  • 3. I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
  • 4. I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) ABOUT ME ▸ Passionate about tech ▸ Technical Evangelist at Auth0, Author at Udemy, Egghead @joel__lord
  • 9. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) OAUTH FLOWS
  • 10. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) OAUTH FLOWS
  • 11. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) OAUTH FLOWS
  • 12. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) OAUTH FLOWS
  • 14. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) OAUTH FLOWS
  • 15. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) OAUTH FLOWS
  • 16. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) OAUTH FLOWS
  • 17. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) OAUTH FLOWS
  • 18. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) OAUTH FLOWS
  • 19. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) OAUTH FLOWS
  • 22. I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) TRADITIONAL APPLICATIONS ▸ Browser requests a login page
  • 23. I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) TRADITIONAL APPLICATIONS ▸ Browser requests a login page
  • 24. I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) TRADITIONAL APPLICATIONS ▸ Browser requests a login page
  • 25. I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) TRADITIONAL APPLICATIONS ▸ Browser requests a login page ▸ Server validates on the database
  • 26. I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) TRADITIONAL APPLICATIONS ▸ Browser requests a login page ▸ Server validates on the database 👍
  • 27. 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 👍
  • 28. "
  • 29. I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) WHAT’S WRONG WITH TRADITIONAL AUTHENTICATION? ▸ Multiple platforms connecting to your application
  • 30. I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) WHAT’S WRONG WITH TRADITIONAL AUTHENTICATION? ▸ Multiple platforms connecting to your application ▸ Tightly coupled
  • 31. 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
  • 32. 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
  • 34. OAUTH GRANTS I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
  • 35. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) AUTHORIZATION CODE
  • 36. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) AUTHORIZATION CODE
  • 37. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) AUTHORIZATION CODE
  • 38. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) AUTHORIZATION CODE 👍
  • 39. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) AUTHORIZATION CODE 👍
  • 40. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) AUTHORIZATION CODE
  • 41. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) AUTHORIZATION CODE
  • 42. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) AUTHORIZATION CODE
  • 43. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) AUTHORIZATION CODE
  • 44. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) AUTHORIZATION CODE
  • 45. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) AUTHORIZATION CODE ⛔
  • 46. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) AUTHORIZATION CODE
  • 47. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) AUTHORIZATION CODE
  • 48. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) IMPLICIT GRANT
  • 49. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) IMPLICIT GRANT
  • 50. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) IMPLICIT GRANT
  • 51. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) IMPLICIT GRANT
  • 52. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) IMPLICIT GRANT 👍
  • 53. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) IMPLICIT GRANT
  • 54. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) IMPLICIT GRANT
  • 55. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) IMPLICIT GRANT
  • 56. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) IMPLICIT GRANT 👍
  • 57. TOKENS 101 I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU)
  • 58. @joel__lord #PyConSK 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
  • 59. 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
  • 60. 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
  • 61. I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) TOKENS ▸ WS Federated ▸ SAML ▸ Custom stuff ▸ …
  • 62. I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) TOKENS ▸ WS Federated ▸ SAML ▸ Custom stuff ▸ JWT
  • 63. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) JSON WEB TOKENS (JWTS, NOT JOTS) eyJhbGciOiJIUzI1NiIsInR5cC I6IkpXVCJ9.eyJzdWIiOiIxMj M0NTY3ODkwIiwibmFtZSI6I kpvZWwgTG9yZCIsImFkbWl uIjp0cnVlLCJzY29wZSI6InBv c3RzOnJlYWQgcG9zdHM6 d3JpdGUifQ.XesR- pKdlscHfUwoKvHnACqfpe2 ywJ6t1BJKsq9rEcg
  • 64. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) JSON WEB TOKENS (JWTS, NOT JOTS) ▸ Header ▸ Payload ▸ Signature eyJhbGciOiJIUzI1NiIsInR5cC I6IkpXVCJ9.eyJzdWIiOiIxMj M0NTY3ODkwIiwibmFtZSI6I kpvZWwgTG9yZCIsImFkbWl uIjp0cnVlLCJzY29wZSI6InBv c3RzOnJlYWQgcG9zdHM6 d3JpdGUifQ.XesR- pKdlscHfUwoKvHnACqfpe2 ywJ6t1BJKsq9rEcg
  • 65. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) JSON WEB TOKENS (JWTS, NOT JOTS) ▸ Header eyJhbGciOiJIUzI1NiIsInR5cC I6IkpXVCJ9.eyJzdWIiOiIxMj M0NTY3ODkwIiwibmFtZSI6I kpvZWwgTG9yZCIsImFkbWl uIjp0cnVlLCJzY29wZSI6InBv c3RzOnJlYWQgcG9zdHM6 d3JpdGUifQ.XesR- pKdlscHfUwoKvHnACqfpe2 ywJ6t1BJKsq9rEcg
  • 66. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) JSON WEB TOKENS (JWTS, NOT JOTS) ▸ Header eyJhbGciOiJIUzI1NiIsInR5cC I6IkpXVCJ9
  • 67. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) JSON WEB TOKENS (JWTS, NOT JOTS) ▸ Header atob(‘eyJhbGciOiJIUzI1NiIsI nR5cCI6IkpXVCJ9’);
  • 68. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) JSON WEB TOKENS (JWTS, NOT JOTS) ▸ Header { "alg": "HS256", "typ": "JWT" }
  • 69. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) JSON WEB TOKENS (JWTS, NOT JOTS) ▸ Payload eyJhbGciOiJIUzI1NiIsInR5cC I6IkpXVCJ9.eyJzdWIiOiIxMj M0NTY3ODkwIiwibmFtZSI6I kpvZWwgTG9yZCIsImFkbWl uIjp0cnVlLCJzY29wZSI6InBv c3RzOnJlYWQgcG9zdHM6 d3JpdGUifQ.XesR- pKdlscHfUwoKvHnACqfpe2 ywJ6t1BJKsq9rEcg
  • 70. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) JSON WEB TOKENS (JWTS, NOT JOTS) ▸ Payload eyJzdWIiOiIxMjM0NTY3OD kwIiwibmFtZSI6IkpvZWwgT G9yZCIsImFkbWluIjp0cnVlL CJzY29wZSI6InBvc3RzOnJl YWQgcG9zdHM6d3JpdGUi fQ
  • 71. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) JSON WEB TOKENS (JWTS, NOT JOTS) ▸ Payload atob(‘eyJzdWIiOiIxMjM0NT Y3ODkwIiwibmFtZSI6IkpvZ WwgTG9yZCIsImFkbWluIjp 0cnVlLCJzY29wZSI6InBvc3R zOnJlYWQgcG9zdHM6d3J pdGUifQ’);
  • 72. @joel__lord #PyConSK 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" }
  • 73. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) JSON WEB TOKENS (JWTS, NOT JOTS) ▸ Signature XesR- pKdlscHfUwoKvHnACqfpe2 ywJ6t1BJKsq9rEcg
  • 74. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) JSON WEB TOKENS (JWTS, NOT JOTS) ▸ Signature hmacsha256( `${header}.${payload}`, SECRET_KEY );
  • 75. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) JSON WEB TOKENS (JWTS, NOT JOTS) Image: https://jwt.io
  • 77. Auth Server API from flask import Flask, jsonify import os from api_auth_header import AuthError, requires_auth from flask_cors import CORS from randopeep import clickbait app = Flask(__name__) CORS(app) @app.errorhandler(AuthError) def handle_auth_error(ex): response = jsonify(ex.error) response.status_code = ex.status_code return response @app.route("/headline") def headline(): return clickbait.headline() @app.route("/protected/headline") @requires_auth def protected_headline(): return clickbait.headline("Joel Lord") if __name__ == '__main__': port = int(os.environ.get('PORT', 8888)) print("Auth server started on port %d " % port) app.run(host='0.0.0.0', port=port, debug=True) import os from flask import Flask, request, abort, redirect from jose import jwt app = Flask(__name__) users = [{"id": 1, "username": "joellord", "password": "joellord"}] @app.route("/login", methods=["GET"]) def login_form(): print("Serve form") callback_url = request.args.get("callback") return "<form method='post'>...</form>" @app.route("/login", methods=["POST"]) def process_login(): username = request.form.get("username") password = request.form.get("password") callback = request.form.get("callback") if (username is "" or password is ""): abort(400) user = next((user for user in users if user["username"] == username and user["password"] == password), False) if (user == False): abort(401) print(user) token = jwt.encode({ "sub": str(user["id"]), "username": user["username"], "scope": "dostuff", "aud": "my-random-clickbait-api", "iss": "my-small-auth-server" }, "my-super-secret-key", algorithm='HS256') redirect_url = callback + "#access_token=" + token return redirect(redirect_url, code=302) if __name__ == '__main__': port = int(os.environ.get('PORT', 8080)) print("Auth server started on port %d " % port) app.run(host='0.0.0.0', port=port, debug=True) ●
  • 78. @joel__lord #PyConSK Auth Server import os from flask import Flask, request, abort, redirect from jose import jwt app = Flask(__name__) // ...
  • 79. @joel__lord #PyConSK Auth Server import os from flask import Flask, request, abort, redirect from jose import jwt app = Flask(__name__) // ...
  • 80. @joel__lord #PyConSK Auth Server import os from flask import Flask, request, abort, redirect from jose import jwt app = Flask(__name__) // ...
  • 81. @joel__lord #PyConSK Auth Server import os from flask import Flask, request, abort, redirect from jose import jwt app = Flask(__name__) // ...
  • 82. @joel__lord #PyConSK Auth Server # imports ... users = [ { "id": 1, "username": "joellord", "password": "joellord" }, { "id": 2, "username": "guest", "password": "guest" } ]
  • 83. @joel__lord #PyConSK Auth Server # imports ... users = [...] @app.route("/login", methods=["GET"]) def login_form(): # login form @app.route("/login", methods=["POST"]) def process_login(): # handle request
  • 84. @joel__lord #PyConSK Auth Server # imports ... users = [...] @app.route("/login", methods=["GET"]) def login_form(): # login form @app.route("/login", methods=["POST"]) def process_login(): # handle request
  • 85. @joel__lord #PyConSK Auth Server @app.route("/login", methods=["GET"]) def login_form(): print("Serve form") callback_url = request.args.get("callback") return "<form method='post'><input type=hidden name=callback value='" + callback_url + "'><input type=text name=username /><input type=text name=password /><input type=submit></form>"
  • 86. @joel__lord #PyConSK Auth Server @app.route("/login", methods=["GET"]) def login_form(): print("Serve form") callback_url = request.args.get("callback") return "<form method='post'><input type=hidden name=callback value='" + callback_url + "'><input type=text name=username /><input type=text name=password /><input type=submit></form>"
  • 87. @joel__lord #PyConSK Auth Server @app.route("/login", methods=["POST"]) def process_login(): username = request.form.get("username") password = request.form.get("password") callback = request.form.get("callback") if (username is "" or password is ""): abort(400) user = next((user for user in users if user["username"] == username and user["password"] == password), False) if (user == False): abort(401) token = jwt.encode({ "sub": str(user["id"]), "username": user["username"], "scope": "dostuff", "aud": "my-random-clickbait-api", "iss": "my-small-auth-server" }, "my-super-secret-key", algorithm=‘HS256') redirect_url = callback + "#access_token=" + token return redirect(redirect_url, code=302)
  • 88. @joel__lord #PyConSK Auth Server @app.route("/login", methods=["POST"]) def process_login(): username = request.form.get("username") password = request.form.get("password") callback = request.form.get("callback") if (username is "" or password is ""): abort(400) user = next((user for user in users if user["username"] == username and user["password"] == password), False) if (user == False): abort(401) token = jwt.encode({ "sub": str(user["id"]), "username": user["username"], "scope": "dostuff", "aud": "my-random-clickbait-api", "iss": "my-small-auth-server" }, "my-super-secret-key", algorithm=‘HS256') redirect_url = callback + "#access_token=" + token return redirect(redirect_url, code=302)
  • 89. @joel__lord #PyConSK Auth Server @app.route("/login", methods=["POST"]) def process_login(): username = request.form.get("username") password = request.form.get("password") callback = request.form.get("callback") if (username is "" or password is ""): abort(400) user = next((user for user in users if user["username"] == username and user["password"] == password), False) if (user == False): abort(401) token = jwt.encode({ "sub": str(user["id"]), "username": user["username"], "scope": "dostuff", "aud": "my-random-clickbait-api", "iss": "my-small-auth-server" }, "my-super-secret-key", algorithm=‘HS256') redirect_url = callback + "#access_token=" + token return redirect(redirect_url, code=302)
  • 90. @joel__lord #PyConSK Auth Server @app.route("/login", methods=["POST"]) def process_login(): username = request.form.get("username") password = request.form.get("password") callback = request.form.get("callback") if (username is "" or password is ""): abort(400) user = next((user for user in users if user["username"] == username and user["password"] == password), False) if (user == False): abort(401) token = jwt.encode({ "sub": str(user["id"]), "username": user["username"], "scope": "dostuff", "aud": "my-random-clickbait-api", "iss": "my-small-auth-server" }, "my-super-secret-key", algorithm=‘HS256') redirect_url = callback + "#access_token=" + token return redirect(redirect_url, code=302)
  • 91. @joel__lord #PyConSK Auth Server @app.route("/login", methods=["POST"]) def process_login(): username = request.form.get("username") password = request.form.get("password") callback = request.form.get("callback") if (username is "" or password is ""): abort(400) user = next((user for user in users if user["username"] == username and user["password"] == password), False) if (user == False): abort(401) token = jwt.encode({ "sub": str(user["id"]), "username": user["username"], "scope": "dostuff", "aud": "my-random-clickbait-api", "iss": "my-small-auth-server" }, "my-super-secret-key", algorithm=‘HS256') redirect_url = callback + "#access_token=" + token return redirect(redirect_url, code=302)
  • 92. @joel__lord #PyConSK Auth Server @app.route("/login", methods=["POST"]) def process_login(): username = request.form.get("username") password = request.form.get("password") callback = request.form.get("callback") if (username is "" or password is ""): abort(400) user = next((user for user in users if user["username"] == username and user["password"] == password), False) if (user == False): abort(401) token = jwt.encode({ "sub": str(user["id"]), "username": user["username"], "scope": "dostuff", "aud": "my-random-clickbait-api", "iss": "my-small-auth-server" }, "my-super-secret-key", algorithm=‘HS256') redirect_url = callback + "#access_token=" + token return redirect(redirect_url, code=302)
  • 93. @joel__lord #PyConSK Auth Server # imports ... users = [...] @app.route("/login", methods=["GET"]) # ... @app.route("/login", methods=["POST"]) # ... if __name__ == '__main__': port = int(os.environ.get('PORT', 8080)) print("Auth server started on port %d " % port) app.run(host='0.0.0.0', port=port, debug=True)
  • 94. @joel__lord #PyConSK API from flask import Flask, jsonify import os from api_auth_header import AuthError, requires_auth from flask_cors import CORS from randopeep import clickbait app = Flask(__name__) CORS(app)
  • 95. @joel__lord #PyConSK API # imports @app.route("/headline") def headline(): return clickbait.headline() @app.route("/protected/headline") @requires_auth def protected_headline(): return clickbait.headline("Joel Lord")
  • 96. @joel__lord #PyConSK API # imports @app.route("/headline") def headline(): return clickbait.headline() @app.route("/protected/headline") @requires_auth def protected_headline(): return clickbait.headline("Joel Lord")
  • 97. @joel__lord #PyConSK API # imports @app.route("/headline") def headline(): return clickbait.headline() @app.route("/protected/headline") @requires_auth def protected_headline(): return clickbait.headline("Joel Lord")
  • 98. @joel__lord #PyConSK API # imports … @app.route("/headline") def headline(): # … @app.route("/protected/headline") @requires_auth def protected_headline(): # … if __name__ == '__main__': port = int(os.environ.get('PORT', 8888)) print("Auth server started on port %d " % port) app.run(host='0.0.0.0', port=port, debug=True)
  • 99. @joel__lord #PyConSK API (@requires_authfrom functools import wraps from jose import jwt from flask import _request_ctx_stack, request class AuthError(Exception): def __init__(self, error, status_code): self.error = error self.status_code = status_code def get_token_auth_header(): # Get and validate the token... return token def requires_auth(f): @wraps(f) def decorated(*args, **kwargs): token = get_token_auth_header() try: payload = jwt.decode(token, "my-super-secret-key", algorithms="HS256") except jwt.ExpiredSignatureError: raise AuthError({"code": "token_expired", "description": "Token is expired"}, 401) # Also check claims and headers _request_ctx_stack.top.current_user = payload return f(*args, **kwargs) return decorated
  • 100. @joel__lord #PyConSK API (@requires_authfrom functools import wraps from jose import jwt from flask import _request_ctx_stack, request class AuthError(Exception): def __init__(self, error, status_code): self.error = error self.status_code = status_code def get_token_auth_header(): # Get and validate the token... return token def requires_auth(f): @wraps(f) def decorated(*args, **kwargs): token = get_token_auth_header() try: payload = jwt.decode(token, "my-super-secret-key", algorithms="HS256") except jwt.ExpiredSignatureError: raise AuthError({"code": "token_expired", "description": "Token is expired"}, 401) # Also check claims and headers _request_ctx_stack.top.current_user = payload return f(*args, **kwargs) return decorated
  • 101. @joel__lord #PyConSK API (@requires_authfrom functools import wraps from jose import jwt from flask import _request_ctx_stack, request class AuthError(Exception): def __init__(self, error, status_code): self.error = error self.status_code = status_code def get_token_auth_header(): # Get and validate the token... return token def requires_auth(f): @wraps(f) def decorated(*args, **kwargs): token = get_token_auth_header() try: payload = jwt.decode(token, "my-super-secret-key", algorithms="HS256") except jwt.ExpiredSignatureError: raise AuthError({"code": "token_expired", "description": "Token is expired"}, 401) # Also check claims and headers _request_ctx_stack.top.current_user = payload return f(*args, **kwargs) return decorated
  • 102. @joel__lord #PyConSK API (@requires_authfrom functools import wraps from jose import jwt from flask import _request_ctx_stack, request class AuthError(Exception): def __init__(self, error, status_code): self.error = error self.status_code = status_code def get_token_auth_header(): # Get and validate the token... return token def requires_auth(f): @wraps(f) def decorated(*args, **kwargs): token = get_token_auth_header() try: payload = jwt.decode(token, "my-super-secret-key", algorithms="HS256") except jwt.ExpiredSignatureError: raise AuthError({"code": "token_expired", "description": "Token is expired"}, 401) # Also check claims and headers _request_ctx_stack.top.current_user = payload return f(*args, **kwargs) return decorated
  • 104. I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) DEAR DEMO GODS, PLEASE LET THIS WORK ▸ https://github.com/joellord/ secure-spa-auth0
  • 113. INTRODUCING OPEN ID CONNECT!
  • 114. @joel__lord #PyConSK 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
  • 115. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) OPEN ID CONNECT ▸ Uses the “scope” to fetch info ▸ openid ▸ Profile ▸ email ▸ address ▸ phone
  • 116. @joel__lord #PyConSK I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) OPEN ID CONNECT Image: https://openidconnect.net
  • 118. @joel__lord #PyConSK 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
  • 119. @joel__lord joellord I DON’T CARE ABOUT SECURITY (AND NEITHER SHOULD YOU) PyCon SK March 23rd, 2019 THANK YOU !
  • 120. TEXT
  • 121. TEXT