0

I'm trying to post to an API secured with a key (MailGun) with swift but it appears that my key is never utilized as I receive a Forbidden 401 error (Unauthorized - No valid API key provided) according to https://documentation.mailgun.com/api-intro.html#errors

I've verified the URL and key are correct by posting using curl, but I am unable to figure out why my key is not used here. I'm hoping someone can point in the right direction as to why this isn't authenticating correctly

My code is as such but I have replaced all the personal info with <>:

// Email the FBO with desired information
let session = NSURLSession.sharedSession()
let request = NSMutableURLRequest(URL: NSURL(string: "https://api.mailgun.net/v3/<My Domain>/messages")!)
request.HTTPMethod = "POST"
let data = "from: Excited User <scheduler@<mg.mydomain.com>>&to: [bar@example.com,<my email>]&subject:Hello&text:Testinggsome Mailgun awesomness!"
request.HTTPBody = data.dataUsingEncoding(NSASCIIStringEncoding)
request.setValue("key-<my key>", forHTTPHeaderField: "api")
let task = session.dataTaskWithRequest(request, 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! NSHTTPURLResponse
        print("response code = \(httpResponse.statusCode)")
    }
})
task.resume()

Update:

Banged away at it for a few hours and I still can't get my head around it. Maybe I'm not exactly sure what you mean? I can successfully get a response with curl by using:

curl -s --user 'api:key-<my personal key>' https://api.mailgun.net/v3/mg.<my domain>.com/messages -F from='Reservation Scheduler <scheduler@mg.<my domain>.com>' -F to=reservations@<my domain>.com -F subject='Curl Test' -F text='Test from terminal'

I tried inputting it explicitly like so:

request.setValue("api", forHTTPHeaderField: "username")
request.setValue("key-<my key>", forHTTPHeaderField: "password")

It looks to me like the basic auth credentials are never sent? How can I be sure that the fields are "user" and "password"?

  • You have misread the documentation. You need to provide "api" as the username and your key as the password for the basic authentication header – Paulw11 Oct 02 '16 at 02:41
  • Spent a few more hours on it last night and this morning and I still can't get my head around it. Posted an update. Can it be that the credentials are not actually sent? Looking at the curl it looks like api -> key to me. I tried to explicitly set username and password. Frustrated, taking a break. – Rudest Buddhist Oct 02 '16 at 18:10
  • I'm trying to print the session so I can see the request but I only receive an octal back. Is it possible to properly print the request so I can debug what is being sent to the wrong location / not sent at all? – Rudest Buddhist Oct 02 '16 at 19:56
  • It seems that maybe I was mistaken and the API key can be used alone. Have you tried this http://stackoverflow.com/questions/33325137/swift-send-email-with-mailgun – Paulw11 Oct 02 '16 at 20:03
  • haha, yeah that's the original question that made me decide to go with MailGun. Now I believe that code is broken as it results in a 401 forbidden (Unauthorized - No valid API key provided) error. Not sure where to go from here. – Rudest Buddhist Oct 02 '16 at 20:14
  • Ok, in that case see how to create the authorisation header here http://stackoverflow.com/questions/24379601/how-to-make-an-http-request-basic-auth-in-swift – Paulw11 Oct 02 '16 at 20:17
  • Hmm, so using Charles I'm able to see that I can send the key and username how I want and it shows up correctly in the header. But I'm still getting a 401 error. – Rudest Buddhist Oct 02 '16 at 20:53
  • Are you just using MailGun as an example or is your goal to use MailGun specifically? If the latter then there is a Swift framework that will do this for you - https://github.com/PiXeL16/SwiftMailgun. If the former, then please update your code to show how you are setting the `authorization` header – Paulw11 Oct 02 '16 at 22:29
  • Using MailGun specifically, I messed around with that pod but I'd rather have my own code to maintain as opposed to someone elses 3rd party libraries. In any case I have a solution! Posting an answer to my own question now! – Rudest Buddhist Oct 03 '16 at 02:28

1 Answers1

0

After verifying my header appeared to be missing the authentication section of the header I was able to get this working properly with a large HTTP response. I put the full path into Keys.plist so that I can upload my code to github and broke out some of the arguments into variables so I can have them programmatically set later down the road.

// Email the FBO with desired information
// Parse our Keys.plist so we can use our path
var keys: NSDictionary?

if let path = NSBundle.mainBundle().pathForResource("Keys", ofType: "plist") {
    keys = NSDictionary(contentsOfFile: path)
}

if let dict = keys {
    // variablize our https path with API key, recipient and message text
    let mailgunAPIPath = dict["mailgunAPIPath"] as? String
    let emailRecipient = "bar@foo.com"
    let emailMessage = "Testing%20email%20sender%20variables"

    // Create a session and fill it with our request
    let session = NSURLSession.sharedSession()
    let request = NSMutableURLRequest(URL: NSURL(string: mailgunAPIPath! + "from=FBOGo%20Reservation%20%3Cscheduler@<my domain>.com%3E&to=reservations@<my domain>.com&to=\(emailRecipient)&subject=A%20New%20Reservation%21&text=\(emailMessage)")!)

    // POST and report back with any errors and response codes
    request.HTTPMethod = "POST"
    let task = session.dataTaskWithRequest(request, 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! NSHTTPURLResponse
            print("response code = \(httpResponse.statusCode)")
        }
    })
    task.resume()
}

The Mailgun Path is in Keys.plist as a string called mailgunAPIPath with the value:

https://API:key-<my key>@api.mailgun.net/v3/<my domain>.com/messages?

Hope this offers a solution to anyone else having issues with MailGun and wanting to avoid a 3rd party solution!