Skip to main content
Memproof supports pluggable auth hooks that run before the pipeline executes. Each hook receives the operation context and returns true (allow) or false (deny). If any hook denies the request, the operation is rejected before risk assessment or policy evaluation begins.

Built-in Auth Hooks

BearerTokenAuth

Validates the actor_id in the operation context against a set of known tokens. Useful for service-to-service authentication where each caller has a static bearer token.
from memproof.auth import BearerTokenAuth

auth = BearerTokenAuth(valid_tokens={"tok-abc", "tok-xyz"})

# Returns True if actor_id matches a valid token
allowed = auth.check(context={"actor_id": "tok-abc"})

ScopeAuth

Grants access based on tenant, project, and agent scope. You register which actor IDs are allowed to operate within specific scopes, and the hook enforces those boundaries at request time.
from memproof.auth import ScopeAuth

scope_auth = ScopeAuth()
scope_auth.grant("agent-1", "tenant-1", "project-1")
scope_auth.grant("agent-2", "tenant-1", "project-2")

# Returns True -- agent-1 is granted access to tenant-1/project-1
scope_auth.check(
    context={"actor_id": "agent-1"},
    scope={"tenant_id": "tenant-1", "project_id": "project-1"},
)

# Returns False -- agent-1 is not granted access to project-2
scope_auth.check(
    context={"actor_id": "agent-1"},
    scope={"tenant_id": "tenant-1", "project_id": "project-2"},
)

CompositeAuth

Chains multiple auth hooks together using AND logic. Every hook must return true for the operation to proceed. This lets you layer authentication (token validation) with authorization (scope checks).
from memproof.auth import BearerTokenAuth, ScopeAuth, CompositeAuth

# Layer 1: token validation
auth = BearerTokenAuth(valid_tokens={"tok-abc", "tok-xyz"})

# Layer 2: scope authorization
scope_auth = ScopeAuth()
scope_auth.grant("agent-1", "tenant-1", "project-1")

# Combined: both must pass
composite = CompositeAuth(auth, scope_auth)

Attaching Auth to Memproof

Pass the auth hook when constructing your Memproof instance. The hook is called before every operation enters the pipeline.
from memproof import Memproof
from memproof.auth import BearerTokenAuth, ScopeAuth, CompositeAuth

auth = BearerTokenAuth(valid_tokens={"tok-abc", "tok-xyz"})
scope_auth = ScopeAuth()
scope_auth.grant("agent-1", "tenant-1", "project-1")
composite = CompositeAuth(auth, scope_auth)

mp = Memproof(
    policy="memproof.yaml",
    adapter="in_memory",
    auth_hook=composite.check,
)

Writing a Custom Auth Hook

Any function that accepts the operation context and scope and returns a boolean can serve as an auth hook. This makes it straightforward to integrate with external identity providers, JWTs, or custom RBAC systems.
from memproof import Memproof

async def my_custom_auth(context: dict, scope: dict) -> bool:
    """Validate actor against an external identity service."""
    actor = context.get("actor_id")
    if not actor:
        return False
    # Call your identity provider, check JWTs, query a database, etc.
    return await my_identity_service.is_authorized(actor, scope)

mp = Memproof(
    policy="memproof.yaml",
    adapter="in_memory",
    auth_hook=my_custom_auth,
)
Auth hooks run on every operation. Keep them fast and avoid expensive I/O in the hot path. If you need to call an external service, consider caching the result with a short TTL.
Use CompositeAuth to separate concerns: one hook for authentication (is this caller who they say they are?) and another for authorization (is this caller allowed to do this?). This makes each hook simpler to test and reason about.