diff --git a/lib/fetch/index.js b/lib/fetch/index.js index 94f2d1e0aca..8014d0fb3ba 100644 --- a/lib/fetch/index.js +++ b/lib/fetch/index.js @@ -1135,12 +1135,12 @@ async function httpRedirectFetch (fetchParams, response) { return makeNetworkError('URL scheme must be a HTTP(S) scheme') } - // 7. If request’s redirect count is twenty, return a network error. + // 7. If request’s redirect count is 20, then return a network error. if (request.redirectCount === 20) { return makeNetworkError('redirect count exceeded') } - // 8. Increase request’s redirect count by one. + // 8. Increase request’s redirect count by 1. request.redirectCount += 1 // 9. If request’s mode is "cors", locationURL includes credentials, and @@ -1195,36 +1195,44 @@ async function httpRedirectFetch (fetchParams, response) { } } - // 13. If request’s body is non-null, then set request’s body to the first return + // 13. If request’s current URL’s origin is not same origin with locationURL’s + // origin, then for each headerName of CORS non-wildcard request-header name, + // delete headerName from request’s header list. + if (!sameOrigin(requestCurrentURL(request), locationURL)) { + // https://fetch.spec.whatwg.org/#cors-non-wildcard-request-header-name + request.headersList.delete('authorization') + } + + // 14. If request’s body is non-null, then set request’s body to the first return // value of safely extracting request’s body’s source. if (request.body != null) { assert(request.body.source) request.body = safelyExtractBody(request.body.source)[0] } - // 14. Let timingInfo be fetchParams’s timing info. + // 15. Let timingInfo be fetchParams’s timing info. const timingInfo = fetchParams.timingInfo - // 15. Set timingInfo’s redirect end time and post-redirect start time to the + // 16. Set timingInfo’s redirect end time and post-redirect start time to the // coarsened shared current time given fetchParams’s cross-origin isolated // capability. timingInfo.redirectEndTime = timingInfo.postRedirectStartTime = coarsenedSharedCurrentTime(fetchParams.crossOriginIsolatedCapability) - // 16. If timingInfo’s redirect start time is 0, then set timingInfo’s + // 17. If timingInfo’s redirect start time is 0, then set timingInfo’s // redirect start time to timingInfo’s start time. if (timingInfo.redirectStartTime === 0) { timingInfo.redirectStartTime = timingInfo.startTime } - // 17. Append locationURL to request’s URL list. + // 18. Append locationURL to request’s URL list. request.urlList.push(locationURL) - // 18. Invoke set request’s referrer policy on redirect on request and + // 19. Invoke set request’s referrer policy on redirect on request and // actualResponse. setRequestReferrerPolicyOnRedirect(request, actualResponse) - // 19. Return the result of running main fetch given fetchParams and true. + // 20. Return the result of running main fetch given fetchParams and true. return mainFetch(fetchParams, true) } diff --git a/test/wpt/server/server.mjs b/test/wpt/server/server.mjs index c62f9658053..86edcf65150 100644 --- a/test/wpt/server/server.mjs +++ b/test/wpt/server/server.mjs @@ -294,6 +294,28 @@ const server = createServer(async (req, res) => { res.end('not actually gzip') break } + case '/fetch/api/resources/dump-authorization-header.py': { + res.setHeader('Content-Type', 'text/html') + res.setHeader('Cache-Control', 'no-cache') + + if (req.headers.origin) { + res.setHeader('Access-Control-Allow-Origin', req.headers.origin) + res.setHeader('Access-Control-Allow-Credentials', 'true') + } else { + res.setHeader('Access-Control-Allow-Origin', '*') + } + + res.setHeader('Access-Control-Allow-Headers', 'Authorization') + res.statusCode = 200 + + if (req.headers.authorization) { + res.end(req.headers.authorization) + return + } + + res.end('none') + break + } default: { res.statusCode = 200 res.end('body') diff --git a/test/wpt/tests/fetch/api/credentials/authentication-redirection.any.js b/test/wpt/tests/fetch/api/credentials/authentication-redirection.any.js new file mode 100644 index 00000000000..b6376368116 --- /dev/null +++ b/test/wpt/tests/fetch/api/credentials/authentication-redirection.any.js @@ -0,0 +1,26 @@ +// META: global=window,worker +// META: script=/common/get-host-info.sub.js + +const authorizationValue = "Basic " + btoa("user:pass"); +async function getAuthorizationHeaderValue(url) +{ + const headers = { "Authorization": authorizationValue}; + const requestInit = {"headers": headers}; + const response = await fetch(url, requestInit); + return response.text(); +} + +promise_test(async test => { + const result = await getAuthorizationHeaderValue("/fetch/api/resources/dump-authorization-header.py"); + assert_equals(result, authorizationValue); +}, "getAuthorizationHeaderValue - no redirection"); + +promise_test(async test => { + const result = await getAuthorizationHeaderValue("/fetch/api/resources/redirect.py?location=" + encodeURIComponent("/fetch/api/resources/dump-authorization-header.py")); + assert_equals(result, authorizationValue); +}, "getAuthorizationHeaderValue - same origin redirection"); + +promise_test(async (test) => { + const result = await getAuthorizationHeaderValue(get_host_info().HTTP_REMOTE_ORIGIN + "/fetch/api/resources/redirect.py?allow_headers=Authorization&location=" + encodeURIComponent(get_host_info().HTTP_ORIGIN + "/fetch/api/resources/dump-authorization-header.py")); + assert_equals(result, "none"); +}, "getAuthorizationHeaderValue - cross origin redirection");