Skip to content

Authentication in BlackSheep

The words "authentication strategy" in the context of a web application refer to the ability to identify the user who is using the application. BlackSheep implements a built-in authentication strategy for request handlers. This page describes:

  • How to use the built-in authentication strategy.
  • How to read the user's context in request handlers.

Note: the word "user" is usually used only to refer to human users, while the word "service" is used to describe non-human clients. In Java and .NET, a common word to describe a generic client is "principal".

Underlying library

The authentication and authorization logic implemented for BlackSheep was packed and published into a dedicated library: guardpost (in pypi).

How to use built-in authentication

Examples of common strategies to identify users in web applications include:

  • reading an Authorization: Bearer xxx request header containing a JWT with claims that identify the user
  • reading an encrypted session token from a cookie

The example below shows how to configure an authentication handler that obtains user's identity from an Authorization request header. The actual code that would parse the value of the authorization header is omitted for the sake of simplicity:

from typing import Optional

from blacksheep.messages import Request
from blacksheep.server import Application
from blacksheep.server.responses import json
from guardpost.asynchronous.authentication import AuthenticationHandler, Identity
from guardpost.authentication import User

app = Application(show_error_details=True)
get = app.router.get


class ExampleAuthHandler(AuthenticationHandler):
    def __init__(self):
        pass

    async def authenticate(self, context: Request) -> Optional[Identity]:
        header_value = context.get_first_header(b"Authorization")
        if header_value:
            # TODO: parse and validate the value of the authorization
            # header to get an actual user's identity
            context.identity = Identity({"name": "Jan Kowalski"}, "MOCK")
        else:
            context.identity = None
        return context.identity


app.use_authentication().add(ExampleAuthHandler())

@get("/")
async def for_anybody(user: Optional[User]):
    if user is None:
        return json({"anonymous": True})

    return json(user.claims)

Note: to have an introduction to parsing JWTs with Python, read this article: Validating JSON web tokens (JWTs) from Azure AD, in Python.

It is possible to configure several authentication handlers to implement different ways to identify users. To differentiate the way the user has been authenticated, use the second parameter of Identity's constructor:

identity = Identity({"name": "Jan Kowalski"}, "AUTHENTICATION_MODE")

The authentication context is the instance of Request created to handle the incoming web request. Authentication handlers must set the identity property on the request.

Testing the example

To test the example above, start a web server as explained in the getting started guide, then navigate to its root. A web request to the root of the application without an Authorization header will produce a response with the following body:

{"anonymous":true}

While a web request with an Authorization header will produce a response with the following body:

{"name":"Jan Kowalski"}

For example, to generate web requests using curl:

$ curl  http://127.0.0.1:44555/open {"anonymous":true}

$ curl -H "Authorization: foo" http://127.0.0.1:44555/open {"name":"Jan
Kowalski"}

The application has been started on port 44555 (e.g. uvicorn server:app --port=44555).

Reading user's context from the request

The example below show how the user's identity can be read from the web request

Directly from the request:

@get("/")
async def for_anybody(request: Request):
    user = request.identity
    # user can be None or an instance of Identity (set in the authentication
    # handler)

Using binders:

from guardpost.authentication import Identity


@get("/")
async def for_anybody(user: Optional[Identity]):
    ...

Next

While authentication deals with identifying users, authorization deals with determining whether the user is authorized to do the action of the web request. The next page describes the built-in authorization strategy in BlackSheep.