Skip to content

models.authStatus: normalize provider ids + tighten env-backed escape hatch#67253

Merged
omarshahine merged 3 commits intoopenclaw:mainfrom
omarshahine:lobster/model-auth-status-followup
Apr 15, 2026
Merged

models.authStatus: normalize provider ids + tighten env-backed escape hatch#67253
omarshahine merged 3 commits intoopenclaw:mainfrom
omarshahine:lobster/model-auth-status-followup

Conversation

@omarshahine
Copy link
Copy Markdown
Contributor

Follow-up to #66211 addressing three P2 review comments from Codex.

Changes

1. Normalize provider ids before expectsOAuth membership check.
buildAuthHealthSummary normalizes provider ids (e.g. z.aizai). The expectsOAuth set was storing raw config keys, so expectsOAuthSet.has(prov.provider) silently missed when an alias was configured. Both the models.providers and auth.profiles loops now store normalized ids.

2. Apply env-backed escape hatch to auth.profiles synthesis too.
Previously the env-backed skip only applied to the models.providers loop, so a config with resolvable apiKey + a matching auth.profiles mode entry re-added the provider and flipped it back to "missing". Escape hatch is now captured once up front (envBacked set) and honored by both loops.

3. Check env secret resolution before treating apiKey as valid credential.
hasEnvCredential = apiKey !== undefined was too permissive — a SecretRef pointing at an unset env var would silently count as env-backed and suppress the "missing" alert for a genuinely broken config. Now uses resolveSecretInputString({ mode: \"inspect\" }) and only skips when status is available. A configured_unavailable SecretRef falls through to normal missing synthesis so the dashboard surfaces it.

Tests

Three new tests, all passing (18 total in the suite):

  • `skips env-backed OAuth providers (resolvable apiKey) from missing synthesis` (updated to use a resolvable value)
  • `still flags provider as missing when apiKey SecretRef is unresolvable`
  • `env-backed escape hatch also applies to auth.profiles entries`
  • `normalizes expectsOAuth provider ids to match buildAuthHealthSummary`

Context

Shipped in `2026.4.15-beta.1` as part of #66211; the original merge happened without human review (auto-added `@openclaw/secops` reviewer slot wasn't enforced). Cc'ing the team so the combined surface can get a proper secops look:

cc @openclaw/secops

@omarshahine omarshahine requested a review from a team as a code owner April 15, 2026 14:55
@omarshahine omarshahine self-assigned this Apr 15, 2026
@openclaw-barnacle openclaw-barnacle bot added gateway Gateway runtime size: S maintainer Maintainer-authored PR labels Apr 15, 2026
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a98cecf290

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/gateway/server-methods/models-auth-status.ts Outdated
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Apr 15, 2026

Greptile Summary

Three targeted fixes to resolveConfiguredProviders: provider IDs are now normalized before storing in expectsOAuth (fixing silent alias misses for e.g. z.aizai), the env-backed skip set is now computed in a pre-pass and applied to both the models.providers and auth.profiles loops, and the credential check was tightened from apiKey !== undefined to an inspect-mode resolveSecretInputString call so only literal inline strings count as env-backed.

Two P2 findings:

  • The code comment on the inspect-mode call says it "tells us whether the SecretRef currently resolves to a real value," but inspect mode is purely structural — it returns configured_unavailable for any SecretRef shape, even when the referenced env var is actually set. A provider using apiKey: { source: "env", id: "LIVE_KEY" } with LIVE_KEY present would still fall through to missing synthesis and show a false dashboard alert.
  • The companion test's delete process.env.MODELS_AUTH_STATUS_TEST_MISSING_KEY line is a no-op for the assertion (inspect mode never reads env vars), making the test's framing "unresolvable SecretRef" subtly misleading.

Confidence Score: 4/5

Safe to merge; the logic fixes are correct and the only concerns are a misleading comment and test no-op that could confuse future maintainers.

All three core fixes (ID normalization, envBacked pre-pass, inspect-mode credential check) are logically sound and well-tested. The P2 findings are about comment accuracy and a misleading test setup — neither blocks correctness. Score is 4 rather than 5 only because the inaccurate inspect-mode comment could lead a future maintainer to assume env-var availability is being verified when it isn't, and a resolvable SecretRef-backed apiKey paired with oauth mode would now surface false "missing" alerts on the dashboard.

src/gateway/server-methods/models-auth-status.ts lines 217–221 (misleading comment about inspect mode semantics)

Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/gateway/server-methods/models-auth-status.ts
Line: 217-221

Comment:
**Misleading comment — `inspect` does not resolve env vars**

The comment says `inspect` mode "tells us whether the SecretRef currently resolves to a real value." It doesn't: `resolveSecretInputString` with `mode: "inspect"` checks structure only — a literal string → `available`, any `SecretRef` object → `configured_unavailable`, neither → `missing`. It never reads `process.env`, executes a command, or reads a file. A provider configured with `apiKey: { source: "env", id: "LIVE_KEY" }` where `LIVE_KEY` IS set in the environment still gets `configured_unavailable` and therefore falls through to missing synthesis, which would show a false "missing" alert on the dashboard.

If the intent is to only treat inline literals as env-backed (SecretRefs are never trusted here), the comment should say so explicitly rather than suggesting the check is equivalent to env-var presence detection.

```suggestion
    // `inspect` mode never throws; it returns `available` only for a
    // literal inline string, and `configured_unavailable` for any SecretRef
    // shape (regardless of whether the referenced env var is set). Only skip
    // the missing synthesis when status is "available" — a SecretRef-based
    // apiKey cannot be verified here without runtime secret resolution, so
    // it always falls through to normal synthesis.
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: src/gateway/server-methods/models-auth-status.test.ts
Line: 324

Comment:
**`delete process.env.…` has no effect on this test's assertion**

Because `resolveSecretInputString({ mode: "inspect" })` never reads the environment (it returns `configured_unavailable` for any `SecretRef` shape regardless of env state), removing or setting `MODELS_AUTH_STATUS_TEST_MISSING_KEY` does not change the test outcome. The assertion passes identically with or without this line, which means the test name "when apiKey SecretRef is unresolvable" is not actually verified — a SecretRef with the env var present would produce the same result.

Consider either removing the `delete` (to avoid implying it matters) or adding a complementary test that sets the var and confirms it still triggers missing synthesis, along with a comment explaining that the escape hatch is intentionally limited to literal strings.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "models.authStatus: normalize provider id..." | Re-trigger Greptile

Comment thread src/gateway/server-methods/models-auth-status.ts Outdated
Comment thread src/gateway/server-methods/models-auth-status.test.ts
@omarshahine omarshahine force-pushed the lobster/model-auth-status-followup branch from 0df3d94 to 03d1816 Compare April 15, 2026 22:28
@omarshahine omarshahine merged commit f2fdb9d into openclaw:main Apr 15, 2026
26 of 27 checks passed
@omarshahine
Copy link
Copy Markdown
Contributor Author

Merged via squash as f2fdb9d.

Prep changes:

  • Added changelog entry under ## Unreleased > ### Fixes
  • Rebased onto latest main to resolve changelog conflict

Verification:

  • pnpm build
  • pnpm check
  • Scoped test: models-auth-status.test.ts 19/19 ✅
  • Full pnpm test: 1 unrelated failure (worker OOM + tooling test timeout on postinstall-bundled-plugins.test.ts) — baseline repo noise, not related to this 2-file auth-status PR

xudaiyanzi pushed a commit to xudaiyanzi/openclaw that referenced this pull request Apr 17, 2026
… hatch (openclaw#67253)

Fix false-positive "missing" alerts on the Model Auth status card:
- Normalize provider ids before expectsOAuth membership check (alias mismatch)
- Apply env-backed escape hatch to auth.profiles loop (not just models.providers)
- Check actual env var resolution for SecretRef apiKeys

Co-authored-by: Omar Shahine <10343873+omarshahine@users.noreply.github.com>
kvnkho pushed a commit to kvnkho/openclaw that referenced this pull request Apr 17, 2026
… hatch (openclaw#67253)

Fix false-positive "missing" alerts on the Model Auth status card:
- Normalize provider ids before expectsOAuth membership check (alias mismatch)
- Apply env-backed escape hatch to auth.profiles loop (not just models.providers)
- Check actual env var resolution for SecretRef apiKeys

Co-authored-by: Omar Shahine <10343873+omarshahine@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gateway Gateway runtime maintainer Maintainer-authored PR size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant