> ## Documentation Index
> Fetch the complete documentation index at: https://langchain-5e9cc07a-preview-cbmdac-1785296168-e67df75.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Add identity to Managed Deep Agents

> Give each caller their own threads, memory, and credentials so agents stay private and secure in multi-user deployments.

Agents are not anonymous chatbots. As soon as more than one person (or one company) uses a deployment, you need to know: **whose conversation is this, and whose data may the agent see or act on?** Identity lets one deployment serve thousands of users safely, with no data leakage between callers.

Managed Deep Agents answers that question before every run. You declare a small contract once, and the runtime partitions threads, [memory](/langsmith/managed-deep-agents-memory), and credentials so callers cannot see or affect each other.

Identity is opt-in. Projects without `identity.ts` or `identity.py` compile and deploy unchanged. When you add a declaration, `mda` wires auth, scoping, and a frozen `runtime.identity` object into tools and middleware.

This page assumes you have an existing Managed Deep Agents project and the `mda` CLI installed. If you are new to Managed Deep Agents, start with the [overview](/langsmith/managed-deep-agents-overview) and [quickstart](/langsmith/managed-deep-agents-quickstart) first.

<Note>
  Managed Deep Agents is in **private [beta](/langsmith/release-stages)**, available on [LangSmith Cloud](/langsmith/cloud) in the US region only. [Join the waitlist](https://www.langchain.com/langsmith-managed-deep-agents-waitlist) to request access.
</Note>

## Why identity matters for agents

Without identity, a Managed Deep Agent has one shared boundary for the whole deployment. That is fine for a personal prototype. It breaks as soon as real users show up:

| What goes wrong       | Example                                                                                                                                                      |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Shared memory**     | Alice asks the agent to remember her API preferences. Bob opens a new chat and the agent already "knows" Alice's details.                                    |
| **Shared threads**    | Anyone who can hit the deployment can resume or inspect another user's conversation.                                                                         |
| **Wrong credentials** | The agent calls GitHub or another API with one shared token, so every user acts as the same account, or you have no safe way to act *as* the signed-in user. |

Deep Agents without identity make this a real problem: they keep durable memory, resume long-running threads, and call tools on the user's behalf. Identity turns "who is calling?" into enforced isolation instead of hoping the prompt or the UI keeps people apart.

A key benefit of identity is that downstream tool calls can act **as the signed-in user** rather than as a shared bot account. For example, with user-scoped credentials, the agent calls GitHub as Alice, not as a single bot token shared across all users.

For deployments with compliance requirements such as SOC 2, GDPR, or HIPAA, identity scoping provides the data segregation boundaries that auditors expect: each caller's threads and memory are isolated, and `runtime.identity` gives you an audit trail of who triggered each run.

<Note>
  Adding identity to a project that previously had none does not delete existing threads or memory. Threads created before identity was enabled remain accessible at the agent scope. New threads are scoped by user (or organization) according to your declaration. To migrate old data, export it and re-create threads under the new scoping rules.
</Note>

## Understand three core concepts

Learn these three concepts before you write any identity config:

| Idea                        | Plain meaning                                                     | Example                                                                          |
| --------------------------- | ----------------------------------------------------------------- | -------------------------------------------------------------------------------- |
| **User**                    | The person or service this run is for                             | `user_123`, a GitHub login, a guest id                                           |
| **Organization** (optional) | The customer or org boundary when one deployment serves many orgs | `acme`, a Slack workspace                                                        |
| **Auth**                    | How the runtime learns who is calling for this request            | Your backend sends identity headers, or the browser sends a verified login token |

A few important clarifications:

* **User** is not the agent. It is the caller the run represents, and it can be a person or a service (`user.kind`).
* **Organization** is not a LangSmith workspace. Single-organization agents have no organization. Users can also carry a read-only `groups` list (every group the caller's token asserts) for authorization checks inside tools; isolation still keys on the single-valued organization.
* **Fail closed** means the runtime rejects any request that is missing a required user or organization. It never falls back to shared memory or threads.

From the user (and optional organization), Managed Deep Agents derives three outcomes:

* **Threads**: who can open or resume a conversation
* **Memory**: which durable [Context Hub](/langsmith/managed-deep-agents-memory) slice the run can see
* **Credentials**: whose token the agent uses for downstream tool calls (the signed-in user, or one shared agent token)

```mermaid theme={null}
flowchart LR
    Caller["Caller"] --> Ingress["Auth authenticates request"]
    Ingress --> Resolve["Resolve user and organization"]
    Resolve --> Scope["Scope threads and memory"]
    Resolve --> Reject["Reject: 403"]
    Scope --> Run["Run agent with runtime.identity"]

    classDef process fill:#E5F4FF,stroke:#006DDD,stroke-width:2px,color:#030710;
    classDef trigger fill:#F6FFDB,stroke:#6E8900,stroke-width:2px,color:#2E3900;
    classDef output fill:#EBD0F0,stroke:#885270,stroke-width:2px,color:#441E33;
    classDef alert fill:#F8E8E6,stroke:#B27D75,stroke-width:2px,color:#634643;
    class Caller trigger;
    class Ingress,Resolve,Scope process;
    class Run output;
    class Reject alert;
```

## Choose a scope

`scope` is the isolation boundary for the deployment. One value covers the common product shapes; per-axis overrides handle the exceptions.

The scope values:

| Value              | Meaning                                                                 |
| ------------------ | ----------------------------------------------------------------------- |
| `user`             | Private to the signed-in person (or service user)                       |
| `organization`     | Shared inside one customer org, isolated from other orgs                |
| `conversation`     | Shared by everyone in the same channel conversation (threads axis only) |
| `agent`            | Shared by the whole deployment                                          |
| *(unset)* / `none` | Not scoped on this axis                                                 |

**Credentials** is often the first thing teams consider:

* **`user`**: downstream calls can act as the signed-in user (for example call GitHub as Alice).
* **`agent`**: downstream calls use one shared bot or service token for everyone.

Choose the declaration that matches your product shape:

| Product shape                           | Declaration                                              | Threads        | Memory         | Credentials |
| --------------------------------------- | -------------------------------------------------------- | -------------- | -------------- | ----------- |
| Private assistant or internal tool      | `defineIdentity()`                                       | `user`         | `user`         | `user`      |
| Multi-tenant SaaS                       | `defineIdentity({ scope: "organization" })`              | `user`         | `organization` | `agent`     |
| Shared channel bot                      | `defineIdentity({ scope: { threads: "conversation" } })` | `conversation` | `user`         | `user`      |
| Service (cron/webhook, no human caller) | `defineIdentity({ scope: "agent" })`                     | `user`         | `agent`        | `agent`     |

All declarations default to `backend` auth (your backend asserts the caller) and optional organizations, except `scope: "organization"`, which requires an organization on every request.

<Tip>
  **How to choose quickly:**

  * One human per conversation who must not see anyone else's data → `defineIdentity()` (the default)
  * SaaS with customer orgs → `defineIdentity({ scope: "organization" })`
  * Shared channel bot → `defineIdentity({ scope: { threads: "conversation" } })`
  * Timer or webhook with no user → `defineIdentity({ scope: "agent" })`
</Tip>

## Add an identity declaration

Create `identity.py` or `identity.ts` next to your agent entry and export a named `identity`. Most projects need no options at all — `defineIdentity()` gives every caller private threads, memory, and credentials behind your backend's auth:

<CodeGroup>
  ```python identity.py theme={null}
  from managed_deepagents import define_identity

  identity = define_identity()
  ```

  ```ts identity.ts theme={null}
  import { defineIdentity } from "managed-deepagents";

  export const identity = defineIdentity();
  ```
</CodeGroup>

That expands to this full contract:

<CodeGroup>
  ```python identity.py theme={null}
  from managed_deepagents import define_identity

  identity = define_identity(
      auth="backend",
      scope={
          "threads": "user",
          "memory": "user",
          "credentials": "user",
      },
  )
  ```

  ```ts identity.ts theme={null}
  import { defineIdentity } from "managed-deepagents";

  export const identity = defineIdentity({
    auth: "backend",
    scope: {
      threads: "user",
      memory: "user",
      credentials: "user",
    },
  });
  ```
</CodeGroup>

Use the full form when you want every field visible, or when you are assembling a config beyond the common shapes. `scope` accepts either one boundary (`"user"`, `"organization"`, `"agent"`, `"none"`) or per-axis overrides such as `{ default: "user", threads: "conversation" }`.

For the full project layout, see the [CLI project file reference](/langsmith/managed-deep-agents-cli#project-file-reference).

When identity is present, `mda` generates the custom auth handler, injects it into the compiled LangGraph app, and only then enables reserved identity headers and token verification.

## Auth: identify the caller

`auth` is the mechanism the runtime uses to identify the user (and organization) for each request. Choose one HTTP mode: `"backend"` or a validated-token provider list.

### Backend (recommended default)

Your own API authenticates the user (session, OAuth, or similar), then proxies LangGraph requests with a shared ingress secret and reserved identity headers. The browser never sends the secret or raw identity-provider (IdP) tokens to Managed Deep Agents.

This is the default, and the recommended choice when you already have a backend in front of the agent. For the broader LangGraph auth model, see [Add auth to your server](/langsmith/add-auth-server).

Required headers (case-insensitive):

| Header                  | Required                        | Purpose                                                           |
| ----------------------- | ------------------------------- | ----------------------------------------------------------------- |
| `X-MDA-Ingress-Secret`  | Yes                             | Shared secret from `MDA_INGRESS_SECRET`                           |
| `X-MDA-User-Id`         | Yes                             | User id for this run                                              |
| `X-MDA-Organization-Id` | When organizations are required | Organization id for this run                                      |
| `X-MDA-Groups`          | No                              | Comma- or space-delimited group ids, exposed as `identity.groups` |

The default declaration already uses backend auth, so there is nothing to set:

<CodeGroup>
  ```python identity.py theme={null}
  from managed_deepagents import define_identity

  identity = define_identity()
  ```

  ```ts identity.ts theme={null}
  import { defineIdentity } from "managed-deepagents";

  export const identity = defineIdentity();
  ```
</CodeGroup>

Put `MDA_INGRESS_SECRET` in `.env` for `mda dev` and as a hosted deployment secret for `mda deploy`. In production, your backend authenticates the user, then attaches the identity headers (`X-MDA-Ingress-Secret`, `X-MDA-User-Id`, and `X-MDA-Organization-Id` when applicable) when proxying agent traffic.

Example shape for a backend proxy (pseudocode):

```ts theme={null}
// After your app authenticates the user
await fetch(`${deploymentUrl}/threads/${threadId}/runs`, {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-MDA-Ingress-Secret": process.env.MDA_INGRESS_SECRET!,
    "X-MDA-User-Id": authenticatedUser.id,
    // "X-MDA-Organization-Id": org.id, // only when organizations are required
  },
  body: JSON.stringify(runBody),
});
```

<Warning>
  Never commit ingress secrets or IdP credentials. Only send `MDA_INGRESS_SECRET` from a trusted backend proxy, never from the browser.
</Warning>

### Validated token (browser-direct)

Use this when the browser talks to the deployment directly and you do not want a proxy that asserts user headers.

The client sends `Authorization: Bearer <token>`. Managed Deep Agents verifies the token server-side and maps claims (fields inside the token, such as user id) into `runtime.identity`.

Verification can use:

* **JWKS**: public keys your IdP publishes so the runtime can verify signed JWTs
* **OIDC discovery**: standard metadata that points the runtime at those keys
* **Opaque introspection**: call the IdP to ask whether a non-JWT token is still valid
* **Guest tokens**: short-lived tokens signed by Managed Deep Agents for anonymous visitors

Pass one provider or a list to `auth` to enable validated-token ingress. The following example combines Supabase sign-in with optional guest access:

<CodeGroup>
  ```python identity.py theme={null}
  from managed_deepagents import auth, define_identity

  identity = define_identity(
      auth=[
          auth.supabase(project_ref="your-project-ref"),
          auth.guest(ttl="24h", user_prefix="guest:"),
      ],
  )
  ```

  ```ts identity.ts theme={null}
  import { auth, defineIdentity } from "managed-deepagents";

  export const identity = defineIdentity({
    auth: [
      auth.supabase({ projectRef: "your-project-ref" }),
      auth.guest({ ttl: "24h", userPrefix: "guest:" }),
    ],
  });
  ```
</CodeGroup>

In validated-token mode, your frontend signs the user in with the same IdP you configured, reads an access token (or ID token where applicable), and passes it to the LangGraph client as `Authorization: Bearer <token>`. Do not send refresh tokens or client secrets to the deployment.

When you configure more than one provider, give each entry a unique `id`. The runtime routes JWT providers by token `iss` (issuer) and returns 401 when the issuer does not match any configured provider.

For provider-specific options and client examples, see [Provider setup guides](#provider-setup-guides).

## Secrets checklist

| Secret                  | How Managed Deep Agents uses it                                                                                                                |
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `MDA_INGRESS_SECRET`    | Shared secret your backend sends in `X-MDA-Ingress-Secret`. The runtime checks it before trusting `X-MDA-User-Id` and `X-MDA-Organization-Id`. |
| `MDA_GUEST_SIGNING_KEY` | Key used to sign guest tokens at `POST /identity/guest` and to verify them on later requests.                                                  |

Put local values in `.env`. `mda deploy` forwards non-reserved `.env` values as hosted deployment secrets. Provider-specific secrets (for example Supabase introspection) are listed in [Provider setup guides](#provider-setup-guides).

<Warning>
  Never commit ingress secrets, guest signing keys, or IdP credentials. Only send `MDA_INGRESS_SECRET` from a trusted backend proxy, never from the browser.
</Warning>

## Use `runtime.identity` in tools and middleware

When identity is declared, authored tools and middleware receive a frozen `runtime.identity` object built from the trusted auth result. Client-supplied spoofable identity keys are stripped from `configurable`.

The identity object looks like this:

```ts theme={null}
runtime.identity = {
  user: { kind: "person" | "service", id: string, email?: string },
  organization?: { id: string },
  groups?: readonly string[],
  source: {
    provider: "http" | "slack" | "schedule" | "cli" | "studio",
    threadId?: string,
  },
  claims?: Record<string, unknown>, // populated for validated-token auth
};
```

Annotate the injected `runtime` parameter as `ManagedDeepAgentRuntime` so you get typed access to `identity` (and optional `credentials`). Use it whenever a tool or middleware hook needs to know *who* triggered the run, for personalization, audit logs, or branching on verified claims, without trusting anything from the request body.

<CodeGroup>
  ```python tools/whoami.py theme={null}
  from langchain.tools import tool
  from managed_deepagents import ManagedDeepAgentRuntime


  @tool
  def whoami(runtime: ManagedDeepAgentRuntime) -> str:
      """Return the authenticated user id for this run."""
      identity = runtime.identity
      if not identity:
          return "No authenticated caller on this run."
      return f"Signed in as {identity['user']['id']}"
  ```

  ```ts tools/whoami.ts theme={null}
  import { z } from "zod";
  import { tool } from "langchain";
  import type { ManagedDeepAgentRuntime } from "managed-deepagents";

  export const whoami = tool(
    async (_input, runtime: ManagedDeepAgentRuntime) => {
      const identity = runtime.identity;
      if (!identity) {
        return "No authenticated caller on this run.";
      }
      return `Signed in as ${identity.user.id}`;
    },
    {
      name: "whoami",
      description: "Return the authenticated user id for this run.",
      schema: z.object({}),
    },
  );
  ```
</CodeGroup>

The same type works in middleware hooks:

<CodeGroup>
  ```python middleware/audit.py theme={null}
  from langchain.agents.middleware import AgentState, before_model
  from managed_deepagents import ManagedDeepAgentRuntime


  def audit_middleware():
      @before_model
      def audit(state: AgentState, runtime: ManagedDeepAgentRuntime) -> dict | None:
          user = runtime.identity["user"]["id"] if runtime.identity else "anonymous"
          print(f"[audit] {user} model call with {len(state['messages'])} messages")
          return None

      return audit
  ```

  ```ts middleware/audit.ts theme={null}
  import { createMiddleware } from "langchain";
  import type { ManagedDeepAgentRuntime } from "managed-deepagents";

  export function auditMiddleware() {
    return createMiddleware({
      name: "audit",
      beforeModel: (state, runtime: ManagedDeepAgentRuntime) => {
        const user = runtime.identity?.user.id ?? "anonymous";
        console.log(
          `[audit] ${user} model call with ${state.messages.length} messages`
        );
        return undefined;
      },
    });
  }
  ```
</CodeGroup>

Prefer `runtime.identity` over client-supplied configurable keys for user or organization ids. For other per-run values such as feature flags, use normal LangChain runtime context.

## Customize scoping

The common shapes cover most cases. To customize, set `scope` per axis:

| Axis          | Values                                  | Meaning                                                 |
| ------------- | --------------------------------------- | ------------------------------------------------------- |
| `threads`     | `user`, `conversation`, `organization`  | Who can open or resume the conversation                 |
| `memory`      | `user`, `organization`, `agent`, `none` | Which Context Hub memory slice is remounted for the run |
| `credentials` | `user`, `agent`, `none`, `custom`       | Whose credentials downstream calls use                  |

Do not set any scoping axis to `"organization"` when organizations are optional, there may be no organization to scope by. If a request is missing the user or organization id that scoping needs, Managed Deep Agents rejects it with 403 instead of falling back to shared data.

For how memory paths remount under each scope, see [Scope memory with identity](/langsmith/managed-deep-agents-memory#scope-memory-with-identity).

### Downstream credentials

Declare `credentials` when downstream calls need more than an agent-wide token. `credentials` accepts one resolver or a map keyed by target name, so one deployment can integrate several platforms. Providing any resolver puts the credentials axis into `custom` mode; tools then call `runtime.credentials.for(target)` to obtain the headers for that request. Resolved credentials are kept in memory and are never written to thread state or traces.

<Note>
  The token that proves a caller's identity is not automatically a credential for downstream APIs. For example, a Supabase access token lets Managed Deep Agents identify the caller, but it is not a GitHub API token. Your backend or credential service must hold (and, when needed, refresh) the caller's separately authorized GitHub credential.
</Note>

For GitHub, the first-party `credentials.github` resolver chains token sources per intent. When the chain reads `"user"` tokens, Connect-with-GitHub OAuth routes mount automatically—there is nothing else to declare:

<CodeGroup>
  ```python identity.py theme={null}
  import os

  from managed_deepagents import auth, credentials, define_identity

  identity = define_identity(
      auth=auth.supabase(project_ref="your-project-ref"),
      credentials={
          "github": credentials.github(
              read=["user", "pat"],
              write=["user"],
              pat=os.environ.get("GITHUB_TOKEN") or os.environ.get("GITHUB_PAT"),
          ),
      },
  )
  ```

  ```ts identity.ts theme={null}
  import { auth, credentials, defineIdentity } from "managed-deepagents";

  export const identity = defineIdentity({
    auth: auth.supabase({ projectRef: "your-project-ref" }),
    credentials: {
      github: credentials.github({
        read: ["user", "pat"],
        write: ["user"],
        pat: process.env.GITHUB_TOKEN ?? process.env.GITHUB_PAT,
      }),
    },
  });
  ```
</CodeGroup>

In a GitHub tool, request the headers with `await runtime.credentials.for({ kind: "connection", name: "github", intent: "write" })` and pass them to your GitHub client.

For other platforms, supply a `resolve` function yourself. The following shape lets a user open pull requests as themselves after your application has stored their GitHub grant. `getGitHubAccessToken` is application code: it looks up and refreshes that grant in your server-side credential store.

```ts identity.ts theme={null}
import { auth, defineIdentity } from "managed-deepagents";
import { getGitHubAccessToken } from "./github-credentials.js";

export const identity = defineIdentity({
  auth: auth.supabase({ projectRef: "your-project-ref" }),
  credentials: {
    github: {
      async resolve({ identity, target }) {
        const credential = await getGitHubAccessToken(identity.user.id);
        if (!credential) {
          throw new Error("Connect GitHub before using GitHub tools.");
        }

        return {
          headers: { Authorization: `Bearer ${credential.token}` },
          expiresAt: credential.expiresAt.toISOString(),
        };
      },
    },
  },
});
```

To expose LangSmith capabilities to browsers or other untrusted callers, add a [LangSmith connector](/langsmith/managed-deep-agents-connectors/langsmith). It requires identity so capability routes can resolve the caller and prove ownership before calling LangSmith server-side.

### Connect-with-X routes

Connect-with-X binds an MDA user to an external account via OAuth—either to link a channel identity (Slack) or to populate a per-user credential vault (GitHub). Routes are **inferred** from the rest of the declaration on user-scoped deployments:

* Connect-with-GitHub mounts when a credential chain reads `"user"` tokens.
* Connect-with-Slack mounts when a [Slack channel](/langsmith/managed-deep-agents-channels/slack) is declared.

Declare `connect` only to add providers the inference cannot see, such as a custom OAuth provider:

<CodeGroup>
  ```python identity.py theme={null}
  from managed_deepagents import connect, define_identity

  identity = define_identity(
      connect=[connect.github(), connect.slack()],
  )
  ```

  ```ts identity.ts theme={null}
  import { connect, defineIdentity } from "managed-deepagents";

  export const identity = defineIdentity({
    connect: [connect.github(), connect.slack()],
  });
  ```
</CodeGroup>

## Provider setup guides

The `auth` namespace ships providers for the common identity providers:

| Provider        | Factory                                | Verification                   |
| --------------- | -------------------------------------- | ------------------------------ |
| Auth0           | `auth.auth0({ domain, audience? })`    | JWKS                           |
| Clerk           | `auth.clerk({ domain })`               | JWKS                           |
| Okta            | `auth.okta({ domain, audience? })`     | JWKS                           |
| Amazon Cognito  | `auth.cognito({ userPoolId, region })` | JWKS                           |
| Microsoft Entra | `auth.entra({ tenantId, audience? })`  | JWKS                           |
| Google          | `auth.google({ audience? })`           | JWKS                           |
| Supabase        | `auth.supabase(...)`                   | JWKS (or legacy introspection) |
| Any OIDC IdP    | `auth.oidc({ issuer, audience? })`     | OIDC discovery                 |
| GitHub          | `auth.github()`                        | Opaque token introspection     |
| Guest           | `auth.guest(...)`                      | MDA-signed tokens              |

Each factory returns a plain provider object, so spread it to override the claim mapping (`user`, `organization`, `groups`, `email`) when the token's claims do not match the defaults. The tabs below cover the providers that need extra setup. Use one provider, or combine them as in the [validated token example](#validated-token-browser-direct).

<Tabs>
  <Tab title="Guest tokens">
    Anonymous visitors get a short-lived, user-scoped session without signing in. Managed Deep Agents signs guest tokens with `MDA_GUEST_SIGNING_KEY` (HS256) and maps `sub` → user.

    | Option                       | Required | Description                                            |
    | ---------------------------- | -------- | ------------------------------------------------------ |
    | `ttl`                        | No       | Token lifetime (for example `"24h"`)                   |
    | `userPrefix` / `user_prefix` | No       | Prefix for generated user ids (for example `"guest:"`) |

    Guest is usually combined with another IdP, as in the [validated token example](#validated-token-browser-direct).

    Set `MDA_GUEST_SIGNING_KEY` in `.env` for `mda dev` and as a hosted deployment secret for `mda deploy`.

    #### Claim a guest token

    With guest issuance enabled, the deployment exposes `POST /identity/guest`. Send an empty `POST` with `Content-Type: application/json`. If the deployment requires a public app key (`LANGGRAPH_AUTH_SECRET`), also send `X-Auth-Key`.

    ```bash theme={null}
    curl -X POST "$LANGGRAPH_API_URL/identity/guest" \
      -H "Content-Type: application/json"
    ```

    On success:

    ```json theme={null}
    {
      "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
    }
    ```

    #### Use the guest token

    Send the token the same way you send IdP access tokens:

    ```http theme={null}
    Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
    ```

    <CodeGroup>
      ```typescript theme={null}
      import { Client } from "@langchain/langgraph-sdk";

      const response = await fetch(`${deploymentUrl}/identity/guest`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
      });
      const { token } = (await response.json()) as { token: string };

      const client = new Client({
        apiUrl: deploymentUrl,
        defaultHeaders: { Authorization: `Bearer ${token}` },
      });
      ```

      ```python theme={null}
      import httpx
      from langgraph_sdk import get_client

      response = httpx.post(f"{deployment_url}/identity/guest")
      response.raise_for_status()
      token = response.json()["token"]

      client = get_client(
          url=deployment_url,
          headers={"Authorization": f"Bearer {token}"},
      )
      ```
    </CodeGroup>

    <Tip>
      For browser apps, proxy guest issuance through your own backend and store the token in an `httpOnly` cookie. That keeps the same guest user across reloads until `exp` and lets you handle rate limits before calling the deployment.
    </Tip>

    Reclaim a token when the current one is expired or missing. While a token is still valid, reuse it so the guest keeps the same user id, threads, and memory scope for the token lifetime.
  </Tab>

  <Tab title="Supabase">
    JWKS by default (asymmetric JWTs). Maps `sub` → user. Pass only one of `projectRef` or `url`.

    | Option                       | Required                     | Description                                                |
    | ---------------------------- | ---------------------------- | ---------------------------------------------------------- |
    | `projectRef` / `project_ref` | One of `projectRef` or `url` | Subdomain before `.supabase.co`                            |
    | `url`                        | One of `projectRef` or `url` | Project URL or custom auth domain                          |
    | `introspect`                 | No                           | `true` for legacy HS256 projects that need `/auth/v1/user` |

    Use `auth.supabase(...)` alone, or combine it with guest as in the [validated token example](#validated-token-browser-direct).

    After sign-in, send `session.access_token` from [@supabase/supabase-js](https://supabase.com/docs/reference/javascript/auth-getsession). See also [Supabase Auth](https://supabase.com/docs/guides/auth) and [JWT signing keys](https://supabase.com/docs/guides/auth/signing-keys).

    For legacy introspection, use `introspect: true` and set `SUPABASE_ANON_KEY` on the deployment.
  </Tab>

  <Tab title="GitHub">
    Opaque token introspection via `GET https://api.github.com/user`. Maps `login` → user, `email` → email. `auth.github()` takes no options.

    <CodeGroup>
      ```python identity.py theme={null}
      from managed_deepagents import auth, define_identity

      identity = define_identity(auth=auth.github())
      ```

      ```ts identity.ts theme={null}
      import { auth, defineIdentity } from "managed-deepagents";

      export const identity = defineIdentity({ auth: auth.github() });
      ```
    </CodeGroup>

    Complete a [GitHub OAuth App](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps) sign-in flow, then send the **user access token**. Do not send OAuth client secrets to the deployment. See also [Authorizing OAuth apps](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps) and [Get the authenticated user](https://docs.github.com/en/rest/users/users#get-the-authenticated-user).

    For production, prefer [backend](#backend-recommended-default) auth: keep the GitHub token on your API, and proxy with `X-MDA-Ingress-Secret` + `X-MDA-User-Id` (for example the GitHub `login`).
  </Tab>
</Tabs>

## Test and deploy

Test the project locally with [`mda dev`](/langsmith/managed-deep-agents-cli#develop-locally), then deploy it with [`mda deploy`](/langsmith/managed-deep-agents-deploy). Open deployment traces in LangSmith to inspect model calls, tool calls, errors, and latency.

Identity misconfiguration usually surfaces as 401 (auth) or 403 (store/thread scope) during local Studio or the first authenticated request. Confirm the matching secret is present and that backend proxies attach the reserved headers.

## Next steps

<CardGroup cols={2}>
  <Card title="Memory" icon="database" href="/langsmith/managed-deep-agents-memory">
    See how identity remounts per-user or per-organization memory.
  </Card>

  <Card title="Custom tools" icon="tool" href="/langsmith/managed-deep-agents-tools">
    Read `runtime.identity` from authored tools.
  </Card>

  <Card title="Evals" icon="flask" href="/langsmith/managed-deep-agents-evals">
    Supply `identity.json` fixtures for Harbor tasks when identity is declared.
  </Card>

  <Card title="Schedules" icon="clock" href="/langsmith/managed-deep-agents-schedules">
    Run cron agents, including the `agent` scope shape.
  </Card>

  <Card title="LangSmith connector" icon="chart-line" href="/langsmith/managed-deep-agents-connectors/langsmith">
    Expose constrained LangSmith capabilities to untrusted callers.
  </Card>

  <Card title="Channels" icon="messages" href="/langsmith/managed-deep-agents-channels">
    Receive Slack Events with a shared bot or Connect-with-Slack linking.
  </Card>

  <Card title="How it works" icon="settings" href="/langsmith/managed-deep-agents-how-it-works">
    See how compile and deploy wire auth into the runtime.
  </Card>

  <Card title="CLI reference" icon="terminal" href="/langsmith/managed-deep-agents-cli">
    Look up project files and identity wiring in `mda`.
  </Card>
</CardGroup>

***

<div className="source-links">
  <Callout icon="terminal-2">
    [Connect these docs](/use-these-docs) to Claude, VSCode, and more via MCP for real-time answers.
  </Callout>

  <Callout icon="edit">
    [Edit this page on GitHub](https://github.com/langchain-ai/docs/edit/main/src/langsmith/managed-deep-agents-identity.mdx) or [file an issue](https://github.com/langchain-ai/docs/issues/new/choose).
  </Callout>
</div>
