What is json web token?
JSON Web Token (JWT) is like a digital ID card for websites and apps. It’s often issued after a successful login and securely tells a website about the user and their accessibility
Secure Cookies also do the same thing. but there is some core differences
Feature | JSON Web Token (JWT) | Secure Cookie |
---|---|---|
Storage | Client-side (browser) | Server-side (browser stores only a key) |
Contents | User data, authorization info, expiration (self-contained) | Typically just a session ID |
CSRF Vulnerability | Less prone | More susceptible |
Scalability | Excellent | Potentially limiting under heavy loads |
State | Stateless | Stateful |
Ideal Use Cases | APIs, microservices, reduced server-side storage | Traditional web apps, CSRF mitigated |
now create our json web token
The structure
Part | What it Contains | Encoded Format |
---|---|---|
Header | Information about the type of token and the signing algorithm used. | Base64Url-encoded JSON object |
Payload | The actual data (claims) about the user, like their ID, roles, or issued-at time. | Base64Url-encoded JSON object |
Signature | A cryptographic signature that helps to verify the token hasn’t been tampered with. | Generated by combining the encoded header, encoded payload, and a secret key. |
Now create our json webtoken with pyhon
We will use hashlib and hmac library for this
As json web token contains three part. We will add this three part in the token. Also we will apply encode decode. So only we will able to know whats inside token payload.
import base64
import hashlib
import hmac
import json
import os
import secrets
import time
import re
# Define constants
SECRET_KEY = '123'
EXPECTED_ISSUER = 'expected_issuer'
EXPECTED_AUDIENCE = 'expected_audience'
# Define regular expression pattern for issuer name
ISSUER_PATTERN = r'^[a-zA-Z0-9_-]+$'
#ISSUER_PATTERN = r'^[a-z0-9_-]{5,20}$' # 5-20 characters, lowercase letters, numbers, underscore, hyphen
def encode_jwt(payload, expiration_time):
header = {'typ': 'JWT', 'alg': 'HS256'}
header_json = json.dumps(header, separators=(',', ':')).encode('utf-8')
# Include expiration time in the payload
payload['exp'] = int(time.time()) + expiration_time
payload_json = json.dumps(payload, separators=(',', ':')).encode('utf-8')
encoded_header = base64.urlsafe_b64encode(header_json).decode('utf-8').rstrip('=')
encoded_payload = base64.urlsafe_b64encode(payload_json).decode('utf-8').rstrip('=')
signature = hmac.new(SECRET_KEY.encode('utf-8'), f"{encoded_header}.{encoded_payload}".encode('utf-8'), hashlib.sha256)
encoded_signature = base64.urlsafe_b64encode(signature.digest()).decode('utf-8').rstrip('=')
jwt_token = f"{encoded_header}.{encoded_payload}.{encoded_signature}"
return jwt_token
def decode_jwt(jwt_token):
# Check if the token contains three parts
parts = jwt_token.split('.')
if len(parts) != 3:
return {'error': 'Invalid token format'}
encoded_header, encoded_payload, encoded_signature = parts
# Validate and decode the payload
try:
payload = base64.urlsafe_b64decode(encoded_payload + '=' * (-len(encoded_payload) % 4)).decode('utf-8')
decoded_payload = json.loads(payload)
except (json.JSONDecodeError, UnicodeDecodeError):
return {'error': 'Invalid payload format'}
# Validate expiration time
if 'exp' in decoded_payload:
if not isinstance(decoded_payload['exp'], int):
return {'error': 'Expiration time must be an integer'}
if decoded_payload['exp'] < int(time.time()):
return {'error': 'Token expired'}
# Validate issuer using regular expression
if 'iss' in decoded_payload:
issuer = decoded_payload['iss']
if not re.match(ISSUER_PATTERN, issuer):
return {'error': 'Invalid issuer name format'}
if issuer != EXPECTED_ISSUER:
return {'error': 'Invalid issuer'}
# Validate audience
if 'aud' in decoded_payload:
if not isinstance(decoded_payload['aud'], str):
return {'error': 'Audience must be a string'}
if EXPECTED_AUDIENCE not in decoded_payload['aud']:
return {'error': 'Invalid audience'}
# Validate signature
expected_signature = base64.urlsafe_b64encode(hmac.new(SECRET_KEY.encode('utf-8'), f"{encoded_header}.{encoded_payload}".encode('utf-8'), hashlib.sha256).digest()).decode('utf-8').rstrip('=')
if not secrets.compare_digest(encoded_signature, expected_signature):
return {'error': 'Invalid signature'}
return decoded_payload
payload = {'user_id': 1, 'username': 'Our UserName', 'iss': 'expected_issuer', 'aud': 'expected_audience'}
# Set expiration time in seconds (e.g., 1 hour)
expiration_time = 3600
# Encoding the payload into a JWT with expiration time
jwt_token = encode_jwt(payload, expiration_time)
print("Encoded JWT with expiration:", jwt_token)
# Decoding the JWT to retrieve the payload
decoded_payload = decode_jwt(jwt_token)
print("Decoded Payload:", decoded_payload)
Simple implementation in flask
from flask import Flask, request, jsonify, render_template, redirect, url_for, make_response
import base64
import hashlib
import hmac
import json
import datetime
app = Flask(__name__)
# Secret key for encoding/decoding JWT
SECRET_KEY = '234231465457'
def encode_jwt(payload):
header = {'typ': 'JWT', 'alg': 'HS256'}
header_json = json.dumps(header, separators=(',', ':')).encode('utf-8')
payload_json = json.dumps(payload, separators=(',', ':')).encode('utf-8')
# Base64 encoding header and payload
encoded_header = base64.urlsafe_b64encode(header_json).decode('utf-8')
encoded_payload = base64.urlsafe_b64encode(payload_json).decode('utf-8')
# Creating signature
signature = hmac.new(SECRET_KEY.encode('utf-8'), f"{encoded_header}.{encoded_payload}".encode('utf-8'), hashlib.sha256)
encoded_signature = base64.urlsafe_b64encode(signature.digest()).decode('utf-8')
# Combining all parts to form JWT
jwt_token = f"{encoded_header}.{encoded_payload}.{encoded_signature}"
return jwt_token
def decode_jwt(jwt_token):
encoded_header, encoded_payload, encoded_signature = jwt_token.split('.')
payload = base64.urlsafe_b64decode(encoded_payload.encode('utf-8')).decode('utf-8')
return json.loads(payload)
@app.route('/')
def home():
# Check if user is already logged in
jwt_token = request.cookies.get('jwt_token')
if jwt_token:
try:
payload = decode_jwt(jwt_token)
print(payload)
return redirect(url_for('dashboard'))
except:
pass # Invalid token, proceed to login
return render_template('index.html')
@app.route('/login', methods=['POST'])
def login():
# Dummy authentication
if request.form['username'] == 'username' and request.form['password'] == 'password':
# Create JWT token with payload
payload = {'username': request.form['username']}
jwt_token = encode_jwt(payload)
# Set the JWT token in a cookie
response = make_response(redirect(url_for('dashboard')))
response.set_cookie('jwt_token', jwt_token, httponly=True, expires=datetime.datetime.now() + datetime.timedelta(minutes=30))
return response
else:
return jsonify({'message': 'Invalid username or password'}), 401
@app.route('/dashboard')
def dashboard():
# Retrieve JWT token from the cookie
jwt_token = request.cookies.get('jwt_token')
if jwt_token:
try:
payload = decode_jwt(jwt_token)
return render_template('dashboard.html', username=payload['username'])
except:
return jsonify({'message': 'Invalid token'}), 401
else:
return jsonify({'message': 'Token missing'}), 401
@app.route('/logout')
def logout():
# Clear the JWT token from the cookie
response = make_response(redirect(url_for('home')))
response.set_cookie('jwt_token', '', expires=0)
return response
if __name__ == '__main__':
app.run(debug=True)
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login</title>
</head>
<body>
<h1>Login</h1>
<p>our example code username is username and password is password</p>
<form action="/login" method="post">
<label for="username">Username:</label><br>
<input type="text" id="username" name="username"><br>
<label for="password">Password:</label><br>
<input type="password" id="password" name="password"><br><br>
<input type="submit" value="Submit">
</form>
</body>
</html>
dashboard.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Protected Dashboard</title>
</head>
<body>
<h1>Welcome, {{ username }}!</h1>
<p>This is your protected dashboard.</p>
<p>Feel free to explore.</p>
<a href="/logout">Logout</a>
</body>
</html>