diff --git a/src/main/java/org/jenkinsci/plugins/oic/OicSecurityRealm.java b/src/main/java/org/jenkinsci/plugins/oic/OicSecurityRealm.java index 488f33fd..aabbfddd 100644 --- a/src/main/java/org/jenkinsci/plugins/oic/OicSecurityRealm.java +++ b/src/main/java/org/jenkinsci/plugins/oic/OicSecurityRealm.java @@ -823,12 +823,15 @@ private OicJsonWebTokenVerifier getJwksVerifier() { if (jwtVerifier == null) { jwtVerifier = new OicJsonWebTokenVerifier( serverConfiguration.getJwksServerUrl(), - new OicJsonWebTokenVerifier.Builder().setHttpTransportFactory(new HttpTransportFactory() { - @Override - public HttpTransport create() { - return httpTransport; - } - })); + new OicJsonWebTokenVerifier.Builder() + .setHttpTransportFactory(new HttpTransportFactory() { + @Override + public HttpTransport create() { + return httpTransport; + } + }) + .setIssuer(getServerConfiguration().getIssuer()) + .setAudience(List.of(clientId))); } return jwtVerifier; } diff --git a/src/main/java/org/jenkinsci/plugins/oic/OicServerConfiguration.java b/src/main/java/org/jenkinsci/plugins/oic/OicServerConfiguration.java index c543ff26..889c7c73 100644 --- a/src/main/java/org/jenkinsci/plugins/oic/OicServerConfiguration.java +++ b/src/main/java/org/jenkinsci/plugins/oic/OicServerConfiguration.java @@ -33,5 +33,8 @@ public abstract class OicServerConfiguration extends AbstractDescribableImpl + + + diff --git a/src/main/resources/org/jenkinsci/plugins/oic/OicServerManualConfiguration/config.properties b/src/main/resources/org/jenkinsci/plugins/oic/OicServerManualConfiguration/config.properties index 56bbf500..857f29e1 100644 --- a/src/main/resources/org/jenkinsci/plugins/oic/OicServerManualConfiguration/config.properties +++ b/src/main/resources/org/jenkinsci/plugins/oic/OicServerManualConfiguration/config.properties @@ -1,6 +1,7 @@ AuthorizationServerUrl=Authorization server url Basic=Basic EndSessionUrl=End session URL for OpenID Provider +Issuer=Issuer JwksServerUrl=Jwks server url Post=Post Scopes=Scopes diff --git a/src/main/resources/org/jenkinsci/plugins/oic/OicServerManualConfiguration/help-issuer.html b/src/main/resources/org/jenkinsci/plugins/oic/OicServerManualConfiguration/help-issuer.html new file mode 100644 index 00000000..7209bfcc --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/oic/OicServerManualConfiguration/help-issuer.html @@ -0,0 +1,3 @@ +
+ Strongly recommended. The received ID Token's issuer must match the specified issuer. +
diff --git a/src/main/resources/org/jenkinsci/plugins/oic/OicServerManualConfiguration/help-issuer_fr.html b/src/main/resources/org/jenkinsci/plugins/oic/OicServerManualConfiguration/help-issuer_fr.html new file mode 100644 index 00000000..0e98f08e --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/oic/OicServerManualConfiguration/help-issuer_fr.html @@ -0,0 +1,3 @@ +
+ Fortement recommandé. Le Token ID reçu doit avoir l'émetteur indiqué. +
diff --git a/src/test/java/org/jenkinsci/plugins/oic/PluginTest.java b/src/test/java/org/jenkinsci/plugins/oic/PluginTest.java index 35048967..94348b6c 100644 --- a/src/test/java/org/jenkinsci/plugins/oic/PluginTest.java +++ b/src/test/java/org/jenkinsci/plugins/oic/PluginTest.java @@ -47,6 +47,7 @@ import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; +import org.jvnet.hudson.test.Issue; import org.jvnet.hudson.test.JenkinsRule; import org.jvnet.hudson.test.Url; import org.kohsuke.stapler.Stapler; @@ -987,6 +988,58 @@ public void loginWithCheckTokenFailure() throws Exception { assertAnonymous(); } + @Test + @Issue("SECURITY-3441") + public void loginWithIncorrectIssuerFails() throws Exception { + mockAuthorizationRedirectsToFinishLogin(); + mockTokenReturnsIdTokenWithGroup(); + jenkins.setSecurityRealm( + new TestRealm.Builder(wireMockRule).WithIssuer("another_issuer").build()); + assertAnonymous(); + webClient.setThrowExceptionOnFailingStatusCode(false); + browseLoginPage(); + assertAnonymous(); + } + + @Test + @Issue("SECURITY-3441") + public void loginWithoutIssuerSetSucceeds() throws Exception { + mockAuthorizationRedirectsToFinishLogin(); + mockTokenReturnsIdTokenWithGroup(); + jenkins.setSecurityRealm( + new TestRealm.Builder(wireMockRule).WithIssuer(null).build()); + assertAnonymous(); + webClient.setThrowExceptionOnFailingStatusCode(false); + browseLoginPage(); + assertTestUser(); + } + + @Test + @Issue("SECURITY-3441") + public void loginWithEmptyIssuerSetSucceeds() throws Exception { + mockAuthorizationRedirectsToFinishLogin(); + mockTokenReturnsIdTokenWithGroup(); + jenkins.setSecurityRealm( + new TestRealm.Builder(wireMockRule).WithIssuer(null).build()); + assertAnonymous(); + webClient.setThrowExceptionOnFailingStatusCode(false); + browseLoginPage(); + assertTestUser(); + } + + @Test + @Issue("SECURITY-3441") + public void loginWithIncorrectAudienceFails() throws Exception { + mockAuthorizationRedirectsToFinishLogin(); + mockTokenReturnsIdTokenWithGroup(); + jenkins.setSecurityRealm(new TestRealm.Builder(wireMockRule) + .WithClient("another_client_id", "client_secret").build()); + assertAnonymous(); + webClient.setThrowExceptionOnFailingStatusCode(false); + browseLoginPage(); + assertAnonymous(); + } + @Test public void testAccessUsingJenkinsApiTokens() throws Exception { mockAuthorizationRedirectsToFinishLogin(); diff --git a/src/test/java/org/jenkinsci/plugins/oic/TestRealm.java b/src/test/java/org/jenkinsci/plugins/oic/TestRealm.java index d7f3cc5a..fd6235b7 100644 --- a/src/test/java/org/jenkinsci/plugins/oic/TestRealm.java +++ b/src/test/java/org/jenkinsci/plugins/oic/TestRealm.java @@ -20,6 +20,7 @@ public class TestRealm extends OicSecurityRealm { public static class Builder { public String clientId = CLIENT_ID; public Secret clientSecret = Secret.fromString("secret"); + public String issuer = "issuer"; public String wellKnownOpenIDConfigurationUrl; public String tokenServerUrl; public String jwksServerUrl = null; @@ -59,6 +60,11 @@ public Builder WithClient(String clientId, String clientSecret) { return this; } + public Builder WithIssuer(String issuer) { + this.issuer = issuer; + return this; + } + public Builder WithUserInfoServerUrl(String userInfoServerUrl) { this.userInfoServerUrl = userInfoServerUrl; return this; @@ -139,6 +145,7 @@ public OicServerConfiguration buildServerConfiguration() { } conf.setJwksServerUrl(jwksServerUrl); conf.setEndSessionUrl(endSessionEndpoint); + conf.setIssuer(issuer); return conf; } catch (Exception e) { throw new IllegalArgumentException(e); diff --git a/src/test/java/org/jenkinsci/plugins/oic/WellKnownOpenIDConfigurationResponseTest.java b/src/test/java/org/jenkinsci/plugins/oic/WellKnownOpenIDConfigurationResponseTest.java index 525e5286..9cf1c184 100644 --- a/src/test/java/org/jenkinsci/plugins/oic/WellKnownOpenIDConfigurationResponseTest.java +++ b/src/test/java/org/jenkinsci/plugins/oic/WellKnownOpenIDConfigurationResponseTest.java @@ -75,6 +75,7 @@ public class WellKnownOpenIDConfigurationResponseTest { Set.of("token_endpoint_auth_methods_supported", "scopes_supported", "grant_types_supported"); private static final List FIELDS = List.of( "authorization_endpoint", + "issuer", "token_endpoint", "userinfo_endpoint", "jwks_uri",