Skip to content

feat(auth): multi-org OAuth session support via --org flag#139

Merged
platinummonkey merged 4 commits intomainfrom
feat/multi-org-support
Feb 28, 2026
Merged

feat(auth): multi-org OAuth session support via --org flag#139
platinummonkey merged 4 commits intomainfrom
feat/multi-org-support

Conversation

@platinummonkey
Copy link
Collaborator

Summary

Adds --org <name> as a global flag enabling multiple named OAuth sessions for Datadog parent/child sub-organizations. Users can now authenticate to child orgs without falling back to API/App key auth.

Closes #134

Changes

  • --org global flag — works across all 283 commands; also readable via DD_ORG env var and config.yaml
  • pup auth list — new subcommand listing all stored sessions as JSON/table/YAML via the formatter
  • Single keychain entry per site — all org tokens are stored as a JSON map under one keychain item (tokens_<site>), so the entry count stays at one per site regardless of how many orgs are added. Avoids per-item authorization dialogs after binary upgrades.
  • Transparent migration — existing bare TokenSet files/entries are promoted to the new map format on first read; no user action required
  • Session registry~/.config/pup/sessions.json tracks org labels (no secrets) for pup auth list
  • pup auth logout --org <name> — removes only the named org's tokens; client credentials (DCR) are preserved since they are site-scoped and shared across orgs

Storage key design

Scenario Keychain / file key Value
Default session tokens_datadoghq_com {"__default__": {...}}
Named org tokens_datadoghq_com {"__default__": {...}, "prod-child": {...}}
Client creds (unchanged) client_datadoghq_com {...}

Usage

# Login to child orgs
pup auth login --org prod-child
pup auth login --org staging-child

# List all stored sessions
pup auth list
pup auth list --output=table

# Use a specific org for any command
pup monitors list --org prod-child
DD_ORG=prod-child pup metrics query --query "avg:system.cpu.user{*}"

# Status / logout for a specific org
pup auth status --org prod-child
pup auth logout --org staging-child   # removes only staging-child
pup auth logout                       # removes only default session

Testing

  • 21 new tests covering org_map_key, parse_token_map (including legacy migration), FileStorage map behaviour (multiple orgs, single file, delete semantics), and the session registry (save, list, dedup, remove)
  • PUP_CONFIG_DIR env var now respected on native builds (matches existing WASI behaviour) to enable isolated test directories
  • All 328 tests passing; cargo clippy -- -D warnings and cargo fmt --check clean

Commits

  • feat(auth): add --org flag for multi-org OAuth session support
  • refactor(auth): consolidate org tokens into one keychain entry per site
  • test(auth): add tests for multi-org storage logic
  • fix(auth): route pup auth list through formatter for clean output

🤖 Generated with Claude Code

platinummonkey and others added 4 commits February 27, 2026 17:50
Adds global --org flag enabling multiple named OAuth sessions for
Datadog parent/child sub-organizations. Users can now authenticate
to child orgs without falling back to API/App key auth.

- Add --org global flag to Cli (works across all 283 commands)
- Add DD_ORG env var support (flag > env > config file > default)
- Add AuthActions::List / pup auth list to view stored sessions
- Storage key: tokens_<site> (default) or tokens_<site>_<org> (named)
- Client credentials remain site-scoped (shared across orgs per site)
- Add SessionEntry + sessions.json registry (~/.config/pup/sessions.json)
- Update login/logout/status/token/refresh to pass org through storage
- Logout with --org only removes that org's tokens (safe by default)
- Add --org to agent schema global_flags for AI assistant awareness

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Instead of one keychain/file entry per org (tokens_<site>_<org>), store
all orgs for a site under a single entry (tokens_<site>) as a JSON map:

  {"__default__": {...}, "prod-child": {...}, "staging-child": {...}}

This means the keychain entry count stays at one per site regardless of
how many orgs are added, avoiding per-item authorization dialogs after
a binary upgrade (when the new binary isn't yet in each item's ACL).

Migration is transparent: on first read, a bare TokenSet (old format)
is promoted to {"__default__": <tokens>} without any user action.

- Remove token_key() helper (no longer needed)
- Add OrgTokenMap, DEFAULT_ORG_KEY, org_map_key(), parse_token_map()
- Rewrite save/load/delete token methods for FileStorage, KeychainStorage,
  and LocalStorageBackend to use the map-in-single-entry approach
- delete_tokens removes the entry entirely when the last org is deleted

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Covers the new logic introduced by the multi-org and keychain-consolidation
work. No tests existed for any of this before this commit.

config.rs:
- config_dir() now respects PUP_CONFIG_DIR on native builds (enables
  isolated test dirs; matches existing WASI behaviour)
- test_config_dir_returns_path: guard with ENV_LOCK + clear PUP_CONFIG_DIR
- test_config_dir_respects_override: new test for the override path

storage.rs (21 new tests):
- org_map_key: None, empty string, named org (3)
- parse_token_map: new map format, multiple orgs, legacy bare-TokenSet
  migration, invalid JSON (4)
- FileStorage: save/load default org, save/load named org, multiple orgs
  write to one file, org isolation, delete last org removes file, delete
  one org preserves others, delete nonexistent is ok, legacy file
  migration (8)
- Session registry: empty list, save and list, dedup, remove, remove
  nonexistent (5)
- TempDir helper for self-cleaning test directories (no external crate)

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Previously list() bypassed the formatter entirely — it eprintln'd a
human-readable preview to stderr and then println'd JSON, resulting in
both appearing together in the terminal regardless of output mode.

Now list() delegates to formatter::output(cfg, &sessions), which:
- --output=json (default): prints only JSON, no extra lines
- --output=table:          prints a table with site/org columns
- --output=yaml:           prints YAML
- agent mode:              prints JSON only (same as default)

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
@platinummonkey platinummonkey merged commit 107387f into main Feb 28, 2026
6 checks passed
@platinummonkey platinummonkey deleted the feat/multi-org-support branch February 28, 2026 02:00
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.

[FEATURE] Support for queries between Parent/Child Organizations

1 participant