Authentication Middleware

Service may require an authentication to authorize http requests.

The authentication part is not a part of the contract of a route, but generally for a whole service or even for the whole registry.

For concistency, every service should use the same authorization pattern.

With blacksmith, the authentication mechanism is declared in the AsyncClientFactory to get the authentication working. It also can be overridden on every api call.

Example

from blacksmith import (
    AsyncClientFactory,
    AsyncConsulDiscovery,
    AsyncHTTPBearerMiddleware,
)

access_token = "abc"

sd = AsyncConsulDiscovery()
auth = AsyncHTTPBearerMiddleware(access_token)
cli = AsyncClientFactory(sd).add_middleware(auth)
# Now every call of the client will have the header
# Authorization: Bearer abc


async def main():
    api = await cli("api")
    protected_resource = await api.protected_resource.get({})  # noqa

In the example above, the bearer token is share for every clients, of the factory, which is ok for a service like prometheus where the token is a configuration key, but most of the time, a token depends on users.

So in the example below, we set the token only on a particular client.

from blacksmith import (
    AsyncClientFactory,
    AsyncConsulDiscovery,
    AsyncHTTPBearerMiddleware,
)

sd = AsyncConsulDiscovery()
cli = AsyncClientFactory(sd)


async def a_dummy_api_view(request):
    api = await cli("api")
    api.add_middleware(AsyncHTTPBearerMiddleware(request.access_token))
    protected_resource = await api.protected_resource.get({})  # noqa

In that example, we have a fake web framework that parse the authorization header and expose the bearer token under a variable request.access_token. And we provide the middleware only for the execution ot that request.

Create a custom authorization

Imagine that you have an api that consume a basic authentication header.

import base64

from blacksmith import (
    AsyncClientFactory,
    AsyncConsulDiscovery,
    AsyncHTTPAuthorizationMiddleware,
)


class AsyncBasicAuthorization(AsyncHTTPAuthorizationMiddleware):
    def __init__(self, username, password):
        userpass = f"{username}:{password}".encode()
        b64head = base64.b64encode(userpass).decode("ascii")
        header = f"Basic {b64head}"
        return super().__init__("Basic", header)


sd = AsyncConsulDiscovery()
auth = AsyncBasicAuthorization("alice", "secret")
cli = AsyncClientFactory(sd).add_middleware(auth)

Create a custom authentication based on http header

Imagine that you have an api that consume a “X-Secret” header to validate call.

from blacksmith import (
    AsyncClientFactory,
    AsyncConsulDiscovery,
    AsyncHTTPAddHeadersMiddleware,
)


class AsyncBasicAuthorization(AsyncHTTPAddHeadersMiddleware):
    def __init__(self, secret):
        return super().__init__(headers={"X-Secret": secret})


sd = AsyncConsulDiscovery()
auth = AsyncBasicAuthorization("secret")
cli = AsyncClientFactory(sd).add_middleware(auth)

Create a custom authentication based on querystring parameter

It is not recommended to pass server in a querystring, because get parameter are oftenly logged by server, and secret should never be logged. So blacksmith does not provide a middleware to handle this query, but, you can still implementing it by yourself.

See how to implement it in the section Generic Middleware.