Used for authentication/authorization such as:
Imagine app with many features + servers + engineers:
Auth Server has secrets; needs security + maintenance
Base64 encoded header.payload.signature:
HEADER: { "alg": "EdDSA", "typ": "JWT" }
PAYLOAD: {"sub": "a", "name": "arbitrary data", "iat": 1 }
SIGNATURE: SU6aXJ0YbH7Vg1jROpQfvnhn98Rt9zBeS7-c5O9jH-L
L5mQqMMFq61eZjf0tLLqExm-dckRUNa3-qT7R2SKmCw
ENCODED JWT: eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9
.eyJzdWIiOiJhIiwibmFtZSI6ImIiLCJpYXQiOjF9
.SU6aXJ0YbH7Vg1jROpQfvnhn98Rt9zBeS7-c5O9jH-L
L5mQqMMFq61eZjf0tLLqExm-dckRUNa3-qT7R2SKmCw
Signed using EdDSA with secret key:
MC4CAQAwBQYDK2VwBCIEIC+D6rD2YbXtV0ccR3smoR0ynhVuyyqvplFLbQWDdAtn
pyjwt
)
@app.route('/support/urgent') # built-in flask decorator
@requires_jwt # custom decorator to validate JWT
@jwt_claims(['paid_support']) # ensures token is for premium user
@jwt_iat(datetime.timedelta(hours=24)) # ensure recent token
def support_urgent():
... # process ending support request
pyjwt
)
@app.route('/support/urgent') # built-in flask decorator
@requires_jwt # custom decorator to validate JWT
@jwt_claims(['paid_support']) # ensures token is for premium user
@jwt_iat(datetime.timedelta(hours=24)) # ensure recent token
def support_urgent():
... # process ending support request
pyjwt
)
@app.route('/support/urgent') # built-in flask decorator
@requires_jwt # custom decorator to validate JWT
@jwt_claims(['paid_support']) # ensures token is for premium user
@jwt_iat(datetime.timedelta(hours=24)) # ensure recent token
def support_urgent():
... # process ending support request
pyjwt
)
@app.route('/support/urgent') # built-in flask decorator
@requires_jwt # custom decorator to validate JWT
@jwt_claims(['paid_support']) # ensures token is for premium user
@jwt_iat(datetime.timedelta(hours=24)) # ensure recent token
def support_urgent():
... # process ending support request
pyjwt
)
@app.route('/support/urgent') # built-in flask decorator
@requires_jwt # custom decorator to validate JWT
@jwt_claims(['paid_support']) # ensures token is for premium user
@jwt_iat(datetime.timedelta(hours=24)) # ensure recent token
def support_urgent():
... # process ending support request
@requires_jwt
def requires_jwt(func):
@wraps(func)
def decorated(*args, **kwargs):
token = request.headers.get("Authorization").split(" ")[1]
if not token:
return 'missing token', 401 # if no token return error
try:
g.decoded_jwt = jwt.decode(
token, algorithms=['EdDSA'],
key=current_app.config['JWT_KEY']) # public key
return func(*args, **kwargs)
except Exception as problem:
return f'{problem=}', 401 # return 401 or other error code
return decorated
@jwt_claims
def jwt_claims(claims_list: typing.Sequence[str]):
def make_decorator(func):
@wraps(func)
def decorated(*args, **kwargs):
missing = [c for c in claims_list
if not g.decoded_jwt.get(c)]
if missing:
return f'Missing claims: {missing}', 401
return func(*args, **kwargs)
return decorated
return make_decorator
{"sub": "Alice", "proxy": "Bob"}
{"sub": "Alice", "proxy": "Bob"}
@APP.route("/issue")
@requires_jwt
def issue():
"Example route to create an issue."
user = g.decoded_jwt.get('proxy', g.decoded_jwt.get('sub'))
msg = f'Created issue assigned to {user}.'
# ... Create the actual issue here
return msg
{"sub": "Alice", "proxy": "Bob"}
@APP.route("/issue")
@requires_jwt
def issue():
"Example route to create an issue."
user = g.decoded_jwt.get('proxy', g.decoded_jwt.get('sub'))
msg = f'Created issue assigned to {user}.'
# ... Create the actual issue here
real_user = g.decoded_jwt['sub']
if real_user != user:
msg += f'\n{real_user} acted on behalf of {user}'
return msg
alg
field or limit possibilities
algorithms=['EdDSA']
kid
, jku
, jwk
, etc.{alg: "EdDSA", jku: "https://good.com/pk.json"}
{alg: "EdDSA", jku: "https://bad.com/pk.json"}
{alg: "EdDSA", jku: "https://good.com/pk.json"}
{alg: "HS256", jku: "https://good.com/pk.json"}
e.g., NGINX can verify before passing to app server
nginx
directory on github.com/aocks/ox_jwt