Skip to content

OCPBUGS-100310: oc login --exec-plugin oc-oidc fails to refresh expired token against Microsoft Entra ID - #2332

Open
michaelryanmcneill wants to merge 1 commit into
openshift:mainfrom
michaelryanmcneill:OCPBUGS-100310
Open

OCPBUGS-100310: oc login --exec-plugin oc-oidc fails to refresh expired token against Microsoft Entra ID#2332
michaelryanmcneill wants to merge 1 commit into
openshift:mainfrom
michaelryanmcneill:OCPBUGS-100310

Conversation

@michaelryanmcneill

@michaelryanmcneill michaelryanmcneill commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes token refresh failures against Microsoft Entra ID when using oc login --exec-plugin oc-oidc.

The go-oidc library's Endpoint() returns an oauth2.Endpoint without setting AuthStyle, which defaults to auto-detect. The golang.org/x/oauth2 auto-detect mechanism tries AuthStyleInHeader (HTTP Basic Auth) first, sending client_id in the Authorization header rather than the POST body. Microsoft Entra ID does not support client_secret_basic — its discovery document only advertises client_secret_post and private_key_jwt — and rejects the request with AADSTS900144: The request body must contain the following parameter: 'client_id'.

This only manifests when the cached ID token has expired, since that is the only time a POST to the token endpoint is required.

Changes

  • Set AuthStyle: oauth2.AuthStyleInParams on the oauth2.Endpoint in NewAuthenticator() so that client_id is always sent in the POST body.

This is correct per RFC 6749 §2.3.1 for public clients and is supported by all targeted OIDC providers. Verified that all seven supported providers advertise client_secret_post in their token_endpoint_auth_methods_supported:

Provider client_secret_post client_secret_basic
Keycloak Yes Yes
Microsoft Entra ID Yes No
ADFS (Windows Server) Yes Yes
GitLab Yes Yes
Google Yes Yes
Okta Yes Yes
Ping Identity Yes Yes

Steps to Reproduce

  1. oc login <api-server> --exec-plugin oc-oidc --client-id <id> --issuer-url <entra-id-issuer>
  2. Wait for the ID token to expire (typically 1 hour)
  3. Run the same oc login command again
  4. Before fix: AADSTS900144: The request body must contain the following parameter: 'client_id'
  5. After fix: Token refreshes successfully

Additional notes for reviewers

There are no existing unit tests in pkg/cli/gettoken/ or pkg/cli/gettoken/oidc/. Adding a meaningful unit test for this change would require significant test infrastructure that does not exist today: a mock OIDC discovery endpoint, a mock JWKS endpoint with test signing keys, a mock token endpoint that validates request format and returns signed JWTs, and JWT generation using the test keys. This is disproportionate to the two-line change, which sets a standard configuration property on the oauth2.Endpoint. The fix is validated by the manual test plan below (initial login and token refresh against Entra ID and a second provider).

Test Plan

  • Verify oc login --exec-plugin oc-oidc succeeds on initial login against Entra ID
  • Wait for token expiry and verify oc login refreshes the token without error
  • Verify initial login and token refresh against a non-Entra provider (e.g., Keycloak)
  • Verify confidential client flow (--client-secret) still works

Summary by CodeRabbit

  • Bug Fixes
    • Improved OIDC authentication compatibility by sending client credentials using request parameters when exchanging tokens.

…en endpoint

The go-oidc library's Endpoint() returns an oauth2.Endpoint without
setting AuthStyle, defaulting to auto-detect. Auto-detect tries
AuthStyleInHeader (HTTP Basic Auth) first, which omits client_id from
the POST body. Microsoft Entra ID does not support client_secret_basic
and rejects this with AADSTS900144.

Explicitly set AuthStyle to AuthStyleInParams so that client_id is
always sent in the POST body. This is correct per RFC 6749 §2.3.1 for
public clients and is supported by all targeted OIDC providers
(Keycloak, Entra ID, ADFS, GitLab, Google, Okta, Ping Identity).

Signed-off-by: michaelryanmcneill <michael@michaelryanmcneill.com>
@openshift-merge-bot

Copy link
Copy Markdown
Contributor

Pipeline controller notification
This repo is configured to use the pipeline controller. Second-stage tests will be triggered either automatically or after lgtm label is added, depending on the repository configuration. The pipeline controller will automatically detect which contexts are required and will utilize /test Prow commands to trigger the second stage.

For optional jobs, comment /test ? to see a list of all defined jobs. To trigger manually all jobs from second stage use /pipeline required command.

This repository is configured in: LGTM mode

@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Jul 30, 2026
@openshift-ci-robot

Copy link
Copy Markdown

@michaelryanmcneill: This pull request references Jira Issue OCPBUGS-99757, which is valid.

3 validation(s) were run on this bug
  • bug is open, matching expected state (open)
  • bug target version (5.0.0) matches configured target version for branch (5.0.0)
  • bug is in the state POST, which is one of the valid states (NEW, ASSIGNED, POST)

The bug has been updated to refer to the pull request using the external bug tracker.

Details

In response to this:

Summary

Fixes token refresh failures against Microsoft Entra ID when using oc login --exec-plugin oc-oidc.

The go-oidc library's Endpoint() returns an oauth2.Endpoint without setting AuthStyle, which defaults to auto-detect. The golang.org/x/oauth2 auto-detect mechanism tries AuthStyleInHeader (HTTP Basic Auth) first, sending client_id in the Authorization header rather than the POST body. Microsoft Entra ID does not support client_secret_basic — its discovery document only advertises client_secret_post and private_key_jwt — and rejects the request with AADSTS900144: The request body must contain the following parameter: 'client_id'.

This only manifests when the cached ID token has expired, since that is the only time a POST to the token endpoint is required.

Changes

  • Set AuthStyle: oauth2.AuthStyleInParams on the oauth2.Endpoint in NewAuthenticator() so that client_id is always sent in the POST body.

This is correct per RFC 6749 §2.3.1 for public clients and is supported by all targeted OIDC providers. Verified that all seven supported providers advertise client_secret_post in their token_endpoint_auth_methods_supported:

Provider client_secret_post client_secret_basic
Keycloak Yes Yes
Microsoft Entra ID Yes No
ADFS (Windows Server) Yes Yes
GitLab Yes Yes
Google Yes Yes
Okta Yes Yes
Ping Identity Yes Yes

Steps to Reproduce

  1. oc login <api-server> --exec-plugin oc-oidc --client-id <id> --issuer-url <entra-id-issuer>
  2. Wait for the ID token to expire (typically 1 hour)
  3. Run the same oc login command again
  4. Before fix: AADSTS900144: The request body must contain the following parameter: 'client_id'
  5. After fix: Token refreshes successfully

Test Plan

  • Verify oc login --exec-plugin oc-oidc succeeds on initial login against Entra ID
  • Wait for token expiry and verify oc login refreshes the token without error
  • Verify initial login and token refresh against a non-Entra provider (e.g., Keycloak)
  • Verify confidential client flow (--client-secret) still works

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci-robot openshift-ci-robot added the jira/valid-bug Indicates that a referenced Jira bug is valid for the branch this PR is targeting. label Jul 30, 2026
@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Walkthrough

NewAuthenticator now configures the OIDC provider endpoint with oauth2.AuthStyleInParams before constructing the OAuth2 configuration.

Changes

OIDC authentication

Layer / File(s) Summary
Configure OIDC endpoint authentication
pkg/cli/gettoken/oidc/client.go
NewAuthenticator captures the provider endpoint, sets its authentication style to AuthStyleInParams, and uses it in oauth2.Config.

Estimated code review effort: 1 (Trivial) | ~3 minutes

Suggested labels: verified

🚥 Pre-merge checks | ✅ 14 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (14 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Stable And Deterministic Test Names ✅ Passed Only pkg/cli/gettoken/oidc/client.go changed; no test files or Ginkgo titles were added or modified.
Test Structure And Quality ✅ Passed Only pkg/cli/gettoken/oidc/client.go changed; no Ginkgo test code was added or modified, so the test-quality checklist is not applicable.
Microshift Test Compatibility ✅ Passed No new Ginkgo e2e tests were added; the PR only changes pkg/cli/gettoken/oidc/client.go, so MicroShift compatibility is not implicated.
Single Node Openshift (Sno) Test Compatibility ✅ Passed Only pkg/cli/gettoken/oidc/client.go changed; no Ginkgo/e2e tests or SNO-specific assumptions were added.
Topology-Aware Scheduling Compatibility ✅ Passed Only an OIDC auth-style change in client.go; no manifests, controllers, node selectors, affinity, or topology logic were introduced.
Ote Binary Stdout Contract ✅ Passed Only oauth2 endpoint auth-style changed in client.go; no main/init/TestMain or stdout writes were added.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed Only pkg/cli/gettoken/oidc/client.go changed; no new Ginkgo e2e tests or IPv4/public-network assumptions were added.
No-Weak-Crypto ✅ Passed Diff only sets oauth2.AuthStyleInParams; no weak algorithms or custom crypto were added.
Container-Privileges ✅ Passed PR only changes pkg/cli/gettoken/oidc/client.go; no container/K8s manifests or privilege settings are added or modified.
No-Sensitive-Data-In-Logs ✅ Passed The patch only sets oauth2.AuthStyleInParams on the endpoint; it adds no logging or new error messages that expose secrets.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the fix for oc-oidc token refresh failures against Microsoft Entra ID.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@openshift-ci
openshift-ci Bot requested review from ardaguclu and tchap July 30, 2026 20:03
@openshift-ci

openshift-ci Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: michaelryanmcneill
Once this PR has been reviewed and has the lgtm label, please assign ardaguclu for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@michaelryanmcneill michaelryanmcneill changed the title OCPBUGS-99757: oc login --exec-plugin oc-oidc fails to refresh expired token against Microsoft Entra ID OCPBUGS-100310: oc login --exec-plugin oc-oidc fails to refresh expired token against Microsoft Entra ID Jul 30, 2026
@openshift-ci-robot

Copy link
Copy Markdown

@michaelryanmcneill: This pull request references Jira Issue OCPBUGS-100310, which is valid. The bug has been moved to the POST state.

3 validation(s) were run on this bug
  • bug is open, matching expected state (open)
  • bug target version (5.0.0) matches configured target version for branch (5.0.0)
  • bug is in the state ASSIGNED, which is one of the valid states (NEW, ASSIGNED, POST)

The bug has been updated to refer to the pull request using the external bug tracker.

Details

In response to this:

Summary

Fixes token refresh failures against Microsoft Entra ID when using oc login --exec-plugin oc-oidc.

The go-oidc library's Endpoint() returns an oauth2.Endpoint without setting AuthStyle, which defaults to auto-detect. The golang.org/x/oauth2 auto-detect mechanism tries AuthStyleInHeader (HTTP Basic Auth) first, sending client_id in the Authorization header rather than the POST body. Microsoft Entra ID does not support client_secret_basic — its discovery document only advertises client_secret_post and private_key_jwt — and rejects the request with AADSTS900144: The request body must contain the following parameter: 'client_id'.

This only manifests when the cached ID token has expired, since that is the only time a POST to the token endpoint is required.

Changes

  • Set AuthStyle: oauth2.AuthStyleInParams on the oauth2.Endpoint in NewAuthenticator() so that client_id is always sent in the POST body.

This is correct per RFC 6749 §2.3.1 for public clients and is supported by all targeted OIDC providers. Verified that all seven supported providers advertise client_secret_post in their token_endpoint_auth_methods_supported:

Provider client_secret_post client_secret_basic
Keycloak Yes Yes
Microsoft Entra ID Yes No
ADFS (Windows Server) Yes Yes
GitLab Yes Yes
Google Yes Yes
Okta Yes Yes
Ping Identity Yes Yes

Steps to Reproduce

  1. oc login <api-server> --exec-plugin oc-oidc --client-id <id> --issuer-url <entra-id-issuer>
  2. Wait for the ID token to expire (typically 1 hour)
  3. Run the same oc login command again
  4. Before fix: AADSTS900144: The request body must contain the following parameter: 'client_id'
  5. After fix: Token refreshes successfully

Additional notes for reviewers

There are no existing unit tests in pkg/cli/gettoken/ or pkg/cli/gettoken/oidc/. Adding a meaningful unit test for this change would require significant test infrastructure that does not exist today: a mock OIDC discovery endpoint, a mock JWKS endpoint with test signing keys, a mock token endpoint that validates request format and returns signed JWTs, and JWT generation using the test keys. This is disproportionate to the two-line change, which sets a standard configuration property on the oauth2.Endpoint. The fix is validated by the manual test plan below (initial login and token refresh against Entra ID and a second provider).

Test Plan

  • Verify oc login --exec-plugin oc-oidc succeeds on initial login against Entra ID
  • Wait for token expiry and verify oc login refreshes the token without error
  • Verify initial login and token refresh against a non-Entra provider (e.g., Keycloak)
  • Verify confidential client flow (--client-secret) still works

Summary by CodeRabbit

  • Bug Fixes
  • Improved OIDC authentication compatibility by sending client credentials using request parameters when exchanging tokens.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci

openshift-ci Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

@michaelryanmcneill: all tests passed!

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

}
}
endpoint := provider.Endpoint()
endpoint.AuthStyle = oauth2.AuthStyleInParams

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

jira/valid-bug Indicates that a referenced Jira bug is valid for the branch this PR is targeting. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants