Working with APIs Using JWT (JSON Web Tokens) #
Welcome back to our programming tutorial series! Today, we’ll explore how to use JSON Web Tokens (JWT) for authenticating and authorizing API requests. JWT is a widely-used standard for securing web APIs, providing a secure way to transmit information between parties.
What Is a JWT? #
A JSON Web Token (JWT) is a compact, URL-safe token that represents a set of claims. JWTs are commonly used for authentication, allowing a client to prove its identity and access protected resources.
A JWT is made up of three parts, separated by periods (.
):
- Header: Specifies the token type and signing algorithm.
- Payload: Contains the claims (such as user information).
- Signature: Verifies the authenticity of the token.
Example of a JWT: #
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
How JWT Works #
When a user logs in, the server generates a JWT and sends it to the client. The client stores the token (typically in localStorage or a cookie) and sends it along with each request to authenticate the user.
- Client requests a JWT by providing valid credentials (e.g., username and password).
- Server generates a JWT and sends it back to the client.
- Client sends the JWT with each request to access protected resources.
- Server verifies the JWT on each request to ensure that the token is valid.
Decoding a JWT #
You can decode a JWT to view its contents using any base64 decoder or libraries like pyjwt
. Here’s how you can decode the header and payload of a JWT in Python:
import jwt
token = "your_jwt_token_here"
decoded = jwt.decode(token, options={"verify_signature": False})
print(decoded)
This code will print the decoded contents of the JWT (without verifying the signature).
Creating and Verifying JWTs in Python #
You can create and verify JWTs in Python using the pyjwt
library. First, install it using pip
:
pip install pyjwt
Creating a JWT: #
import jwt
import datetime
secret_key = "your_secret_key"
payload = {
"user_id": 123,
"name": "John Doe",
"exp": datetime.datetime.utcnow() + datetime.timedelta(minutes=30)
}
token = jwt.encode(payload, secret_key, algorithm="HS256")
print(token)
In this example, we create a JWT with a payload containing the user ID, name, and expiration time.
Verifying a JWT: #
import jwt
secret_key = "your_secret_key"
token = "your_jwt_token_here"
try:
decoded = jwt.decode(token, secret_key, algorithms=["HS256"])
print(decoded) # Outputs the decoded payload if the token is valid
except jwt.ExpiredSignatureError:
print("Token has expired.")
except jwt.InvalidTokenError:
print("Invalid token.")
This code verifies the JWT using the secret key. If the token is valid, it decodes the payload; otherwise, it handles errors like expired or invalid tokens.
Using JWT in API Requests #
Once you have a JWT, you can use it to authenticate API requests by including it in the Authorization
header with the Bearer
scheme.
Example: #
import requests
token = "your_jwt_token_here"
url = "https://api.example.com/protected-data"
headers = {"Authorization": f"Bearer {token}"}
response = requests.get(url, headers=headers)
print(response.json())
In this example, the JWT is sent with the API request, allowing the server to verify the client’s identity and grant access to protected resources.
JWT Expiration and Refresh Tokens #
JWTs typically have an expiration time to limit how long they can be used. Once the token expires, the client must obtain a new one. This is often done using refresh tokens.
A refresh token is a long-lived token that the client can use to request a new access token without requiring the user to log in again.
Example of Refreshing a JWT: #
import requests
refresh_token = "your_refresh_token_here"
url = "https://api.example.com/refresh"
data = {"refresh_token": refresh_token}
response = requests.post(url, json=data)
new_token = response.json()["access_token"]
print(new_token)
Best Practices for JWT #
When using JWTs, follow these best practices to ensure security:
- Use HTTPS: Always send JWTs over secure connections to prevent token interception.
- Short Expiration Time: Set a short expiration time for access tokens and use refresh tokens to extend sessions.
- Store Tokens Securely: Store JWTs in a secure location, such as HTTP-only cookies, to reduce the risk of XSS attacks.
- Blacklist Compromised Tokens: Implement token blacklisting to prevent the use of stolen or compromised tokens.
Practical Exercise: Build a JWT Authentication System #
In this exercise, you will build a simple JWT-based authentication system using Flask.
- Implement a
/login
route that generates a JWT when the user provides valid credentials. - Implement a
/protected
route that requires the JWT to access protected data. - Implement token expiration and error handling.
Here’s a starter example:
from flask import Flask, request, jsonify
import jwt
import datetime
app = Flask(__name__)
secret_key = "your_secret_key"
@app.route("/login", methods=["POST"])
def login():
auth = request.json
if auth["username"] == "admin" and auth["password"] == "password":
token = jwt.encode({
"user": auth["username"],
"exp": datetime.datetime.utcnow() + datetime.timedelta(minutes=30)
}, secret_key, algorithm="HS256")
return jsonify({"token": token})
return jsonify({"message": "Invalid credentials"}), 401
@app.route("/protected", methods=["GET"])
def protected():
token = request.headers.get("Authorization").split(" ")[1]
try:
decoded = jwt.decode(token, secret_key, algorithms=["HS256"])
return jsonify({"message": "Welcome!", "user": decoded["user"]})
except jwt.ExpiredSignatureError:
return jsonify({"message": "Token has expired"}), 401
except jwt.InvalidTokenError:
return jsonify({"message": "Invalid token"}), 401
if __name__ == "__main__":
app.run(debug=True)
What’s Next? #
You’ve just learned how to use JWTs for secure authentication in APIs. Understanding JWTs is essential for building modern web applications that require secure communication between clients and servers. In the next post, we’ll explore how to combine JWT authentication with OAuth 2.0 for more complex authorization workflows.
Related Articles #
- OAuth and API Authentication: Accessing Secure APIs
- Rate Limiting, Error Handling, and Best Practices for API Design
- Advanced API Usage: Pagination, Filtering, and Handling Large Datasets
Happy coding, and we’ll see you in the next lesson!