Skip to content

feat(auth): support client credentials grant - #147

Merged
shadowhand merged 8 commits into
mainfrom
feat/client-credentials
Oct 9, 2026
Merged

shadowhand merged 8 commits into
mainfrom
feat/client-credentials

Conversation

@shadowhand

Copy link
Copy Markdown
Contributor

Closes CL-9

Summary

  • Adds a non-interactive auth option: a profile can store a client_id + client_secret, which rw exchanges for an access token via the OAuth client_credentials grant at the existing per-stage WorkOS token_url.
  • Credentials live in auth/{profile}.json (mode 0600) as a new AuthCache::ClientCredentials variant, declared before Bearer because the enum is untagged. The exchanged token is cached in the same file and re-exchanged within 60s of expiry.
  • rw config profile auth <name> --client-id … --client-secret … stores them (secret prompted if omitted; conflicts with --username/--password).
  • rw auth status, rw config profile show and rw config doctor report the new type without exposing the secret. Doctor exchanges against the profile's saved stage, not the -g override.
  • Docs updated: docs/config.md, README.md, CONTRIBUTING.md.

Out of scope: environment-variable credentials (can be layered on later).

Test plan

  • cargo test (349 lib, 17 bin, 14 integration)
  • cargo clippy --all-targets --all-features -- -D warnings
  • cargo fmt --check
  • Manual: rw config profile auth <p> --client-id … --client-secret … then rw config doctor against a real WorkOS M2M app (whether the RoundingWell API accepts machine tokens, and whether /clinicians/me is meaningful for them, is unverified)

Deferred minors

Tracked in CL-9: concurrent exchange can overwrite a credential rotation; auth status has no expires_at; profile auth --json omits the auth type; whitespace-only values accepted; README doesn't mention logout/login replacing the secret; no retry on 401 for a revoked cached token.

@shadowhand shadowhand left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an automated review by Rory.

Looks good, no issues found. The untagged enum order is correct and has a test. The secret stays out of status, show, and doctor output. The token goes to the AuthKit /oauth2/token endpoint for the profile's saved stage, and the new paths have tests.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Logout cannot recover from malformed auth caches, and related documentation remains incomplete or inaccurate.

4 open findings
What changed in this PR

Adds OAuth client-credentials authentication for non-interactive workflows.

Changes:

  • Stores and exchanges client credentials with token caching.
  • Adds CLI configuration, status, logout, and doctor support.
  • Updates user and contributor documentation.
File Description
src/​prompt.rs Generalizes secure secret prompting.
src/​commands/​config/​profile.rs Configures and reports client credentials.
src/​commands/​config/​doctor.rs Diagnoses client-credential authentication.
src/​commands/​auth.rs Exchanges, caches, reports, and clears tokens.
src/​cli.rs Adds client credential flags.
src/​auth_cache.rs Adds the client-credentials cache variant.
README.md Documents the new workflow.
docs/​config.md Documents the cache format.
CONTRIBUTING.md Updates the architecture overview.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/commands/auth.rs Outdated
Comment thread docs/config.md Outdated
Comment thread src/cli.rs
Comment thread src/commands/config/doctor.rs
@shadowhand
shadowhand merged commit 2c3b517 into main Oct 9, 2026
7 checks passed
@shadowhand
shadowhand deleted the feat/client-credentials branch October 9, 2026 08:36
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.

2 participants