Tariq Oliver
Tariq Oliver

Reputation: 13

401 Authorization Error when using URLSession

I am trying to retrieve JSON from my app's server which needs a user/password authentication. Does anyone know why I am not being allowed entry into the server? I tried including an authorization header with the credentials needed but still get this error.

func retrieveJSON(){
    let login = "[email protected]"
    let password = "password"
    
    let url = NSURL(string: "http://server/admin/data.json")
    let request = NSMutableURLRequest(url: url! as URL)
    
    let config = URLSessionConfiguration.default
    let userPasswordString = "\(login):\(password)"
    let userPasswordData = userPasswordString.data(using: String.Encoding.utf8)
    let base64EncodedCredential = userPasswordData!.base64EncodedString()
    let authString = "Basic \(base64EncodedCredential)"
    config.httpAdditionalHeaders = ["Authorization" : authString]
    
    let session = URLSession(configuration: config)
    let task = session.dataTask(with: request as URLRequest) { (data, response, error) -> Void in
        debugPrint(response)
    }
    task.resume()
}

Response message with 401 error

Server authorization needed

Upvotes: 0

Views: 1674

Answers (1)

Rob
Rob

Reputation: 437372

Your web site does not appear to be using “basic” auth. It would appear to be using some different authentication scheme, which we cannot determine from an image of the HTML login page.


If it were using “Basic” auth (which it likely is not in your particular case), you could simplify the code a bit, removing NSURL, NSMutableURLRequest, the casts, etc. Also, if you’re going to create a URLSession, you will want to finishTasksAndInvalidate:

func retrieveJSON() {
    let login = "[email protected]"
    let password = "password"

    let url = URL(string: "http://server/admin/data.json")!
    let request = URLRequest(url: url)  // use `var` if you really need it to be mutable

    let config = URLSessionConfiguration.default
    let base64EncodedCredential = (login + ":" + password)
        .data(using: .utf8)!
        .base64EncodedString()
    config.httpAdditionalHeaders = ["Authorization": "Basic " + base64EncodedCredential]

    let session = URLSession(configuration: config)
    let task = session.dataTask(with: request) { data, response, error in
        debugPrint(response ?? "No response")
    }
    task.resume()
    session.finishTasksAndInvalidate()
}

Alternatively, rather than building the Authorization header yourself, you can call CFHTTPMessageAddAuthentication to add authentication headers to a request. And, as an aside, adding the authentication to the request itself, you don’t have to create your own URLSession, but can use the shared instance.

func retrieveJSON() {
    let login = "[email protected]"
    let password = "password"

    let url = URL(string: "http://server/admin/data.json")!
    var request = URLRequest(url: url)
    request.updateBasicAuth(for: login, password: password)
    let task = URLSession.shared.dataTask(with: request) { data, response, error in
        debugPrint(response ?? "No response")
    }
    task.resume()
}

Where

extension URLRequest {

    /// Update request for HTTP authentication
    ///
    /// - parameter username:        The username
    /// - parameter password:        The password
    /// - parameter authentication:  The type of authentication to be applied

    mutating func updateBasicAuth(for username: String, password: String, authentication: String = kCFHTTPAuthenticationSchemeBasic as String) {
        let message = CFHTTPMessageCreateRequest(kCFAllocatorDefault, httpMethod! as CFString, url! as CFURL, kCFHTTPVersion1_1).takeRetainedValue()
        if !CFHTTPMessageAddAuthentication(message, nil, username as CFString, password as CFString, authentication as CFString?, false) {
            print("authentication not added")
        }
        if let authorizationString = CFHTTPMessageCopyHeaderFieldValue(message, "Authorization" as CFString)?.takeRetainedValue() {
            setValue(authorizationString as String, forHTTPHeaderField: "Authorization")
        } else {
            print("didn't find authentication header")
        }
    }
}

But refinements to the “Basic” authentication are somewhat academic if your server is using a different authentication scheme.

Upvotes: 1

Related Questions