The backend APIs I'm working with require a token to be sent with every request against the HTTP header key Authorization
in this format - Token xxxxxxxxxx
.
Right now, I'm doing the following.
var getRequest = URLRequest(url: url)
getRequest.addValue("Token xxxxxxxx", forHTTPHeaderField: "Authorization")
This works sometimes and some other times, the header field Authorization
is stripped when the request is sent out. I checked this using Charles proxy.
Apple's documentation states the following.
An NSURLSession object is designed to handle various aspects of the HTTP protocol for you. As a result, you should not modify the following headers: Authorization, Connection, Host, WWW-Authenticate
As a solution to this, many suggest using the didReceiveAuthenticationChallenge
delegate method for URLSession.
Here, you need to pass a URLSession.AuthChallengeDisposition
instance to tell how you want to respond to the challenge and a URLCredential
instance to pass the credentials to respond to the authenticate challenge.
I do not know how to and if I can create a URLCredential
instance that will add Token xxxxxxxx
for the header field Authorization
.
Can somebody more knowledgeable please help me how to go about solving this?
PS - All code mentioned in this question is in Swift 3.
This question asks something similar to what I have. But, the answers given there don't work for me. And, some of the questions asked under the questions regarding Apple not allowing headers for Authorization
to be added have gone unanswered.
Edit:
Posting relevant code.
var getRequest = URLRequest(url: url)
getRequest.httpMethod = "GET"
getRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
getRequest.addValue("application/json", forHTTPHeaderField: "Accept")
let token = DataProvider.sharedInstance.token
getRequest.addValue("Token \(token)", forHTTPHeaderField: "Authorization")
let getTask = URLSession.shared.dataTask(with: getRequest) { (data, response, error) in
if let data = data {
print("--------GET REQUEST RESPONSE START--------")
print("CODE: \((response as? HTTPURLResponse)?.statusCode ?? 0)")
print("Response Data:")
print(String(data: data, encoding: .utf8) ?? "")
print("--------GET REQUEST RESPONSE END--------")
}
}
getTask.resume()
Here, I can confirm that the header field for 'Authorization' is getting added to the request's header dictionary.
But, when I check what request hits the server, the header field for 'Authorization' is missing. Any thoughts?
I ran into this exact same issue and discovered that my lack of a trailing slash, /
, was the problem.
The server was sending back a 301 Redirect
response. URLSession
automatically follows the redirect, but will also drop the Authorization
header. This is likely due to Authorization
's "special status". According to URLSessionConfiguration
's documentation:
An URLSession object is designed to handle various aspects of the HTTP protocol for you. As a result, you should not modify the following headers:
- Authorization
- Connection
- Host
- WWW-Authenticate
If the Authorization
header is required, implement urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)
from URLSessionTaskDelegate
. That method is passed the new request on redirect, allowing you to add back the header.
e.g.
class APIClient: URLSessionTaskDelegate {
let session: URLSession!
initializeSession() {
// Create a new session with APIClient as the delegate
session = URLSession(configuration: URLSessionConfiguration.default,
delegate: self,
delegateQueue: nil)
}
// Perform the request
func fetchRecords(handler: () => void) {
var request = URLRequest(url: URL(string: "http://127.0.0.1:8000/api/employee/records")!)
request.setValue(retrieveToken(), forHTTPHeaderField: "Authorization")
session.dataTask(with: request, completionHandler: handler).resume()
}
// Implement URLSessionTaskDelegate's HTTP Redirection method
func urlSession(_ session: URLSession, task: URLSessionTask, willPerformHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest, completionHandler: @escaping (URLRequest?) -> Void) {
// Create a mutable copy of the new request, add the header, call completionHandler
var newRequest = request
newRequest.addValue(retrieveToken(), forHTTPHeaderField: "Authorization")
completionHandler(newRequest)
}
}
IMPORTANT NOTE!
It's a bad idea to blindly trust a redirect. You should ensure that the URL you're being redirected to is the same domain as the original request. I've left that out of my code example for brevity.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With