From 5578fba35eedca1afb6843f573a67b43162b6fb2 Mon Sep 17 00:00:00 2001 From: Iain Adams Date: Tue, 15 Nov 2022 17:08:56 +0000 Subject: [PATCH 1/4] allow id_token lifetime to be configurable --- .../oidc_provider/IdTokenCredentials.java | 4 ++-- .../config/IdTokenConfiguration.java | 10 ++++++++ .../config/IdTokenConfiguration/config.jelly | 4 ++++ .../help-tokenLifetime.html | 3 +++ .../ConfigurationAsCodeTest.java | 1 + .../oidc_provider/IdTokenCredentialsTest.java | 24 +++++++++++++++++++ .../jenkins/plugins/oidc_provider/global.yaml | 1 + 7 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 src/main/resources/io/jenkins/plugins/oidc_provider/config/IdTokenConfiguration/help-tokenLifetime.html diff --git a/src/main/java/io/jenkins/plugins/oidc_provider/IdTokenCredentials.java b/src/main/java/io/jenkins/plugins/oidc_provider/IdTokenCredentials.java index 0948644..8db83fd 100644 --- a/src/main/java/io/jenkins/plugins/oidc_provider/IdTokenCredentials.java +++ b/src/main/java/io/jenkins/plugins/oidc_provider/IdTokenCredentials.java @@ -181,11 +181,12 @@ RSAPublicKey publicKey() { ))); protected final @NonNull String token() { + IdTokenConfiguration cfg = IdTokenConfiguration.get(); JwtBuilder builder = Jwts.builder(). setHeaderParam("kid", getId()). setIssuer(issuer != null ? issuer : findIssuer().url()). setAudience(audience). - setExpiration(Date.from(new Date().toInstant().plus(1, ChronoUnit.HOURS))). + setExpiration(Date.from(new Date().toInstant().plus(cfg.getTokenLifetime(), ChronoUnit.SECONDS))). setIssuedAt(new Date()); Map env; if (build != null) { @@ -198,7 +199,6 @@ RSAPublicKey publicKey() { // EnvVars.masterEnvVars might not be safe to expose env = Collections.singletonMap("JENKINS_URL", Jenkins.get().getRootUrl()); } - IdTokenConfiguration cfg = IdTokenConfiguration.get(); AtomicBoolean definedSub = new AtomicBoolean(); Consumer> addClaims = claimTemplates -> { for (ClaimTemplate t : claimTemplates) { diff --git a/src/main/java/io/jenkins/plugins/oidc_provider/config/IdTokenConfiguration.java b/src/main/java/io/jenkins/plugins/oidc_provider/config/IdTokenConfiguration.java index 5d0f66e..808e3dd 100644 --- a/src/main/java/io/jenkins/plugins/oidc_provider/config/IdTokenConfiguration.java +++ b/src/main/java/io/jenkins/plugins/oidc_provider/config/IdTokenConfiguration.java @@ -57,6 +57,8 @@ return ExtensionList.lookupSingleton(IdTokenConfiguration.class); } + private Integer tokenLifetime = 7200; + private @CheckForNull List claimTemplates; private @CheckForNull List buildClaimTemplates; private @CheckForNull List globalClaimTemplates; @@ -80,6 +82,14 @@ public IdTokenConfiguration() { } } + public Integer getTokenLifetime() { + return tokenLifetime; + } + + @DataBoundSetter public void setTokenLifetime(final Integer lifetime) { + this.tokenLifetime = lifetime; + } + public @NonNull List getClaimTemplates() { return claimTemplates != null ? claimTemplates : DEFAULT_CLAIM_TEMPLATES; } diff --git a/src/main/resources/io/jenkins/plugins/oidc_provider/config/IdTokenConfiguration/config.jelly b/src/main/resources/io/jenkins/plugins/oidc_provider/config/IdTokenConfiguration/config.jelly index d7d2f7e..9604a52 100644 --- a/src/main/resources/io/jenkins/plugins/oidc_provider/config/IdTokenConfiguration/config.jelly +++ b/src/main/resources/io/jenkins/plugins/oidc_provider/config/IdTokenConfiguration/config.jelly @@ -26,6 +26,10 @@ THE SOFTWARE. + + + + diff --git a/src/main/resources/io/jenkins/plugins/oidc_provider/config/IdTokenConfiguration/help-tokenLifetime.html b/src/main/resources/io/jenkins/plugins/oidc_provider/config/IdTokenConfiguration/help-tokenLifetime.html new file mode 100644 index 0000000..d5b7f14 --- /dev/null +++ b/src/main/resources/io/jenkins/plugins/oidc_provider/config/IdTokenConfiguration/help-tokenLifetime.html @@ -0,0 +1,3 @@ +
+ The time in seconds the issued id_token is valid for. +
diff --git a/src/test/java/io/jenkins/plugins/oidc_provider/ConfigurationAsCodeTest.java b/src/test/java/io/jenkins/plugins/oidc_provider/ConfigurationAsCodeTest.java index 33b83ba..9c86066 100644 --- a/src/test/java/io/jenkins/plugins/oidc_provider/ConfigurationAsCodeTest.java +++ b/src/test/java/io/jenkins/plugins/oidc_provider/ConfigurationAsCodeTest.java @@ -59,6 +59,7 @@ public class ConfigurationAsCodeTest { @ConfiguredWithCode("global.yaml") @Test public void globalConfiguration() throws Exception { IdTokenConfiguration cfg = IdTokenConfiguration.get(); + assertEquals(Integer.valueOf(60), cfg.getTokenLifetime()); assertEquals(ClaimTemplate.xmlForm(Collections.singletonList(new ClaimTemplate("ok", "true", new BooleanClaimType()))), ClaimTemplate.xmlForm(cfg.getClaimTemplates())); assertEquals(ClaimTemplate.xmlForm(Collections.singletonList(new ClaimTemplate("sub", "jenkins", new StringClaimType()))), diff --git a/src/test/java/io/jenkins/plugins/oidc_provider/IdTokenCredentialsTest.java b/src/test/java/io/jenkins/plugins/oidc_provider/IdTokenCredentialsTest.java index cc4bbb0..316decb 100644 --- a/src/test/java/io/jenkins/plugins/oidc_provider/IdTokenCredentialsTest.java +++ b/src/test/java/io/jenkins/plugins/oidc_provider/IdTokenCredentialsTest.java @@ -40,8 +40,11 @@ import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import java.math.BigInteger; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.Arrays; import java.util.Collections; +import java.util.Date; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import jenkins.model.Jenkins; @@ -132,6 +135,27 @@ public class IdTokenCredentialsTest { }); } + @Test public void tokenLifetime() throws Throwable { + rr.then(r -> { + IdTokenStringCredentials c = new IdTokenStringCredentials(CredentialsScope.GLOBAL, "test", null); + CredentialsProvider.lookupStores(r.jenkins).iterator().next().addCredentials(Domain.global(), c); + IdTokenConfiguration cfg = IdTokenConfiguration.get(); + cfg.setTokenLifetime(60); + cfg.setClaimTemplates(Collections.singletonList(new ClaimTemplate("ok", "true", new BooleanClaimType()))); + cfg.setGlobalClaimTemplates(Collections.singletonList(new ClaimTemplate("sub", "jenkins", new StringClaimType()))); + cfg.setBuildClaimTemplates(Arrays.asList(new ClaimTemplate("sub", "${JOB_NAME}", new StringClaimType()), new ClaimTemplate("num", "${BUILD_NUMBER}", new IntegerClaimType()))); + String idToken = c.getSecret().getPlainText(); + System.out.println(idToken); + Claims claims = Jwts.parserBuilder(). + setSigningKey(c.publicKey()). + build(). + parseClaimsJws(idToken). + getBody(); + + assertTrue(new Date().toInstant().plus(61, ChronoUnit.SECONDS).isAfter(claims.getExpiration().toInstant())); + }); + } + @Test public void customClaims() throws Throwable { rr.then(r -> { IdTokenStringCredentials c = new IdTokenStringCredentials(CredentialsScope.GLOBAL, "test", null); diff --git a/src/test/resources/io/jenkins/plugins/oidc_provider/global.yaml b/src/test/resources/io/jenkins/plugins/oidc_provider/global.yaml index 026a9c8..861cc0b 100644 --- a/src/test/resources/io/jenkins/plugins/oidc_provider/global.yaml +++ b/src/test/resources/io/jenkins/plugins/oidc_provider/global.yaml @@ -1,5 +1,6 @@ security: idToken: + tokenLifetime: 60 claimTemplates: - name: ok format: "true" From 60033473eea545dc3cf9464c7a516ee7e6ec9a6d Mon Sep 17 00:00:00 2001 From: Iain Adams Date: Wed, 16 Nov 2022 09:32:53 +0000 Subject: [PATCH 2/4] fix default lifetime to 1h --- .../plugins/oidc_provider/config/IdTokenConfiguration.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/jenkins/plugins/oidc_provider/config/IdTokenConfiguration.java b/src/main/java/io/jenkins/plugins/oidc_provider/config/IdTokenConfiguration.java index 808e3dd..0c7b60a 100644 --- a/src/main/java/io/jenkins/plugins/oidc_provider/config/IdTokenConfiguration.java +++ b/src/main/java/io/jenkins/plugins/oidc_provider/config/IdTokenConfiguration.java @@ -57,7 +57,7 @@ return ExtensionList.lookupSingleton(IdTokenConfiguration.class); } - private Integer tokenLifetime = 7200; + private Integer tokenLifetime = 3600; private @CheckForNull List claimTemplates; private @CheckForNull List buildClaimTemplates; From e81ab739ed599bf4d967793cbc5e072f69cc2b98 Mon Sep 17 00:00:00 2001 From: Iain Adams Date: Sat, 19 Nov 2022 12:52:13 +0000 Subject: [PATCH 3/4] add review changes --- .../io/jenkins/plugins/oidc_provider/IdTokenCredentials.java | 3 ++- .../oidc_provider/config/IdTokenConfiguration/config.jelly | 2 +- .../plugins/oidc_provider/IdTokenCredentialsTest.java | 5 +---- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/jenkins/plugins/oidc_provider/IdTokenCredentials.java b/src/main/java/io/jenkins/plugins/oidc_provider/IdTokenCredentials.java index 8db83fd..1de0a65 100644 --- a/src/main/java/io/jenkins/plugins/oidc_provider/IdTokenCredentials.java +++ b/src/main/java/io/jenkins/plugins/oidc_provider/IdTokenCredentials.java @@ -51,6 +51,7 @@ import java.security.interfaces.RSAPublicKey; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.RSAPublicKeySpec; +import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Arrays; import java.util.Base64; @@ -186,7 +187,7 @@ RSAPublicKey publicKey() { setHeaderParam("kid", getId()). setIssuer(issuer != null ? issuer : findIssuer().url()). setAudience(audience). - setExpiration(Date.from(new Date().toInstant().plus(cfg.getTokenLifetime(), ChronoUnit.SECONDS))). + setExpiration(Date.from(Instant.now().plus(cfg.getTokenLifetime(), ChronoUnit.SECONDS))). setIssuedAt(new Date()); Map env; if (build != null) { diff --git a/src/main/resources/io/jenkins/plugins/oidc_provider/config/IdTokenConfiguration/config.jelly b/src/main/resources/io/jenkins/plugins/oidc_provider/config/IdTokenConfiguration/config.jelly index 9604a52..1a905ef 100644 --- a/src/main/resources/io/jenkins/plugins/oidc_provider/config/IdTokenConfiguration/config.jelly +++ b/src/main/resources/io/jenkins/plugins/oidc_provider/config/IdTokenConfiguration/config.jelly @@ -27,7 +27,7 @@ THE SOFTWARE. - + diff --git a/src/test/java/io/jenkins/plugins/oidc_provider/IdTokenCredentialsTest.java b/src/test/java/io/jenkins/plugins/oidc_provider/IdTokenCredentialsTest.java index 316decb..02db887 100644 --- a/src/test/java/io/jenkins/plugins/oidc_provider/IdTokenCredentialsTest.java +++ b/src/test/java/io/jenkins/plugins/oidc_provider/IdTokenCredentialsTest.java @@ -141,9 +141,6 @@ public class IdTokenCredentialsTest { CredentialsProvider.lookupStores(r.jenkins).iterator().next().addCredentials(Domain.global(), c); IdTokenConfiguration cfg = IdTokenConfiguration.get(); cfg.setTokenLifetime(60); - cfg.setClaimTemplates(Collections.singletonList(new ClaimTemplate("ok", "true", new BooleanClaimType()))); - cfg.setGlobalClaimTemplates(Collections.singletonList(new ClaimTemplate("sub", "jenkins", new StringClaimType()))); - cfg.setBuildClaimTemplates(Arrays.asList(new ClaimTemplate("sub", "${JOB_NAME}", new StringClaimType()), new ClaimTemplate("num", "${BUILD_NUMBER}", new IntegerClaimType()))); String idToken = c.getSecret().getPlainText(); System.out.println(idToken); Claims claims = Jwts.parserBuilder(). @@ -152,7 +149,7 @@ public class IdTokenCredentialsTest { parseClaimsJws(idToken). getBody(); - assertTrue(new Date().toInstant().plus(61, ChronoUnit.SECONDS).isAfter(claims.getExpiration().toInstant())); + assertTrue(Instant.now().plus(61, ChronoUnit.SECONDS).isAfter(claims.getExpiration().toInstant())); }); } From b6cbddc71f6ef8835238171253ae25b258beb5a1 Mon Sep 17 00:00:00 2001 From: Iain Adams Date: Mon, 21 Nov 2022 13:17:09 +0000 Subject: [PATCH 4/4] pr feedback --- .../plugins/oidc_provider/config/IdTokenConfiguration.java | 6 +++--- .../plugins/oidc_provider/ConfigurationAsCodeTest.java | 2 +- .../plugins/oidc_provider/IdTokenCredentialsTest.java | 1 - 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/jenkins/plugins/oidc_provider/config/IdTokenConfiguration.java b/src/main/java/io/jenkins/plugins/oidc_provider/config/IdTokenConfiguration.java index 0c7b60a..af73211 100644 --- a/src/main/java/io/jenkins/plugins/oidc_provider/config/IdTokenConfiguration.java +++ b/src/main/java/io/jenkins/plugins/oidc_provider/config/IdTokenConfiguration.java @@ -57,7 +57,7 @@ return ExtensionList.lookupSingleton(IdTokenConfiguration.class); } - private Integer tokenLifetime = 3600; + private int tokenLifetime = 3600; private @CheckForNull List claimTemplates; private @CheckForNull List buildClaimTemplates; @@ -82,11 +82,11 @@ public IdTokenConfiguration() { } } - public Integer getTokenLifetime() { + public int getTokenLifetime() { return tokenLifetime; } - @DataBoundSetter public void setTokenLifetime(final Integer lifetime) { + @DataBoundSetter public void setTokenLifetime(final int lifetime) { this.tokenLifetime = lifetime; } diff --git a/src/test/java/io/jenkins/plugins/oidc_provider/ConfigurationAsCodeTest.java b/src/test/java/io/jenkins/plugins/oidc_provider/ConfigurationAsCodeTest.java index 9c86066..24504a1 100644 --- a/src/test/java/io/jenkins/plugins/oidc_provider/ConfigurationAsCodeTest.java +++ b/src/test/java/io/jenkins/plugins/oidc_provider/ConfigurationAsCodeTest.java @@ -59,7 +59,7 @@ public class ConfigurationAsCodeTest { @ConfiguredWithCode("global.yaml") @Test public void globalConfiguration() throws Exception { IdTokenConfiguration cfg = IdTokenConfiguration.get(); - assertEquals(Integer.valueOf(60), cfg.getTokenLifetime()); + assertEquals(60, cfg.getTokenLifetime()); assertEquals(ClaimTemplate.xmlForm(Collections.singletonList(new ClaimTemplate("ok", "true", new BooleanClaimType()))), ClaimTemplate.xmlForm(cfg.getClaimTemplates())); assertEquals(ClaimTemplate.xmlForm(Collections.singletonList(new ClaimTemplate("sub", "jenkins", new StringClaimType()))), diff --git a/src/test/java/io/jenkins/plugins/oidc_provider/IdTokenCredentialsTest.java b/src/test/java/io/jenkins/plugins/oidc_provider/IdTokenCredentialsTest.java index 02db887..090c313 100644 --- a/src/test/java/io/jenkins/plugins/oidc_provider/IdTokenCredentialsTest.java +++ b/src/test/java/io/jenkins/plugins/oidc_provider/IdTokenCredentialsTest.java @@ -44,7 +44,6 @@ import java.time.temporal.ChronoUnit; import java.util.Arrays; import java.util.Collections; -import java.util.Date; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import jenkins.model.Jenkins;