From: Aaron Plattner Date: Tue, 2 Jun 2026 16:11:48 +0000 (-0700) Subject: http: preserve wwwauth_headers across redirects X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=bb88a926f48667eebc60573de1ffd98b156deec4;p=thirdparty%2Fgit.git http: preserve wwwauth_headers across redirects When cURL follows a redirect, it calls the CURLOPT_HEADERFUNCTION for each header received including ones from a redirect. http_request() sets fwrite_wwwauth() as the header function, which will record the wwwauth[] entries for the last step in the redirection chain. However, when http_request_recoverable() sees that cURL followed a redirect, it attempts to update the credentials for the request from the new URL using credential_from_url(). The first thing that does is call credential_clear(), which clears everything including wwwauth_headers. If the new URL should use a credential helper rather than credentials embedded in the URL, this loses the list of authentication methods that the server provided in the redirect. For example, I have a server that supports HTTP but always redirects to HTTPS before handling requests. This redirect breaks OAuth authentication: $ git ls-remote http://server/git => Send header: GET /git/info/refs?service=git-upload-pack HTTP/1.1 <= Recv header: HTTP/1.1 302 Found <= Recv header: Location: https://server.nvidia.com/git/info/refs?service=git-upload-pack == Info: Issue another request to this URL: 'https://server.nvidia.com/git/info/refs?service=git-upload-pack' => Send header: GET /git/info/refs?service=git-upload-pack HTTP/1.1 <= Recv header: HTTP/1.1 401 Unauthorized <= Recv header: WWW-Authenticate: Bearer error="invalid_request", error_description="No bearer token found in the request", msal-tenant-id="", msal-client-id="" trace: run_command: 'git credential-cache --timeout 7200 get' trace: start_command: /bin/sh -c 'git credential-cache --timeout 7200 get' 'git credential-cache --timeout 7200 get' trace: built-in: git credential-cache --timeout 7200 get trace: run_command: 'git credential-msal get' trace: start_command: /bin/sh -c 'git credential-msal get' 'git credential-msal get' trace: exec: git-credential-msal get trace: run_command: git-credential-msal get trace: start_command: /usr/bin/git-credential-msal get Username for 'https://server.nvidia.com': ^C When git invokes the credential helper, it doesn't include the wwwauth[] array, so git-credential-msal doesn't think that OAuth is supported [1]. Fix the problem by preserving the wwwauth_headers strvec across the call to credential_from_url(). [1] https://github.com/Binary-Eater/git-credential-msal/blob/trunk/src/git_credential_msal/main.py#L69 Signed-off-by: Aaron Plattner Signed-off-by: Junio C Hamano --- diff --git a/http.c b/http.c index 67c9c6fc60..82f2562f30 100644 --- a/http.c +++ b/http.c @@ -2354,7 +2354,21 @@ static int http_request_recoverable(const char *url, if (options->effective_url && options->base_url) { if (update_url_from_redirect(options->base_url, url, options->effective_url)) { + struct strvec wwwauth_headers = STRVEC_INIT; + + /* + * Preserve wwwauth_headers across the call to + * credential_from_url(): if the effective URL doesn't + * specify its own credentials, a credential helper + * might need the wwwauth[] array from the server's + * redirect response in order to authenticate. + */ + strvec_pushv(&wwwauth_headers, + http_auth.wwwauth_headers.v); credential_from_url(&http_auth, options->base_url->buf); + strvec_pushv(&http_auth.wwwauth_headers, + wwwauth_headers.v); + strvec_clear(&wwwauth_headers); url = options->effective_url->buf; } } diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf index 40a690b0bb..664f23fc6c 100644 --- a/t/lib-httpd/apache.conf +++ b/t/lib-httpd/apache.conf @@ -202,6 +202,7 @@ RewriteRule ^/dumb-redir/(.*)$ /dumb/$1 [R=301] RewriteRule ^/smart-redir-perm/(.*)$ /smart/$1 [R=301] RewriteRule ^/smart-redir-temp/(.*)$ /smart/$1 [R=302] RewriteRule ^/smart-redir-auth/(.*)$ /auth/smart/$1 [R=301] +RewriteRule ^/custom_auth_redir/(.*)$ /custom_auth/$1 [R=302] RewriteRule ^/smart-redir-limited/(.*)/info/refs$ /smart/$1/info/refs [R=301] RewriteRule ^/ftp-redir/(.*)$ ftp://localhost:1000/$1 [R=302] diff --git a/t/t5563-simple-http-auth.sh b/t/t5563-simple-http-auth.sh index 0063581615..04c8b15324 100755 --- a/t/t5563-simple-http-auth.sh +++ b/t/t5563-simple-http-auth.sh @@ -557,6 +557,51 @@ test_expect_success 'access using bearer auth' ' EOF ' +test_expect_success 'bearer auth after redirect preserves wwwauth headers' ' + test_when_finished "per_test_cleanup" && + + set_credential_reply get <<-EOF && + capability[]=authtype + authtype=Bearer + credential=YS1naXQtdG9rZW4= + EOF + + cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF && + id=1 creds=Bearer YS1naXQtdG9rZW4= + EOF + + cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF && + id=1 status=200 + id=default response=WWW-Authenticate: FooBar param1="value1" param2="value2" + id=default response=WWW-Authenticate: Bearer authorize_uri="id.example.com" p=1 q=0 + id=default response=WWW-Authenticate: Basic realm="example.com" + EOF + + test_config_global credential.helper test-helper && + test_config_global credential.useHttpPath true && + git ls-remote "$HTTPD_URL/custom_auth_redir/repo.git" && + + expect_credential_query get <<-EOF && + capability[]=authtype + capability[]=state + protocol=http + host=$HTTPD_DEST + path=custom_auth/repo.git + wwwauth[]=FooBar param1="value1" param2="value2" + wwwauth[]=Bearer authorize_uri="id.example.com" p=1 q=0 + wwwauth[]=Basic realm="example.com" + EOF + + expect_credential_query store <<-EOF + capability[]=authtype + authtype=Bearer + credential=YS1naXQtdG9rZW4= + protocol=http + host=$HTTPD_DEST + path=custom_auth/repo.git + EOF +' + test_expect_success 'access using bearer auth with invalid credentials' ' test_when_finished "per_test_cleanup" &&