diff --git a/src/main/java/org/jenkinsci/plugins/github_branch_source/GitHubAppCredentials.java b/src/main/java/org/jenkinsci/plugins/github_branch_source/GitHubAppCredentials.java index b191ad9bd..0c76d8a99 100644 --- a/src/main/java/org/jenkinsci/plugins/github_branch_source/GitHubAppCredentials.java +++ b/src/main/java/org/jenkinsci/plugins/github_branch_source/GitHubAppCredentials.java @@ -5,6 +5,7 @@ import com.cloudbees.plugins.credentials.impl.BaseStandardCredentials; import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.Extension; import hudson.Util; import hudson.util.FormValidation; @@ -27,6 +28,7 @@ import static org.jenkinsci.plugins.github_branch_source.GitHubSCMNavigator.DescriptorImpl.getPossibleApiUriItems; import static org.jenkinsci.plugins.github_branch_source.JwtHelper.createJWT; +@SuppressFBWarnings(value = "SE_NO_SERIALVERSIONID", justification = "XStream") public class GitHubAppCredentials extends BaseStandardCredentials implements StandardUsernamePasswordCredentials { private static final String ERROR_AUTHENTICATING_GITHUB_APP = "Couldn't authenticate with GitHub app ID %s"; @@ -44,6 +46,9 @@ public class GitHubAppCredentials extends BaseStandardCredentials implements Sta private String owner; + private transient String cachedToken; + private transient long tokenCacheTime; + @DataBoundConstructor @SuppressWarnings("unused") // by stapler public GitHubAppCredentials( @@ -138,7 +143,15 @@ public Secret getPassword() { apiUri = "https://api.github.com"; } - String appInstallationToken = generateAppInstallationToken(appID, privateKey.getPlainText(), apiUri, owner); + long now = System.currentTimeMillis(); + String appInstallationToken; + if (cachedToken != null && now - tokenCacheTime < JwtHelper.VALIDITY_MS /* extra buffer */ / 2) { + appInstallationToken = cachedToken; + } else { + appInstallationToken = generateAppInstallationToken(appID, privateKey.getPlainText(), apiUri, owner); + cachedToken = appInstallationToken; + tokenCacheTime = now; + } return Secret.fromString(appInstallationToken); } diff --git a/src/main/java/org/jenkinsci/plugins/github_branch_source/JwtHelper.java b/src/main/java/org/jenkinsci/plugins/github_branch_source/JwtHelper.java index ef22228ba..abc498e0c 100644 --- a/src/main/java/org/jenkinsci/plugins/github_branch_source/JwtHelper.java +++ b/src/main/java/org/jenkinsci/plugins/github_branch_source/JwtHelper.java @@ -10,12 +10,17 @@ import java.security.spec.PKCS8EncodedKeySpec; import java.util.Base64; import java.util.Date; -import java.util.Objects; import static java.util.Objects.requireNonNull; +import java.util.concurrent.TimeUnit; class JwtHelper { + /** + * Somewhat less than the maximum JWT validity. + */ + static final long VALIDITY_MS = TimeUnit.MINUTES.toMillis(8); + /** * Create a JWT for authenticating to GitHub as an app installation * @param githubAppId the app ID @@ -42,9 +47,7 @@ static String createJWT(String githubAppId, final String privateKey) { .setIssuer(githubAppId) .signWith(signingKey, signatureAlgorithm); - long oneMinuteInMillis = 60L * 1000L; - long expMillis = nowMillis + (oneMinuteInMillis * 8); - Date exp = new Date(expMillis); + Date exp = new Date(nowMillis + VALIDITY_MS); builder.setExpiration(exp); return builder.compact();