Skip to main content

SpiceDB authorization for LangChain RAG pipelines and agents - fine-grained access control for AI applications

Project description

LangChain-SpiceDB Integration

Authorization library for RAG (Retrieval-Augmented Generation) pipelines using SpiceDB. Designed for LangChain and LangGraph integrations with support for any vector store (Pinecone, FAISS, Weaviate, Chroma, etc.).

This package follows LangChain's official integration guidelines and provides standard LangChain components (BaseRetriever, BaseTool) plus additional middleware patterns.

Features

  • LangChain & LangGraph Integration: First-class support for modern LLM frameworks
  • Vector Store Agnostic: Compatible with Pinecone, FAISS, Weaviate, Chroma, and more
  • Post-Filter Authorization: Retrieve semantically, then filter by SpiceDB permissions
  • Pre-Filter Authorization: Fetch authorized resource IDs via LookupResources first, then run a filtered vector store search — ideal when users have access to a small fraction of a large corpus
  • Efficient Bulk Permissions: Uses SpiceDB's native bulk API for optimal performance
  • Observable: Returns detailed metrics about authorization decisions
  • Type-Safe: Full type hints for better IDE support
  • Async by Default: Built for high-performance async operations

Why This Package?

Most RAG pipelines retrieve documents without considering user permissions. This package solves that by:

  1. Post-retrieval filtering: Retrieve best semantic matches first, then filter by permissions
  2. Pre-retrieval filtering: Fetch all resource IDs the user can access via SpiceDB's LookupResources API, then run a filtered vector store search — no unauthorized documents are retrieved
  3. Deterministic authorization: Every document is checked against SpiceDB before being used
  4. Framework integration: Native LangChain and LangGraph components for seamless integration
  5. Vector store agnostic: Not tied to any specific vector database

Which Component Should I Use?

Component Pattern Use Case
SpiceDBAuthFilter Post-filter LangChain LCEL chains. Reusable across different users via config.
SpiceDBPreFilterRetriever Pre-filter LangChain LCEL chains. Users have access to a small fraction of a large corpus. Requires a filter_factory matching your vector store's filter syntax.
create_check_permissions_node Post-filter LangGraph workflows. Multi-step workflows with state management and authorization metrics.
create_lookup_resources_node Pre-filter LangGraph workflows. Single node that calls LookupResources then runs a filtered vector search. No separate retrieval step needed.
SpiceDBPermissionTool Check Agents that need to check a single permission before acting.
SpiceDBBulkPermissionTool Check Agents that need to check permissions on multiple resources at once.

Quick Decision Guide

Post-filter vs Pre-filter:

  • Use post-filter (SpiceDBAuthFilter, create_check_permissions_node) when users have access to most documents — semantic search quality is highest because all documents are candidates.
  • Use pre-filter (SpiceDBPreFilterRetriever, create_lookup_resources_node) when users have access to a small subset of a large corpus — avoids retrieving unauthorized content entirely.

LangChain vs LangGraph:

  • Use LangChain components (SpiceDBAuthFilter, SpiceDBPreFilterRetriever) for LCEL chains.
  • Use LangGraph components (create_check_permissions_node, create_lookup_resources_node) for state graph workflows.

Example: Same Pipeline, Different Patterns

Pattern 1: SpiceDBAuthFilter (post-filter, LCEL)

auth = SpiceDBAuthFilter(...)
chain = retriever | auth | prompt | llm

# Same chain, different users at call time
await chain.ainvoke("question", config={"configurable": {"subject_id": "alice"}})
await chain.ainvoke("question", config={"configurable": {"subject_id": "bob"}})

Pattern 2: SpiceDBPreFilterRetriever (pre-filter, LCEL)

retriever = SpiceDBPreFilterRetriever(
    vector_store=vector_store,
    filter_factory=lambda ids: {"filter": {"article_id": {"$in": ids}}},
    subject_id="alice",
    ...
)
chain = retriever | prompt | llm

Pattern 3: create_check_permissions_node (post-filter, LangGraph)

graph.add_node("authorize", create_check_permissions_node(...))
# Authorization metrics available in state['auth_results']

Pattern 4: create_lookup_resources_node (pre-filter, LangGraph)

# Single node replaces separate retrieve + authorize nodes
graph.add_node("retrieve_authorized", create_lookup_resources_node(
    vector_store=vector_store,
    filter_factory=lambda ids: {"filter": {"article_id": {"$in": ids}}},
    ...
))
graph.add_edge("retrieve_authorized", "generate")

Pattern 5: Agent Tool

tools = [SpiceDBPermissionTool(...)]
agent = create_agent(llm, tools, system_prompt="...")
# Agent can check "Can user alice delete document 123?" and explain the result

Installation

pip install langchain-spicedb

Optional Dependencies

# Install with LangChain support
pip install langchain-spicedb[langchain]

# Install with LangGraph support
pip install langchain-spicedb[langgraph]

# Install everything (recommended)
pip install langchain-spicedb[all]

Development Installation

git clone https://github.com/authzed/langchain-spicedb.git
cd langchain-spicedb
pip install -e ".[all,dev]"

Quick Start

1. Start SpiceDB

docker run --rm -p 50051:50051 authzed/spicedb serve \
    --grpc-preshared-key "sometoken" \
    --grpc-no-tls

2. Define Schema and Permissions

from authzed.api.v1 import Client, WriteSchemaRequest
from grpcutil import insecure_bearer_token_credentials

client = Client("localhost:50051", insecure_bearer_token_credentials("sometoken"))

schema = """
definition user {}

definition article {
    relation viewer: user
    permission view = viewer
}
"""

await client.WriteSchema(WriteSchemaRequest(schema=schema))

3. Use in LangChain

from langchain_spicedb import SpiceDBAuthFilter
from langchain_core.runnables import RunnableParallel, RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser

auth = SpiceDBAuthFilter(
    spicedb_endpoint="localhost:50051",
    spicedb_token="sometoken",
    subject_type="user",
    resource_type="article",
    resource_id_key="article_id",
    permission="view",
)

chain = (
    RunnableParallel({
        "context": retriever | auth,
        "question": RunnablePassthrough(),
    })
    | prompt
    | llm
    | StrOutputParser()
)

answer = await chain.ainvoke(
    "Your question?",
    config={"configurable": {"subject_id": "alice"}}
)

4. Use in LangGraph

from langgraph.graph import StateGraph, END
from langchain_spicedb import create_check_permissions_node, RAGAuthState

graph = StateGraph(RAGAuthState)

graph.add_node("retrieve", retrieve_node)
graph.add_node("authorize", create_check_permissions_node(
    spicedb_endpoint="localhost:50051",
    spicedb_token="sometoken",
    resource_type="article",
    resource_id_key="article_id",
))
graph.add_node("generate", generate_node)

graph.set_entry_point("retrieve")
graph.add_edge("retrieve", "authorize")
graph.add_edge("authorize", "generate")
graph.add_edge("generate", END)

app = graph.compile()
result = await app.ainvoke({
    "question": "What is SpiceDB?",
    "subject_id": "alice",
})

Documentation

  • Configuration Guide - Detailed configuration options, metadata requirements, and error handling
  • LangGraph Guide - Advanced LangGraph patterns, custom state, and visualization
  • Examples - Complete working examples and tutorials
  • Testing Guide - Running tests and integration testing

Components

SpiceDBAuthFilter

Post-filter authorization as a LangChain Runnable. Sits between a retriever and the rest of the chain, filtering documents based on SpiceDB permissions. Reusable across users via config:

from langchain_spicedb import SpiceDBAuthFilter

auth = SpiceDBAuthFilter(
    spicedb_endpoint="localhost:50051",
    spicedb_token="sometoken",
    resource_type="article",
    subject_type="user",
    resource_id_key="article_id",
    permission="view",
)

authorized_docs = await auth.ainvoke(
    docs,
    config={"configurable": {"subject_id": "alice"}}
)

SpiceDBPreFilterRetriever

Pre-filter authorization as a LangChain BaseRetriever. Calls SpiceDB's LookupResources to get the user's authorized IDs, then runs a filtered vector search:

from langchain_spicedb import SpiceDBPreFilterRetriever

retriever = SpiceDBPreFilterRetriever(
    vector_store=vector_store,
    filter_factory=lambda ids: {"filter": {"article_id": {"$in": ids}}},
    subject_id="alice",
    spicedb_endpoint="localhost:50051",
    spicedb_token="sometoken",
    resource_type="article",
    permission="view",
)

docs = await retriever.ainvoke("What is Python?")

SpiceDBPermissionTool

LangChain tool for agents to check a single permission:

from langchain_spicedb import SpiceDBPermissionTool

tool = SpiceDBPermissionTool(
    spicedb_endpoint="localhost:50051",
    spicedb_token="sometoken",
    subject_type="user",
    resource_type="article",
)

result = tool.invoke({
    "subject_id": "alice",
    "resource_id": "doc123",
    "permission": "view"
})
# Returns: "true" or "false"

SpiceDBBulkPermissionTool

Same as SpiceDBPermissionTool but checks multiple resources at once:

from langchain_spicedb import SpiceDBBulkPermissionTool

tool = SpiceDBBulkPermissionTool(
    spicedb_endpoint="localhost:50051",
    spicedb_token="sometoken",
    subject_type="user",
    resource_type="article",
)

result = tool.invoke({
    "subject_id": "alice",
    "resource_ids": "doc1,doc2,doc3",
    "permission": "view"
})
# Returns: "alice can access: doc1, doc2" or "alice cannot access any..."

Performance

  • Native Bulk API: Uses SpiceDB's CheckBulkPermissionsRequest for optimal performance
  • Single API Call: All permission checks happen in one request, not N individual calls
  • Async Operations: All operations are async for better performance

Testing

# Run unit tests
pytest tests/unit_tests/

# Run integration tests (requires SpiceDB)
SPICEDB_ENDPOINT=localhost:50051 SPICEDB_TOKEN=sometoken pytest tests/integration_tests/

# With coverage
pytest tests/ --cov=langchain_spicedb

Contributing

Contributions welcome! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Submit a pull request

License

Apache-2.0 License

Related Projects


Need help? Check out the examples or open an issue on GitHub.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

langchain_spicedb-0.2.0.tar.gz (22.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

langchain_spicedb-0.2.0-py3-none-any.whl (23.1 kB view details)

Uploaded Python 3

File details

Details for the file langchain_spicedb-0.2.0.tar.gz.

File metadata

  • Download URL: langchain_spicedb-0.2.0.tar.gz
  • Upload date:
  • Size: 22.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for langchain_spicedb-0.2.0.tar.gz
Algorithm Hash digest
SHA256 87645f9c0970ead889a69b5b4cafa1fb24d629803a96b42415ef75eb7a881c05
MD5 798238d1c6272c775612d6726f2b25f5
BLAKE2b-256 9a70df9944f5aea4c6dc438e3e5dd0e1ad9a03f08017fc1dec8359304514bccd

See more details on using hashes here.

Provenance

The following attestation bundles were made for langchain_spicedb-0.2.0.tar.gz:

Publisher: publish-to-pypi.yml on authzed/langchain-spicedb

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file langchain_spicedb-0.2.0-py3-none-any.whl.

File metadata

File hashes

Hashes for langchain_spicedb-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 22d7b0408504bc2492ef8ffbef3ed09e2372c329a7b9e2b5c26370878a2d8142
MD5 dbb440ca97c047b0eb6c86969a6b5e95
BLAKE2b-256 3a9a1a3bfedf7d807c18fb533ee3fcabf2f016ef4e5965ba4d75c33c9cfc4dee

See more details on using hashes here.

Provenance

The following attestation bundles were made for langchain_spicedb-0.2.0-py3-none-any.whl:

Publisher: publish-to-pypi.yml on authzed/langchain-spicedb

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page