Skip to content

[IDENTITY-7909] Scope logout revocation to the CLI Auth0 client - #3432

Draft
Matt Carle (mcarle) wants to merge 1 commit into
mainfrom
mcarle/IDENTITY-7909
Draft

[IDENTITY-7909] Scope logout revocation to the CLI Auth0 client#3432
Matt Carle (mcarle) wants to merge 1 commit into
mainfrom
mcarle/IDENTITY-7909

Conversation

@mcarle

@mcarle Matt Carle (mcarle) commented Aug 7, 2026

Copy link
Copy Markdown
Member

Release Notes

Bug Fixes

  • confluent logout now revokes only the CLI's own Confluent Cloud session. It previously revoked every Auth0 refresh token for the account, which signed the user out of the web UI and other clients as well.

Checklist

  • I have successfully built and used a custom CLI binary, without linter issues from this PR.
  • I have clearly specified in the What section below whether this PR applies to Confluent Cloud, Confluent Platform, or both.
  • I have verified this PR in Confluent Cloud pre-prod or production environment, if applicable.
  • I have verified this PR in Confluent Platform on-premises environment, if applicable.
  • I have attached manual CLI verification results or screenshots in the Test & Review section below.
  • I have added appropriate CLI integration or unit tests for any new or updated commands and functionality.
  • I confirm that this PR introduces no breaking changes or backward compatibility issues.
  • I have indicated the potential customer impact if something goes wrong in the Blast Radius section below.
  • I have put checkmarks below confirming that the feature associated with this PR is enabled in:
    • Confluent Cloud prod
    • Confluent Cloud stag
    • Confluent Platform
    • Check this box if the feature is enabled for certain organizations only

This PR must not merge until cc-flow-service#3440 is deployed to prod. See References.

What

Confluent Cloud only; Confluent Platform and gov (Okta) logout are untouched.

confluent logout called DELETE /api/sessions, which revokes every rotating refresh token belonging to the user's email across all Auth0 clients. Logging out of a terminal therefore also tore down the user's browser session and any IDE-plugin sessions.

DELETE /iam/v2/sessions scopes revocation to a single Auth0 client when given a client ID. This switches the Auth0 logout path to that endpoint, passing the CLI's own client ID — the same value already sent to cc-flow-service at login in pkg/auth/auth_token_handler.go. The session JWT rides along as a bearer token via the existing OAuth2 transport on c.Client.HttpClient, so no new auth plumbing is involved.

Two deliberate behaviour changes beyond the endpoint swap:

Revocation failures no longer abort logout. Previously a transport error returned before local credentials were cleared, leaving the user logged in locally with no way to log out offline. The v1 SDK also swallowed non-200 responses entirely, so server-side failures were invisible. Now local credentials are always cleared, and a failure is both logged and printed to stderr — the user is told their remote session may still be active. Exit code stays 0 so scripted logout calls don't start failing on a transient error.

Any 2xx counts as success. The endpoint returns 200 today; accepting the range avoids a false failure if it ever returns 204.

The request is hand-rolled rather than routed through an SDK. ccloud-sdk-go-v1-public has no v2 sessions method, and the generated SessionsV2Api in ccloud-sdk-go-v2-internal only exposes CreateV2Session (POST) — there is no generated DELETE, and that module isn't currently a dependency of the CLI. Happy to move this into ccloud-sdk-go-v1-public as a LogoutV2 method if reviewers would rather keep all session calls behind Client.Auth; it just costs an extra repo and release.

Blast Radius

Confluent Cloud users running confluent logout. If the new endpoint misbehaves, logout still completes locally — credentials are removed and the user sees a warning — but the Auth0 session and refresh tokens may survive until they expire on their own (30 minutes for refresh tokens, up to 8 hours for the Auth0 session). The failure mode is a session that outlives the logout, not a user who cannot log out.

Gov/FedRAMP users are unaffected: sso.IsOkta still routes them to DELETE /api/okta/auth/sessions, and the upstream revocation RPCs reject FedRAMP calls regardless.

If this ships before cc-flow-service#3440 reaches prod, the client_id parameter is ignored and revocation silently falls back to the old unscoped behaviour — no error, but no improvement either. That is the reason for the merge gate above.

References

Test & Review

Unit tests in internal/logout/command_test.go cover the request shape (method, path, client_id), a 204 response, and a non-2xx response surfacing an error.

go test ./internal/logout/
--- PASS: TestLogout
--- PASS: TestDeleteSession
--- PASS: TestDeleteSessionNoContent
--- PASS: TestDeleteSessionError
ok  	github.com/confluentinc/cli/v4/internal/logout

The test server gained a /api/iam/v2/sessions route asserting the method and a non-empty client_id, so the existing logout integration tests now exercise the new path end to end. I confirmed the route is genuinely reached by temporarily failing inside the handler and watching the suite fail — worth noting because a handler that is never called would otherwise pass silently.

make integration-test INTEGRATION_TEST_ARGS="-run 'TestCLI/(TestLogout|TestLogin)'"
--- PASS: TestCLI (11.97s)

Not yet verified against a deployed environment — that needs cc-flow-service#3440 in devel first, and I'll do that before marking this ready for review.

Two pre-existing failures in my local environment, both reproduced on an unmodified main checkout and unrelated to this change:

  • pkg/flink/internal/controller panics with open /dev/tty: device not configured (needs a real TTY).
  • make lint-go fails with can't load config: unsupported version of the configuration (local golangci-lint version vs .golangci.yml). go vet ./... is clean.

DELETE /api/sessions revokes every rotating refresh token for the user,
so logging out of the terminal also tore down the browser session.
Call DELETE /api/iam/v2/sessions with the CLI client_id instead, which
scopes revocation to the CLI. Gov environments keep the Okta path.

Revocation failures no longer abort logout; local credentials are
cleared either way and the failure is reported on stderr.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@confluent-cla-assistant

Copy link
Copy Markdown

🎉 All Contributor License Agreements have been signed. Ready to merge.
Please push an empty commit if you would like to re-run the checks to verify CLA status for all contributors.

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.

Pull request overview

Updates confluent logout (Confluent Cloud only) to revoke sessions via the IAM v2 sessions endpoint with an explicit Auth0 client_id, so logout only revokes the CLI’s own session rather than all refresh tokens for the account.

Changes:

  • Switches cloud logout revocation from the v1 /api/sessions path to DELETE /api/iam/v2/sessions?client_id=....
  • Makes remote revocation best-effort (warnings on failure) while always clearing local credentials.
  • Extends unit + integration test coverage by adding a new mocked route/handler and direct request-shape tests.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
internal/logout/command.go Implements the new DELETE /api/iam/v2/sessions revocation flow and makes revocation failures non-fatal to local logout.
internal/logout/command_test.go Adds unit tests for the new delete-session request shape and status-code handling.
test/test-server/ccloud_router.go Registers the new /api/iam/v2/sessions route in the integration-test server router.
test/test-server/ccloud_handlers.go Adds a handler asserting the DELETE method and presence of client_id for /api/iam/v2/sessions.
Suppressed comments (1)

internal/logout/command_test.go:72

  • This test currently hard-codes the expected Auth0 client_id string. Deriving the expected value via the same sso helper used by deleteSession() avoids duplicating constants and keeps the test resilient to future client ID updates.
	// A httptest URL matches no known environment, so the client ID falls through to prod's.
	require.Equal(t, "oX2nvSKl5jvBKVgwehZfvR4K8RhsZIEs", req.URL.Query().Get("client_id"))

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 3 to 7
import (
"net/http"
"net/http/httptest"
"testing"

@sonarqube-confluent

Copy link
Copy Markdown

Quality Gate failed Quality Gate failed

Failed conditions
61.5% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube

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