From 053b0c88e5a97d8d96c298f7020f6db6b8e7abde Mon Sep 17 00:00:00 2001 From: Chris Botelho Date: Wed, 6 May 2026 14:41:15 -0500 Subject: [PATCH] auth login: allow user-scoped API key without --oid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The validator currently rejects `auth login --uid X --api-key Y` with "Error: --oid and --api-key are required for API key login.", even though `--uid` is documented as the flag for user-scoped API keys. This makes brand-new accounts un-bootstrappable from the CLI because they have no organization yet — chicken-and-egg, since the user can't list/create their first org without working CLI auth, but can't auth without an OID, but doesn't have an OID until they create an org. The User API Key itself is a fully valid credential — env-var auth (LC_UID + LC_API_KEY) works for `auth list-orgs` already, which is the only call needed to bootstrap from "I have a User API Key" to "I know my OIDs". The bug was purely in the `auth login` validator forcing an --oid constraint that the rest of the CLI doesn't need. Fix: rework the validator to accept either: --oid + --api-key (org-scoped key — existing behavior) --uid + --api-key (user-scoped key — new path, --oid optional) When neither --oid nor --uid is provided, emit a clear error distinguishing the two key types. The `write_credentials` call already handles oid=None correctly (skips writing the field), so no downstream changes needed. Tested: - `auth login --uid X --api-key Y` (no --oid) → succeeds - `auth list-orgs` against those credentials → returns the user's orgs - `auth login --api-key Y` (neither --oid nor --uid) → clear error - `auth login --oid X --api-key Y` (existing org-scoped path) → unchanged - `auth login --oauth --provider google` → unchanged --- limacharlie/commands/auth.py | 40 +++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/limacharlie/commands/auth.py b/limacharlie/commands/auth.py index 405e554..c14409c 100644 --- a/limacharlie/commands/auth.py +++ b/limacharlie/commands/auth.py @@ -129,18 +129,34 @@ def login(ctx: click.Context, oid: str | None, api_key: str | None, environment: if oauth: _login_oauth(ctx, oid, env_name, provider, no_browser) - else: - if not oid or not api_key: - click.echo( - "Error: --oid and --api-key are required for API key login.\n" - "Suggestion: Use --oauth for browser-based OAuth login, or provide both --oid and --api-key.", - err=True, - ) - ctx.exit(4) - return - write_credentials(env_name, oid=oid, api_key=api_key, uid=uid or "") - if not ctx.obj.quiet: - click.echo(f"Credentials saved for environment '{env_name}'.") + return + + # API key login. Two valid shapes: + # 1. --oid + --api-key — org-scoped key (and optional --uid for service accounts). + # 2. --uid + --api-key (oid optional) — user-scoped key on a brand-new account with no orgs yet. + if not api_key: + click.echo( + "Error: --api-key is required for API key login.\n" + "Suggestion: Use --oauth for browser-based OAuth login, or provide --api-key with " + "either --oid (org-scoped key) or --uid (user-scoped key).", + err=True, + ) + ctx.exit(4) + return + + if not oid and not uid: + click.echo( + "Error: provide either --oid (org-scoped key) or --uid (user-scoped key) along with --api-key.\n" + "Suggestion: --uid is correct for User API Keys generated under your account profile; " + "--oid is correct for Organization API Keys generated under an org's settings.", + err=True, + ) + ctx.exit(4) + return + + write_credentials(env_name, oid=oid, api_key=api_key, uid=uid or "") + if not ctx.obj.quiet: + click.echo(f"Credentials saved for environment '{env_name}'.") def _login_oauth(ctx: click.Context, oid: str | None, env_name: str, provider: str, no_browser: bool) -> None: