basic auth URLSession POST

Hi developers im trying to do a request like my postman using basic auth with an Username & password, but i can't archieve, heres what i have done so far


this is how postman look like

Authorization

-> Type

->Basic Auth
username: example_Username

password: example_Password


Body

-> form-data

KEY: grant_type VALUE: password

KEY: username VALUE: test@gmail.com

KEY: password VALUE: 03ac674216f3e15c761ee1a5e255f067953623c8b388b4459e13f978d7c846f4





first i tryied this way


func postDataFormRequest(resource: TokenRequest, completion: @escaping (Result<Token, NetworkError>) -> Void) {
        
        let headers = [
            "Username": "example_api",
            "Password": "example_api_password",
            "Content-Type": "application/form-data"
        ]
        
//this information comes from TokenRequestStruct once i call it form my VC
        let body = "grant_type=\(resource.grant_type)&username=\(resource.username)&password=\(resource.password)"
        
        guard let url = URL(string: "example/") else { return }
        
        var request = URLRequest(url: url)
        request.httpMethod = HTTPMethods.POST
        request.timeoutInterval = 60
        request.httpBody = body.data(using: .utf8)
        request.allHTTPHeaderFields = headers
        
        URLSession.shared.dataTask(with: request) { (data, response, error) in

            guard let data = data, error == nil else {
                completion(.failure(.domainError))
                return
            }

            let result = try? JSONDecoder().decode(Token.self, from: data)
            if let result = result {
                DispatchQueue.main.async {
                    completion(.success(result))
                }
            } else {
                DispatchQueue.main.async {
                    completion(.failure(.decodingError))
                }
            }
        }.resume()
    }


using this method response =


CredStore - performQuery - Error copying matching creds.

Error=-25300, query={

atyp = http;

class = inet;

"m_Limit" = "m_LimitAll";

ptcl = http;

"r_Attributes" = 1;

sdmn = "oauth2/client";

srvr = "3.216.253.237";

sync = syna;

}


tried to fix it by using arbitrary loads = YES

didn't work



Second way


static func getPostString(params:[String:Any]) -> String
    {
        var data = [String]()
        for(key, value) in params
        {
            data.append(key + "=\(value)")
        }
        return data.map { String($0) }.joined(separator: "&")
    }
    
    static func formData(url:URL, params:[String:Any], finish: @escaping ((message:String, data:Data?)) -> Void)
        {
            var request = URLRequest(url: url)
            request.httpMethod = "POST"

            let postString = self.getPostString(params: params)
            request.httpBody = postString.data(using: .utf8)

            var result:(message:String, data:Data?) = (message: "Fail", data: nil)
            let task = URLSession.shared.dataTask(with: request) { data, response, error in

                if(error != nil)
                {
                    result.message = "Fail Error not null : \(error.debugDescription)"
                }
                else
                {
                    result.message = "Success"
                    result.data = data
                }

                finish(result)
            }
            task.resume()
        
    }



usage:

in my VC

let dict:[String:String] = ["username": view.emailField.text ?? "", "password": view.passField.text ?? "", "grant_type": "password"]

            let url = URL(string: “example”)!
            NetworkManager.formData(url: url, params: dict) { (message, data) in
                print(message)
                print(data)
          


didn't work either it says Mising grant_type



third way:


public static func LoginGenerateTokenPOST(userName: String, password: String, completion: @escaping (Bool) -> ()) {
       let baseUrl = "example"

        
        let url = URL(string: "\(baseUrl)example")!
        var request = URLRequest(url: url)
        request.setValue("application/form-data", forHTTPHeaderField: "Content-Type")
        //request.setValue("application/json", forHTTPHeaderField: "Accept")
        request.setValue("example_api", forHTTPHeaderField: "example_api_password")
        request.httpMethod = "POST"



        let postString = "username=\(userName.lowercased())&password=\(password.sha256())&grant_type=password"
        request.httpBody = postString.data(using: .utf8)
        
        let task = URLSession.shared.dataTask(with: request) { data, response, error in
            guard let data = data, error == nil else {                                                 // check for fundamental networking error
                 completion(false)
                print("error")
                return
            }

            if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 {           // check for http errors
                print("statusCode should be 200, but is \(httpStatus.statusCode)")
                completion(false)

               
            }

                do {
                if let convertedJsonIntoDict = try JSONSerialization.jsonObject(with: data, options: []) as? NSDictionary {

                   print(convertedJsonIntoDict)
                    
                    
                    
                }
            }
            catch let error as NSError {
                completion(false)
                print(error.localizedDescription)
            }

        }
        task.resume()

        }

this is my third function didn't work either



so my question is:


what is the best way to use basic auth URLSession POST, what did i do wrong in any of the 3 cases?, or all 3 are useless

Replies

Please try this `headers` for your #1 code and see what you get.

let basicCred = "example_api:example_api_password".data(using: .utf8)!.base64EncodedString()
let headers = [
    "Authorization": "Basic \(basicCred)",
    "Content-Type": "application/x-www-form-urlencoded"
]

I recommend against trying to manually set an

Authorization
header. Rather, you should respond to authentication challenges. See Handling an Authentication Challenge.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"
Hello Quinn,
I hope you are well.

I have the same problem. In my case the code responds to the authentication challenge. Valid username and password credentials are provided. However Console out shows:

Code Block
CredStore - performQuery - Error copying matching creds.  Error=-25300, query={
    class = inet;
    "m_Limit" = "m_LimitAll";
    ptcl = htps;
    "r_Attributes" = 1;
    sdmn = "https%3A%2F%2Fmydomain.com";
    srvr = “mydomain.com";
    sync = syna;
}


It’s worth noting that this is a simple command line Swift utility to retrieve some JSON over https using basic authentication.

I note your answer on https://developer.apple.com/forums/thread/109402
However I’m confused why responding to the authentication challenge is causing some uncalled for Keychain lookup.

Any thoughts on how to resolve this? Apple docs on CredStore and it’s function would also be of interest.

Thanks and Best Wishes