Securing a Python Flask API with JWTs
On this page
Overview
A library which provides an extension for protecting APIs with OAuth when using Flask. The filter can be used in two ways with Flask:
- Run before all routes with the same authorization requirement, which protects all endpoints.
- Use the decorator pattern to secure only selected paths.
Note
The example uses the Curity Identity Server, but you can run the code against any standards-based authorization server.
Create a Python API
Create your own Python API according to an Online Article of your choice, which you can then start with 'flask run'. Next install the Curity Flask OAuth library with the following command:
Python3 -m pip install -U flask-of-oil
The library needs to be provided with the JWKS Endpoint of your Authorization Server, which will be a value such as this.
https://idsvr.example.com/oauth/v2/oauth-anonymous/jwks
Integrate the Security Library
Only a couple of lines of code are required to integrate the flask of oil library. The code for a working OAuth secured Python Flask API is provided below:
- The OAuth filter is configured to run before API requests
- The filter verifies the token signature and the expected issuer / audience claims
- API routes can then access JWT claims in the request object
- In this example a role claim has been configured in the Curity Identity Server
import jsonfrom flask import g, Flask, requestfrom flask_of_oil.oauth_filter import OAuthFilter_app = Flask(__name__)_oauth = OAuthFilter()_oauth.configure_with_jwt("https://idsvr.example.com/oauth/v2/oauth-anonymous/jwks","https://idsvr.example.com/oauth/v2/oauth-anonymous","guide-client")_app.before_request(_oauth.filter)@_app.route('/api/data', methods=['GET'])def get_data():role = request.claims['role']scope = request.claims['scope']message = "Data for user role '{0}' and scope '{1}'".format(role, scope)return json.dumps({"message": message}), 200
Validate JWTs
The Flask of Oil library manages the technical details of JWT validation:
- Looking up the kid value from the JWT header
- Downloading the token signing public key from the Authorization Server's JWKS endpoint
- Using the public key to verify the digital signature of the JWT
- Checking of required scopes and claims from the options object
- Caching the public key in memory for subsequent requests with the same kid value
Use Scopes and Claims
Once the token is validated, the API can access its scopes and claims via Python's request object. A real world API would then use the scopes and claims from the JWT to authorize access to business data, according to the below articles:
Test the API
The API's clients will call it via HTTP requests with an access token in the Authorization header, and this can be easily tested via a CURL request:
curl -i http://localhost:5000/api/data -H "Authorization: Bearer eyJraWQiOiIyMTg5NTc5MTYiLC ..."
In the event of an invalid token, the library will return a 401 HTTP status. Similarly, if there is a technical problem when contacting the Authorization Server a 500 HTTP status will be returned. To customize error behavior you can write your own handlers to log details and to return tailored response messages:
@_app.errorhandler(401)def unauthorized(error):return json.dumps({"code": "invalid_token","message": "The access token was missing, invalid or expired"}), \401, {'Content-Type': 'application/json; charset=utf-8'}
Other Library Options
The Flask of Oil library has a number of more advanced options that can also be used if required. These are described in the GitHub repository's README file.
Conclusion
Integrating the Curity Flask OAuth library is an easy way to implement OAuth security in your Python APIs. Once done you have full access to the access token's scopes and claims, so that your API can secure access to its data.
Join our Newsletter
Get the latest on identity management, API Security and authentication straight to your inbox.
Start Free Trial
Try the Curity Identity Server for Free. Get up and running in 10 minutes.
Start Free Trial