Skip to content

Scoped tokens: supabase link needs API Key Secrets even though db push only needs the DB password #7165

Open

Description

@Spiatron

Affected area

Auth

Supabase CLI version

2.108.0

Operating system

Ubuntu 24.04 (GitHub Actions ubuntu-latest); also reproduced on Windows 11

Installation method

None

Command

SUPABASE_ACCESS_TOKEN=<project-scoped PAT> supabase link --project-ref <ref> --debug

Actual output

Using access token from env var...
Supabase CLI 2.120.0
HTTP GET: https://api.supabase.com/v1/projects/<ref>
HTTP GET: https://api.supabase.com/v1/projects/<ref>/api-keys
Authorization failed for the access token and project ref pair: {"message":"Your account does not have the necessary privileges to access this endpoint. For more details, refer to our documentation https://supabase.com/docs/guides/platform/access-control"}

Expected behavior

supabase link (followed by supabase db push) works with a project-scoped token that has Migrations read-write and project/database read, without needing API Key Secrets. db push uses the database password and never needs the secret service_role key, so link shouldn't fetch secret keys, or should skip them when the token lacks API Key Secrets. If a permission is missing, the 403 should name it in missing_permissions, as the docs say.

Steps to reproduce

  1. Create a scoped personal access token (resource access: one project) with: Migrations read-write; Project Settings, Database, Database Config, Connection Pooling, API Keys read. Created by the organization Owner.
  2. Run: SUPABASE_ACCESS_TOKEN= supabase link --project-ref --debug
  3. It fails on GET /v1/projects//api-keys with "Your account does not have the necessary privileges to access this endpoint".
  4. Add API Key Secrets: read to a new token → link succeeds.
    Workaround: supabase db push --db-url (no link, no token).

Crash report ID

No response

Docker and service versions

Additional context

No response

Activity

  1. drunkeninja42 commented on Oct 24, 2026

    @drunkeninja42

    I looked into the link code for this. The same bug is tracked in #6392, which is assigned to @7ttp. Are you already working on it? If so, no worries, I'll leave it. If not, I'd like to pick it up.

    The 403 comes from the reveal=true request here:

    // 2. Resolve service keys (auth check).
    const keys = yield* api.v1
    .getProjectApiKeys({ ref, reveal: true })
    .pipe(Effect.catch(mapApiKeysError));
    const { anon, serviceRole } = extractServiceKeys(keys);
    if (anon.length === 0 && serviceRole.length === 0) {
    return yield* new LinkMissingKeyError({ message: "Anon key not found." });
    }

    As tested in #6392, the plain /api-keys request returns 200 with the same token; only reveal=true is refused. The secret key is only used for the optional version checks in linkServicesCore, which already skip silently on failure, and bootstrap runs those checks with the anon key:

    /**
    * Tenant API key used for the service version probes: `link` passes the
    * service-role key, `bootstrap` passes the anon key.
    */

    So my idea:

    • if reveal=true comes back 403, request the keys again without reveal
    • use the anon key for the version checks and print a warning saying secret keys weren't available
    • 401, 404 and network errors keep failing like they do today

    I'd add integration tests for the 403 fallback and the 401 case, and update link/SIDE_EFFECTS.md. If that works for you, it would need the open-for-contribution label.

  2. Spiatron commented on Oct 24, 2026

    @Spiatron
    Author

    @drunkeninja42 thanks for digging into this, your analysis matches what we saw. With a project-scoped token (Migrations read-write; Project Settings, Database and API Keys read), link fails on the api-keys call, and adding API Key Secrets read fixes it. Your fallback would solve it for us.

    Happy to help: once there's a PR I can test its build against our setup (scoped token, no API Key Secrets) and confirm link + db push work. And if neither of you ends up taking it, I'd be glad to pick it up.

    Fine to close this one as a duplicate of #6392 if that's easier to track.

  3. drunkeninja42 commented on Oct 24, 2026

    @drunkeninja42

    Thanks @Spiatron, good to have it confirmed. I'll hold until @7ttp or a maintainer weighs in, and I'll ping you for testing if I end up opening the PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions