Add --scope flag to xero login (env: XERO_SCOPES)#8
Open
DrBradStanfield wants to merge 1 commit into
Open
Conversation
Allows callers to override the hardcoded SCOPES list when authenticating. Required openid/profile/email/offline_access are auto-prepended via a new resolveScopes() helper to prevent footguns (refresh tokens require offline_access). Also extracts REQUIRED_OAUTH_SCOPES as a const so the existing SCOPES list spreads from it — single source of truth, no drift if one is edited later. Use case: Xero apps created after 2 March 2026 cannot grant umbrella scopes like accounting.transactions or accounting.journals.read on the Starter plan, so the hardcoded SCOPES set causes login to fail with 'unauthorized_client'. This flag lets users request a narrower scope set (e.g. read-only granular scopes for a finance dashboard / agent integration) without forking.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a
--scopeflag (env:XERO_SCOPES) toxero loginthat overrides the hardcodedSCOPESlist. Required OAuth scopes (openid profile email offline_access) are auto-prepended so callers can pass just the API scopes they need without breaking refresh tokens.Motivation
The current
SCOPESconstant insrc/lib/oauth.tsrequests broad write scopes (accounting.contacts,accounting.invoices,accounting.payments,accounting.banktransactions,accounting.manualjournals) plusaccounting.journals.read. This is fine for full-featured workflows but fails or over-grants in two real cases:Read-only integrations. Building a finance dashboard, agent integration, or read-only reporting tool? You don't want a token that can mutate. Currently the only options are forking the CLI or accepting write capability the integration will never use.
Apps created after 2 March 2026. Per Xero's scope migration, new apps lose access to umbrella scopes like
accounting.transactions[.read]and (without an Advanced plan)accounting.journals.read. The hardcodedSCOPESset includes both, so plainxero loginreturnsError 500 / unauthorized_client / "Unknown client or client not enabled"on a new Starter-plan PKCE app — which is misleading because it sounds like the Client ID is wrong.In both cases users currently must fork the CLI just to change one line.
Changes
src/commands/login.ts: newscopeflag (--scope <value>, envXERO_SCOPES). Passed toperformLogin(clientId, scope).src/lib/oauth.ts:REQUIRED_OAUTH_SCOPES = ['openid', 'profile', 'email', 'offline_access']as a const, and rebuilt the existingSCOPESto spread from it. Single source of truth — eliminates the drift risk where someone could edit one list and forget the other.resolveScopes(scopes?)helper: whenscopesis provided, returnsREQUIRED_OAUTH_SCOPESplus the user-supplied scopes (deduped viaSet, preserves insertion order). When omitted, returns the existing hardcodedSCOPES— behaviour unchanged for callers not using--scope.No new dependencies. No behaviour change for existing users who don't pass
--scope.Example
Tests
All 63 existing tests pass. Patch is additive — no existing tests modified.
Notes for reviewers
--scope-preset readonly) but it's brittle: the right scope set depends on which CLI subcommands the user wants to run, and Xero's scope catalog changes (the 2026-03-02 migration is a recent example). A raw--scopeflag is more durable.offline_accessyou can't refresh, and withoutopenidyou can't/connections. Worth the very small loss of flexibility to prevent the most common footgun.README.mdwould be a follow-up if this is accepted.