Skip to content

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

Open
rmacias12 wants to merge 1 commit intoxdevplatform:mainfrom
rmacias12:fix/fix-app-flag
Open

fix: --app flag not applied to subcommands#46
rmacias12 wants to merge 1 commit intoxdevplatform:mainfrom
rmacias12:fix/fix-app-flag

Conversation

@rmacias12
Copy link

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
Copy link

CLAassistant commented Mar 8, 2026

CLA assistant check
All committers have signed the CLA.

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.

--app flag does not switch credentials for shortcut subcommands

2 participants