Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Docs] Add auth docs #2797

Merged
merged 33 commits into from
Dec 18, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
245cf83
[Docs] Add auth tutorial
hinthornw Dec 17, 2024
3dd1e67
Add mermaid
hinthornw Dec 17, 2024
6d160a2
Merge branch 'main' into wfh/docs/auth
hinthornw Dec 17, 2024
23e18e8
Add how-tos
hinthornw Dec 17, 2024
12f2a48
Add concepts
hinthornw Dec 18, 2024
ba56ba1
Add langgraph reference
hinthornw Dec 18, 2024
7904fdc
Update explanations
hinthornw Dec 18, 2024
97b3d1b
Simplify tutorial
hinthornw Dec 18, 2024
1c97bdd
Fix cross linking
hinthornw Dec 18, 2024
b85c996
Split into 3
hinthornw Dec 18, 2024
30e647a
Add links
hinthornw Dec 18, 2024
2df2f41
Merge branch 'main' into wfh/docs/auth
hinthornw Dec 18, 2024
8fc3f20
Add pt 2 and 3
hinthornw Dec 18, 2024
b3230cf
More guidance
hinthornw Dec 18, 2024
83e4e6c
Update docs/docs/tutorials/auth/add_auth_server.md
hinthornw Dec 18, 2024
51c57cc
[SDK] Add studio user object
hinthornw Dec 18, 2024
e20bd15
lib: Fix incorrect default for Command.update
nfcampos Dec 18, 2024
7e8a68f
Fix
nfcampos Dec 18, 2024
ef88b80
0.2.60
nfcampos Dec 18, 2024
dabd75f
build(deps-dev): bump tornado from 6.4.1 to 6.4.2 (#2519)
dependabot[bot] Dec 18, 2024
532bc71
langgraph[patch]: format messages in state (#2199)
baskaryan Dec 18, 2024
6b45a28
fix example in docs of state_schema in create_react_agent (#2109)
jokerkeny Dec 18, 2024
d9cc227
Unpin install command in doc
hinthornw Dec 18, 2024
1e07a9a
Add admonition
hinthornw Dec 18, 2024
7f0bfdc
Feedback
hinthornw Dec 18, 2024
6954e63
Numbering
hinthornw Dec 18, 2024
948027a
Notebook style
hinthornw Dec 18, 2024
2e1971b
Remaining feedback
hinthornw Dec 18, 2024
f89fe49
Merge branch 'main' into wfh/docs/auth
hinthornw Dec 18, 2024
06e1e4e
Update cross-linking
hinthornw Dec 18, 2024
d4a1fe5
concept
hinthornw Dec 18, 2024
1709cd3
concept
hinthornw Dec 18, 2024
b7f57b1
Merge branch 'main' into wfh/docs/auth
hinthornw Dec 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 32 additions & 3 deletions docs/docs/cloud/reference/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,11 @@ The LangGraph command line interface includes commands to build and run a LangGr

The LangGraph CLI requires a JSON configuration file with the following keys:

| Key | Description |
| Key | Description |
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `dependencies` | **Required**. Array of dependencies for LangGraph Cloud API server. Dependencies can be one of the following: (1) `"."`, which will look for local Python packages, (2) `pyproject.toml`, `setup.py` or `requirements.txt` in the app directory `"./local_package"`, or (3) a package name. |
| `graphs` | **Required**. Mapping from graph ID to path where the compiled graph or a function that makes a graph is defined. Example: <ul><li>`./your_package/your_file.py:variable`, where `variable` is an instance of `langgraph.graph.state.CompiledStateGraph`</li><li>`./your_package/your_file.py:make_graph`, where `make_graph` is a function that takes a config dictionary (`langchain_core.runnables.RunnableConfig`) and creates an instance of `langgraph.graph.state.StateGraph` / `langgraph.graph.state.CompiledStateGraph`.</li></ul> |
| `auth` | _(Added in v0.0.11)_ Auth configuration containing the path to your authentication handler. Example: `./your_package/auth.py:auth`, where `auth` is an instance of `langgraph_sdk.Auth`. See [authentication guide](../../concepts/auth.md) for details. |
| `env` | Path to `.env` file or a mapping from environment variable to its value. |
| `store` | Configuration for adding semantic search to the BaseStore. Contains the following fields: <ul><li>`index`: Configuration for semantic search indexing with fields:<ul><li>`embed`: Embedding provider (e.g., "openai:text-embedding-3-small") or path to custom embedding function</li><li>`dims`: Dimension size of the embedding model. Used to initialize the vector table.</li><li>`fields` (optional): List of fields to index. Defaults to `["$"]`, meaningto index entire documents. Can be specific fields like `["text", "summary", "some.value"]`</li></ul></li></ul> |
| `python_version` | `3.11` or `3.12`. Defaults to `3.11`. |
Expand Down Expand Up @@ -120,6 +121,35 @@ def embed_texts(texts: list[str]) -> list[list[float]]:
return [[0.1, 0.2, ...] for _ in texts] # dims-dimensional vectors
```

#### Adding custom authentication

```json
{
"dependencies": ["."],
"graphs": {
"chat": "./chat/graph.py:graph"
},
"auth": {
"path": "./auth.py:auth",
"openapi": {
"securitySchemes": {
"apiKeyAuth": {
"type": "apiKey",
"in": "header",
"name": "X-API-Key"
}
},
"security": [
{"apiKeyAuth": []}
]
},
"disable_studio_auth": false
}
}
```

See the [authentication conceptual guide](../../concepts/auth.md) for details, and the [setting up custom authentication](../../tutorials/auth/getting_started.md) guide for a practical walk through of the process.

## Commands

The base command for the LangGraph CLI is `langgraph`.
Expand Down Expand Up @@ -257,5 +287,4 @@ RUN set -ex && \

RUN PIP_CONFIG_FILE=/pipconfig.txt PYTHONDONTWRITEBYTECODE=1 pip install --no-cache-dir -c /api/constraints.txt -e /deps/*

ENV LANGSERVE_GRAPHS='{"agent": "/deps/__outer_graphs/src/agent.py:graph", "storm": "/deps/__outer_graphs/src/storm.py:graph"}'
```
ENV LANGSERVE_GRAPHS='{"agent": "/deps/__outer_graphs/src/agent.py:graph", "storm": "/deps/__outer_graphs/src/storm.py:graph"}'
1 change: 0 additions & 1 deletion docs/docs/cloud/reference/sdk/python_sdk_ref.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
::: langgraph_sdk.schema
handler: python


::: langgraph_sdk.auth
handler: python

Expand Down
291 changes: 291 additions & 0 deletions docs/docs/concepts/auth.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,291 @@
# Authentication & Access Control

LangGraph Platform provides a flexible authentication and authorization system that can integrate with most authentication schemes. This guide explains the core concepts and how they work together.
hinthornw marked this conversation as resolved.
Show resolved Hide resolved

!!! note "Python only"

We currently only support custom authentication and authorization in Python deployments with `langgraph-api>=0.0.11`. Support for LangGraph.JS will be added soon.

## Core Concepts

### Authentication vs Authorization

While often used interchangeably, these terms represent distinct security concepts:

- **Authentication** ("AuthN") verifies _who_ you are. This runs as middleware for every request.
- **Authorization** ("AuthZ") determines _what you can do_. This validates the user's privileges and roles on a per-resource basis.

In LangGraph Platform, authentication is handled by your [`@auth.authenticate`](../cloud/reference/sdk/python_sdk_ref.md#langgraph_sdk.auth.Auth.authenticate) handler, and authorization is handled by your [`@auth.on`](../cloud/reference/sdk/python_sdk_ref.md#langgraph_sdk.auth.Auth.on) handlers.

## System Architecture

A typical authentication setup involves three main components:
hinthornw marked this conversation as resolved.
Show resolved Hide resolved

1. **Authentication Provider** (Identity Provider/IdP)
- A dedicated service that manages user identities and credentials
- Examples: Auth0, Supabase Auth, Okta, or your own auth server
- Handles user registration, login, password resets, etc.
- Issues tokens (JWT, session tokens, etc.) after successful authentication

2. **LangGraph Backend** (Resource Server)
- Your LangGraph application that contains business logic and protected resources
- Validates tokens with the auth provider
- Enforces access control based on user identity and permissions
- Never stores user credentials directly

3. **Client Application** (Frontend)
- Web app, mobile app, or API client
- Collects user credentials and sends to auth provider
- Receives tokens from auth provider
- Includes tokens in requests to LangGraph backend

Here's how these components typically interact:

```mermaid
sequenceDiagram
participant Client as Client App
participant Auth as Auth Provider
participant LG as LangGraph Backend

Client->>Auth: 1. Login (username/password)
Auth-->>Client: 2. Return token
Client->>LG: 3. Request with token
LG->>Auth: 4. Validate token
Auth-->>LG: 5. Confirm validity
Note over LG: 6. Apply access control
LG-->>Client: 7. Return resources
```

Your `@auth.authenticate` handler in LangGraph handles steps 4-5, while your `@auth.on` handlers implement step 6.

## Authentication

Authentication in LangGraph runs as middleware on every request. Your `@auth.authenticate` handler receives request information and must:

1. Validate the credentials
2. Return user information if valid
3. Raise an HTTP exception if invalid (or AssertionError)

```python
from langgraph_sdk import Auth

auth = Auth()

@auth.authenticate
async def authenticate(headers: dict) -> Auth.types.MinimalUserDict:
hinthornw marked this conversation as resolved.
Show resolved Hide resolved
# Validate credentials (e.g., API key, JWT token)
api_key = headers.get("x-api-key")
if not api_key or not is_valid_key(api_key):
raise Auth.exceptions.HTTPException(
status_code=401,
detail="Invalid API key"
)

# Return user info - only identity and is_authenticated are required
# Add any additional fields you need for authorization
return {
"identity": "user-123", # Required: unique user identifier
"is_authenticated": True, # Optional: assumed True by default
"permissions": ["read", "write"] # Optional: for permission-based auth
# You can add more custom fields if you want to implement other auth patterns
"role": "admin",
"org_id": "org-456"

}
```

The returned user information is available:

- To your authorization handlers via `ctx.user`
- In your application via `config["configuration"]["langgraph_auth_user"]`

## Authorization

After authentication, LangGraph calls your `@auth.on` handlers to control access to specific resources (e.g., threads, assistants, crons). These handlers can:

1. Add metadata to be saved during resource creation by mutating the `value["metadata"]` dictionary directly.
2. Filter resources by metadata during search/list or read operations by returning a filter dictionary.
hinthornw marked this conversation as resolved.
Show resolved Hide resolved
3. Raise an HTTP exception if access is denied.

If you want to just implement simple user-scoped access control, you can use a single `@auth.on` handler for all resources and actions.

```python
@auth.on
async def add_owner(ctx: Auth.types.AuthContext, value: dict):
hinthornw marked this conversation as resolved.
Show resolved Hide resolved
"""Add owner to resource metadata and filter by owner."""
filters = {"owner": ctx.user.identity}
metadata = value.setdefault("metadata", {})
metadata.update(filters)
return filters
```

### Resource-Specific Handlers

You can register handlers for specific resources and actions using the `@auth.on` decorator.
hinthornw marked this conversation as resolved.
Show resolved Hide resolved
When a request is made, the most specific handler that matches that resource and action is called.

hinthornw marked this conversation as resolved.
Show resolved Hide resolved
```python
# Generic / global handler catches calls that aren't handled by more specific handlers
@auth.on
async def reject_unhandled_requests(ctx: Auth.types.AuthContext, value: Any) -> None:
hinthornw marked this conversation as resolved.
Show resolved Hide resolved
print(f"Request to {ctx.path} by {ctx.user.identity}")
return False

# Thread creation
@auth.on.threads.create
async def on_thread_create(
ctx: Auth.types.AuthContext,
value: Auth.types.threads.create.value
):
metadata = value.setdefault("metadata", {})
metadata["owner"] = ctx.user.identity
return {"owner": ctx.user.identity}

# Thread retrieval
@auth.on.threads.read
async def on_thread_read(
ctx: Auth.types.AuthContext,
value: Auth.types.threads.read.value
):
return {"owner": ctx.user.identity}

# Run creation, streaming, updates, etc.
@auth.on.threads.create_run
async def on_run_create(
ctx: Auth.types.AuthContext,
value: Auth.types.threads.create_run.value
):
# Inherit thread's access control
return {"owner": ctx.user.identity}

# Assistant creation
@auth.on.assistants.create
async def on_assistant_create(
ctx: Auth.types.AuthContext,
value: Auth.types.assistants.create.value
):
if "admin" not in ctx.user.get("role", []):
raise Auth.exceptions.HTTPException(
status_code=403,
detail="Only admins can create assistants"
)
```

Using the setup above, a request to create a `thread` would match the `on_thread_create` handler, since it is the most specific handler for that resource and action. A request to create a `cron`, on the other hand, would match the global handler, since no more specific handler is registered for that resource and action.
hinthornw marked this conversation as resolved.
Show resolved Hide resolved

### Filter Operations

Authorization handlers can return a filter dictionary to filter resources during all operations (both reads and writes). The filter dictionary supports two additional operators:
hinthornw marked this conversation as resolved.
Show resolved Hide resolved

- `$eq`: Exact match (e.g., `{"owner": {"$eq": user_id}}`) - this is equivalent to `{"owner": user_id}`
- `$contains`: List membership (e.g., `{"allowed_users": {"$contains": user_id}}`)
hinthornw marked this conversation as resolved.
Show resolved Hide resolved

A dictionary with multiple keys is converted to a logical `AND` filter. For example, `{"owner": user_id, "org_id": org_id}` is converted to `{"$and": [{"owner": user_id}, {"org_id": org_id}]}`

## Common Access Patterns

Here are some typical authorization patterns:

### Single-Owner Resources
hinthornw marked this conversation as resolved.
Show resolved Hide resolved

```python
@auth.on
async def owner_only(ctx: Auth.types.AuthContext, value: dict):
metadata = value.setdefault("metadata", {})
metadata["owner"] = ctx.user.identity
return {"owner": ctx.user.identity}
```

### Permission-based Access

hinthornw marked this conversation as resolved.
Show resolved Hide resolved
```python
# In your auth handler:
@auth.authenticate
async def authenticate(headers: dict) -> Auth.types.MinimalUserDict:
...
return {
"identity": "user-123",
"is_authenticated": True,
"permissions": ["threads:write", "threads:read"] # Define permissions in auth
}

def _default(ctx: Auth.types.AuthContext, value: dict):
metadata = value.setdefault("metadata", {})
metadata["owner"] = ctx.user.identity
return {"owner": ctx.user.identity}

@auth.on.threads.create
async def create_thread(ctx: Auth.types.AuthContext, value: dict):
if "threads:write" not in ctx.permissions:
raise Auth.exceptions.HTTPException(
status_code=403,
detail="Unauthorized"
)
return _default(ctx, value)


@auth.on.threads.read
async def rbac_create(ctx: Auth.types.AuthContext, value: dict):
if "threads:read" not in ctx.permissions and "threads:write" not in ctx.permissions:
raise Auth.exceptions.HTTPException(
status_code=403,
detail="Unauthorized"
)
return _default(ctx, value)
```

## Supported Resources

LangGraph provides authorization handlers for the following resource types:

### Threads
hinthornw marked this conversation as resolved.
Show resolved Hide resolved
- `@auth.on.threads.create` - Thread creation
- `@auth.on.threads.read` - Thread retrieval
- `@auth.on.threads.update` - Thread updates
- `@auth.on.threads.delete` - Thread deletion
- `@auth.on.threads.search` - Listing threads

**Runs:** are scoped to their parent thread for access control. This means permissions are typically inherited from the thread, reflecting the conversational nature of the data model.

- `@auth.on.threads.create_run` - Creating or updating a run

All other run operations (reading, listing) are controlled by the thread's handlers, since runs are always accessed in the context of their thread.

### Assistants
- `@auth.on.assistants.create` - Assistant creation
- `@auth.on.assistants.read` - Assistant retrieval
- `@auth.on.assistants.update` - Assistant updates
- `@auth.on.assistants.delete` - Assistant deletion
- `@auth.on.assistants.search` - Listing assistants

### Crons
- `@auth.on.crons.create` - Cron job creation
- `@auth.on.crons.read` - Cron job retrieval
- `@auth.on.crons.update` - Cron job updates
- `@auth.on.crons.delete` - Cron job deletion
- `@auth.on.crons.search` - Listing cron jobs

You can also use the global `@auth.on` handler to implement a single access control policy across all resources and actions, or resource level `@auth.on.threads`, etc. handlers to implement control over all actions of a single resource.

## Default Security Models

LangGraph Platform provides different security defaults:

### LangGraph Cloud

- Uses LangSmith API keys by default
- Requires valid API key in `x-api-key` header
- Can be customized with your auth handler

### Self-Hosted

- No default authentication
- Complete flexibility to implement your security model
- You control all aspects of authentication and authorization

## Next Steps

For implementation details:

- [Setting up authentication](../tutorials/auth/getting_started.md)
- [Custom auth handlers](../how-tos/auth/custom_auth.md)
1 change: 1 addition & 0 deletions docs/docs/concepts/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ The LangGraph Platform comprises several components that work together to suppor
- [Web-hooks](./langgraph_server.md#webhooks): Webhooks allow your running LangGraph application to send data to external services on specific events.
- [Cron Jobs](./langgraph_server.md#cron-jobs): Cron jobs are a way to schedule tasks to run at specific times in your LangGraph application.
- [Double Texting](./double_texting.md): Double texting is a common issue in LLM applications where users may send multiple messages before the graph has finished running. This guide explains how to handle double texting with LangGraph Deploy.
- [Authentication & Access Control](./auth.md): Learn about options for authentication and access control when deploying the LangGraph Platform.

### Deployment Options

Expand Down
Loading
Loading