feat(auth): add OIDC device authorization flow for CLI authentication - #29
Conversation
0cf3cf1 to
df8df45
Compare
Implement OAuth 2.0 Device Authorization Grant (RFC 8628) for the DCM CLI. This adds `dcm login` and `dcm logout` commands, token storage with OS keyring primary and file fallback, and an authenticated HTTP transport with lazy token loading and auto-refresh. Key capabilities: - `dcm login` performs OIDC device flow via Keycloak dcm-cli client - `dcm logout` revokes refresh token and clears stored credentials - AuthTransport injects Bearer tokens with automatic refresh on expiry - DCM_TOKEN / --token bypasses OIDC flow for CI/scripting - Config persistence: login writes issuer-url to the active config file - Token file permissions: 0600 file, 0700 directory - HTTP scheme warning when sending tokens over unencrypted connections - Login/logout and token refresh use TLS from the issuer URL/base transport Signed-off-by: Chad Crum <ccrum@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Expand unit and command tests with an OIDC mock server, covering device flow login/logout, AuthTransport refresh behavior, token store fallbacks, and FileStore inaccessible-path error handling. Signed-off-by: Chad Crum <ccrum@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Update go.mod/go.sum for OIDC and keyring dependencies, and document login/logout, issuer URL, token bypass, and auth architecture in README.md and CLAUDE.md. Signed-off-by: Chad Crum <ccrum@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com>
df8df45 to
c761e33
Compare
Rename unused Save params and gofumpt-align the AuthTransport literal so CI lint passes. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Chad Crum <ccrum@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com>
gabriel-farache
left a comment
There was a problem hiding this comment.
Review done with an agent. It supported the other comments and added one
|
/agentic_review |
Code Review by Qodo
1.
|
Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Chad Crum <ccrum@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Chad Crum <ccrum@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Chad Crum <ccrum@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Chad Crum <ccrum@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Chad Crum <ccrum@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Chad Crum <ccrum@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Chad Crum <ccrum@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Chad Crum <ccrum@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Chad Crum <ccrum@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Thanks @gabriel-farache - the TLS refresh one was valid. Fixed in 0152e50 (issuer-derived RefreshTransport). |
Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Chad Crum <ccrum@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com>
## Summary - Trim auth test plan E2E gaps to **TC-36 – TC-42** (CLI and JWT-negative cases removed from this plan) - Block SP / instance happy path on [FLPATH-4622](https://redhat.atlassian.net/browse/FLPATH-4622); UI path on [FLPATH-4645](https://redhat.atlassian.net/browse/FLPATH-4645) - Fix `POST /catalog-item-instances` (HTTP 201), provider `health_status`, port convention (`:8080` local / `:9080` Ecosystem Jenkins) - Add subsystem + E2E checklist tables with ❗ markers; document Jenkins dead `--auth-enabled` on `run-e2e.sh` - Clarify TC-08 as control-plane API only ## Out of scope here - CLI auth → [utilities#34](#34) / [cli#29](dcm-project/cli#29) - Wrong audience / `alg:none` → ❗ should cover on TC-14/TC-15 (subsystem) ## Test plan - [x] Review feedback addressed (Gloria / Chad) - [ ] Confirm sanitization notice still holds - [ ] Optional: pipeline fix for dead `--auth-enabled`; subsystem wrong-aud / alg:none follow-ups Signed-off-by: Vladislav Kolodny <vkolodny@redhat.com> Co-authored-by: Cursor <cursoragent@cursor.com>
) ## Summary - Add the FLPATH-4477 DCM CLI OIDC authentication e2e test plan under `test-plans/` - Relocate it from dcm-project/cli because the cases need a live Keycloak/control-plane stack ## Related PRs - Spec: dcm-project/cli#30 - Implementation: dcm-project/cli#29 --------- Signed-off-by: Chad Crum <ccrum@redhat.com> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com>
jordigilh
left a comment
There was a problem hiding this comment.
Our thread (unused issuerURL + gofumpt in transport_test.go) is fixed. No further concerns.
## Summary - Add FLPATH-4477 OIDC auth `.ai` spec and design decisions - Point `dcm-cli.spec.md` at the new OIDC auth spec and remove auth from out-of-scope - Split docs from the CLI implementation so review can land independently ## Related PRs - Implementation: #29 - E2E test plan: dcm-project/utilities#34 --------- Signed-off-by: Chad Crum <ccrum@redhat.com> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Summary
OIDC device authorization for the DCM CLI -
dcm login/dcm logout, token storage (keyring with file fallback), authenticated HTTP transport with refresh, and aDCM_TOKEN/--tokenbypass for CI.Split into three commits for easier review:
feat(auth):core code - auth / commands / configtest(auth):all test fileschore(auth):go.mod / go.sum + CLAUDE.md / README.mdRelated PRs
Closes https://issues.redhat.com/browse/FLPATH-4477