Xcode 7, iOS 9, Alamofire 2.0.1
When following a redirect, the headers added to the original request, will not be added to the new request.
Example (#788):
let headers = [
"Authorization": userData.userAPIKey!,
]
Alamofire.request(.GET, url, parameters: nil, encoding: .URL, headers: headers).response { (request, response, data, error) -> Void in
}
If the server responds with a 301 Moved permanently, a new request is made to the location returned. But the new request will not have the Authorization-Header set.
I saw #314, #317, and #424 But I think this is a different issue. IMHO Alamofire should either follow redirects and add the headers added to the original-request or it should not follow redirects at all and report them to the user. In #788 I found out the hard way looking at what was sent over the wire.
As a user of Alamofire, I don't get any notification/log-message, when the library is following a redirect. This is very handy, but I would expect, that the my extra-headers would also be applied to the new-request.
Thanks for filing this issue @eburi. I'll need to put some tests together to figure out exactly why the headers are being dropped. Are you overriding any of the session delegate closures?
Okay @eburi, you're going to have to take this one up with Apple.
I've added a fairly robust test in cdeb5c19 that demonstrates the behavior. It appears that most headers are passed through from the original to the redirect request with the exception of the Authorization header. I can't find any documentation on this behavior, but the test verifies what is actually going on. If you need to pass the Authorization header through, you'll have to do it manually by overriding the closure.
func testThatRedirectedRequestContainsAllHeadersFromOriginalRequest() {
// Given
let redirectURLString = "https://httpbin.org/get"
let URLString = "https://httpbin.org/redirect-to?url=\(redirectURLString)"
let headers = [
"Authorization": "1234",
"Custom-Header": "foobar",
]
// NOTE: It appears that most headers are maintained during a redirect with the exception of the `Authorization`
// header. It appears that Apple's strips the `Authorization` header from the redirected URL request. If you
// need to maintain the `Authorization` header, you need to manually append it to the redirected request.
Alamofire.Manager.sharedInstance.delegate.taskWillPerformHTTPRedirection = { session, task, response, request in
var redirectedRequest = request
if let
originalRequest = task.originalRequest,
headers = originalRequest.allHTTPHeaderFields,
authorizationHeaderValue = headers["Authorization"]
{
let mutableRequest = request.mutableCopy() as! NSMutableURLRequest
mutableRequest.setValue(authorizationHeaderValue, forHTTPHeaderField: "Authorization")
redirectedRequest = mutableRequest
}
return redirectedRequest
}
let expectation = expectationWithDescription("Request should redirect to \(redirectURLString)")
var response: Response<AnyObject, NSError>?
// When
Alamofire.request(.GET, URLString, headers: headers)
.responseJSON { closureResponse in
response = closureResponse
expectation.fulfill()
}
waitForExpectationsWithTimeout(defaultTimeout, handler: nil)
// Then
XCTAssertNotNil(response?.request, "request should not be nil")
XCTAssertNotNil(response?.response, "response should not be nil")
XCTAssertNotNil(response?.data, "data should not be nil")
XCTAssertTrue(response?.result.isSuccess ?? false, "response result should be a success")
if let
JSON = response?.result.value as? [String: AnyObject],
headers = JSON["headers"] as? [String: String]
{
XCTAssertEqual(headers["Custom-Header"], "foobar", "Custom-Header should be equal to foobar")
XCTAssertEqual(headers["Authorization"], "1234", "Authorization header should be equal to 1234")
}
}
All you need to do is override the taskWillPerformHTTPRedirection closure on the delegate as shown in the test. If you feel strongly about this behavior being incorrect, then I'd suggest you file a radar with Apple.
Cheers. 馃嵒
Thanks, for the quick reply!
I won't take this to Apple - this is most likely by design for security reasons.
And now we know how to work around it!
@cnoon I may be missing something, but why doesn't Alamofire just implement this in the taskWillPerformHTTPRedirection internally?
Is it because, since Apple is doing it this way, we are assuming it's a security issue?
If you are willing to accept a PR that implements this, I'd be happy to make one. I think it shouldn't be too difficult. However, I also understand if you don't want to do this, as it may be seen as a way to work around Apple's own security measures.
It's most likely not implemented that way for security purposes, but more for usability. Forwarding on the Authorization headers can fail the redirected request on certain servers. Some servers can throw 400s if they are sent Authorization headers when they are not expecting it. Therefore, it's good to follow Apple's lead here.
If you do need to append the headers to the redirected request, you'll need to do so manually as shown in the test logic above.
This work around does not work if you are using
request.authenticate(user: username, password: password)
Is there a way for me to avoid manually building the Authorization header?
@cnoon
Okay @eburi, you're going to have to take this one up with Apple.
I've added a fairly robust test in cdeb5c1 that demonstrates the behavior. It appears that most headers are passed through from the original to the redirect request with the exception of the
Authorizationheader. I can't find any documentation on this behavior, but the test verifies what is actually going on. If you need to pass theAuthorizationheader through, you'll have to do it manually by overriding the closure.func testThatRedirectedRequestContainsAllHeadersFromOriginalRequest() { // Given let redirectURLString = "https://httpbin.org/get" let URLString = "https://httpbin.org/redirect-to?url=\(redirectURLString)" let headers = [ "Authorization": "1234", "Custom-Header": "foobar", ] // NOTE: It appears that most headers are maintained during a redirect with the exception of the `Authorization` // header. It appears that Apple's strips the `Authorization` header from the redirected URL request. If you // need to maintain the `Authorization` header, you need to manually append it to the redirected request. Alamofire.Manager.sharedInstance.delegate.taskWillPerformHTTPRedirection = { session, task, response, request in var redirectedRequest = request if let originalRequest = task.originalRequest, headers = originalRequest.allHTTPHeaderFields, authorizationHeaderValue = headers["Authorization"] { let mutableRequest = request.mutableCopy() as! NSMutableURLRequest mutableRequest.setValue(authorizationHeaderValue, forHTTPHeaderField: "Authorization") redirectedRequest = mutableRequest } return redirectedRequest } let expectation = expectationWithDescription("Request should redirect to \(redirectURLString)") var response: Response<AnyObject, NSError>? // When Alamofire.request(.GET, URLString, headers: headers) .responseJSON { closureResponse in response = closureResponse expectation.fulfill() } waitForExpectationsWithTimeout(defaultTimeout, handler: nil) // Then XCTAssertNotNil(response?.request, "request should not be nil") XCTAssertNotNil(response?.response, "response should not be nil") XCTAssertNotNil(response?.data, "data should not be nil") XCTAssertTrue(response?.result.isSuccess ?? false, "response result should be a success") if let JSON = response?.result.value as? [String: AnyObject], headers = JSON["headers"] as? [String: String] { XCTAssertEqual(headers["Custom-Header"], "foobar", "Custom-Header should be equal to foobar") XCTAssertEqual(headers["Authorization"], "1234", "Authorization header should be equal to 1234") } }All you need to do is override the
taskWillPerformHTTPRedirectionclosure on the delegate as shown in the test. If you feel strongly about this behavior being incorrect, then I'd suggest you file a radar with Apple.Cheers. 馃嵒
How replicate this with Alamofire 5?
You can control redirects through the RedirectHandler API and add one to a Session or a Request.
Most helpful comment
Okay @eburi, you're going to have to take this one up with Apple.
I've added a fairly robust test in cdeb5c19 that demonstrates the behavior. It appears that most headers are passed through from the original to the redirect request with the exception of the
Authorizationheader. I can't find any documentation on this behavior, but the test verifies what is actually going on. If you need to pass theAuthorizationheader through, you'll have to do it manually by overriding the closure.All you need to do is override the
taskWillPerformHTTPRedirectionclosure on the delegate as shown in the test. If you feel strongly about this behavior being incorrect, then I'd suggest you file a radar with Apple.Cheers. 馃嵒