AuthenticationHandler is the base class for all authentication logic. Subclass
it and implement the authenticate method to read credentials from a context
and, when valid, set context.identity.
fromguardpostimportAuthenticationHandler,IdentityclassMyHandler(AuthenticationHandler):asyncdefauthenticate(self,context)->None:# Read credentials from context, validate them, then:context.identity=Identity({"sub":"user-1"},"Bearer")
The context parameter is whatever your application uses to represent a
request — GuardPost imposes no specific type on it. In
BlackSheep this is the Request
object; in other frameworks it could be any object you choose.
fromguardpostimportAuthenticationHandler,IdentityclassAsyncBearerHandler(AuthenticationHandler):scheme="Bearer"asyncdefauthenticate(self,context)->None:token=getattr(context,"token",None)iftoken:# e.g. validate token against a remote serviceuser_info=awaitfetch_user_info(token)ifuser_info:context.identity=Identity(user_info,self.scheme)
The optional scheme class property names the authentication scheme this
handler implements (e.g. "Bearer", "ApiKey", "Cookie"). Naming
schemes is useful when multiple handlers are registered and you need to
identify which one authenticated a request.
fromguardpostimportIdentityidentity=Identity({"sub":"user-42","name":"Bob","email":"bob@example.com","roles":["editor"],"iss":"https://auth.example.com",},"Bearer",)# Convenience propertiesprint(identity.sub)# "user-42"print(identity.name)# "Bob"print(identity.access_token)# None — not set# Dict-style accessprint(identity["email"])# "bob@example.com"print(identity.get("roles"))# ["editor"]# Authentication modeprint(identity.authentication_mode)# "Bearer"# Authentication checkprint(identity.is_authenticated())# True — authentication_mode is set# Anonymous identity: claims present, but no authentication_modeanon=Identity({"sub":"guest"})print(anon.is_authenticated())# False
Anonymous vs no identity
An Identity created without authentication_mode (or authentication_mode=None)
is anonymous: it has claims, but is_authenticated() returns False. This is
different from context.identity being None, which means no identity was resolved
at all. AuthorizationStrategy raises UnauthorizedError in both cases.
AuthenticationStrategy manages a list of handlers and calls them in sequence.
Once a handler sets context.identity, the remaining handlers are skipped.
When multiple handlers are registered, they are tried in the order they are
passed to AuthenticationStrategy. The first handler to set context.identity
wins; subsequent handlers are not called.
strategy=AuthenticationStrategy(JWTHandler(),# tried firstApiKeyHandler(),# tried second, only if JWT handler didn't set identityCookieHandler(),# tried third, only if both above didn't set identity)
This is useful for APIs that support multiple credential types simultaneously.
You can inspect context.identity.authentication_mode after authentication to know which
handler authenticated the request, and apply different logic accordingly.