From ecfbfa1b90111446253fefcdfaa7f56aba08cd1f Mon Sep 17 00:00:00 2001 From: William FH <13333726+hinthornw@users.noreply.github.com> Date: Thu, 9 Jan 2025 16:58:19 -0800 Subject: [PATCH] Update auth docstrings (#2977) --- docs/docs/how-tos/auth/custom_auth.md | 9 ++++++++- docs/docs/tutorials/auth/resource_auth.md | 7 +++++++ libs/sdk-py/langgraph_sdk/auth/__init__.py | 13 +++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/docs/docs/how-tos/auth/custom_auth.md b/docs/docs/how-tos/auth/custom_auth.md index 007883315..3944553cb 100644 --- a/docs/docs/how-tos/auth/custom_auth.md +++ b/docs/docs/how-tos/auth/custom_auth.md @@ -37,7 +37,7 @@ async def authenticate(authorization: str) -> str: detail="Invalid token" ) -# Optional: Add authorization rules +# Add authorization rules to actually control access to resources @my_auth.on async def add_owner( ctx: Auth.types.AuthContext, @@ -48,6 +48,13 @@ async def add_owner( metadata = value.setdefault("metadata", {}) metadata.update(filters) return filters + +# Assumes you organize information in store like (user_id, resource_type, resource_id) +@my_auth.on.store() +async def authorize_store(ctx: Auth.types.AuthContext, value: dict): + namespace: tuple = value["namespace"] + assert namespace[0] == ctx.user.identity, "Not authorized" + ``` ## 2. Update configuration diff --git a/docs/docs/tutorials/auth/resource_auth.md b/docs/docs/tutorials/auth/resource_auth.md index 46494e4ff..5aae86110 100644 --- a/docs/docs/tutorials/auth/resource_auth.md +++ b/docs/docs/tutorials/auth/resource_auth.md @@ -275,6 +275,13 @@ async def on_assistants( status_code=403, detail="User lacks the required permissions.", ) + +# Assumes you organize information in store like (user_id, resource_type, resource_id) +@auth.on.store() +async def authorize_store(ctx: Auth.types.AuthContext, value: dict): + # The "namespace" field for each store item is a tuple you can think of as the directory of an item. + namespace: tuple = value["namespace"] + assert namespace[0] == ctx.user.identity, "Not authorized" ``` Notice that instead of one global handler, we now have specific handlers for: diff --git a/libs/sdk-py/langgraph_sdk/auth/__init__.py b/libs/sdk-py/langgraph_sdk/auth/__init__.py index 8b3e21779..e0dff9c49 100644 --- a/libs/sdk-py/langgraph_sdk/auth/__init__.py +++ b/libs/sdk-py/langgraph_sdk/auth/__init__.py @@ -69,6 +69,10 @@ async def authorize_default(params: Auth.on.value): async def authorize_thread_create(params: Auth.on.threads.create.value): # Allow the allowed user to create a thread assert params.get("metadata", {}).get("owner") == "allowed_user" + + @auth.on.store + async def authorize_store(ctx: Auth.types.AuthContext, value: Auth.types.on): + assert ctx.user.identity in value["namespace"], "Not authorized" ``` ???+ note "Request Processing Flow" @@ -157,6 +161,15 @@ async def rate_limit_writes(ctx: AuthContext, value: Any) -> bool: # Implement rate limiting for write operations return await check_rate_limit(ctx.user.identity) ``` + + Auth for the `store` resource is a bit different since its structure is developer defined. + You typically want to enforce user creds in the namespace. Y + ```python + @auth.on.store + async def check_store_access(ctx: AuthContext, value: Auth.types.on) -> bool: + # Assuming you structure your store like (store.aput((user_id, application_context), key, value)) + assert value["namespace"][0] == ctx.user.identity + ``` """ # These are accessed by the API. Changes to their names or types is # will be considered a breaking change.