Securing a Python Flask API with JWTs

Securing a Python Flask API with JWTs

Code Examples / api-integration

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.

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.

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 json
from flask import g, Flask, request
from 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.

Let’s Stay in Touch!

Get the latest on identity management, API Security and authentication straight to your inbox.

Keep up with our latest articles and how-tos using RSS feeds