4

I have the following code but consistently get error 401 forbidden when attempting to run it:

func email() {
    let session = URLSession.shared
    let request = NSMutableURLRequest(url: NSURL(string: "https://api.mailgun.net/v3/{edited_out}/messages")! as URL)
    request.httpMethod = "POST"
    let data = "from: Swift Email <(test@test.com)>&to: [my_email_address@gmail.com,(my_email_address@gmail.com)]&subject:Hello&text:Testing_some_Mailgun_awesomness"
    request.httpBody = data.data(using: String.Encoding.ascii)
    request.setValue("application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type")
    request.setValue("key-{edited_out}", forHTTPHeaderField: "api")
    let task = session.dataTask(with: request as URLRequest, completionHandler: {(data, response, error) in
        if let error = error {
            print(error)
        }
        if let response = response {
            print("url = \(response.url!)")
            print("response = \(response)")
            let httpResponse = response as! HTTPURLResponse
            print("response code = \(httpResponse.statusCode)")
        }
    })
    task.resume()
}

The error is:

    url = https://api.mailgun.net/v3/{edited_out}/messages
response = <NSHTTPURLResponse: 0x600000226a20> { URL: https://api.mailgun.net/v3/{edited_out}/messages } { status code: 401, headers {
    Connection = "keep-alive";
    "Content-Length" = 9;
    "Content-Type" = "text/html; charset=utf-8";
    Date = "Thu, 29 Dec 2016 21:22:46 GMT";
    Server = nginx;
    "Www-Authenticate" = "Basic realm=\"MG API\"";
} }
response code = 401

If I send such a request via curl with my credentials it works fine.

Any ideas?

jtbandes
  • 115,675
  • 35
  • 233
  • 266
RJH
  • 358
  • 3
  • 12

2 Answers2

0

You need to set the uesrname and the password.

Something like this:

request.setValue("Basic \(base64Credentials)", forHTTPHeaderField: "Authorization")

and base64Credentials is the :

let credentials= String(format: "%@:%@", username, password)
let base64Credentials= credentials.data(using: String.Encoding.utf8)!
William Kinaan
  • 28,059
  • 20
  • 85
  • 118
  • How do you pass your credentials in CURL ? – William Kinaan Dec 30 '16 at 09:55
  • 1
    curl -s --user 'api:key-{omitted}’ https://api.mailgun.net/v3/{omitted}/messages -F from='Excited User ' -F to={omitted}@gmail.com -F subject='Hello' -F text='Testing' ----- (stack overflow is converting https:// to a link) – RJH Dec 30 '16 at 10:15
  • normally you should pass the credentials using cURL like this: `curl -u username:password` or `curl --user username:password` so i would say that your username is `api` and your password is `key-blablabla`, **is that the case?** Are you sure you've tried my answer include the space after *Basic* ? and you've already decoded the credentials to base64? – William Kinaan Dec 30 '16 at 10:22
  • Yeah, username api, password key-XXXXXXX. I've added your three lines as above and ensured theres a space after the word Basic... if I print(base64Credentials) it shows up as 40 bytes so its definitely encoded – RJH Dec 30 '16 at 10:27
  • @RJH that's weird, Could you try this link from your browser `https://api:key-xxxxxx@api.mailgun.net/v3/samples.mailgun.org/log` ? what do you get? (You may need to replay `samples.mailgun.org` by your domain, but I'm not sure, so please try both – William Kinaan Dec 30 '16 at 10:34
  • @RJH I would suggest your to check the real request going from your mobile (simulator) to your server. Use any network monetoring tool to check the HTTP request being sent – William Kinaan Dec 30 '16 at 14:01
  • Im swaying towards it being something in the code because it works fine with a HTTP post via web and its not limited to simulator - same error with an actual device – RJH Dec 30 '16 at 14:34
  • Off course it's something in the code but you said you're using the correct credentials so I was just trying to let you debug the problem – William Kinaan Dec 30 '16 at 14:35
  • im not sure I will be able to check the HTTP request being sent as its over secure connection tho? – RJH Dec 30 '16 at 14:37
  • @RJH create a dummy server that works locally and asks for authentication, and send the request to it and check what's being received. 10 minutes java working not more :) – William Kinaan Dec 30 '16 at 14:38
0

Finally got it working with the following code, if it helps anyone else:

func email() {
    let session = URLSession.shared
    let request = NSMutableURLRequest(url: NSURL(string: "https://api.mailgun.net/v3/{edited_out}/messages")! as URL)

    request.httpMethod = "POST"
    let credentials = "api:key-{omitted}"
    request.setValue("Basic \(credentials.toBase64())", forHTTPHeaderField: "Authorization")

    let data = "from: Swift Email <(test@test.com)>&to: [my_email_address@gmail.com,(my_email_address@gmail.com)]&subject:Hello&text:Testing_some_Mailgun_awesomness"
    request.httpBody = data.data(using: String.Encoding.ascii)

    let task = session.dataTask(with: request as URLRequest, completionHandler: {(data, response, error) in
        if let error = error {
            print(error)
        }
        if let response = response {
            print("url = \(response.url!)")
            print("response = \(response)")
            let httpResponse = response as! HTTPURLResponse
            print("response code = \(httpResponse.statusCode)")
        }
    })
    task.resume()
}


extension String {

    func fromBase64() -> String? {
        guard let data = Data(base64Encoded: self) else {
            return nil
        }

        return String(data: data, encoding: .utf8)
    }

    func toBase64() -> String {
        return Data(self.utf8).base64EncodedString()
    }
}

So I guess the answer from William would have worked with:

let base64Credentials = credentials.data(using: String.Encoding.utf8)!.base64EncodedString()

Instead of:

let base64Credentials = credentials.data(using: String.Encoding.utf8)!
RJH
  • 358
  • 3
  • 12
  • Did you replace {omitted} with your key – RJH Mar 17 '17 at 12:20
  • Yes of course. The authentication part is fine as as soon I change the key I got a 401. – Nico Mar 17 '17 at 18:58
  • 1
    This code still throws 400 error for me. I'm not able to debug the issue. I copied the code exactly and replaced it with my verified domain name. Also, added my own API key. – Karthik Kannan Mar 02 '18 at 17:18