Skip to content

Instantly share code, notes, and snippets.

@kibotu
Created January 19, 2026 12:24
Show Gist options
  • Select an option

  • Save kibotu/c982a0950f395c536b89b5f28aeef810 to your computer and use it in GitHub Desktop.

Select an option

Save kibotu/c982a0950f395c536b89b5f28aeef810 to your computer and use it in GitHub Desktop.
Gradle Authenticator
if (!project.hasProperty('artifactory_contextUrl')) throw new GradleException('the "artifactory_contextUrl" needs to be set in gradle.properties!')
if (!project.hasProperty('artifactory_token')) throw new GradleException('the "artifactory_token" needs to be set in gradle.properties!')
if (!project.hasProperty('artifactory_reader_1')) throw new GradleException('the "artifactory_reader_1" needs to be set in gradle.properties!')
if (!project.hasProperty('artifactory_reader_2')) throw new GradleException('the "artifactory_reader_2" needs to be set in gradle.properties!')
if (!project.hasProperty('artifactory_reader_3')) throw new GradleException('the "artifactory_reader_3" needs to be set in gradle.properties!')
if (!project.hasProperty('artifactory_reader_token_1')) throw new GradleException('the "artifactory_reader_token_1" needs to be set in gradle.properties!')
if (!project.hasProperty('artifactory_reader_token_2')) throw new GradleException('the "artifactory_reader_token_2" needs to be set in gradle.properties!')
if (!project.hasProperty('artifactory_reader_token_3')) throw new GradleException('the "artifactory_reader_token_3" needs to be set in gradle.properties!')
Authenticator.setDefault (new Authenticator() {
private def current_user
private def current_token
private def users
private def tokens
private def maxRetryAttempts
private def initialBackoffMs
private def maxBackoffMs
private def authValidityMs
private def lastAuthTime = 0
private final Object authLock = new Object()
private static final String getBasicAuthenticationHeader(String username, String password) {
String valueToEncode = username + ":" + password;
return "Basic " + Base64.getEncoder().encodeToString(valueToEncode.getBytes())
}
{
// Retry configuration - read from properties with defaults
maxRetryAttempts = project.hasProperty('artifactory_max_retries') ?
Integer.parseInt(project.property('artifactory_max_retries').toString()) : 10
initialBackoffMs = project.hasProperty('artifactory_initial_backoff') ?
Integer.parseInt(project.property('artifactory_initial_backoff').toString()) : 500
maxBackoffMs = project.hasProperty('artifactory_max_backoff') ?
Integer.parseInt(project.property('artifactory_max_backoff').toString()) : 30000
authValidityMs = project.hasProperty('artifactory_auth_validity') ?
Integer.parseInt(project.property('artifactory_auth_validity').toString()) : 60000
users = ["$artifactory_reader_1", "$artifactory_reader_2", "$artifactory_reader_3"]
tokens = ["$artifactory_reader_token_1", "$artifactory_reader_token_2", "$artifactory_reader_token_3"]
// Initial authentication
authenticateWithRetry()
}
private void authenticateWithRetry() {
def client = java.net.http.HttpClient.newHttpClient()
def authenticated = false
def attempt = 0
def backoffMs = initialBackoffMs
while (!authenticated && attempt < maxRetryAttempts) {
attempt++
println("Authentication attempt ${attempt}/${maxRetryAttempts}")
// Shuffle users for each retry attempt to distribute load
def indices = (0..<users.size()).toList().shuffled()
for (i in indices) {
try {
def request = java.net.http.HttpRequest.newBuilder()
.header("Authorization", getBasicAuthenticationHeader(users[i], tokens[i]))
.method("HEAD", java.net.http.HttpRequest.BodyPublishers.noBody())
.uri(URI.create("$artifactory_contextUrl/libs-release"))
.timeout(java.time.Duration.ofSeconds(30))
.build()
def response = client.send(request, java.net.http.HttpResponse.BodyHandlers.discarding())
def statusCode = response.statusCode()
println(" User: ${users[i]} -> Status: ${statusCode}")
if (statusCode == 302) {
current_user = users[i]
current_token = tokens[i]
authenticated = true
lastAuthTime = System.currentTimeMillis()
println("✓ Successfully authenticated with user: ${users[i]}")
break
} else if (statusCode == 401) {
println(" ⚠ User ${users[i]} temporarily blocked (401), trying next user...")
// Continue to next user
} else {
println(" ⚠ Unexpected status code ${statusCode} for user ${users[i]}")
}
} catch (Exception e) {
println(" ✗ Error testing user ${users[i]}: ${e.message}")
// Continue to next user
}
}
// If not authenticated after trying all users, wait before next attempt
if (!authenticated && attempt < maxRetryAttempts) {
println(" All users blocked or unavailable. Waiting ${backoffMs}ms before retry...")
Thread.sleep(backoffMs)
// Exponential backoff with cap
backoffMs = Math.min(backoffMs * 2, maxBackoffMs)
}
}
if (current_user == null) {
throw new Exception("Failed to authenticate after ${attempt} attempts. All artifactory users appear to be blocked or invalid!")
}
}
protected PasswordAuthentication getPasswordAuthentication() {
if (artifactory_contextUrl.contains(this.requestingHost)) {
synchronized (authLock) {
def currentTime = System.currentTimeMillis()
// If authentication is stale, try to re-authenticate
if (currentTime - lastAuthTime > authValidityMs) {
println("⚠ Authentication credentials may be stale, re-authenticating...")
try {
authenticateWithRetry()
} catch (Exception e) {
println("✗ Re-authentication failed: ${e.message}")
// Continue with existing credentials as fallback
}
}
return new PasswordAuthentication(current_user, current_token.toCharArray())
}
}
return super.getPasswordAuthentication()
}
})
@kibotu
Copy link
Author

kibotu commented Jan 19, 2026

settings.gradle

apply from: "authenticator.gradle"

gradle.properties

artifactory_contextUrl=https://artifactory.mydomain.de/artifactory
artifactory_token=my-token
artifactory_reader_1=my-reader-token-1
artifactory_reader_2=my-reader-token-2
artifactory_reader_3=my-reader-token-3
artifactory_reader_token_1=my-first-token
artifactory_reader_token_2=my-second-token
artifactory_reader_token_3=my-third-token

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