Skip to content

Rr3khan/support personal token env auth - #116

Draft
rr3khan wants to merge 2 commits into
apono-io:mainfrom
rr3khan:rr3khan/support-personal-token-env-auth
Draft

Rr3khan/support personal token env auth#116
rr3khan wants to merge 2 commits into
apono-io:mainfrom
rr3khan:rr3khan/support-personal-token-env-auth

Conversation

@rr3khan

@rr3khan rr3khan commented Jul 16, 2026

Copy link
Copy Markdown

Motivation

This enables secret managers to authenticate the Apono CLI per-invocation, without a prior apono login and without the token ever being written to disk.

The concrete consumer is 1Password Shell Plugins: 1Password/shell-plugins#625 adds an Apono plugin that stores the personal API token in 1Password. Today the CLI's only token entry point is apono login --personal-token, which persists the token in plaintext in config.json. With this change, a credential manager (or CI job) can inject APONO_PERSONAL_TOKEN into the environment of each command instead, and the token never touches disk — which also lets the shell plugin cover every apono command with biometric unlock, the same UX as the AWS/GitHub/etc. plugins.

What this does

  • pkg/config: adds GetSessionConfigFromEnv(), which builds an unpersisted SessionConfig from APONO_PERSONAL_TOKEN. APONO_API_URL optionally overrides the API endpoint for non-production tenants (defaults to https://api.apono.io).
  • pkg/aponoapi: CreateClient resolves its session with explicit precedence:
    1. an explicit --profile flag (unchanged behavior),
    2. a token from the environment,
    3. the persisted config's active profile (unchanged behavior).

The env-var session reuses the existing HTTPClientWithPersonalToken bearer transport; nothing downstream changes. Session.AccountID/UserID are empty in this mode — they're only populated at login time today and nothing outside the login flow reads them (account context at runtime comes from the GetUserSession API, which works with a bearer token).

What doesn't change

  • No behavior change when APONO_PERSONAL_TOKEN is unset.
  • An explicit --profile flag still always wins, so profile-pinned invocations (e.g. the access-details URI handler) are unaffected.
  • apono login / logout / profiles are untouched.

Testing

  • Unit tests for the new env resolution and the precedence rules (stdlib style, t.Setenv).
  • Full go test -race ./... passes.
  • Manual verification with a built binary and an isolated $HOME:
    • no env var → Error: no profiles configured, run 'apono login' to create a profile (unchanged)
    • APONO_PERSONAL_TOKEN set → the profile requirement is bypassed and the CLI makes an authenticated API call (verified end-to-end against api.apono.io)

Open questions for maintainers

  • Naming: happy to rename the env vars if you have a house convention.
  • Docs: if this lands, https://docs.apono.io personal-token and CLI pages should mention the env var — glad to draft that too.

rr3khan added 2 commits July 16, 2026 15:38
Adds GetSessionConfigFromEnv, which builds an unpersisted session from
the APONO_PERSONAL_TOKEN environment variable, with APONO_API_URL as an
optional endpoint override for non-production tenants. Returns nil when
the token variable is unset, so existing config-file behavior is
unaffected.
CreateClient now resolves its session with explicit precedence: an
explicit --profile flag wins, then a token from the environment, then
the persisted config. This lets secret managers (e.g. 1Password Shell
Plugins) authenticate every command per-invocation without a prior
`apono login` and without the token being written to disk. Behaviour is
unchanged when the environment variable is not set.
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.

1 participant