Authenticator
The Framefox authentication system is built around the AuthenticatorInterface and AbstractAuthenticator classes, which provide a flexible foundation for implementing various authentication strategies.
Source : framefox/core/security/authenticator/
AuthenticatorInterface
The interface that all authenticators must implement.
class AuthenticatorInterface ( ABC ):
@abstractmethod
async def authenticate ( self , request : Request) -> Passport | None :
pass
@abstractmethod
async def on_authentication_success (
self , request : Request, passport : Passport
) -> Response | None :
pass
@abstractmethod
async def on_authentication_failure (
self , request : Request, exception : Exception
) -> Response | None :
pass
AbstractAuthenticator
Base class providing common authentication functionality.
Source : framefox/core/security/authenticator/abstract_authenticator.py
Methods
authenticate()
Attempts to authenticate the user from the request.
async def authenticate ( self , request : Request) -> Passport | None
The incoming HTTP request
A Passport object containing user information if authentication succeeds, None otherwise
Returns:
Passport object with user data if credentials are valid
None if authentication fails or credentials are missing
on_authentication_success()
Called when authentication succeeds.
async def on_authentication_success (
self , request : Request, passport : Passport
) -> Response | None
The passport containing authenticated user information
Optional response (e.g., redirect). Return None to continue with the original request.
on_authentication_failure()
Called when authentication fails.
async def on_authentication_failure (
self , request : Request, exception : Exception
) -> Response | None
The exception that caused authentication to fail
Optional response (e.g., redirect to login page)
Creating a Custom Authenticator
from framefox.core.security.authenticator.abstract_authenticator import AbstractAuthenticator
from framefox.core.security.passport.passport import Passport
from framefox.core.security.user.user_badge import UserBadge
from starlette.requests import Request
from starlette.responses import Response, RedirectResponse
import logging
class FormLoginAuthenticator ( AbstractAuthenticator ):
def __init__ ( self , user_repository , password_hasher ):
self .user_repository = user_repository
self .password_hasher = password_hasher
self .logger = logging.getLogger( "FormAuthenticator" )
async def authenticate ( self , request : Request) -> Passport | None :
# Only authenticate on login POST
if request.url.path != "/login" or request.method != "POST" :
return None
# Get credentials from form
form = await request.form()
email = form.get( "email" )
password = form.get( "password" )
if not email or not password:
return None
# Find user
user = await self .user_repository.find_one_by({ "email" : email})
if not user:
self .logger.warning( f "User not found: { email } " )
return None
# Verify password
if not self .password_hasher.verify(password, user.password):
self .logger.warning( f "Invalid password for user: { email } " )
return None
# Create passport
user_badge = UserBadge(user.id, user.email)
return Passport(user_badge, user.roles)
async def on_authentication_success (
self , request : Request, passport : Passport
) -> Response | None :
self .logger.info( f "User authenticated: { passport.user_badge.email } " )
return RedirectResponse( url = "/dashboard" , status_code = 302 )
async def on_authentication_failure (
self , request : Request, exception : Exception
) -> Response | None :
self .logger.error( f "Authentication failed: { exception } " )
return RedirectResponse( url = "/login?error=1" , status_code = 302 )
JWT Authenticator
from framefox.core.security.authenticator.abstract_authenticator import AbstractAuthenticator
from framefox.core.security.passport.passport import Passport
from framefox.core.security.user.user_badge import UserBadge
from framefox.core.security.token_manager import TokenManager
from starlette.requests import Request
import logging
class JWTAuthenticator ( AbstractAuthenticator ):
def __init__ ( self , token_manager : TokenManager):
self .token_manager = token_manager
self .logger = logging.getLogger( "JWTAuthenticator" )
async def authenticate ( self , request : Request) -> Passport | None :
# Get token from Authorization header
auth_header = request.headers.get( "Authorization" )
if not auth_header or not auth_header.startswith( "Bearer " ):
return None
token = auth_header[ 7 :] # Remove "Bearer " prefix
# Decode token
payload = self .token_manager.decode_token(token)
if not payload:
return None
# Create passport from token data
user_badge = UserBadge(payload[ "sub" ], payload[ "email" ])
return Passport(user_badge, payload.get( "roles" , []))
async def on_authentication_success (
self , request : Request, passport : Passport
) -> Response | None :
# No redirect needed for API authentication
return None
async def on_authentication_failure (
self , request : Request, exception : Exception
) -> Response | None :
# Return 401 for API
from fastapi.responses import JSONResponse
return JSONResponse(
{ "error" : "Unauthorized" },
status_code = 401
)
OAuth Authenticator
For OAuth providers (Google, GitHub, etc.), extend AbstractOAuthAuthenticator:
from framefox.core.security.authenticator.abstract_oauth_authenticator import AbstractOAuthAuthenticator
class GoogleAuthenticator ( AbstractOAuthAuthenticator ):
def __init__ ( self , client_id : str , client_secret : str ):
super (). __init__ (
provider_name = "google" ,
authorization_url = "https://accounts.google.com/o/oauth2/v2/auth" ,
token_url = "https://oauth2.googleapis.com/token" ,
user_info_url = "https://www.googleapis.com/oauth2/v2/userinfo" ,
client_id = client_id,
client_secret = client_secret,
scopes = [ "openid" , "email" , "profile" ]
)
Registration
Authenticators are configured in config/security.yaml:
security :
firewalls :
main :
authenticators :
- FormLoginAuthenticator
- JWTAuthenticator
entry_point : /login
See Also
Passport User authentication flow
Token Manager JWT token management
Access Manager Role-based access control
Security Guide Complete security documentation