Skip to main content
← Back to list
01Issue
BugClosedSwamp CLITeam
AssigneesNone

Relationships

#1388 An empty stored apiKey counts as authenticated — saveIdentityCache writes apiKey:'' on a virgin machine, permanently satisfying the mandatory-account gate with no credential

Opened by magistr · 7/24/2026

Summary

setAuthenticated() is computed as bearerToken !== undefined, where bearerToken is the stored apiKey verbatim. An empty string is not undefined, so an auth.json containing apiKey: "" satisfies the mandatory-account gate on every team feature. This is not a hypothetical file: saveIdentityCache() writes exactly that on a machine that has never logged in.

Where (source)

  • src/cli/load_identity.ts:56-57bearerToken = credentials?.apiKey.
  • src/cli/mod.ts:1401setAuthenticated(autoResolverIdentity.bearerToken !== undefined).
  • src/infrastructure/persistence/auth_repository.ts:173apiKey: existing?.apiKey ?? "" in the record saveIdentityCache builds.
  • src/cli/mod.ts:1360-1374 — reaches saveIdentityCache on the personal-key path whenever SWAMP_API_KEY is set and the cached credentials carry no username.
  • The behavior is pinned by an existing test: auth_repository_test.ts:352-378 asserts loaded.apiKey === "".

Also note the gate accepts any non-empty junk: requireScope returns early for anything lacking the swamp_org_ prefix (auth_context.ts:87), so a one-character SWAMP_API_KEY passes both the account gate and the scope gate.

Deterministic repro

A — the empty key satisfies the gate

export SWAMP_HOME="$(mktemp -d)"; mkdir -p "$SWAMP_HOME/config"
cat > "$SWAMP_HOME/config/auth.json" <<'JSON'
{"serverUrl":"https://swamp-club.com","apiKey":"","apiKeyId":"id",
 "username":"repro","collectives":[],"apiKeyFingerprint":""}
JSON

swamp vault create env my-vault

Observed:

Error: Unknown vault type: env. Available types: local_encryption. ...

That is the type error, meaning the account gate passed. With the same command and no auth.json at all, the result is the paywall ("Non-local vaults are a team feature..."). The only difference between the two runs is a credential that is the empty string.

B — how the file gets there without logging in

On a machine with no auth.json, run any single command with a personal SWAMP_API_KEY set. mod.ts:1367 calls saveIdentityCache, which creates auth.json with apiKey: "" (the env key itself is deliberately not persisted). Unset the env var afterwards and the CLI reports itself as authenticated forever, holding no credential at all.

Impact

The gate that is supposed to require an account can be satisfied by a file containing no account. Two practical consequences:

  • The friendly local error is skipped and replaced by a worse remote one. Instead of "requires a free swamp-club.com account, run swamp auth login", the user proceeds into the operation and gets an opaque server 401 — or, for the client-side-only gates (vault create, datastore setup extension), no error at all, because those paths never call swamp-club.
  • It is trivially self-inflicted. Anyone who uses SWAMP_API_KEY once on a fresh machine ends up in this state; nothing about it is visible in swamp auth whoami, which will report a username while apiKey is empty.

I am not filing this as a privilege-escalation report, because the gated operations are local and no server-side authorization depends on this flag. It is a correctness bug that makes the account requirement unreliable in both directions.

Expected

  • Treat an empty (or whitespace-only) apiKey as absent: setAuthenticated(Boolean(bearerToken)), and have AuthRepository.load() return undefined rather than a record whose apiKey is "".
  • Do not write a credential record with an empty apiKey at all — if there is no key to cache, cache identity under a separate key or skip the write.
  • Consider validating the shape of SWAMP_API_KEY (non-empty, recognized prefix) rather than accepting any string.

Environment

  • Verified against source main @ 10943ef. Gate shipped in a519305 (#1934), 2026-07-23; installed stable 20260722.210030.0-sha.7ff1e2f0 predates it.
  • macOS 14.
02Bog Flow
OPENTRIAGEDIN PROGRESSCLOSED

Closed

7/24/2026, 10:45:14 PM

No activity in this phase yet.

03Sludge Pulse
Editable. Press Enter to edit.

stack72 commented 7/24/2026, 10:45:14 PM

@magistr Thanks for the detailed reports — we appreciate the work you're putting in to make the tool better. Fixes are in #1956 and will be released shortly.

Sign in to post a ripple.