Skip to content

fix(auth): Self-hosted instances dead-end with no usable credential - #86

Merged
khvn26 merged 12 commits into
mainfrom
fix/old-server-nudge
Aug 13, 2026
Merged

fix(auth): Self-hosted instances dead-end with no usable credential#86
khvn26 merged 12 commits into
mainfrom
fix/old-server-nudge

Conversation

@khvn26

@khvn26 khvn26 commented Aug 13, 2026

Copy link
Copy Markdown
Member

Closes #76.
Closes #77.

Accommodates auth-related errors for self-hosted use cases (host-scoped environment variables):

  • Hints name the variable that is actually read, on the Admin and SDK surfaces alike.
  • An unscoped variable that is being ignored says so, instead of reporting no credential.
  • A malformed key is reported against the variable it came from.
  • The README documents the scoping rule.

Additionally, adds a /version so people are offered to upgrade in case of missing OAuth metadata.

khvn26 added 8 commits August 13, 2026 08:58
The 404 from /.well-known/oauth-authorization-server carried its own
recovery guidance ("is this a Flagsmith API URL?"). Hints don't live in
message strings, and this one is about to become conditional: an old
Flagsmith needs a different answer from a URL that isn't Flagsmith at all.
Wrap ErrNoDiscovery instead, and map it to hintAPIURL alongside the
existing hintSDKAPIURL.

beep boop
Answers what a Flagsmith supports before there is a credential to ask
with, which is exactly when the CLI needs to know: the login flow has to
tell an instance too old for OAuth apart from a URL that is not Flagsmith.

Root-scoped, not under /api/v1, and unauthenticated — so a package
function rather than a Client method, whose newRequest would need auth
that does not exist yet at this point in the flow.

beep boop
Off api.flagsmith.com the unscoped credential variables are ignored by
design, so every hint naming one told self-hosted users to set something
that would have no effect — including the "not logged in" hint they reach
by following the previous one.

The variable name depends on the instance, so the hints that carry one
become functions of it. hintMasterKeyOrLogin names none and stays a const.

beep boop
`flagsmith init` and `flagsmith login` against a Flagsmith older than
2.255.0 dead-ended on a 404 from the discovery endpoint, with no mention
of the Master API key that would have worked.

Probe /version only once discovery has already failed, so the happy path
costs nothing. An image tag that is not a version says nothing about age,
so anything unparseable keeps the generic hint rather than sending someone
with a healthy instance chasing an upgrade. Anyone reaching this is
self-hosted by definition, so the key variable is named host-scoped.

beep boop
A self-hosted user who exported FLAGSMITH_API_KEY was told "not logged in"
and hinted to set FLAGSMITH_API_KEY — the thing they had just done. Their
key was fine; only its name was, and the CLI knew that and didn't say so.

Say which variable is being ignored and which one to set instead. This no
longer wraps ErrNotLoggedIn, so `init` stops offering a browser login over
a credential the user explicitly set: silently discarding it is the bug.
The redirected-host tests now assert on the withholding itself rather than
the sentinel that used to stand in for it.

beep boop
The scoping rule was only discoverable by hitting it, which is how it was
reported.

beep boop
FLAGSMITH_ENVIRONMENT_KEY is host-scoped like the Admin variables, but off
its own surface — sdkApiUrl, which defaults to Edge and so is usually not
even the same host as the API. The hints naming it had the same defect as
the Admin ones: correct on SaaS, useless anywhere else.

The ser.* context guard needed the SDK URL to name the variable, and ran
before that URL was resolved, so it moves below the resolution. Both
surface URLs are now published as soon as resolveContext knows them rather
than on the way out of applyContext, which is also what lets the guard
name them at all.

beep boop
The Master API key variable is host-scoped, so a sentinel that spells
FLAGSMITH_API_KEY into its own message names the wrong variable for every
self-hosted instance — telling a user their FLAGSMITH_API_KEY holds a
server-side key when what they set was FLAGSMITH_API_KEY_flagsmith_example_com.

The rejections become predicates and loadCredential supplies the subject:
the name envCredential actually read the value from. This is how
config.ErrServerSideKey already reads against its file path.

beep boop
@coderabbitai

coderabbitai Bot commented Aug 13, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@khvn26, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 52 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 4ef88086-bae2-4ce4-8f03-098d1f722071

📥 Commits

Reviewing files that changed from the base of the PR and between eeaee68 and ae5d42f.

📒 Files selected for processing (1)
  • README.md
📝 Walkthrough

Walkthrough

The CLI now supports host-scoped static credentials for self-hosted instances. It resolves separate Admin API and SDK URLs and generates credential hints from the active host. Authentication errors use source-neutral validation messages. OAuth discovery failures use a sentinel error. Browser login checks the server version and provides version-aware guidance for older instances. Tests cover credential resolution, dynamic hints, OAuth discovery, and version responses.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Mergeability Score: 🟡 Moderate · up to eeaee

The change updates host-scoped credential resolution, but distinct IPv6 authorities can currently map to the same environment-variable name, potentially selecting the wrong credential and causing authentication failures. This concrete issue should be fixed before merging.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: f5cbdf2f-c64c-4037-9535-89dd191c7973

📥 Commits

Reviewing files that changed from the base of the PR and between 86b189b and bf7edc9.

📒 Files selected for processing (20)
  • README.md
  • go.mod
  • internal/api/version.go
  • internal/api/version_test.go
  • internal/auth/kind.go
  • internal/auth/oauth.go
  • internal/auth/oauth_test.go
  • internal/cmd/auth.go
  • internal/cmd/cmd_test.go
  • internal/cmd/context.go
  • internal/cmd/envcred.go
  • internal/cmd/envcred_test.go
  • internal/cmd/errors.go
  • internal/cmd/errors_test.go
  • internal/cmd/evaluate.go
  • internal/cmd/init.go
  • internal/cmd/login.go
  • internal/cmd/resolve.go
  • internal/cmd/root.go
  • internal/cmd/sentinel_test.go

Comment thread internal/auth/oauth.go
Comment thread internal/cmd/envcred.go
@Zaimwa9
Zaimwa9 self-requested a review August 13, 2026 09:30
@Zaimwa9 Zaimwa9 removed their assignment Aug 13, 2026
khvn26 added 2 commits August 13, 2026 10:48
Every non-200 was reported as "no authorization server metadata", so a
rate-limited or briefly broken instance was described as one that has no
OAuth support and hinted to check --api-url for a mistake that isn't there.

A refusal that is not a 404 comes from a server that does have the
document, and joins the other protocol surprises in this function as
reportable.

beep boop
An instance addressed by IPv6 literal scoped to FLAGSMITH_API_KEY_[__1]_8000,
which no POSIX shell can export — so the variable could be neither set nor
found, and the hints added here would have asked for it by name.

The brackets are URL syntax rather than part of the host, so they are
dropped instead of encoded. This does cost injectivity: `::` and `-` both
land on `__`. Nothing reverses these names, and a name that cannot be typed
is worse than one that cannot be reversed.

beep boop

@Zaimwa9 Zaimwa9 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we check the config-load -> hint ordering? I proposed to fallback to the base but you might have something better in mind.

And suggesting to also add the scope encoding in the readme for clarity

Comment thread README.md Outdated
Comment thread internal/cmd/envcred.go Outdated
A config file that fails to load aborts resolveContext before it publishes
either surface URL, so a hint rendered from that error scoped its variable
name to an empty host and asked for FLAGSMITH_ENVIRONMENT_KEY_ — a name
that cannot be set and identifies no instance.

Fall back to the unscoped name when no host is known. It is the right
answer for the default host and the only answer available for a user whose
instance URL was in the file that just failed to parse.

Every test in the package shares the resolved-URL globals, so the one
pinning this clears them: a real run is one command in its own process,
which is the state that exposed this.

beep boop

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/cmd/envcred.go (1)

31-36: 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

Use a one-to-one encoding for IPv6 hosts and ports.

http://[::1]:8000 and http://[::1:8000] both produce FLAGSMITH_API_KEY___1_8000 and FLAGSMITH_ENVIRONMENT_KEY___1_8000. envCredential can therefore select one authority’s credential for the other. Use distinct separators for the IPv6 host and port, and add collision tests for Admin API and SDK variable names in internal/cmd/envcred_test.go.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: ca1de01a-9777-42ec-8ea9-cdb280cafe55

📥 Commits

Reviewing files that changed from the base of the PR and between 50d3952 and eeaee68.

📒 Files selected for processing (3)
  • internal/cmd/cmd_test.go
  • internal/cmd/envcred.go
  • internal/cmd/envcred_test.go

A single example of a host that needed no escaping left the reader to guess
what happens to a hyphen, which is the one character whose encoding is not
obvious. The example now carries a hyphen and a port, so it demonstrates
every part of the rule it states.

beep boop
@khvn26
khvn26 force-pushed the fix/old-server-nudge branch from 5e4ce67 to ae5d42f Compare August 13, 2026 11:56
@khvn26
khvn26 requested a review from Zaimwa9 August 13, 2026 11:57

@Zaimwa9 Zaimwa9 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good to me!

@khvn26
khvn26 merged commit 777b918 into main Aug 13, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

FLAGSMITH_API_KEY is silently ignored for self-hosted instances flagsmith init obscure error against older Flagsmith versions

2 participants