Try Maven downloads anonymously first, retry with credentials on 4xx#7447
Try Maven downloads anonymously first, retry with credentials on 4xx#7447timtebeek wants to merge 2 commits into
Conversation
When Maven settings.xml credentials are rejected by the remote repository (401/403), retry the JAR download without authentication. Mirrors `MavenPomDownloader.requestAsAuthenticatedOrAnonymous()` and Apache Maven's behavior, so anonymous-accessible artifacts resolve even when configured credentials are invalid. Also fixes two nits observed during troubleshooting: - local cache filename was missing the hyphen before the classifier (`foo-1.0.0recipes.jar` → `foo-1.0.0-recipes.jar`) - download error message omitted the classifier
|
digging a bit into how Maven handles auth on requests: haven't traced into HttpClient yet, but it seems like it does try an unauthenticated request first and then retry with auth if that fails. if that's accurate, then the risk of 2x requests is expected, and we should probably match Maven and try anon first. |
Mirrors Apache Maven Resolver's DeferredCredentialsProvider behavior: issue an unauthenticated request first, then send credentials only when the server challenges with a 4xx. This matches what users get from running Maven directly, and avoids leaking credentials to public artifacts.
|
Good call — flipped to anonymous-first in 8408c64. Now we send no auth at all unless the server challenges us with a 4xx, matching Apache Maven Resolver's |
|
hey - just noticed this was still in draft. the changes seem sensible to me; any pending concerns? |
How Apache Maven handles authenticated HTTP requestsMaven 3.x delegates all artifact transport to Maven Resolver (formerly Aether). Findings verified against 1. Default behavior is challenge-response, NOT preemptive
2. Apache HttpClient transport (default since Maven 3.9)The transport registers a // ApacheTransporter.java
.setDefaultCredentialsProvider(toCredentialsProvider(server, repoAuthContext, ...))
public Credentials getCredentials(AuthScope authScope) {
// resolve via Factory only when HttpClient asks
...
delegate.setCredentials(entry.getKey(), entry.getValue().newCredentials());
return delegate.getCredentials(authScope);
}So the flow for a single
The 3. JDK HttpClient transport (alternative)Same model, different mechanism. Credentials are registered on the JDK builder.authenticator(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return authentications.get(getRequestorType());
}
});The JDK's private void prepare(HttpRequest.Builder requestBuilder) {
if (preemptiveAuth || (preemptivePutAuth && method.equals("PUT"))) {
if (serverAuthentication != null) {
requestBuilder.setHeader("Authorization", getBasicAuthValue(...));
}
}
}4. What this means for the reported scenarioWhen
That's why 5. What this PR does vs. what Maven does
Functional parity for the reported case. The missing piece is the per-session auth cache: each artifact currently pays a 401 round-trip when creds are required. For recipe-jar downloads (typically a handful of artifacts) this is negligible, but if profiling shows it matters we can add a |
Follow-up: avoiding the wasted anonymous round-trip when credentials are actually requiredThe anonymous-first approach in this PR costs one extra request per artifact when the remote actually requires auth. For a recipe-bundle resolution that's typically small, but if profiling shows it matters, two options worth considering: Option B — Per-instance "auth-required hosts" set in each downloaderEach of
Option D — Flag on
|
Summary
MavenArtifactDownloadernow tries each JAR download anonymously first and only sendssettings.xmlcredentials if the server challenges with a 4xx — mirroring Apache Maven Resolver'sDeferredCredentialsProvider. This unblocks anonymous-accessible artifacts when configured credentials are invalid (reported case: 401 against internal Artifactory) and avoids leaking credentials to public artifacts.LocalMavenArtifactCachecache filename missing the hyphen before the classifier (foo-1.0.0recipes.jar→foo-1.0.0-recipes.jar).MavenDownloadingExceptionmessage so artifact coordinates are complete during troubleshooting.Follow-up to #6845 where this scenario was anticipated; the reporter has now hit it in practice.
Test plan
publicArtifactsResolveAnonymouslyEvenWhenCredentialsAreInvalid— bad credentials configured, public artifact, single anonymous request, no auth header sent.retriesWithCredentialsWhenAnonymousReturns401— private artifact, two requests: first anonymous (401), second authenticated (200).