from langgraph_sdk import Authauth = Auth()@auth.authenticateasync def authenticate(headers: dict) -> Auth.types.MinimalUserDict: # Validate credentials (e.g., API key, JWT token) api_key = headers.get(b"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" }
사용자 정의 인증은 위임된 접근을 허용합니다. @auth.authenticate에서 반환하는 값은 실행 컨텍스트에 추가되므로, 에이전트에 사용자 범위 자격 증명을 제공하면 사용자를 대신하여 리소스에 접근할 수 있습니다.인증 후 플랫폼은 구성 가능한 컨텍스트를 통해 그래프 및 모든 노드에 전달되는 특수 구성 객체를 생성합니다.
이 객체에는 @auth.authenticate 핸들러에서 반환한 사용자 정의 필드를 포함하여 현재 사용자에 대한 정보가 포함됩니다.에이전트가 사용자를 대신하여 작업할 수 있도록 하려면 사용자 정의 인증 미들웨어를 사용하세요. 이를 통해 에이전트는 사용자를 대신하여 MCP 서버, 외부 데이터베이스, 심지어 다른 에이전트와 같은 외부 시스템과 상호 작용할 수 있습니다.자세한 내용은 사용자 정의 인증 사용 가이드를 참조하세요.
인증 후 LangGraph는 @auth.on 핸들러를 호출하여 특정 리소스(예: 스레드, 어시스턴트, 크론)에 대한 접근을 제어합니다. 이러한 핸들러는 다음을 수행할 수 있습니다:
value["metadata"] 딕셔너리를 직접 변경하여 리소스 생성 시 저장될 메타데이터를 추가합니다. 각 작업에 대해 값이 가질 수 있는 유형 목록은 지원되는 작업 표를 참조하세요.
필터 딕셔너리를 반환하여 검색/목록 또는 읽기 작업 중 메타데이터로 리소스를 필터링합니다.
접근이 거부된 경우 HTTP 예외를 발생시킵니다.
단순히 사용자 범위 접근 제어를 구현하려면 모든 리소스 및 작업에 대해 단일 @auth.on 핸들러를 사용할 수 있습니다. 리소스 및 작업에 따라 다른 제어를 원하면 리소스별 핸들러를 사용할 수 있습니다. 접근 제어를 지원하는 리소스의 전체 목록은 지원되는 리소스 섹션을 참조하세요.
Copy
@auth.onasync def add_owner( ctx: Auth.types.AuthContext, value: dict # The payload being sent to this access method) -> dict: # Returns a filter dict that restricts access to resources """Authorize all access to threads, runs, crons, and assistants. This handler does two things: - Adds a value to resource metadata (to persist with the resource so it can be filtered later) - Returns a filter (to restrict access to existing resources) Args: ctx: Authentication context containing user info, permissions, the path, and value: The request payload sent to the endpoint. For creation operations, this contains the resource parameters. For read operations, this contains the resource being accessed. Returns: A filter dictionary that LangGraph uses to restrict access to resources. See [Filter Operations](#filter-operations) for supported operators. """ # Create filter to restrict access to just this user's resources filters = {"owner": ctx.user.identity} # Get or create the metadata dictionary in the payload # This is where we store persistent info about the resource metadata = value.setdefault("metadata", {}) # Add owner to metadata - if this is a create or update operation, # this information will be saved with the resource # So we can filter by it later in read operations metadata.update(filters) # Return filters to restrict access # These filters are applied to ALL operations (create, read, update, search, etc.) # to ensure users can only access their own resources return filters
@auth.on 데코레이터로 리소스와 작업 이름을 연결하여 특정 리소스 및 작업에 대한 핸들러를 등록할 수 있습니다.
요청이 이루어지면 해당 리소스 및 작업과 일치하는 가장 구체적인 핸들러가 호출됩니다. 다음은 특정 리소스 및 작업에 대한 핸들러를 등록하는 방법의 예입니다. 다음 설정의 경우:
인증된 사용자는 스레드를 생성하고, 스레드를 읽고, 스레드에서 실행을 생성할 수 있습니다
“assistants:create” 권한이 있는 사용자만 새 어시스턴트를 생성할 수 있습니다
다른 모든 엔드포인트(예: 어시스턴트 삭제, 크론, 저장소)는 모든 사용자에 대해 비활성화됩니다.
지원되는 핸들러
지원되는 리소스 및 작업의 전체 목록은 아래의 지원되는 리소스 섹션을 참조하세요.
Copy
# Generic / global handler catches calls that aren't handled by more specific handlers@auth.onasync def reject_unhandled_requests(ctx: Auth.types.AuthContext, value: Any) -> False: print(f"Request to {ctx.path} by {ctx.user.identity}") raise Auth.exceptions.HTTPException( status_code=403, detail="Forbidden" )# Matches the "thread" resource and all actions - create, read, update, delete, search# Since this is **more specific** than the generic @auth.on handler, it will take precedence# over the generic handler for all actions on the "threads" resource@auth.on.threadsasync def on_thread( ctx: Auth.types.AuthContext, value: Auth.types.threads.create.value): # Setting metadata on the thread being created # will ensure that the resource contains an "owner" field # Then any time a user tries to access this thread or runs within the thread, # we can filter by owner metadata = value.setdefault("metadata", {}) metadata["owner"] = ctx.user.identity return {"owner": ctx.user.identity}# Thread creation. This will match only on thread create actions# Since this is **more specific** than both the generic @auth.on handler and the @auth.on.threads handler,# it will take precedence for any "create" actions on the "threads" resources@auth.on.threads.createasync def on_thread_create( ctx: Auth.types.AuthContext, value: Auth.types.threads.create.value): # Reject if the user does not have write access if "write" not in ctx.permissions: raise Auth.exceptions.HTTPException( status_code=403, detail="User lacks the required permissions." ) # Setting metadata on the thread being created # will ensure that the resource contains an "owner" field # Then any time a user tries to access this thread or runs within the thread, # we can filter by owner metadata = value.setdefault("metadata", {}) metadata["owner"] = ctx.user.identity return {"owner": ctx.user.identity}# Reading a thread. Since this is also more specific than the generic @auth.on handler, and the @auth.on.threads handler,# it will take precedence for any "read" actions on the "threads" resource@auth.on.threads.readasync def on_thread_read( ctx: Auth.types.AuthContext, value: Auth.types.threads.read.value): # Since we are reading (and not creating) a thread, # we don't need to set metadata. We just need to # return a filter to ensure users can only see their own threads return {"owner": ctx.user.identity}# Run creation, streaming, updates, etc.# This takes precedenceover the generic @auth.on handler and the @auth.on.threads handler@auth.on.threads.create_runasync def on_run_create( ctx: Auth.types.AuthContext, value: Auth.types.threads.create_run.value): metadata = value.setdefault("metadata", {}) metadata["owner"] = ctx.user.identity # Inherit thread's access control return {"owner": ctx.user.identity}# Assistant creation@auth.on.assistants.createasync def on_assistant_create( ctx: Auth.types.AuthContext, value: Auth.types.assistants.create.value): if "assistants:create" not in ctx.permissions: raise Auth.exceptions.HTTPException( status_code=403, detail="User lacks the required permissions." )
위의 예에서 전역 핸들러와 리소스별 핸들러를 혼합하고 있습니다. 각 요청은 가장 구체적인 핸들러에 의해 처리되므로 thread를 생성하는 요청은 on_thread_create 핸들러와 일치하지만 reject_unhandled_requests 핸들러와는 일치하지 않습니다. 그러나 스레드를 업데이트하는 요청은 해당 리소스 및 작업에 대한 더 구체적인 핸들러가 없으므로 전역 핸들러에 의해 처리됩니다.
필터 딕셔너리는 리소스 메타데이터와 일치하는 키를 가진 딕셔너리입니다. 세 가지 연산자를 지원합니다:
기본값은 정확히 일치하는 “$eq”의 약칭입니다. 예를 들어 {"owner": user_id}는 메타데이터에 {"owner": user_id}가 포함된 리소스만 포함합니다
$eq: 정확히 일치 (예: {"owner": {"$eq": user_id}}) - 이는 위의 약칭인 {"owner": user_id}와 동일합니다
$contains: 리스트 멤버십 (예: {"allowed_users": {"$contains": user_id}}) 또는 리스트 포함 (예: {"allowed_users": {"$contains": [user_id_1, user_id_2]}}). 여기서 값은 각각 리스트의 요소이거나 리스트 요소의 하위 집합이어야 합니다. 저장된 리소스의 메타데이터는 리스트/컨테이너 유형이어야 합니다.
여러 키가 있는 딕셔너리는 논리적 AND 필터를 사용하여 처리됩니다. 예를 들어 {"owner": org_id, "allowed_users": {"$contains": user_id}}는 메타데이터의 “owner”가 org_id이고 “allowed_users” 리스트에 user_id가 포함된 리소스만 일치시킵니다.
자세한 내용은 여기 참조를 확인하세요.
LangGraph는 가장 일반적인 것부터 가장 구체적인 것까지 세 가지 수준의 권한 부여 핸들러를 제공합니다:
전역 핸들러 (@auth.on): 모든 리소스 및 작업과 일치
리소스 핸들러 (예: @auth.on.threads, @auth.on.assistants, @auth.on.crons): 특정 리소스에 대한 모든 작업과 일치
작업 핸들러 (예: @auth.on.threads.create, @auth.on.threads.read): 특정 리소스에 대한 특정 작업과 일치
가장 구체적으로 일치하는 핸들러가 사용됩니다. 예를 들어 @auth.on.threads.create는 스레드 생성의 경우 @auth.on.threads보다 우선합니다.
더 구체적인 핸들러가 등록되면 해당 리소스 및 작업에 대해 더 일반적인 핸들러가 호출되지 않습니다.
“타입 안전성”
각 핸들러는 Auth.types.on.<resource>.<action>.value에서 value 매개변수에 대한 타입 힌트를 사용할 수 있습니다. 예를 들어:
Copy
@auth.on.threads.createasync def on_thread_create(ctx: Auth.types.AuthContext,value: Auth.types.on.threads.create.value # Specific type for thread creation):...@auth.on.threadsasync def on_threads(ctx: Auth.types.AuthContext,value: Auth.types.on.threads.value # Union type of all thread actions):...@auth.onasync def on_all(ctx: Auth.types.AuthContext,value: dict # Union type of all possible actions):...
“실행에 대하여”실행은 접근 제어를 위해 상위 스레드로 범위가 지정됩니다. 이는 권한이 일반적으로 스레드에서 상속되어 데이터 모델의 대화 특성을 반영함을 의미합니다. 생성을 제외한 모든 실행 작업(읽기, 목록 조회)은 스레드의 핸들러에 의해 제어됩니다.
핸들러에서 볼 수 있는 인수가 더 많기 때문에 새 실행을 생성하기 위한 특정 create_run 핸들러가 있습니다.