JWT — JSON Web Tokens
What are JSON Web Tokens?
JWTs are JSON snippets that are used for provability and authorisation to allow a user into different parts of a website, for instance, allowing one user to hit a specific endpoint whereas others should not be able to access it.
The flow is as follows:
So the user must first login with their credentials, and then the server will present a JWT back that has been signed by a key that will be held on the webserver (only the admin should know this — as it’s a secret key).
These three parts are separated by .
like so:
eyJhbGciOiJIUzI1NiJ9.eyJhZG1pbiI6InRydWUifQ.QuQw_6tejPckAoTfuJ4-XcY-7ElK9MtWsjYEudBd1SA
eyJhbGciOiJIUzI1NiJ9
= header
eyJhZG1pbiI6InRydWUifQ
= payload
QuQw_6tejPckAoTfuJ4-XcY-7ElK9MtWsjYEudBd1SA
= Signature
🐍Python code: 🐍
Generate a symmetric key:
from jwcrypto import jwt, jwk
key = jwk.JWK(generate='oct', size=256)
print(key.export())
Import a previously generated symmetric key
key = jwk.JWK.from_json('{"k":"CzRJ_eQw7fDz6pFGQU4161LZlV_l0Jp7MRi36gM-CBI","kty":"oct"}')
Creating a token with claims
Token = jwt.JWT(header={"alg": "HS256"},
claims={"admin": "true"})
Token.make_signed_token(key)
print(Token.serialize())
If you have two separate web apps on two different servers, you can store the jwt on the client, and then have the secret that verifies the jwt on both of the servers, and this will allow the user to navigate between both applications without continually logging in
Installed flask
pip install flask
Create the following program:
from flask import Flask
from flask_jwt import JWT, jwt_required, current_identity
from werkzeug.security import safe_str_cmpclass User(object):
def __init__(self, id, username, password):
self.id = id
self.username = username
self.password = password def __str__(self):
return "User(id='%s')" % self.idusers = [
User(1, 'matthew', 'testing123'),
User(2, 'user2', 'abcxyz'),
]username_table = {u.username: u for u in users}
userid_table = {u.id: u for u in users}def authenticate(username, password):
user = username_table.get(username, None)
if (user and safe_str_cmp(user.password.encode('utf-8'),
password.encode('utf-8'))):
return userdef identity(payload):
user_id = payload['identity']
return userid_table.get(user_id, None)app = Flask(__name__)
app.debug = True
app.config['SECRET_KEY'] = 'testing123'jwt = JWT(app, authenticate, identity)@app.route('/protected')
@jwt_required()
def protected():
return "you unlocked the secret content!"if __name__ == '__main__':
app.run(host='0.0.0.0')
Then we’ll go to postman and create the following request:
This should return you the your own jwt token, like so:
Now you want to try and get the /protected api content, so we will need to authenticate ourselves with the jwt token. Normally, this would be done through the browser. We would have the JWT token saved in a session or cookie. As we’re using postman, we shall add a new header with key: Authorization and value of jwt your_token like shown in the screenshot below:
Something interesting to do is the follow, go to https://jwt.io/#debugger
Now, paste your token in here, and the result should be something similar to the following:
The payload means:
- identity:1 — this is the user that we defined in our python flask program.
- iat = issued at
- nbf = not to be used before
- exp = expiration
Due to the nature of JWT tokens, we can generate our own, provided we have our secret, which we have!
pip install jwt
jwt is a standardised format, so if we add the same headers as seen in the jwt.io website, and copy the same payload (the only unique part of our payload is the identity part. that means we can change the values of iat, nbf, and exp as these are meta-data tags that are added by jwt) Just make sure that your values make sense, for instance your expiration is in the future.
And voila! 🥳