PlainID’s LangChain Authorizer brings policy-based decisioning to each stage of the LLM workflow, enabling you to restrict which prompts users are allowed to submit, control which documents users are authorized to retrieve from vector stores and Redact sensitive output fields based on dynamic entitlements.
By externalizing access decisions into PlainID’s centralized Policy framework, enterprises can maintain consistency with existing security and compliance practices without hardcoding rules into the application.
Ensure your PlainID Environment supports Policies that enforce prompt evaluation, data access, MCP tools, and output redaction before beginning.
For a complete walkthrough example, including a guide on how to set up these Policies and a downloadable code sample, see the LangChain Authorizer Use-case.
Refer to our overview of the Langchain Authorizer for more information.
To get started, ensure that the SQL Authorizer Service has been deployed.
Follow these steps to implement the PlainID Langchain Authorizer in your Agentic AI Application:
PlainIDPermissionsProvider
Use the PlainIDPermissionsProvider to retrieve user entitlements and expose them to guardrail components in your LangChain workflow. This enables Policy-based filtering and output redaction based on user identity and context.
PlainIDPermissionsProvider is a class that retrieves user entitlements from the PlainID Policy Decision Point (PDP) and makes them available to the guardrail components.
from langchain_plainid import PlainIDPermissionsProvider
permissions_provider = PlainIDPermissionsProvider(
base_url="https://plainid.example.com",
client_id="CLIENT_ID",
client_secret="CLIENT_SECRET",
entity_id="USER_ID",
entity_type_id="User"
)
Parameters
| Parameter | Description |
|---|---|
base_url |
The base URL used by the LangChain Authorizer to evaluate Policies. |
clientId |
The Client ID used by the LangChain Authorizer to authenticate with the PlainID. |
clientSecret |
The Client Secret used to authenticate with the PlainID. |
entityId |
The unique identifier of the identity (e.g., user ID) making the LangChain request. |
entityTypeId |
The Identity Template (e.g., User, ServiceAccount) associated with the entityId. Required if a full user object is not provided. |
Note: The Categorizer and Anonymizer components utilize this internally for PlainID integration.
Guardrail 1: Prompt Categorization in PlainID
The objective of this guardrail is to validate whether the user is authorized to ask about the topic by classifying the prompt and checking it against PlainID Policies.
LLM-Based Prompt Classification
The PlainIDCategorizer acts as a pre-query enforcement layer. It uses the LLMCategoryClassifierProvider to map prompts to semantic categories (e.g., billing, security, product_support) using an underlying LLM (e.g., Ollama, OpenAI).
This abstraction supports both local and API-based language models and enables model-agnostic prompt categorization. The resulting category is then validated against the Policies configured in PlainID.
Note: Use high-quality models for more accurate classification results.
To classify the prompt:
2) Set up your language model for prompt classification:
from langchain_community.llms import Ollama
llm = Ollama(model="llama3", temperature=0)
Note: You can use any supported model, not just LLaMA. Choose one that fits your performance and privacy requirements. The categorization result highlight depends on the LLM used.
- Enforce Prompt Categorization in your application code:
from langchain_plainid.classification import LLMCategoryClassifierProvider
from langchain_plainid import PlainIDCategorizer
# Step 1: Initialize the LLM-based classifier
classifier = LLMCategoryClassifierProvider(llm=my_llm)
# Step 2: Initialize the categorizer
categorizer = PlainIDCategorizer(
classifier_provider=classifier,
permissions_provider=permissions_provider
)
# Step 3: Categorize and authorize the prompt
try:
category = categorizer.invoke(prompt)
print(f"[Gate 1: ALLOWED] Query categorized as '{category}', proceeding.")
except ValueError as e:
print(f"[Gate 1: DENIED] {e}")
print("→ Response:\nYou’re not authorized to ask about these topics.")
Guardrail 2: RAG Data Control
- RAG Control in PlainID
ThePlainIDRetrieverenforces fine-grained access control during vector store searches by injecting dynamic filters based on user context and PlainID Policies. This prevents unauthorized access to documents, even when results are semantically relevant.
For example, a support analyst located in the EU may only retrieve documents tagged with region=eu-central, even if U.S. documents match the query intent.
LangChain integrates with vector databases such as Chroma, FAISS, and Weaviate to perform semantic searches across embedded documents. These documents must be enriched with structured metadata (e.g., region, topic) to enable Policy-based filtering.
⚠️ Note: LangChain’s query filter translator supports a subset of vector stores. Compatibility should be validated per use case and tested with Chroma and FAISS.
- Enforce RAG Control (Guardrail 2) in your application code:
from langchain_community.vectorstores import Chroma
from langchain_core.documents import Document
from langchain_core.embeddings import FakeEmbeddings
from langchain_plainid import PlainIDRetriever
from langchain_plainid.providers import PlainIDFilterProvider
# Step 1: Define your vector store
docs = [...] # Pre-tagged documents with metadata like 'region', 'topic'
vector_store = Chroma.from_documents(documents=docs, embedding=FakeEmbeddings())
# Step 2: Initialize the filter provider
filter_provider = PlainIDFilterProvider(
base_url="https://plainid.example.com",
client_id="plainid-client",
client_secret="plainid-client-secret",
entity_id="user-id",
entity_type_id="User"
)
# Step 3: Wrap the retriever with policy enforcement
policy_enforced_retriever = PlainIDRetriever(
vectorstore=vector_store,
filter_provider=filter_provider
)
# Step 4: Execute a query
filtered_docs = policy_enforced_retriever.invoke("Show me outage reports in Europe")
Guardrail 3: Define Output Redaction in PlainID
Prevent unauthorized exposure of sensitive or personally identifiable information (PII) in model-generated responses using the PlainIDAnonymizer. This component masks or encrypts output fields based on user-specific entitlements, even if generated by the model.
The PlainIDAnonymizer inspects the final output and dynamically applies redaction rules, masking or encrypting specified fields (required to choose at least one action), based on Policies defined in PlainID. This acts as a final guardrail, ensuring sensitive content does not reach unauthorized users regardless of what the LLM produces.
- Anonymize Sensitive Output in your application code:
from langchain_plainid import PlainIDAnonymizer
# Initialize the anonymizer with a permissions provider
plainid_anonymizer = PlainIDAnonymizer(
permissions_provider=permissions_provider,
encrypt_key="your_encryption_key" # Optional, only required if using ENCRYPT
)
# Redact sensitive fields in the model's answer
final_output = plainid_anonymizer.invoke(answer)
Note: The categories need to be aligned with Presidio. The list of possible anonymization sources are based on PII entities. We are use the Microsoft Presidio library to detect PII entities in your text. You can find the list of supported entities here.
Native Integration with the SQL Authorizer
The SQL Authorizer allows applications to send SQL queries to PlainID for policy evaluation before execution. Based on the configured Policies, the SQL Authorizer may modify the query to enforce data access controls and return a policy-compliant SQL statement.
Setup
To use the SQL Authorizer, obtain the following from your PlainID deployment:
- SQL Authorizer service base URL (for example:
https://your-sql-authz.plainid.cloud) - Client ID for authentication
- Client Secret or a JWT token for authentication (at least one is required)
Authentication
The client supports two authentication methods:
- Client credentials – provide
client_idandclient_secretwhen initializing the client. - JWT token – provide an
auth_tokenwith each request. TheBearerprefix is added automatically if it is not included.
Authorizing a SQL Query
Use the PlainIDSQLAuthorizer to send a SQL statement to the PlainID SQL Authorizer service and receive an authorized query. See the parameters and code format below:
Request Parameters
The SQL Authorizer returns the policy-enforced SQL query, which can then be executed by your database client.
| Parameter | Type | Required | Description |
|---|---|---|---|
sql |
str |
Yes | The SQL query to authorize and enforce policies on. |
entity_id |
str |
No | The identifier of the entity (for example, a user ID) used for policy evaluation. |
entity_type_id |
str |
No | The identity template associated with the entity (for example, User). |
user |
str |
No | Optional user identification string. |
flags |
SQLAuthorizerFlags |
No | Optional configuration that controls SQL modification behavior. |
entity_attributes |
dict |
No | Additional attributes associated with the entity that may be used during policy evaluation. |
context_data |
dict |
No | Additional contextual data passed to the policy evaluation request. |
environment |
dict |
No | Environment-specific data used during policy evaluation. |
runtime_fine_tune |
SQLAuthorizerRuntimeFineTune |
No | Optional parameters used to adjust policy decision resolution at runtime. |
Request Flags
| Flag | Type | Description |
|---|---|---|
empty_rls_treat_as_denied |
bool |
Treat empty row-level security policies as denied. |
empty_cls_treat_as_permitted |
bool |
Treat empty column-level security policies as permitted. |
ignore_runtime_cls_response |
bool |
Ignore runtime column-level security responses from the policy engine. |
expand_star_column |
bool |
Expand * in the query into explicit column names when applying policies. |
opposite_column_filtering_behavior |
bool |
Apply the opposite behavior for column filtering during enforcement. |
policies_join_operation |
PoliciesJoinOperation |
Defines how multiple policies are combined (OR or AND). |
runtime_cls_as_masked |
bool |
Treat runtime column-level security results as masked values. |
columns_resource_type |
str |
Resource type used when evaluating column-level policies. |
Code Format
from langchain_plainid import (
PlainIDSQLAuthorizer,
SQLAuthorizerFlags,
SQLAuthorizerRequest,
)
from langchain_plainid.models.sql_authorizer_models import PoliciesJoinOperation
sql_authorizer = PlainIDSQLAuthorizer(
base_url="https://your-sql-authz.plainid.cloud",
client_id="your_client_id",
client_secret="your_client_secret",
)
request = SQLAuthorizerRequest(
sql="SELECT * FROM accounts WHERE country = 'US'",
entity_id="your_entity_id",
entity_type_id="User",
flags=SQLAuthorizerFlags(
empty_rls_treat_as_denied=True,
empty_cls_treat_as_permitted=True,
expand_star_column=True,
policies_join_operation=PoliciesJoinOperation.OR,
),
)
response = sql_authorizer.authorize_sql(request)
print(response.sql) # The modified SQL query
print(response.was_modified) # True if policies were applied
print(response.error) # Empty string if no errors
Using a JWT token instead of client secret
sql_authorizer = PlainIDSQLAuthorizer(
base_url="https://your-sql-authz.plainid.cloud",
client_id="your_client_id",
)
response = sql_authorizer.authorize_sql(request, auth_token="your_jwt_token")
For a practical demonstration of these capabilities and a detailed walkthrough of a real-world implementation, refer to the Langchain Authorizer Use-case documentation.