Blacksheep has built-in authentication and authorization support and allows us to integrate with services like Auth0, Azure Active Directory, Azure Active Directory B2C, or Okta.
For this tutorial, we will build an API with the Blacksheep framework with JWT authentication. In our API there will be a public endpoint and a private endpoint only for authorized users.
We are going to use the same example shown in the documentation, but instead, we are going to use Auth0. So, we need an Auth0 account.
Prerequisites
Basic Python knowledge
How to create a basic web app with Blacksheep Framework
If you don't know how to create an app with Blacksheep, here is the documentation explaining it.
Auth0
According to its documentation:
Auth0 is an identity platform to add sophisticated authentication and authorization to your applications. Centralize and manage users from multiple identity providers and give them branded, seamless signup and login experiences. Finely control access with a degree of customization that can accommodate even the most complex security requirements. Easily deploy your implementation and monitor status and services.
Create an Auth0 application
After we create an account, we click on "Sign Up".
After our account is created. We go to our Dashboard a click on "Create Application".
We select "Regular Web Applications". And then, choose Python.
.env file
AUTHORITY={DOMAIN}
AUDIENCE=https://{DOMAIN}/api/v2/
ISSUERS= https://{DOMAIN}
Then, we go to our app's settings copy the "Domain" and "Client ID" and paste them inside a .env
file.
We will use the Client ID when we need to request the token.
Setup
We create our virtual environment.
py -m venv venv
cd venv/Scripts
activate
Then, we install the packages we are going to use.
pip install uvicorn blacksheep[full] python-dotenv
When we import blacksheep[full]
we are importing the necessary dependencies to use JWT Bearer authentication.
"The authentication and authorization logic implemented for BlackSheep was packed and published into a dedicated library:
guardpost
(in pypi)."
Blacksheep uses GuarPost to handle authentication and authorization. It provides a basic framework to handle authentication and authorization for any kind of Python application.
main.py
from blacksheep import Application
from blacksheep.server.authorization import auth
from guardpost.common import AuthenticatedRequirement, Policy
from blacksheep.server.authentication.jwt import JWTBearerAuthentication
from dotenv import load_dotenv
import os
load_dotenv()
app = Application()
app.use_authentication().add(
JWTBearerAuthentication(
authority=os.environ.get("AUTHORITY"),
valid_audiences=[os.environ.get("AUDIENCE")],
valid_issuers=[os.environ.get("ISSUERS")],
)
)
authorization = app.use_authorization()
authorization += Policy("any_name", AuthenticatedRequirement())
get = app.router.get
@get("/")
def home():
return "Hello, World"
@auth("any_name")
@get("/api/message")
def example():
return "This is only for authenticated users"
We create an instance of JWTBearerAuthentication
class and add the parameters: Authority, audiences, and issuers.
JWTBearerAuthentication
is a child class of AuthenticationHandler
, that can parse and verify JWT Bearer access tokens to identify users.
Creates a new instance of JWTBearerAuthentication, which tries to obtains the identity of the user from the "Authorization" request header, handling JWT Bearer tokens. Only standard authorization headers starting with the
Bearer
string are handled.
JWTBearerAuthentication
has more attributes, we can see them here.
When we start our server we will notice that we can't access the api/message
endpoint. The server returns an "Unauthorized" status code.
Getting a token
We need the following information from our Auth0 Dashboard: Client ID and Client Secret.
Using a web client, we make a POST request to the URL: https://{your domain}.com/oauth/token
. And with the following body:
{"client_id":"CLIENT ID","client_secret":"CLIENT SECRET","audience":"https://{Your domain}.us.auth0.com/api/v2/","grant_type":"client_credentials"}
If the HTTP response is OK, we will receive a response body like this:
"access_token":"TOKEN","expires_in":86400,"token_type":"Bearer"}
Now, we make a GET request to our protected endpoint, but we add the "Bearer" authorization header and paste the token.
Conclusion
I liked the approach of Blacksheep, it is the first time I use an authentication service like Auth0. I think it offers something different from other frameworks. And it saves a lot of time. I still have to learn how to write a custom authentication handler, and probably write about it.
Thank you for taking the time to read this article.
If you have any recommendations about other packages, architectures, how to improve my code, English, or anything; please leave a comment or contact me through Twitter, or LinkedIn.
The source code is here