15

I am trying to pass authorization key in header of a URLRequest. But at the server end the key is not received. The same API when called from postman working fine. Any other key in the header is working fine, even authorizations key is visible at server end.

Here is my code:

let headers = [
    "authorization": "token abcd"
]

var request = URLRequest.init(url: NSURL(string:
    "http://127.0.0.1:7000/api/channels?filter=contributed")! as URL)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers
let config = URLSessionConfiguration.default
config.httpAdditionalHeaders = headers
let session = URLSession.init(configuration: config)

let dataTask = session.dataTask(with: request, completionHandler: { (data, response, error) -> Void in
    if (error != nil) {
        print(error ?? "")
    } else {
        let httpResponse = response as? HTTPURLResponse
        print(httpResponse ?? "")
    }
})

As you can see, I tried to set the token in both session config and request but none is working.

rmaddy
  • 314,917
  • 42
  • 532
  • 579
Santosh
  • 1,254
  • 2
  • 16
  • 31

4 Answers4

12

This seems to be working:

// Set the security header
private var credentials: String {
    return "\(participantId):\(password)"
}

private var basicAuthHeader: String {
    return "Basic \(credentials)"
}

func getSettings(participantId: Int, password: String) -> Bool {

    self.participantId = participantId
    self.password = password

    let path = "/settings/\(participantId)"
    guard let url = URL(string: "\(BASE_URL)\(path)") else {
        Log.e("Invalid URL string, could not convert to URL")
        return false
    }

    var urlRequest = URLRequest(url: url)
    urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
    urlRequest.setValue(basicAuthHeader, forHTTPHeaderField: "Authorization")
    urlRequest.setValue(APP_FILE_NAME, forHTTPHeaderField: "User-Agent")

    // This is a synchronous wrapper extension around URLSession.dataTask()
    let (data, response, error) = URLSession.shared.synchronousDataTask(with: urlRequest)
    // Process the result...
}

Note: code written by my coworker. Thanks John!

William T. Mallard
  • 1,562
  • 2
  • 25
  • 33
  • 2
    Official docs for URLRequest point to [this page](https://developer.apple.com/documentation/foundation/nsurlrequest#1776617), which mentions to avoid modifying certain headers such as `Authorization`. [This thread](https://forums.developer.apple.com/thread/68809) on Apple forums provide an example using delegates callback for handling username/password basic and digest authentication. For Bearer token authentication, [this other thread](https://forums.developer.apple.com/thread/89811) suggests that there is no client-side alternative to modifying the URLRequest `Authorization` header, however. – ettore May 20 '20 at 18:47
  • True. Worth noting however that not all REST API services supply an authentication challenge. In that case, the recommended Apple delegate approach will fail. Always check the REST API docs first. – Max MacLeod Jul 03 '20 at 12:25
3

Looks like the problem is that you are modifying Authorization header using httpAdditionalHeaders which is something you should not do.

From the Doc

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, Proxy-Authenticate, Proxy-Authorization, WWW-Authenticate

Removing the line config.httpAdditionalHeaders = headers should fix the issue.

Rukshan
  • 7,902
  • 6
  • 43
  • 61
1

If you want token to be hardcoded, I guess it has to be like this:

urlRequest.httpMethod = "GET"
urlRequest.setValue("Token <Your Token>", forHTTPHeaderField: "Authorization")
0

I found the same thing: setting the header field Authorization just didn't do the trick.

Here's the solution I settled on (which works well):

I added the URLSessionDelegate protocol to my current class. This unfortunately means inheriting from NSObject.

Then, when defining my URLSession, I set its delegate to 'self'.

Finally, I provide an authentication challenge handler.

In code, this all looks like:

public class SomeHTTPTask: NSObject, URLSessionDelegate {
    public init() {
        ... initialize variables ...
        super.init()
        ... now you are free to call methods on self ...
    }

    public func httpTask(withURL url: URL) {
        let request = URLRequest(url: url)
        ... set up request ...
        let config = URLSessionConfiguration.default
        let session = URLSession(configuration: config, delegate: self, delegateQueue: nil)
        let task = session.dataTask(with: request) {data, response, error in
            ... now you have a result ...
        }
    }

    public func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
        guard let user = Credentials.sharedInstance.userId, let password = Credentials.sharedInstance.password else {
            completionHandler(.performDefaultHandling, nil)
            return
        }
        let userCredential = URLCredential(user: user,
                                           password: password,
                                           persistence: .permanent)
        completionHandler(.useCredential, userCredential)
    }

}

Hopefully, the bits and pieces are self-explanatory. It's just an authentication challenge handler that provides credentials, if it can. The underlying URLSession will deal with the details, wither it's an NTLM or Basic auth, those sorts of things.

In the end, this seems a solid solution. At least, it worked for me.

Here's a nice reference document from Apple if you like reading that kind of thing.