Skip to content

fix(track,growth): product APIs reject the OAuth token issued by the FerrLabs authorization server #316

Description

@BryanFRD

Authorising ferrlabs-track and ferrlabs-growth over the standard OAuth flow succeeds, but every authenticated tool call then fails. The token is minted by api.ferrlabs.com (the authorization server named in each /.well-known/oauth-protected-resource), relayed by the MCP as x-api-token + Authorization: Bearer, and rejected by the product API.

Reproduction

Authorize the server, then call any authenticated tool:

  • ferrlabs-track -> list_projects -> 401
  • ferrlabs-growth -> list_sites -> 401

Both surface as the UnauthorizedError raised in packages/mcp-core/src/api-client.ts:50-56:

Stored token rejected by the FerrLabs API (likely revoked, expired, or issued
under an incompatible format). The token has been cleared - retry the call to
trigger a fresh OAuth login.

Retrying does not help: a fresh authorization yields another token of the same format, which is rejected the same way.

What is not the cause

  • Not a client misconfiguration. The stored credential is a valid 61-character access token with a clientId, written by a completed authorization-code flow.
  • Not an outage. api.ferrtrack.com/health and api.ferrgrowth.com/health both return 200.
  • Not the MCP transport. ferrlabs against api.ferrlabs.com works on the same flow and returns real data, and ferrlabs-fleet gets past auth to a 404, so the bearer relay itself is correct.

Likely root cause

FerrTrack and FerrGrowth verify sessions with a shared HS256 secret rather than the format the FerrLabs authorization server issues. FerrVault is the EdDSA reference implementation. The authorization server and the product APIs therefore disagree on the token format, and the OAuth flow can never produce a token these APIs accept.

That makes the current sub-MCP OAuth path unusable for both products regardless of client configuration.

Scope

The fix is unlikely to land in this repo. It belongs to the session-verification code shared through Kit and to the product APIs. Filing here because this is where the failure surfaces and where the sub-MCPs are documented as working. Verify ferrlabs-vault too, it was not exercised.

Note

Each 401 also triggers #239, which clears the server-side persisted token even though the credential came from a per-request bearer. Reproducing this issue reproduces that one.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1High prioritybugSomething isn't workingsecuritySecurity-related

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions