From 767d1a8339cd65fcf954db867e2bdec74e366c35 Mon Sep 17 00:00:00 2001 From: Stephen Rosenthal Date: Fri, 22 May 2026 15:22:16 -0700 Subject: [PATCH 1/3] docs: document multi-org sessions and fix token file layout Adds upstream-accurate docs for pup's named-session model (`--org`, `DD_ORG`, `--subdomain`, `--org-uuid`, `pup auth list`) and the site-selection rules that let `--org ` recall the correct site without re-passing `DD_SITE` on every command. Also corrects two specific inaccuracies in the existing docs: 1. Token file layout. The current docs claim named sessions are stored in `tokens__.json`. The actual implementation (`src/auth/storage.rs`) uses one `tokens_.json` per site with org name as an inner map key. The same applies to the keychain backend (one entry per site, org-keyed payload). 2. Storage default on platforms with a keychain. The README's Token Storage paragraph already mentioned keychain, but the file-structure block in `docs/OAUTH2.md` only showed `~/.config/pup/`. Clarified that `tokens_.json` only exists when no keychain is available or `DD_TOKEN_STORAGE=file` is set. Examples use real public DD_SITE values (datadoghq.com, datadoghq.eu, us3/us5/ap1/ap2.datadoghq.com, ddog-gov.com) rather than placeholders. Verified against source (`config.rs:find_session_site`, `auth/storage.rs:FileStorage`) and empirically with `pup auth status --org ` and `pup logs query --org ` without `DD_SITE` set. --- README.md | 52 +++++++++++++++++-- docs/OAUTH2.md | 135 +++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 168 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 50ca7bd..4a18ba1 100644 --- a/README.md +++ b/README.md @@ -218,8 +218,10 @@ Pup supports two authentication methods. **OAuth2 is preferred** and will be use OAuth2 provides secure, browser-based authentication with automatic token refresh. ```bash -# Set your Datadog site (optional) -export DD_SITE="datadoghq.com" # Defaults to datadoghq.com +# Set your Datadog site (optional, defaults to datadoghq.com). +# Valid values: datadoghq.com, datadoghq.eu, us3.datadoghq.com, +# us5.datadoghq.com, ap1.datadoghq.com, ap2.datadoghq.com, ddog-gov.com +export DD_SITE="datadoghq.com" # Login via browser pup auth login @@ -234,7 +236,51 @@ pup auth status pup auth logout ``` -**Token Storage**: Tokens are stored securely in your system's keychain (macOS Keychain, Windows Credential Manager, Linux Secret Service). Set `DD_TOKEN_STORAGE=file` to use file-based storage instead. +#### Multiple sites and orgs + +Pup persists each login as a separate session, so you can authenticate against multiple Datadog sites and orgs and switch between them with `--org ` (or `DD_ORG=`) on any subcommand. + +```bash +# Login to a non-default site. The site is persisted with the session. +pup auth login --site datadoghq.eu +pup monitors list --site datadoghq.eu # or: DD_SITE=datadoghq.eu pup monitors list + +# Named session for a parent/child sub-org on the same site. +pup auth login --org staging-child +pup monitors list --org staging-child # site recalled from the session, no DD_SITE needed + +# Named session on another site. DD_SITE / --site is only needed at login. +pup auth login --site ap2.datadoghq.com --org ap2-prod +pup monitors list --org ap2-prod # site recalled + +# SAML/SSO org. --subdomain narrows the consent page to one org for tenants +# with subdomain-routed SSO. It is only used during the browser flow and is +# not persisted. +pup auth login --org acme-prod --subdomain acme + +# Pre-target a specific org by UUID (sent as dd_oid). Skips the org switcher +# when the browser session already matches and pre-routes SAML/SSO. The UUID +# is persisted and re-emitted on subsequent `pup auth login` invocations for +# the same named session. +pup auth login --org acme-prod --org-uuid 11111111-2222-3333-4444-555555555555 + +# List all stored sessions (sites, orgs, expiry). +pup auth list + +# Refresh or log out a specific named session. +pup auth refresh --org staging-child +pup auth logout --org staging-child # clears only that named session +``` + +**Site selection rules** (when pup needs a site for a non-auth command, it checks in order): +1. `--site` flag, if passed. +2. `DD_SITE` env var, if set. +3. The site recorded in `~/.config/pup/sessions.json` for the named `--org` / `DD_ORG`, when the lookup is unambiguous. +4. Default: `datadoghq.com`. + +If multiple sessions share the same org name on different sites, step 3 is skipped (ambiguous) and pup warns to stderr; pass `DD_SITE` to disambiguate. Default (unnamed) sessions on multiple sites also can't be disambiguated by `--org`; set `DD_SITE` to pick one. + +**Token Storage**: Tokens are stored securely in your system's keychain by default (macOS Keychain, Windows Credential Manager, Linux Secret Service via the `keyring` crate). When no keychain is available, pup falls back to JSON files under `~/.config/pup/` with `0600` permissions. Set `DD_TOKEN_STORAGE=file` to force file storage. Tokens for all orgs at a given site share one storage entry (one keychain entry or one `tokens_.json` file), keyed internally by org name. **Note**: OAuth2 requires Dynamic Client Registration (DCR) to be enabled on your Datadog site. If DCR is not available yet, use API key authentication. diff --git a/docs/OAUTH2.md b/docs/OAUTH2.md index f658b38..4307aa9 100644 --- a/docs/OAUTH2.md +++ b/docs/OAUTH2.md @@ -27,7 +27,9 @@ Pup supports OAuth2 authentication with PKCE (Proof Key for Code Exchange) for s ### 1. Login ```bash -pup auth login +pup auth login # default site (datadoghq.com), default org +pup auth login --site datadoghq.eu # a different Datadog site +pup auth login --org staging-child # a named session for a second org ``` This will: @@ -39,6 +41,9 @@ This will: 6. Exchange the authorization code for access/refresh tokens 7. Store tokens securely (OS keychain, or JSON file under `~/.config/pup/` with `0600` permissions when no keychain is available) +See [Multi-Site Support](#multi-site-support) and +[Multi-Org Support](#multi-org-support) for managing multiple sessions. + ### 2. Check Status ```bash @@ -61,10 +66,12 @@ Manually refresh your access token using the refresh token. This happens automat ### 4. Logout ```bash -pup auth logout +pup auth logout # clears the default (unnamed) session for the current site +pup auth logout --org staging-child # clears only the named session, leaves other sessions intact ``` -Clears all stored tokens and client credentials for the current site. +See [Multi-Org Support](#multi-org-support) for managing multiple named +sessions side-by-side. ## OAuth2 Flow Details @@ -152,6 +159,11 @@ keychain is unavailable, pup falls back to a JSON file at `~/.config/pup/tokens_.json` with `0600` permissions. Set `DD_TOKEN_STORAGE=file` to force file storage. +Each site gets one storage entry. When a site has multiple named-org +sessions (see [Multi-Org Support](#multi-org-support)), all of their +tokens live inside that single entry, keyed by org name; there is no +separate `tokens__.json` file. + The token payload is: ```json @@ -262,29 +274,114 @@ Pup supports all Datadog sites with separate credentials per site: ```bash # US1 (default) -export DD_SITE="datadoghq.com" -pup auth login +pup auth login --site datadoghq.com # EU1 -export DD_SITE="datadoghq.eu" -pup auth login +pup auth login --site datadoghq.eu # US3 -export DD_SITE="us3.datadoghq.com" -pup auth login +pup auth login --site us3.datadoghq.com # US5 -export DD_SITE="us5.datadoghq.com" -pup auth login +pup auth login --site us5.datadoghq.com # AP1 -export DD_SITE="ap1.datadoghq.com" -pup auth login +pup auth login --site ap1.datadoghq.com + +# AP2 +pup auth login --site ap2.datadoghq.com + +# Gov +pup auth login --site ddog-gov.com + +# DD_SITE env var also works on any of the above. +DD_SITE=datadoghq.eu pup auth login ``` Each site maintains separate: - Client credentials (`client_.json`) -- Access/refresh tokens (`tokens_.json`) +- Access/refresh tokens (one storage entry per site, keyed internally by org) + +## Multi-Org Support + +Pup supports multiple Datadog orgs side-by-side via *named sessions*. Each +named session is a `(site, org)` pair, and `--org ` (or +`DD_ORG=`) selects which session a command runs against. The flag is +global and works on every subcommand, not just `auth`. + +### Logging into multiple orgs + +```bash +# Two child orgs on the default site (US1). +pup auth login --org prod-child +pup auth login --org staging-child + +# A child org on a different site. --site is only needed at login; +# subsequent commands recall it from the session registry. +pup auth login --site ap2.datadoghq.com --org ap2-prod + +# A SAML/SSO org. --subdomain narrows the consent page to one org for +# tenants with subdomain-routed SSO. It is only used during the browser +# flow and is not persisted with the session. +pup auth login --org acme-prod --subdomain acme + +# Pre-target a specific org by UUID (sent as `dd_oid`). Skips the org +# switcher when the existing browser session matches, and pre-routes +# SAML/SSO routing for first-time logins. The UUID is persisted with the +# session and re-emitted on subsequent `pup auth login` invocations for +# the same named session. +pup auth login --org acme-prod --org-uuid 11111111-2222-3333-4444-555555555555 +``` + +### Using a named session + +```bash +# Site is recalled from sessions.json; no DD_SITE / --site needed. +pup monitors list --org prod-child +pup logs query --org ap2-prod --query "service:web-store" --limit 10 + +# DD_ORG env var is equivalent to --org. +DD_ORG=prod-child pup metrics query --query "avg:system.cpu.user{*}" +``` + +### Inspecting and managing sessions + +```bash +# List every stored session (site, org, expiry, status). +pup auth list + +# Refresh a specific named session. +pup auth refresh --org prod-child + +# Log out of a single named session (other sessions are untouched). +pup auth logout --org staging-child + +# Log out of the default (unnamed) session for the current site. +pup auth logout +``` + +### Site selection rules + +When pup needs a site for a non-auth command, it resolves in this order: + +1. `--site` flag, if passed. +2. `DD_SITE` env var, if set. +3. The site recorded in `~/.config/pup/sessions.json` for the named + `--org` / `DD_ORG`, when the lookup is unambiguous. +4. Default: `datadoghq.com`. + +If multiple sessions share the same org name on different sites, step 3 +is skipped (ambiguous) and pup warns to stderr; pass `DD_SITE` to +disambiguate. Default (unnamed) sessions on different sites cannot be +disambiguated by `--org` either, since they have no name; pass `DD_SITE` +to pick one. + +### Session registry + +Named-session metadata lives in `~/.config/pup/sessions.json`. The file +records the `site`, `org`, and (when supplied at login) `org_uuid` for +each session. No tokens or secrets are stored here. The registry is what +enables `--org ` to recall the right site on a non-auth command. ## Troubleshooting @@ -396,10 +493,16 @@ This indicates a potential security issue. Run `pup auth login` again to start a ``` ~/.config/pup/ -├── client_datadoghq_com.json # DCR client credentials -└── tokens_datadoghq_com.json # OAuth2 tokens +├── client_.json # DCR client credentials, one per site (shared across orgs) +├── tokens_.json # OAuth2 tokens, one per site (keyed internally by org) +└── sessions.json # Named-session registry (site, org, org_uuid; no secrets) ``` +On platforms with a keychain, `tokens_.json` is replaced by a +single keychain entry per site (the named-by-org map is stored as the +entry's value). `client_.json` and `sessions.json` are always +file-based. + ### Code Structure ``` From 8096eb44bbf3e1fd273e464b1969db24734bdfe1 Mon Sep 17 00:00:00 2001 From: Stephen Rosenthal Date: Fri, 22 May 2026 15:49:38 -0700 Subject: [PATCH 2/3] Address review feedback from /review-fix-loop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three iterations of code-reviewer (Claude) + Codex review against the pup source surfaced these issues, all now fixed: - `--site` is only on `pup auth login` and `pup auth status`, not a global flag. Removed `pup monitors list --site ...` and dropped `--site` from the "Site selection rules" list in both files; added a clarifying sentence about which two subcommands accept it. - `client_.json` is not "always file-based". On secure-store platforms, client credentials live alongside tokens in the per-site store entry (SiteData struct). Rewrote File Structure trailer in docs/OAUTH2.md. - Token Storage paragraphs were inaccurate per-platform. macOS uses Apple's Security framework (Touch ID), not the `keyring` crate. Windows shards large per-site blobs across multiple WinCred records. Rewrote both files' Token Storage descriptions. - `pup auth logout` has no `--site` flag — added a `DD_SITE=` example and an explicit note. - Default-session logout deletes the shared per-site DCR client credentials, which breaks refresh for any named-org session on the same site. Added a "Side effect on sibling sessions" callout in docs/OAUTH2.md and a matching note in README.md. - File-mode storage layout had separate `tokens_.json` and `client_.json` files, but the new wording implied one entry. Clarified the secure-store-vs-file distinction in both files. - `~/.config/pup/config.yaml` `site:` field was missing from the site precedence list. Added. - "Disambiguation" wording for default (unnamed) sessions implied there was some ambiguity to resolve. Rewrote to reflect that unnamed sessions are simply not addressable via `--org`. Also reframed the DD_SITE list as "Common values" rather than "Valid values" so it doesn't read as exhaustive (omitting datad0g.com is intentional — staging is internal-only, but the CLI does accept it). Added a "Recommended pattern" callout in the Multi-Org Support section suggesting that users working with multiple orgs give every session an explicit `--org` name rather than mixing the default slot with named sessions — both because the default slot is easy to re-log into different orgs by accident, and because logging out the default tears down the shared DCR client credentials. --- README.md | 28 ++++++++++------- docs/OAUTH2.md | 84 +++++++++++++++++++++++++++++++++++--------------- 2 files changed, 76 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 4a18ba1..12ab649 100644 --- a/README.md +++ b/README.md @@ -219,8 +219,9 @@ OAuth2 provides secure, browser-based authentication with automatic token refres ```bash # Set your Datadog site (optional, defaults to datadoghq.com). -# Valid values: datadoghq.com, datadoghq.eu, us3.datadoghq.com, -# us5.datadoghq.com, ap1.datadoghq.com, ap2.datadoghq.com, ddog-gov.com +# Common values: datadoghq.com, datadoghq.eu, us3.datadoghq.com, +# us5.datadoghq.com, ap1.datadoghq.com, ap2.datadoghq.com, ddog-gov.com. +# Other Datadog sites are also accepted. export DD_SITE="datadoghq.com" # Login via browser @@ -241,9 +242,11 @@ pup auth logout Pup persists each login as a separate session, so you can authenticate against multiple Datadog sites and orgs and switch between them with `--org ` (or `DD_ORG=`) on any subcommand. ```bash -# Login to a non-default site. The site is persisted with the session. +# Login to a non-default site. --site is only accepted by `pup auth login` +# and `pup auth status`. For other commands, select the site via DD_SITE +# (or attach --org at login so subsequent commands recall it; see below). pup auth login --site datadoghq.eu -pup monitors list --site datadoghq.eu # or: DD_SITE=datadoghq.eu pup monitors list +DD_SITE=datadoghq.eu pup monitors list # Named session for a parent/child sub-org on the same site. pup auth login --org staging-child @@ -272,15 +275,18 @@ pup auth refresh --org staging-child pup auth logout --org staging-child # clears only that named session ``` -**Site selection rules** (when pup needs a site for a non-auth command, it checks in order): -1. `--site` flag, if passed. -2. `DD_SITE` env var, if set. -3. The site recorded in `~/.config/pup/sessions.json` for the named `--org` / `DD_ORG`, when the lookup is unambiguous. -4. Default: `datadoghq.com`. +Note: `pup auth logout` (default session) also deletes the shared DCR client credentials for that site. Any named-org sessions on the same site will then fail to refresh until you `pup auth login --org ` again. Logging out a named session (`--org `) does not touch the shared client credentials. -If multiple sessions share the same org name on different sites, step 3 is skipped (ambiguous) and pup warns to stderr; pass `DD_SITE` to disambiguate. Default (unnamed) sessions on multiple sites also can't be disambiguated by `--org`; set `DD_SITE` to pick one. +**Site selection rules** (when pup resolves a site for a non-auth command): +1. `DD_SITE` env var (or `site:` in `~/.config/pup/config.yaml`), if set. +2. The site recorded in `~/.config/pup/sessions.json` for the named `--org` / `DD_ORG`, when the lookup is unambiguous. +3. Default: `datadoghq.com`. -**Token Storage**: Tokens are stored securely in your system's keychain by default (macOS Keychain, Windows Credential Manager, Linux Secret Service via the `keyring` crate). When no keychain is available, pup falls back to JSON files under `~/.config/pup/` with `0600` permissions. Set `DD_TOKEN_STORAGE=file` to force file storage. Tokens for all orgs at a given site share one storage entry (one keychain entry or one `tokens_.json` file), keyed internally by org name. +`pup auth login` and `pup auth status` additionally accept `--site`, which wins over the above for those two commands. + +If multiple sessions share the same org name on different sites, step 2 is skipped (ambiguous) and pup warns to stderr; pass `DD_SITE` to disambiguate. An unnamed (default) session can't be selected by `--org` at all -- if you have multiple unnamed sessions on different sites, set `DD_SITE` to pick one. + +**Token Storage**: By default, OAuth tokens and DCR client credentials are stored in your platform's secure store: macOS Keychain (via Apple's Security framework, with Touch ID prompts), Linux Secret Service (via the `keyring` crate), or Windows Credential Manager (via the `keyring` crate; sharded across multiple WinCred entries when the per-site blob exceeds the size limit). When no secure store is available, pup falls back to JSON files under `~/.config/pup/` with `0600` permissions; in file mode tokens and client credentials are kept in separate files (`tokens_.json`, `client_.json`). Set `DD_TOKEN_STORAGE=file` to force file storage. In either mode, all tokens for a given site share one tokens entry, keyed internally by org name. **Note**: OAuth2 requires Dynamic Client Registration (DCR) to be enabled on your Datadog site. If DCR is not available yet, use API key authentication. diff --git a/docs/OAUTH2.md b/docs/OAUTH2.md index 4307aa9..fb9c304 100644 --- a/docs/OAUTH2.md +++ b/docs/OAUTH2.md @@ -66,10 +66,22 @@ Manually refresh your access token using the refresh token. This happens automat ### 4. Logout ```bash -pup auth logout # clears the default (unnamed) session for the current site -pup auth logout --org staging-child # clears only the named session, leaves other sessions intact +pup auth logout # default session for the current site +DD_SITE=datadoghq.eu pup auth logout # default session for a non-default site +pup auth logout --org staging-child # one named session, leaves others intact ``` +`pup auth logout` itself doesn't accept a `--site` flag; use `DD_SITE` to +pick which default session to clear. + +**Side effect on sibling sessions:** logging out the default (unnamed) +session for a site also deletes that site's shared DCR client +credentials. Any named-org sessions on the same site will still hold +valid access tokens, but their next automatic refresh will fail (no +client credentials), so you'll need to `pup auth login --org ` +again to re-register. Logging out a named session (`--org `) does +not touch the shared client credentials. + See [Multi-Org Support](#multi-org-support) for managing multiple named sessions side-by-side. @@ -153,16 +165,23 @@ Proof Key for Code Exchange prevents authorization code interception: #### Token Storage -Tokens are stored in the OS keychain by default (macOS Keychain, Windows -Credential Manager, Linux Secret Service via the `keyring` crate). When a -keychain is unavailable, pup falls back to a JSON file at -`~/.config/pup/tokens_.json` with `0600` permissions. Set -`DD_TOKEN_STORAGE=file` to force file storage. - -Each site gets one storage entry. When a site has multiple named-org -sessions (see [Multi-Org Support](#multi-org-support)), all of their -tokens live inside that single entry, keyed by org name; there is no -separate `tokens__.json` file. +By default, OAuth tokens and DCR client credentials are stored in your +platform's secure store: macOS Keychain (via Apple's Security framework, +with Touch ID prompts), Linux Secret Service (via the `keyring` crate), +or Windows Credential Manager (via the `keyring` crate). When the +secure store is unavailable, pup falls back to JSON files under +`~/.config/pup/` with `0600` permissions. Set `DD_TOKEN_STORAGE=file` +to force file storage. + +In secure-store mode each site has one per-site entry holding both +tokens and client credentials (on Windows, sharded across multiple +WinCred records when the per-site blob is large). In file mode tokens +and client credentials are kept in separate files +(`tokens_.json` and `client_.json`). In either mode, when a +site has multiple named-org sessions (see +[Multi-Org Support](#multi-org-support)) all of their tokens live +inside the per-site tokens entry, keyed internally by org name; there +is no separate `tokens__.json` file. The token payload is: @@ -309,6 +328,15 @@ named session is a `(site, org)` pair, and `--org ` (or `DD_ORG=`) selects which session a command runs against. The flag is global and works on every subcommand, not just `auth`. +**Recommended pattern if you work with more than one org:** give every +session an explicit `--org ` rather than mixing the default +(unnamed) session with named ones. This way `--org ` always +appears in your commands and there's no ambiguity about which org a +query targeted. Sharing one default slot across multiple orgs is easy +to get wrong (you re-log into a different org without realizing it), +and as noted in [Logout](#4-logout), logging out the default also +removes the shared DCR client credentials for that site. + ### Logging into multiple orgs ```bash @@ -362,19 +390,22 @@ pup auth logout ### Site selection rules -When pup needs a site for a non-auth command, it resolves in this order: +When pup resolves a site for a non-auth command: -1. `--site` flag, if passed. -2. `DD_SITE` env var, if set. -3. The site recorded in `~/.config/pup/sessions.json` for the named +1. `DD_SITE` env var (or `site:` in `~/.config/pup/config.yaml`), if set. +2. The site recorded in `~/.config/pup/sessions.json` for the named `--org` / `DD_ORG`, when the lookup is unambiguous. -4. Default: `datadoghq.com`. +3. Default: `datadoghq.com`. + +`pup auth login` and `pup auth status` additionally accept `--site`, +which wins over the above for those two commands. No other subcommand +accepts `--site`. -If multiple sessions share the same org name on different sites, step 3 +If multiple sessions share the same org name on different sites, step 2 is skipped (ambiguous) and pup warns to stderr; pass `DD_SITE` to -disambiguate. Default (unnamed) sessions on different sites cannot be -disambiguated by `--org` either, since they have no name; pass `DD_SITE` -to pick one. +disambiguate. An unnamed (default) session can't be selected by `--org` +at all (it has no name to look up), so if you have multiple unnamed +sessions on different sites, set `DD_SITE` to pick one. ### Session registry @@ -498,10 +529,13 @@ This indicates a potential security issue. Run `pup auth login` again to start a └── sessions.json # Named-session registry (site, org, org_uuid; no secrets) ``` -On platforms with a keychain, `tokens_.json` is replaced by a -single keychain entry per site (the named-by-org map is stored as the -entry's value). `client_.json` and `sessions.json` are always -file-based. +On platforms using the secure-store backend (macOS, plus Linux/Windows +when a keychain is available), both `client_.json` and +`tokens_.json` are absent: their contents live together in a +per-site secure-store entry. On Windows, this entry may be sharded +across multiple WinCred records when the per-site blob exceeds the +WinCred size limit. `sessions.json` is always file-based regardless of +backend. ### Code Structure From 8feb71d747755de67272e5bebe19b3cab92a9370 Mon Sep 17 00:00:00 2001 From: Stephen Rosenthal Date: Fri, 22 May 2026 16:08:19 -0700 Subject: [PATCH 3/3] Address review feedback (round 2) Second pass through /review-fix-loop (3 iterations, converged) caught a few residual issues that the first pass missed: - README "Login to a non-default site" example previously implied that any subsequent command would recall the site from the session. Recall only happens when the command itself carries --org / DD_ORG. Made the parenthetical explicit. - README + OAUTH2 logout side-effect note previously said sibling named sessions must be re-logged-in to recover. Actually any subsequent `pup auth login` on that site (default or named) re-registers the shared DCR client credentials, after which the other sessions can refresh normally. - OAUTH2 Multi-Site "Each site maintains separate" bullets mixed file-mode filenames with mode-agnostic wording. Rewrote both bullets mode-agnostically and pointed at the Token Storage section. - `pup auth list` description differed between README and OAUTH2 (README listed three fields, OAUTH2 listed four). Aligned both to the full set: site, org, org_uuid, scopes, expiry, status. - Windows WinCred sharding was described as conditional ("when blob exceeds size limit") in three places. Per the source, chunking is unconditional: every save produces a count record plus one or more chunk records. Rewrote all three locations. --- README.md | 9 +++++---- docs/OAUTH2.md | 34 +++++++++++++++++++--------------- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 12ab649..1c0a113 100644 --- a/README.md +++ b/README.md @@ -244,7 +244,8 @@ Pup persists each login as a separate session, so you can authenticate against m ```bash # Login to a non-default site. --site is only accepted by `pup auth login` # and `pup auth status`. For other commands, select the site via DD_SITE -# (or attach --org at login so subsequent commands recall it; see below). +# (or use a named session and pass --org on every subsequent command; see +# the Named session examples below). pup auth login --site datadoghq.eu DD_SITE=datadoghq.eu pup monitors list @@ -267,7 +268,7 @@ pup auth login --org acme-prod --subdomain acme # the same named session. pup auth login --org acme-prod --org-uuid 11111111-2222-3333-4444-555555555555 -# List all stored sessions (sites, orgs, expiry). +# List all stored sessions (site, org, org_uuid, scopes, expiry, status). pup auth list # Refresh or log out a specific named session. @@ -275,7 +276,7 @@ pup auth refresh --org staging-child pup auth logout --org staging-child # clears only that named session ``` -Note: `pup auth logout` (default session) also deletes the shared DCR client credentials for that site. Any named-org sessions on the same site will then fail to refresh until you `pup auth login --org ` again. Logging out a named session (`--org `) does not touch the shared client credentials. +Note: `pup auth logout` (default session) also deletes the shared DCR client credentials for that site. Named-org sessions on the same site keep their access tokens but will fail to refresh until the shared credentials are re-registered, which happens automatically on the next `pup auth login` on that site (any org, named or default). Logging out a named session (`--org `) does not touch the shared client credentials. **Site selection rules** (when pup resolves a site for a non-auth command): 1. `DD_SITE` env var (or `site:` in `~/.config/pup/config.yaml`), if set. @@ -286,7 +287,7 @@ Note: `pup auth logout` (default session) also deletes the shared DCR client cre If multiple sessions share the same org name on different sites, step 2 is skipped (ambiguous) and pup warns to stderr; pass `DD_SITE` to disambiguate. An unnamed (default) session can't be selected by `--org` at all -- if you have multiple unnamed sessions on different sites, set `DD_SITE` to pick one. -**Token Storage**: By default, OAuth tokens and DCR client credentials are stored in your platform's secure store: macOS Keychain (via Apple's Security framework, with Touch ID prompts), Linux Secret Service (via the `keyring` crate), or Windows Credential Manager (via the `keyring` crate; sharded across multiple WinCred entries when the per-site blob exceeds the size limit). When no secure store is available, pup falls back to JSON files under `~/.config/pup/` with `0600` permissions; in file mode tokens and client credentials are kept in separate files (`tokens_.json`, `client_.json`). Set `DD_TOKEN_STORAGE=file` to force file storage. In either mode, all tokens for a given site share one tokens entry, keyed internally by org name. +**Token Storage**: By default, OAuth tokens and DCR client credentials are stored in your platform's secure store: macOS Keychain (via Apple's Security framework, with Touch ID prompts), Linux Secret Service (via the `keyring` crate), or Windows Credential Manager (via the `keyring` crate; sharded across multiple WinCred entries to stay within WinCred's per-record size limit). When no secure store is available, pup falls back to JSON files under `~/.config/pup/` with `0600` permissions; in file mode tokens and client credentials are kept in separate files (`tokens_.json`, `client_.json`). Set `DD_TOKEN_STORAGE=file` to force file storage. In either mode, all tokens for a given site share one tokens entry, keyed internally by org name. **Note**: OAuth2 requires Dynamic Client Registration (DCR) to be enabled on your Datadog site. If DCR is not available yet, use API key authentication. diff --git a/docs/OAUTH2.md b/docs/OAUTH2.md index fb9c304..5ab88b7 100644 --- a/docs/OAUTH2.md +++ b/docs/OAUTH2.md @@ -78,9 +78,11 @@ pick which default session to clear. session for a site also deletes that site's shared DCR client credentials. Any named-org sessions on the same site will still hold valid access tokens, but their next automatic refresh will fail (no -client credentials), so you'll need to `pup auth login --org ` -again to re-register. Logging out a named session (`--org `) does -not touch the shared client credentials. +client credentials). The shared credentials are re-registered +automatically by the next `pup auth login` on that site (any org, +named or default), and the sibling sessions can refresh again from +that point on. Logging out a named session (`--org `) does not +touch the shared client credentials. See [Multi-Org Support](#multi-org-support) for managing multiple named sessions side-by-side. @@ -175,10 +177,10 @@ to force file storage. In secure-store mode each site has one per-site entry holding both tokens and client credentials (on Windows, sharded across multiple -WinCred records when the per-site blob is large). In file mode tokens -and client credentials are kept in separate files -(`tokens_.json` and `client_.json`). In either mode, when a -site has multiple named-org sessions (see +WinCred records to stay within WinCred's per-record size limit). In +file mode tokens and client credentials are kept in separate files +(`tokens_.json` and `client_.json`). In either mode, when +a site has multiple named-org sessions (see [Multi-Org Support](#multi-org-support)) all of their tokens live inside the per-site tokens entry, keyed internally by org name; there is no separate `tokens__.json` file. @@ -317,9 +319,11 @@ pup auth login --site ddog-gov.com DD_SITE=datadoghq.eu pup auth login ``` -Each site maintains separate: -- Client credentials (`client_.json`) -- Access/refresh tokens (one storage entry per site, keyed internally by org) +Each site maintains separate state: +- Client credentials, shared across orgs on the same site. +- Access/refresh tokens, in a single per-site entry keyed internally by org. + +See [Token Storage](#token-storage) for the secure-store-vs-file layout. ## Multi-Org Support @@ -375,7 +379,7 @@ DD_ORG=prod-child pup metrics query --query "avg:system.cpu.user{*}" ### Inspecting and managing sessions ```bash -# List every stored session (site, org, expiry, status). +# List every stored session (site, org, org_uuid, scopes, expiry, status). pup auth list # Refresh a specific named session. @@ -532,10 +536,10 @@ This indicates a potential security issue. Run `pup auth login` again to start a On platforms using the secure-store backend (macOS, plus Linux/Windows when a keychain is available), both `client_.json` and `tokens_.json` are absent: their contents live together in a -per-site secure-store entry. On Windows, this entry may be sharded -across multiple WinCred records when the per-site blob exceeds the -WinCred size limit. `sessions.json` is always file-based regardless of -backend. +per-site secure-store entry. On Windows, this entry is sharded into +multiple WinCred records (one count record plus one or more chunk +records) to stay within WinCred's per-record size limit. +`sessions.json` is always file-based regardless of backend. ### Code Structure