Maven username/password only works when embedded in url for some servers

With Gradle v1.11/v1.12, specifying a maven username/password via credentials is not working:

maven {
     credentials {
        username "${GITHUB_USERNAME}"
        password "${GITHUB_PASSWORD}"
    }
    url "https://raw.githubusercontent.com/foobar/repo/master/releases"
}

but specifying it inside the url is working:

maven {
     url "https://${GITHUB_USERNAME}:${GITHUB_PASSWORD}@raw.githubusercontent.com/foobar/repo/master/releases"
}

What is going on here?

I put the credentials into “gradle.properties” and in “build.gradle” I have this:

maven {
     url 'http://mycompany.ycy.de/artifactory/repo'
  credentials {
   username artifactoryUser
   password artifactoryPassword
  }
 }

This works (at least in 1.11).

I wonder if it depends on the way that the server does auth. Perhaps github does it differently than your server.

On thing that’s sort of unusual about github: if you don’t auth with raw.githubusercontent.com, it returns a 404 Not Found rather than a 401 Unauthorized. In the debug logs I can see that gradle is trying to do an HTTP HEAD on the artifact url, where it gets a 404 (which implies that it didn’t send auth info).

Perhaps gradle doesn’t auth until it does an HTTP GET, or it only auths if the server returns 404 Not Found?

Looking more closely at the debug log, the line that says

Target auth state: UNCHALLENGED

is really suspicious. Why isn’t it sending along auth info?

14:23:23.927 [DEBUG] [org.gradle.api.internal.externalresource.transport.http.HttpClientHelper] Performing HTTP HEAD: https://raw.githubusercontent.com/foobar/repo/master/releases/com/foobar/myartifact/0.0.1/mylib-0.0.1.pom
14:23:24.116 [DEBUG] [org.apache.http.impl.conn.PoolingClientConnectionManager] Connection request: [route: {s}->https://raw.githubusercontent.com][total kept alive: 0; route allocated: 0 of 5; total allocated: 0 of 10]
14:23:24.128 [DEBUG] [org.apache.http.impl.conn.PoolingClientConnectionManager] Connection leased: [id: 0][route: {s}->https://raw.githubusercontent.com][total kept alive: 0; route allocated: 1 of 5; total allocated: 1 of 10]
14:23:24.990 [DEBUG] [org.apache.http.impl.conn.DefaultClientConnectionOperator] Connecting to raw.githubusercontent.com:443
14:23:25.319 [DEBUG] [org.apache.http.client.protocol.RequestAddCookies] CookieSpec selected: best-match
14:23:25.331 [DEBUG] [org.apache.http.client.protocol.RequestAuthCache] Auth cache not set in the context
14:23:25.331 [DEBUG] [org.apache.http.client.protocol.RequestTargetAuthentication] Target auth state: UNCHALLENGED
14:23:25.332 [DEBUG] [org.apache.http.client.protocol.RequestProxyAuthentication] Proxy auth state: UNCHALLENGED
14:23:25.333 [DEBUG] [org.apache.http.impl.client.SystemDefaultHttpClient] Attempt 1 to execute request
14:23:25.334 [DEBUG] [org.apache.http.impl.conn.DefaultClientConnection] Sending request: HEAD /foobar/repo/master/releases/com/foobar/myartifact/0.0.1/mylib-0.0.1.pom HTTP/1.1
14:23:25.336 [DEBUG] [org.apache.http.headers] >> HEAD /foobar/repo/master/releases/com/foobar/myartifact/0.0.1/mylib-0.0.1.pom HTTP/1.1
14:23:25.336 [DEBUG] [org.apache.http.headers] >> Accept-Encoding: gzip,deflate
14:23:25.337 [DEBUG] [org.apache.http.headers] >> Host: raw.githubusercontent.com
14:23:25.338 [DEBUG] [org.apache.http.headers] >> Connection: Keep-Alive
14:23:25.338 [DEBUG] [org.apache.http.headers] >> User-Agent: Gradle/1.12 (Mac OS X;10.9.2;x86_64) (Oracle Corporation;1.7.0_45;24.45-b08)
14:23:25.464 [DEBUG] [org.apache.http.impl.conn.DefaultClientConnection] Receiving response: HTTP/1.1 404 Not Found
14:23:25.464 [DEBUG] [org.apache.http.headers] << HTTP/1.1 404 Not Found
14:23:25.465 [DEBUG] [org.apache.http.headers] << Date: Thu, 15 May 2014 20:23:25 GMT
14:23:25.465 [DEBUG] [org.apache.http.headers] << Server: Apache
14:23:25.466 [DEBUG] [org.apache.http.headers] << Content-Security-Policy: default-src 'none'
14:23:25.466 [DEBUG] [org.apache.http.headers] << Access-Control-Allow-Origin: https://render.githubusercontent.com
14:23:25.467 [DEBUG] [org.apache.http.headers] << X-XSS-Protection: 1; mode=block
14:23:25.467 [DEBUG] [org.apache.http.headers] << X-Frame-Options: deny
14:23:25.468 [DEBUG] [org.apache.http.headers] << X-Content-Type-Options: nosniff
14:23:25.468 [DEBUG] [org.apache.http.headers] << Strict-Transport-Security: max-age=31536000
14:23:25.469 [DEBUG] [org.apache.http.headers] << Content-Length: 9
14:23:25.469 [DEBUG] [org.apache.http.headers] << Accept-Ranges: bytes
14:23:25.470 [DEBUG] [org.apache.http.headers] << Via: 1.1 varnish
14:23:25.470 [DEBUG] [org.apache.http.headers] << X-Served-By: cache-sv95-SJC3
14:23:25.471 [DEBUG] [org.apache.http.headers] << X-Cache: MISS
14:23:25.471 [DEBUG] [org.apache.http.headers] << X-Cache-Hits: 0
14:23:25.472 [DEBUG] [org.apache.http.headers] << Vary: Authorization,Accept-Encoding
14:23:25.472 [DEBUG] [org.apache.http.headers] << Expires: Thu, 15 May 2014 20:28:25 GMT
14:23:25.472 [DEBUG] [org.apache.http.headers] << Source-Age: 0
14:23:25.473 [DEBUG] [org.apache.http.headers] << Keep-Alive: timeout=10, max=50
14:23:25.473 [DEBUG] [org.apache.http.headers] << Connection: Keep-Alive

Just to be clear, even tho its showing 404 Not Found, the file really is there. I can curl it with my credentials and it pulls down fine:

curl -u myusername https://raw.githubusercontent.com/foobar/repo/master/releases/com/foobar/myartifact/0.0.1/mylib-0.0.1.pom

I think the problem is that gradle isn’t sending auth info for HTTP HEAD, and github returns 404 Not Found when it doesn’t get auth for private repos.

More digging: Why am I getting a 404 error on a repository that exists?

Hi,

Thanks for your digging.

What we probably should be doing is preemptively authenticating. I’m going to ask some of the other developers to comment on this.

Would you be interested in helping to add preemptive auth support?

Hm, it seems like this was done on purpose (!):

http://www.gradle.org/docs/1.1/release-notes#dependency-resolution-supports-http-digest-authentication It’s not a bug, its a feature! And it looks like its been causing problems for others, like GRADLE-2968.

As much as I’d like to, this probably isn’t as simple as me diving in and “fixing it”, as there’s some reason this behavior is in Gradle that I don’t have visibility into.

From what I can gather, the best practice is to not send auth info if you don’t have to. I suspect that Gradle (or the http client lib its using) is expecting a 401 challenge-response, and the 404 is throwing it off.

I can see a couple ways of handling this problem:

  1. Turn pre-emptive auth back on (probably not happening)

  2. Have gradle consider a 404 equivalent to a 401. ie, if it gets a 404, it will retry the GET/HEAD with an auth

  3. Add a flag to the gradle dsl to turn pre-emptive auth on, something like this:

maven {
     credentials {
        username "${GITHUB_USERNAME}"
        password "${GITHUB_PASSWORD}"
        preemptive true
    }
    url "https://raw.githubusercontent.com/foobar/repo/master/releases"
}

I’d say we’ll go with option #2 there.

Did something change with this in Gradle 2? While this was working with 1.12 when I try to run a build where it will need to fetch a resource from GH in this way the build fails instead. I didn’t notice anything in the release notes and there aren’t any additional comments on the linked ticket.

Can we please get some movement on this? I can’t simply go in and unilaterally change this behavior-- gradle removed it for some reason and the devs need to discuss the options.

i also prefer option 3, because i´m using jenkins with maven repository plugin. if jenkins is secured and auth is not send, it returns 403 (forbidden). so not to forget or add each status code the flag “preemptive” is the best solution and please add it to one of the next versions!

I’m working on a gradle plugin to allow users to easily use a git repo as a maven repository, but I need this fixed in order for it to work. I’ll start diving into the source when I can to look into it, but it maybe a little while before I have time.

did you ever resolve this?

The pull request is just waiting to be reviewed by a member of the gradle team (has been for almost a month). I’m using it personally with a custom build, but someone from Gradleware has to approve it before it makes it into the main project.

So does this work with gradle 2.x and private repos? We cannot access our private repo with Gradle 2.x using https://:@ … format or by using the credentials {} within the repo definition. We would like to upgrade gradle (and our ide’s) but we cannot atm.

Yeah, You can check out the pull request here: https://github.com/gradle/gradle/pull/386

Check out the diff and you can see how to implement a build of that branch to read from private github repos.

Ok thanks for the link - I will take a look :slight_smile:

Hi Ryan,

Have you tried to use this version of gradle with Android Studio at all?

Thanks,

Marcus

Hey Marcus, I have, though I don’t use anything in AS to call uploadArchives, that happens from the command line. That being said, all AS is doing is calling the same Gradle as the command line, so it should work from the Gradle pane on the right.