Skip to content

Introduce OidcProperty extension point#644

Merged
jtnord merged 19 commits into
jenkinsci:masterfrom
Vlatombe:oicproperty
Sep 9, 2025
Merged

Introduce OidcProperty extension point#644
jtnord merged 19 commits into
jenkinsci:masterfrom
Vlatombe:oicproperty

Conversation

@Vlatombe
Copy link
Copy Markdown
Member

@Vlatombe Vlatombe commented Aug 22, 2025

Cloudbees-internal issue

In the context of CloudBees CI, I needed the ability to implement new
optional behaviour customizing the OIDC configuration and client.

The new extension point, named OidcProperty, allows to capture a state
through OidcPropertyExecution that can be used for customize both the
OIDC configuration and client.

I have also taken the opportunity to fold some advanced options under
their own class. Ultimately, all advanced fields could be implemented
as OidcProperty but for some of them it can be challenging due to the
way they are currently integrated in the plugin.

Migrated fields

  • Allowed token expiration clock skew
  • Disable nonce
  • Disable token verification
  • Escape hatch
  • Login query parameters
  • Logout query parameters
  • Enable PKCE
Screenshots Security-Jenkins--08-22-2025_03_33_PM

Testing done

Submitter checklist

  • Make sure you are opening from a topic/feature/bugfix branch (right side) and not your main branch!
  • Ensure that the pull request title represents the desired changelog entry
  • Please describe what you did
  • Link to relevant issues in GitHub or Jira
  • Link to relevant pull requests, esp. upstream and downstream changes
  • Ensure you have provided tests that demonstrate the feature works or the issue is fixed

In the context of CloudBees CI, I needed the ability to implement new
optional behaviour customizing the OIDC configuration and client.

The new extension point, named `OicProperty`, allows to capture a state
through `OicPropertyExecution` that can be used for customize both the
OIDC configuration and client.

I have also taken the opportunity to fold some advanced options under
their own class. Ultimately, all advanced fields could be implemeneted
as `OicProperty` but for some of them it can be challenging due to the
way they are currently integrated in the plugin.

Migrated fields
* Allowed token expiration clock skew
* Disable nonce
* Disable token verification
* Escape hatch
* Login query parameters
* Logout query parameters
* Enable PKCE
@Vlatombe Vlatombe requested a review from a team as a code owner August 22, 2025 13:26
@Vlatombe Vlatombe requested review from PereBueno and jtnord August 22, 2025 13:27
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Actually moved to EscapeHatch

* Convert the OicServerConfiguration to {@link OIDCProviderMetadata} for use by the client.
*/
public abstract OIDCProviderMetadata toProviderMetadata();
public final OIDCProviderMetadata toProviderMetadata() {
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Moved from OicSecurityRealm

@codecov
Copy link
Copy Markdown

codecov Bot commented Aug 27, 2025

Codecov Report

❌ Patch coverage is 75.25773% with 72 lines in your changes missing coverage. Please review.
✅ Project coverage is 74.74%. Comparing base (5e6e0ae) to head (0d6d19c).
⚠️ Report is 23 commits behind head on master.

Files with missing lines Patch % Lines
.../jenkinsci/plugins/oic/properties/EscapeHatch.java 42.55% 24 Missing and 3 partials ⚠️
...va/org/jenkinsci/plugins/oic/OicSecurityRealm.java 64.51% 12 Missing and 10 partials ⚠️
.../jenkinsci/plugins/oic/OicServerConfiguration.java 90.21% 6 Missing and 3 partials ⚠️
...ic/properties/AllowedTokenExpirationClockSkew.java 69.23% 4 Missing ⚠️
...org/jenkinsci/plugins/oic/LoginQueryParameter.java 0.00% 1 Missing and 1 partial ⚠️
...rg/jenkinsci/plugins/oic/LogoutQueryParameter.java 0.00% 1 Missing and 1 partial ⚠️
...i/plugins/oic/properties/LoginQueryParameters.java 85.71% 1 Missing and 1 partial ⚠️
.../plugins/oic/properties/LogoutQueryParameters.java 77.77% 1 Missing and 1 partial ⚠️
.../jenkinsci/plugins/oic/OidcPropertyDescriptor.java 75.00% 1 Missing ⚠️
...ugins/oic/properties/DisableTokenVerification.java 95.45% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##             master     #644      +/-   ##
============================================
- Coverage     74.97%   74.74%   -0.23%     
- Complexity      289      326      +37     
============================================
  Files            24       33       +9     
  Lines          1179     1299     +120     
  Branches        167      179      +12     
============================================
+ Hits            884      971      +87     
- Misses          202      241      +39     
+ Partials         93       87       -6     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown
Contributor

@PereBueno PereBueno left a comment

Choose a reason for hiding this comment

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

From the part of the plugin I'm familiar with (FIPS and nonce checks mainly) everything seems fine.
Only problem I found is migrating the clockskew property

// set many more as needed...

OIDCProviderMetadata oidcProviderMetadata = serverConfiguration.toProviderMetadata();
filterNonFIPS140CompliantAlgorithms(oidcProviderMetadata);
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Moved to OicServerConfiguration

OIDCProviderMetadata oidcProviderMetadata = serverConfiguration.toProviderMetadata();
filterNonFIPS140CompliantAlgorithms(oidcProviderMetadata);
OidcOpMetadataResolver opMetadataResolver;
if (this.isDisableTokenVerification()) {
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Moved to DisableTokenVerification

// auto configuration does not need to supply scopes
conf.setScope(oidcProviderMetadata.getScopes().toString());
}
conf.setUseNonce(!this.nonceDisabled);
Copy link
Copy Markdown
Member Author

@Vlatombe Vlatombe Sep 2, 2025

Choose a reason for hiding this comment

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

Moved to DisableNonce.

conf.setScope(oidcProviderMetadata.getScopes().toString());
}
conf.setUseNonce(!this.nonceDisabled);
if (allowedTokenExpirationClockSkewSeconds != null) {
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Moved AllowedTokenExpirationClockSkew

conf.setMaxClockSkew(allowedTokenExpirationClockSkewSeconds.intValue());
}
conf.setResourceRetriever(getResourceRetriever());
if (this.isPkceEnabled()) {
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Moved to Pkce

conf.setDisablePkce(true);
}
opMetadataResolver.init();
if (loginQueryParameters != null && !loginQueryParameters.isEmpty()) {
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Moved to LoginQueryParameters

return conf;
}

// Visible for testing
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Moved to OicServerConfiguration

return new SecurityComponents(new AuthenticationManager() {
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if (authentication instanceof AnonymousAuthenticationToken) return authentication;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Moved to EscapeHatch

uriComponentsBuilder.queryParam(
"post_logout_redirect_uri", URLEncoder.encode(postLogoutRedirectUrl, StandardCharsets.UTF_8));
}
if (logoutQueryParameters != null && !logoutQueryParameters.isEmpty()) {
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Moved to LogoutQueryParameters

Comment thread src/main/java/org/jenkinsci/plugins/oic/AbstractKeyValueDescribable.java Outdated
Copy link
Copy Markdown
Member

@jtnord jtnord left a comment

Choose a reason for hiding this comment

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

comments inline, but also the docs will need updating for the casc changes

Comment thread src/main/java/org/jenkinsci/plugins/oic/AbstractKeyValueDescribable.java Outdated
Comment thread src/main/java/org/jenkinsci/plugins/oic/AbstractQueryParameter.java Outdated
* Allows a property to authenticate the user.
* @see org.jenkinsci.plugins.oic.properties.EscapeHatch
*/
public Optional<Authentication> authenticate(Authentication authentication) {
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.

I'm confused as to how this can work generically in practice with multiple authenticators?

Would this work with say bearer tokens?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

For bearer token, I think you would need to add a way to plug a new filter into the authentication chain (doesn't exist currently, I only added the bare minumum for EscapeHatch to work), to convert the http headers to a BearerTokenAuthentication (to be written). And then an implementation of this method does what https://github.com/jenkinsci/oic-auth-plugin/pull/632/files#diff-9c2c6436b143fde652866ea92ee9bec48671b094dbb23ae4805621c5d40ed59bR1514-R1587 currently does I guess ?

Comment thread src/main/java/org/jenkinsci/plugins/oic/OicProperty.java
Comment thread src/main/java/org/jenkinsci/plugins/oic/properties/EscapeHatch.java Outdated
Comment thread src/main/java/org/jenkinsci/plugins/oic/properties/LogoutQueryParameters.java Outdated
@jtnord
Copy link
Copy Markdown
Member

jtnord commented Sep 8, 2025

spotless needs to be applied, otherwise LGTM

@Vlatombe Vlatombe changed the title Introduce OicProperty extension point Introduce OidcProperty extension point Sep 8, 2025
@Vlatombe Vlatombe requested a review from jtnord September 9, 2025 09:13
@Vlatombe
Copy link
Copy Markdown
Member Author

Vlatombe commented Sep 9, 2025

Still need to update the doc...

Vlatombe and others added 2 commits September 9, 2025 15:24
As the on disk format has changed, update the compatibleSinceVersion
last release was 4.524.v38858a_b_1c6a_4 so 4.525 will be enough.
@jtnord jtnord enabled auto-merge September 9, 2025 15:16
@jtnord jtnord merged commit d5a61e0 into jenkinsci:master Sep 9, 2025
17 of 18 checks passed
/**
* Represents a property that can be configured for OIDC authentication.
*/
public abstract class OidcProperty extends AbstractDescribableImpl<OidcProperty> {
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.

import hudson.model.Descriptor;
import org.pac4j.oidc.config.OidcConfiguration;

public abstract class OidcPropertyDescriptor extends Descriptor<OidcProperty> implements ExtensionPoint {
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.

Did you mean to narrow-override getDescriptor in OidcProperty?

Comment on lines +31 to +34
return new ExecutionImpl(valueSeconds);
}

private record ExecutionImpl(int valueSeconds) implements OidcPropertyExecution {
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.

Is there some advantage over just using an anonymous inner class?

}
}

@CheckForNull
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.

ditto

- "pkce"
- loginQueryParameters:
items:
- key: "loginkey1x\"xx@me"
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.

(ignore WS, even though it is significant)

@jglick
Copy link
Copy Markdown
Member

jglick commented Sep 9, 2025

BTW I find it helpful for both the author and reviewers to mark conversations resolved as promptly as possible, so it is easy to get an overview of all outstanding threads. (Enforcing this in branch protection is too draconian; sometimes there is a conversation you want to keep visible but still go ahead and merge.)

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants