Skip to content

fix: --app flag not applied to subcommands - #46

Merged
santiagomed merged 2 commits into
xdevplatform:mainfrom
rmacias12:fix/fix-app-flag
Apr 19, 2026
Merged

fix: --app flag not applied to subcommands#46
santiagomed merged 2 commits into
xdevplatform:mainfrom
rmacias12:fix/fix-app-flag

Conversation

@rmacias12

Copy link
Copy Markdown
Contributor

Fix: --app flag not applied to subcommands

Fixes /issues/38

Similar to PR #39 with few extra areas covered.

Summary

The global --app flag was silently ignored when used with any subcommand
(e.g. xurl whoami --app my-app). The command would always authenticate
using the default app instead of the specified one. A comprehensive audit
found the same class of bug in six files across the codebase.

Root Cause

The --app flag correctly stored the app name in a.appName via
WithAppName, but every place that subsequently read tokens, saved tokens,
or cleared tokens called the non-ForApp store variants, which always pass
"" to ResolveApp and therefore always resolve to the default app.


Bugs Fixed

Bug 1 — Token reads in auth/auth.go ignored --app

Every method that retrieves tokens called the non-ForApp store variants:

Method Old call Problem
GetOAuth1Header TokenStore.GetOAuth1Tokens() ignores a.appName
GetOAuth2Header TokenStore.GetOAuth2Token(username) / GetFirstOAuth2Token() ignores a.appName
RefreshOAuth2Token TokenStore.GetOAuth2Token(username) / GetFirstOAuth2Token() ignores a.appName
GetBearerTokenHeader TokenStore.GetBearerToken() ignores a.appName

Fix: switch every call to the corresponding ForApp variant passing a.appName.
When a.appName is "" (no --app flag), ResolveApp("") falls through to
the default app — existing behaviour is fully preserved.


Bug 2 — Named app's credentials were ignored when env vars were set

WithAppName only updated clientID/clientSecret when they were already
empty. If CLIENT_ID/CLIENT_SECRET env vars were set at startup, the named
app's stored credentials were silently ignored, causing OAuth flows to use the
wrong client application.

// before — env-var credentials could never be overridden
if a.clientID == "" {
    a.clientID = app.ClientID
}

// after — named app's credentials take precedence when they exist
if app.ClientID != "" {
    a.clientID = app.ClientID
}

Bug 3 — Auth auto-detection in api/client.go probed the wrong app

When no --auth flag is given, getAuthHeader probes for available tokens
in order (OAuth2 → OAuth1 → Bearer). The existence checks called non-ForApp
variants, so if the default app had no OAuth2 tokens but the named app did,
the OAuth2 path was skipped entirely — even though the subsequent retrieval
call would have succeeded.

Fix: use ForApp variants for the probe checks via a new Auth.AppName()
getter, added to expose appName to the api package without breaking
encapsulation.


Bug 4 — OAuth2 tokens saved back to the wrong app (auth/auth.go)

OAuth2Flow and RefreshOAuth2Token both called SaveOAuth2Token(...) (the
non-ForApp variant). This meant that when a user ran
xurl --app my-app auth oauth2, the resulting token was written to the
default app, not my-app. Refreshed tokens had the same problem.

Fix: both methods now call SaveOAuth2TokenForApp(a.appName, ...).


Bug 5 — Token write commands in cli/auth.go ignored --app

xurl auth app --bearer-token, xurl auth oauth1, and all variants of
xurl auth clear called non-ForApp store methods. Running any of these
with --app my-app would read from or write to the default app.

Fix: all four clear paths and both save paths updated to ForApp variants
with a.AppName().


Bug 6 — Webhook CRC validation in cli/webhook.go ignored --app

webhook start fetched the OAuth1 consumer secret (needed to sign CRC
challenge responses) via TokenStore.GetOAuth1Tokens(), always reading from
the default app.

Fix: GetOAuth1TokensForApp(authInstance.AppName()).


Files Changed

auth/auth.go

  • GetOAuth1Header: GetOAuth1Tokens()GetOAuth1TokensForApp(a.appName)
  • GetOAuth2Header: both token lookups use ForApp variants with a.appName
  • RefreshOAuth2Token: both token lookups use ForApp variants; token save uses SaveOAuth2TokenForApp(a.appName, ...)
  • GetBearerTokenHeader: GetBearerToken()GetBearerTokenForApp(a.appName)
  • OAuth2Flow: SaveOAuth2Token(...)SaveOAuth2TokenForApp(a.appName, ...)
  • WithAppName: guard condition flipped so named app credentials override env vars
  • Added AppName() string getter to expose appName to the api package

auth/auth_test.go

Four new test functions:

  • TestWithAppNameOverridesEnvCredentials — verifies that the named app's
    stored credentials replace env-var credentials when --app is used.
  • TestAppFlagTokenIsolation — verifies end-to-end that token lookups
    resolve to the correct named app, return errors for missing tokens, and fall
    back to the default app when no --app flag is given (regression guard).
  • TestRefreshOAuth2TokenSavesToNamedApp — uses a mock token server to
    confirm that a refreshed OAuth2 token is written back to the named app (not
    the default app) when --app is set.
  • TestRefreshOAuth2TokenSavesToDefaultAppWhenNoOverride — regression
    guard confirming that without --app, refreshed tokens are saved to the
    default app as before.

api/client.go

  • getAuthHeader auto-detection probes: both existence checks now use
    GetFirstOAuth2TokenForApp / GetOAuth1TokensForApp with c.auth.AppName()

api/client_test.go

Two new sub-tests in TestGetAuthHeader:

  • Auto-detect uses named app bearer token when --app is set — confirms
    the named app's bearer token is selected when the default app has none.
  • Auto-detect falls back to default app when no --app flag — regression
    guard confirming the default app is used when AppName() returns "".

cli/auth.go

  • auth app --bearer-token: SaveBearerTokenSaveBearerTokenForApp(a.AppName(), ...)
  • auth oauth1: SaveOAuth1TokensSaveOAuth1TokensForApp(a.AppName(), ...)
  • auth clear --all: ClearAll()ClearAllForApp(a.AppName())
  • auth clear --oauth1: ClearOAuth1Tokens()ClearOAuth1TokensForApp(a.AppName())
  • auth clear --oauth2-username: ClearOAuth2Token(...)ClearOAuth2TokenForApp(a.AppName(), ...)
  • auth clear --bearer: ClearBearerToken()ClearBearerTokenForApp(a.AppName())

cli/webhook.go

  • webhook start: GetOAuth1Tokens()GetOAuth1TokensForApp(authInstance.AppName())

Behaviour After Fix

Invocation App used
xurl whoami default app (unchanged)
xurl whoami --app my-app my-app
xurl /2/users/me --app my-app my-app
xurl --app my-app auth oauth2 authenticates and saves token to my-app
xurl --app my-app auth clear --all clears tokens from my-app only
xurl --app my-app webhook start uses my-app's OAuth1 secret for CRC
any command without --app default app (unchanged)
The --app flag was silently ignored across the codebase. While WithAppName
correctly stored the app name, every subsequent token read, write, and clear
still resolved to the default app.

- fix token reads in auth.go (GetOAuth1Header, GetOAuth2Header,
  RefreshOAuth2Token, GetBearerTokenHeader) to use ForApp variants
- fix OAuth2 token saves in OAuth2Flow and RefreshOAuth2Token to write
  back to the named app instead of the default
- fix WithAppName to replace credentials even when env vars were already set
- fix auto-detection probes in api/client.go (getAuthHeader) to check
  the named app for available tokens
- fix cli/auth.go save and clear commands (bearer, oauth1, clear) to
  operate on the named app
- fix cli/webhook.go CRC validation to read OAuth1 secret from the named app
- add AppName() getter on Auth to expose appName to the api package
- add tests covering token isolation per app, credential override, refresh
  token save target, and default-app regression guards
@CLAassistant

CLAassistant commented Mar 8, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

Signed-off-by: Santiago Medina <santiagm08@gmail.com>

@santiagomed santiagomed left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks for the thorough fix, @rmacias12!

@santiagomed
santiagomed merged commit eeeb501 into xdevplatform:main Apr 19, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

3 participants