Skip to content

Custom Auth Handler

Want to bring in your own authentication system with your own users? The Cat provides a useful API to customize the default authentication flow and add a custom auth handler which delegates authentication and authorization to third party systems.

Preliminary steps

Since the Cat has its own auth handler mechanism, which is always inserted in the auth handler pipe, you need to lock your instance following the instructions in the authentication section.

Building a Custom AuthHandler

Let's walk through the process of creating a custom AuthHandler. While the exact implementation is subject to the provider or system you choose to rely on, the overall structure will remain the same.

Create the Custom Handler

Start by extending the BaseAuthHandler class to define the behaviour for handling tokens and validating users. Here's an example:

from cat.factory.custom_auth_handler import BaseAuthHandler
from cat.auth.permissions import (
    AuthPermission, AuthResource, AuthUserInfo, get_base_permissions
)
import httpx

class CustomAuthHandler(BaseAuthHandler):

    def __init__(self, **config):
        self.server_url = config.get("server_url")


    async def authorize_user_from_jwt(self, token: str, auth_resource: str, auth_permission: str):
        jwt_validation_url = f"{self.server_url}/validate-token"

        async with httpx.AsyncClient() as client:
            response = await client.get(jwt_validation_url, headers={
                "Authorization": f"Bearer {token}"
            })

        if response.status_code == 200:
            user_data = response.json()

            # Return the user information mapped to AuthUserInfo
            return AuthUserInfo(
                id=user_data.get("username"),
                name=user_data.get("name", ""),
                extra=user_data.get("extra") # Optional data to be passed to the Cat
            )
        else:
            return None  # If the server responds with an error or doesn't respond at all


    async def authorize_user_from_key(self, protocol: str, user_id: str, api_key: str, auth_resource, auth_permission):
        # Optional: Handle API key authentication, if applicable
        return None

Create the Custom Handler Config

Create a new AuthHandlerConfig class that inherits from BaseAuthHandlerConfig. This will be the configuration class that contains all the necessary settings to initialize your custom handler.

from cat.factory.auth_handler import AuthHandlerConfig
from typing import Type
from pydantic import ConfigDict, Field

class CustomAuthHandlerConfig(AuthHandlerConfig):
    _pyclass: Type = CustomAuthHandler

    server_url: str = Field(..., description="The URL of the identity provider")

    model_config = ConfigDict(
        json_schema_extra={
            "humanReadableName": "Custom Auth Handler",
            "description": "Delegate auth to a custom identity provider."
        }
    )

Register the Custom Handler

Register the new handler with the factory_auth_handlers hook.

from cat.mad_hatter.decorators import hook
from typing import List

@hook(priority=0)
def factory_allowed_auth_handlers(allowed: List[AuthHandlerConfig], cat) -> List:
    allowed.append(CustomAuthHandlerConfig)
    return allowed

Activate the Custom Handler

Activate the new handler using the proper endpoint in your Cat installation.

curl --location --request PUT 'http://{your_cat_instance}/auth_handler/settings/CustomAuthHandlerConfig' \
--header 'Content-Type: application/json' \
--data '{
   "server_url": {your_idp_url},
}'

That's it! Now every incoming request will be authenticated using the custom auth handler.

Key Components and Concepts

BaseAuthHandler

BaseAuthHandler is the class that you will extend to implement your custom auth flow. This is an abstract class that forces you to implement two basic methods: authorize_user_from_jwt and authorize_user_from_key. You'll write your own auth logic in there inserting, for example, credentials validation request to external identity providers.

  • authorize_user_from_jwt: Validates and processes incoming JWT tokens, extracting user information and
  • mapping it into Cheshire Cat’s AuthUserInfo structure.
  • authorize_user_from_key: Allows validation of requests based on an API key, usually used for machine to machine communication.

AuthHandlerConfig

The AuthHandlerConfig class provides a base configuration that you can extend to define custom settings for your authentication handler. It includes all the necessary settings to initialize your custom handler. These may include: information about the external identity provider, authentication endpoint, client secrets, etc.

Allowed Auth Handlers Hook

The factory_allowed_auth_handlers hook allows you to add your own auth handlers to the list of allowed auth handlers.

AuthUserInfo

The AuthUserInfo class represents the decoded content of an authentication token. This class is used to standardize the output of AuthHandlers, ensuring that token details are consistent across different authentication systems. The AuthUserInfo object is crucial for session management within Cat's core, as it either retrieves or creates a user session, known as a StrayCat.