Skip to content

supabase link / projects api-keys: 403 insufficient privileges, but identical token succeeds via direct API call #6392

Open

Description

@snoutly-vkm

supabase link / supabase projects api-keys fail with "insufficient privileges" while the identical token succeeds via direct API call

Bug report

supabase link (and supabase projects api-keys) fail with an "insufficient privileges" error, while the exact same personal access token, hitting the equivalent Management API endpoint, succeeds via a plain curl call.

Environment

  • CLI version: 2.116.0 (also reproduced on 2.107.0 before upgrading)
  • OS: macOS (Darwin 25.6.0)
  • Install method: Homebrew
  • Org plan: Pro, Owner role, billing/payment method on file

Steps to reproduce

supabase link --project-ref <ref>

Output:

{"_tag":"Error","error":{"code":"LegacyLinkProjectStatusError","message":"Unexpected error retrieving remote project status: {\"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\"}"}}

supabase projects api-keys --project-ref <ref> fails the same way.

Proof the token itself is fine

Using the same SUPABASE_ACCESS_TOKEN the CLI is resolving (verified via env var, not --profile, see below), a direct call to the Management API succeeds:

curl -s -o /dev/null -w "%{http_code}\n" -X GET \
  "https://api.supabase.com/v1/projects/<ref>" \
  -H "Authorization: Bearer $SUPABASE_ACCESS_TOKEN"
# => 200

Same token, same project, same account — CLI rejects it, raw REST call accepts it.

What we ruled out

  • Token scope — token has full scope, confirmed via the dashboard.
  • Org role — Owner.
  • Billing — payment method on file, no past-due invoices.
  • Plan tier — Pro.
  • CLI version — reproduced on both 2.107.0 and the latest 2.116.0 at time of filing.
  • Local CLI state/cache — deleted ~/.supabase/profile and other local CLI state, re-authenticated from scratch, still reproduces.
  • --profile isolation — also found that --profile doesn't appear to isolate credentials as expected (logging a token under an unrelated profile name still authenticated as the real account), so we're resolving the token via SUPABASE_ACCESS_TOKEN env var instead, not --profile. Not certain this is related to the 403, flagging in case it's the same root cause (e.g. profile/account resolution picking the wrong context internally before the privilege check).

Impact

Can't use supabase link / supabase db push / supabase projects api-keys at all against this project. Working around it by scripting equivalent calls directly against the Management API (POST /v1/projects/{ref}/database/query), which work fine and confirms the backend/account itself has no permission issue — this looks CLI-internal.

Activity

  1. self-assigned this
    on Aug 31, 2026
  2. DotNetTitan commented on Sep 10, 2026

    @DotNetTitan

    I am also hitting this one, and it's a weird one because nothing changed on my end. Same token that worked earlier today suddenly started getting 403s from the CLI, while the API itself is perfectly happy with it.

    • CLI 2.111.0 and 2.117.0; Windows 11 and GitHub Actions (ubuntu)
    • Scoped token, read-write on 33 capabilities, scoped to the one project
    • Worked earlier the same day on the identical CLI/token; broke with no repo/token changes
    • Happy to share the project ref privately with a maintainer if that helps

    supabase link --project-ref --debug

    HTTP GET: https://api.supabase.com/v1/projects/<project-ref>
    HTTP GET: https://api.supabase.com/v1/projects/<project-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"}
    

    Same token, direct REST, both 200:

    curl -s -o /dev/null -w "%{http_code}\n" https://api.supabase.com/v1/projects/<project-ref>          -H "Authorization: Bearer $SUPABASE_ACCESS_TOKEN"  # 200
    curl -s -o /dev/null -w "%{http_code}\n" https://api.supabase.com/v1/projects/<project-ref>/api-keys -H "Authorization: Bearer $SUPABASE_ACCESS_TOKEN"  # 200
    

    So the token and /api-keys both work directly, but the CLI's request gets denied.

    Workaround that still works for me (link/api-keys seems to be the only broken path):

    supabase db push --project-ref <project-ref> --password <db-password>   # works
    supabase functions deploy <fn> --project-ref <project-ref>              # works
    supabase secrets set --project-ref <project-ref> ...                    # works
    

    Impact: supabase link (and any --linked flow) is unusable, so automated CI migrations/deploys are blocked. Thanks!

  3. SBub commented on Sep 21, 2026

    @SBub

    Another data point, narrowed to a single endpoint: the 403 is on ?reveal=true, not on /api-keys itself.

    Probing three Management API endpoints with the same PAT, from a GitHub Actions runner, status codes only (curl -s -o /dev/null -w '%{http_code}'):

    endpoint status
    GET /v1/projects/{ref} 200
    GET /v1/projects/{ref}/api-keys 200
    GET /v1/projects/{ref}/api-keys?reveal=true 403

    supabase link --project-ref {ref} then fails in the same run with Authorization failed for the access token and project ref pair: {"message":"Your account does not have the necessary privileges to access this endpoint. ..."}.

    So listing the key entries is permitted to this token while revealing their values is not, and link reveals them in order to cache them locally. That seems to be the whole of it. supabase projects list also succeeds on the same token and returns the project, so it is not project visibility.

    Same result on both CLI versions, same project, same token, minutes apart:

    • 2.116.0: 200 / 200 / 403, link fails.
    • 2.117.0 (verified via supabase --version in the run, since supabase/setup-cli does not log what it resolved): 200 / 200 / 403, link fails identically.

    That rules out a regression between those two releases, which matches this issue already reproducing on 2.107.0.

    Two things that might be worth considering upstream, if the ?reveal=true gate is intentional:

    1. link could degrade rather than fail when the key reveal is refused. Resolving the project succeeds, and the cached key values are not needed by every subsequent command.
    2. The error text could name the endpoint that was refused. "Authorization failed for the access token and project ref pair" points at the token/ref pair, which is what sent us looking in the wrong place for a while, since both of those are in fact fine.

    Workaround for CI, in case it helps anyone else here: skip link entirely and pass --db-url (session pooler host, port 5432) to supabase db push and supabase migration list. That keeps the Management API off the path completely.

  4. Spiatron commented on Oct 14, 2026

    @Spiatron

    Same here: project-scoped PAT created by the org Owner, CLI 2.120.0, GitHub Actions (ubuntu-latest). link fails on GET /v1/projects//api-keys and only passes after adding the "API Key Secrets: read" capability.

    That means a CI token has to be able to read the service_role key just to run db push, which itself only needs the database password. Opened #7165 for that least-privilege side.

    +1 for link degrading gracefully when key reveal is refused. Working around it with db push --db-url (session pooler, port 5432) and no access token.

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions