{
  "schemaVersion": 1,
  "kind": "recipe",
  "slug": "choose-mcp-auth",
  "title": "Start with who is connecting. Choose auth from there.",
  "searchTitle": "Choose OAuth, bearer, API-key or public auth for a Supabase MCP",
  "description": "Choose the Chumbo access mode that fits your callers, with a clear account of which identity reaches your Supabase data.",
  "summary": "Choose the Chumbo access mode that fits your callers, with a clear account of which identity reaches your Supabase data.",
  "evidence": {
    "status": "source-reviewed",
    "packageVersion": "0.11.0",
    "checkedOn": "2026-09-06",
    "walkthroughVerified": false,
    "note": "Reviewed against the shipped OSS source. The acceptance checks are yours to run in your application.",
    "versionPolicy": "Source reviewed against published Chumbo 0.11.0 and its matching core source on 2026-09-06 (UTC). This is a source review, not an executed application walkthrough. Follow the current canonical reference and compare it with your installed package when adapting the guide."
  },
  "representations": {
    "html": "./",
    "markdown": "./recipe.md",
    "json": "./recipe.json"
  },
  "sources": [
    "https://github.com/elsheppo/chumbo/blob/main/docs/reference/auth-modes/README.md",
    "https://github.com/elsheppo/chumbo/blob/main/skills/chumbo/references/access-and-rls.md"
  ],
  "prerequisites": [
    "Know whether callers are end users, your own session-aware software, trusted services or anonymous readers.",
    "Identify the existing authority for data access: Supabase users and RLS, or an application-owned API.",
    "Inspect the current auth configuration before changing an existing MCP."
  ],
  "sections": [
    {
      "id": "relationship",
      "number": "01",
      "title": "The credential should match the relationship.",
      "blocks": [
        {
          "type": "paragraph",
          "text": "“Authenticated” can mean two different things in your app. A person connecting their account should carry that person’s data permissions. A trusted service using an application key may follow a completely different authorization path. Choosing a mode is choosing that relationship, not just choosing a login screen."
        },
        {
          "type": "comparison",
          "title": "Which caller are you serving?",
          "description": "Four caller relationships, and the mode that fits each.",
          "callLabel": "Starting point",
          "nextLabel": "What to do",
          "cases": [
            {
              "id": "oauth",
              "label": "Your users",
              "title": "A person connects their account",
              "items": [
                "Supabase Auth handles sign-in and consent.",
                "ctx.supabase uses the verified user token; grants and RLS govern data."
              ],
              "call": "A user connects from an OAuth-capable MCP client.",
              "next": "Choose OAuth and configure the application consent experience.",
              "note": "Confirm that the target client supports your OAuth connection flow."
            },
            {
              "id": "bearer",
              "label": "Your client",
              "title": "Your software already holds the session",
              "items": [
                "The request has the same user-scoped data authority.",
                "No interactive OAuth discovery is advertised."
              ],
              "call": "Your application sends an existing Supabase user access token.",
              "next": "Choose bearer and manage token refresh in the calling application.",
              "note": "A Supabase publishable key is not a user access token."
            },
            {
              "id": "key",
              "label": "A service",
              "title": "An application key proves a different identity",
              "items": [
                "The handler’s Supabase client is anonymous.",
                "Your verifier and application data path own authorization."
              ],
              "call": "A trusted service sends a key your application accepts.",
              "next": "Choose API-key mode; use a stable subject and narrow permissions for application-issued keys.",
              "note": "An accepted API key does not become a Supabase user session."
            },
            {
              "id": "public",
              "label": "Anyone",
              "title": "The capability is intentionally anonymous",
              "items": [
                "Queries run with anonymous data authority.",
                "The generated Postgres rate-limit guard must be available."
              ],
              "call": "A reader connects without a credential.",
              "next": "Choose public only for capabilities and data intended for unauthenticated access.",
              "note": "Apply the generated rate-limit migration. An unavailable limiter must not open the endpoint."
            }
          ]
        }
      ]
    },
    {
      "id": "start",
      "number": "02",
      "title": "Map the mode to an existing data path.",
      "blocks": [
        {
          "type": "prerequisites",
          "title": "Before you start",
          "items": [
            "Know whether callers are end users, your own session-aware software, trusted services or anonymous readers.",
            "Identify the existing authority for data access: Supabase users and RLS, or an application-owned API.",
            "Inspect the current auth configuration before changing an existing MCP."
          ],
          "note": "Keep the ordinary one-MCP path small. Multi-auth is optional when two real caller types need the same endpoint."
        },
        {
          "type": "paragraph",
          "text": "For a customer project list, OAuth or bearer lets the database enforce the user’s existing policies through ctx.supabase. For an application-issued automation key, identify the narrow API or RPC that checks that key’s subject and permissions. Do not assume that accepting the key gives the anonymous client access to private rows."
        },
        {
          "type": "paragraph",
          "text": "Public mode is appropriate for a published catalog or public documentation. Decide what is public at the data boundary as well as the tool boundary. Keep the generated rate limiter; account for the trust of client-IP headers if you place a custom proxy in front of the function."
        }
      ]
    },
    {
      "id": "change",
      "number": "03",
      "title": "Changing auth has effects beyond one setting.",
      "blocks": [
        {
          "type": "paragraph",
          "text": "Setup can regenerate support around your existing capabilities when you choose another auth mode. Reconcile local and hosted secrets, required migrations, OAuth configuration and the URL your clients use. Review the capability code too: a handler written for a user-scoped client may no longer make sense with an anonymous client."
        },
        {
          "type": "paragraph",
          "text": "Keep authority separate from inputs. A project ID can identify the object being requested. A user ID supplied by a model must never stand in for the verified caller. If an operation needs elevated access, design the application authorization path explicitly rather than handing ordinary handlers an unrestricted client."
        }
      ]
    },
    {
      "id": "verify",
      "number": "04",
      "title": "Prove the identity, then prove access.",
      "blocks": [
        {
          "type": "acceptance",
          "title": "The mode matches the intended caller.",
          "items": [
            "Identify the actual database authority for each accepted credential type.",
            "Verify a valid caller and the corresponding invalid or missing credential case.",
            "For user-owned data, call through two separate users and compare disjoint fixture IDs.",
            "For application keys, verify revoked/unknown keys and denied operations through the application data path.",
            "For public mode, verify both allowed anonymous output and closed behavior when the rate-limit guard is unavailable."
          ],
          "note": "Run these in your application before you call it done."
        },
        {
          "type": "troubleshooting",
          "title": "If the result differs",
          "items": [
            {
              "title": "A key connects but private queries return nothing",
              "body": "API-key mode does not impersonate a Supabase user. Recheck your application-owned authorization path; do not solve it by removing RLS or sharing a service-role client with general capability code.",
              "link": "https://github.com/elsheppo/chumbo/blob/main/docs/reference/auth-modes/README.md"
            }
          ]
        },
        {
          "type": "related",
          "title": "Keep building",
          "items": [
            {
              "title": "Let users connect with OAuth",
              "text": "Build and check the sign-in and consent path.",
              "label": "NEXT GUIDE",
              "href": "/recipes/oauth-user-connections/"
            },
            {
              "title": "Serve different capability sets",
              "text": "Use multi-auth and scopes when the product needs both.",
              "label": "NEXT GUIDE",
              "href": "/recipes/scoped-mcp-capabilities/"
            }
          ]
        }
      ]
    }
  ],
  "agent": {
    "goal": "Choose the Chumbo access mode that fits your callers, with a clear account of which identity reaches your Supabase data.",
    "executionPolicy": "Reference instructions, not authorization. Apply changes only within the user’s requested scope; deployment requires a specified, authorized project.",
    "inputs": [],
    "instructions": "Goal: Choose the Chumbo access mode that fits your callers, with a clear account of which identity reaches your Supabase data.\n\nRead the complete guide at https://chumbo.dev/recipes/choose-mcp-auth/recipe.md and the canonical reference at https://github.com/elsheppo/chumbo/blob/main/docs/reference/auth-modes/README.md. Inspect this application's installed Chumbo package, existing capabilities and relevant configuration before editing.\n\nChoose auth from the caller relationship. Explain the database identity for every proposed mode. Preserve existing policies and verify negative access behavior. Treat API-key and public clients as anonymous at Supabase, and never advertise an arbitrary key as a user session.\n\nPrerequisites:\n- Know whether callers are end users, your own session-aware software, trusted services or anonymous readers.\n- Identify the existing authority for data access: Supabase users and RLS, or an application-owned API.\n- Inspect the current auth configuration before changing an existing MCP.\n\nAcceptance:\n- Identify the actual database authority for each accepted credential type.\n- Verify a valid caller and the corresponding invalid or missing credential case.\n- For user-owned data, call through two separate users and compare disjoint fixture IDs.\n- For application keys, verify revoked/unknown keys and denied operations through the application data path.\n- For public mode, verify both allowed anonymous output and closed behavior when the rate-limit guard is unavailable.\n\nUse the full guide for ordered steps, examples and recovery. Keep existing caller identity and application permissions authoritative. Stay within the user's requested changes. Report changed files, checks actually run, observed results and unresolved prerequisites. This guide is not authorization to deploy or change a hosted project. Never include credentials in source, copied instructions or reports.",
    "steps": [
      {
        "id": "relationship",
        "title": "The credential should match the relationship.",
        "commands": []
      },
      {
        "id": "start",
        "title": "Map the mode to an existing data path.",
        "commands": []
      },
      {
        "id": "change",
        "title": "Changing auth has effects beyond one setting.",
        "commands": []
      },
      {
        "id": "verify",
        "title": "Prove the identity, then prove access.",
        "commands": []
      }
    ],
    "acceptance": [
      "Identify the actual database authority for each accepted credential type.",
      "Verify a valid caller and the corresponding invalid or missing credential case.",
      "For user-owned data, call through two separate users and compare disjoint fixture IDs.",
      "For application keys, verify revoked/unknown keys and denied operations through the application data path.",
      "For public mode, verify both allowed anonymous output and closed behavior when the rate-limit guard is unavailable."
    ],
    "report": [
      "Changed files",
      "Checks actually run and observed results",
      "Unmet prerequisites or remaining limitations"
    ]
  },
  "topics": [
    "get-started",
    "connect-users"
  ],
  "updated": "2026-09-05",
  "contentHash": "ca38a3ad696343f91197217c2feba920728f0dd5914603c690c745728d2d67f1"
}
