JWT Authentication with Wordpress REST API in Swift - swift

I am trying to make posts to my Wordpress blog via Swift. I am using the Wordpress REST API with Java Web Token (JWT) authentication. The first step is to pass the user credentials to the server using the HTTP POST method. The server then returns a JSON Object containing the JWT token needed for authentication. I can get this all working using a REST API program like Postman, but I am having trouble with the Swift code.
My credentials are sent just fine and the server returns some data, but I am having trouble getting the token from this data.
This Swift code was generated by the Postman app:
import Foundation
let headers = [
"Content-Type": "application/json",
"cache-control": "no-cache",
]
let parameters = [
"username": "myUsername",
"password": "myPassword"
] as [String : Any]
let postData = JSONSerialization.data(withJSONObject: parameters, options: [])
let request = NSMutableURLRequest(url: NSURL(string: "https://myDomain/wp-json/jwt-auth/v1/token")! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
print(error)
} else {
let httpResponse = response as? HTTPURLResponse
print(httpResponse)
}
})
dataTask.resume()
When I do the HTTP POST command in Postman I get back this JSON:
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczpcL1wvY3F1aWNrLmNhIiwiaWF0IjoxNTQ3Njc3MDMzLCJuYmYiOjE1NDc2NzcwMzMsImV4cCI6MTU0ODI4MTgzMywiZGF0YSI6eyJ1c2VyIjp7ImlkIjoiMyJ9fX0.DgxmmSFKnEdXuWi5EiBk1BpFvWrD57KIE8TiWazId-4",
"user_email": "myEmail",
"user_nicename": "myUsername",
"user_display_name": "myUsername"
}
Is there anyway to get this same result using Swift? I imagine that this is being returned by the session.dataTask method, I just have no idea how to parse it.

Okay, so the answer was to add this to the session.dataTask method (in the else closure).
let jsonObject = try! JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as! NSDictionary
let query = jsonObject["token"] as! String
print(query)

Related

what is unsupported grant type error in swift?

I'm trying to make a simple login post request with URLSession with my userDetails object as input and request is of content type "application/x-www-form-urlencoded", in response i am supposed to get an object of "access token", "refresh token", "userdetails".
but i keep getting:
error = "unsupported_grant_type"
request on postman works but something is not right when I make a request in my project. what am i doing wrong here?? API team says the input object has to be correct which is exactly from the postman.
func loginUser() {
/// login request here
let url = URL(string: EndPoint.loginUser)!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
let userData: [String: Any] = [
"UserName": "myUserEmail",
"Password": "myPassword",
"grant_type": "password"
]
guard let httpBody = try? JSONSerialization.data(withJSONObject: userData, options: .prettyPrinted) else { return }
request.httpBody = httpBody
let session = URLSession.shared
session.dataTask(with: request) { (data, urlResponse, error) in
if let data = data {
do {
let jsonData = try JSONSerialization.jsonObject(with: data, options: [])
print(jsonData)
} catch let error {
debugPrint(error.localizedDescription)
}
}
}.resume()
}

access json with the rapidapi provided nsurlsession [duplicate]

This question already has answers here:
Correctly Parsing JSON in Swift 3
(10 answers)
Closed 1 year ago.
I'm trying to use this weather api https://rapidapi.com/interzoid/api/us-weather-by-zip-code/endpoints in my xcode project with swift. They provide me with the code
import Foundation
let headers = [
"x-rapidapi-host": "us-weather-by-zip-code.p.rapidapi.com",
"x-rapidapi-key": "my api key"
]
let request = NSMutableURLRequest(url: NSURL(string: "https://us-weather-by-zip-code.p.rapidapi.com/getweatherzipcode?zip=11214")! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
print(error)
} else {
let httpResponse = response as? HTTPURLResponse
print(httpResponse)
}
})
dataTask.resume()
After running it I get the response headers but I wish to get the reponse body which is the json. I'm still pretty new to this and hope you can help.
You need to parse the response. The JSONSerialization class method jsonObject(with:options:) returns a value of type Any and throws an error if the data couldn’t be parsed.
let json = try? JSONSerialization.jsonObject(with: data, options: [])
Check out this question for more details: Correctly Parsing JSON in Swift 3
P.S. I'm not a Swift expert but here to help you.

Github GraphQL query "Not Found" in Swift

I am writing the following code to write a simple GraphQL Query to ask current user's login name but I am getting a 404 for this request. I followed the instructions from here. Code here is actually another StackOverflow from here. Could someone help me debug this. This is the first time I am trying to write GraphQL queries in Swift.
import Foundation
import PlaygroundSupport
let headers = ["content-type": "application/json",
"Authorization": "Bearer Valid-Github-Personal-Access-Token"]
let parameters = [ "query" : "query { viewer { login } }"] as [String : Any]
let postData = try JSONSerialization.data(withJSONObject: parameters, options: [])
let request = NSMutableURLRequest(url: NSURL(string: "https://api.github.com/graphql/")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
print(error)
} else {
let httpResponse = response as? HTTPURLResponse
print(httpResponse)
}
})
dataTask.resume()
PlaygroundPage.current.needsIndefiniteExecution = true
#felix, did you get the bearer token that you need to authenticate in the API? On your code, I see a hardcoded string "Bearer Valid-Github-Personal-Access-Token". If you are actually sending this instead of a real token, it might explain a 404.
Hope it helps :)
Github GraphQL endpoint is https://api.github.com/graphql and I had https://api.github.com/graphql/

Translating Alamofire call to URLSession

I've got a old codebase that I'm trying to migrate out of. The network calls currently use Alamofire 4.0 and I'm trying to use URLSession instead. I'm having trouble figuring out what's wrong, here's what I've been doing. I start off with the route and params that I want to post:
// route to user creation
let url = ...
let params = ["user": ["first_name": "John", "last_name": "Appleseed", "email": "john#apple.com", "password": "asdfgh"]]
Here's the old and new network calls:
// old network request
Alamofire.request(url, method: .post, parameters: newUser, encoding: JSONEncoding.default).responseJSON { response in
// ...
}
// new code
var request = URLRequest(url: url)
let jsonData = try! JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
request.httpBody = jsonData
request.httpMethod = "POST"
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else { return print("error=\(error)") }
if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 {
print("status code should be 200, but is \(httpStatus.statusCode)")
print("response = \(response)")
}
let responseString = String(data: data, encoding: .utf8)
print("responseString = \(responseString)")
}
task.resume()
For some reason, I'm getting a status code of 400 in my URLSession attempt. Alamofire works fine.
Any advice appreciated.
The problem is that the request isn't recognized as a JSON call. Write the following assignment after you create your URLRequest:
request.allHTTPHeaderFields = ["Content-Type": "application/json"]

Swift Send Email with MailGun

Problem
I would like to use the MailGun service to send emails from a pure Swift app.
Research So Far
As I understand it, there are two methods to send an email via MailGun. One is to email MailGun with the emails, and MailGun will redirect it (See Send via SMTP). That will, as I understand it, not work, as iOS cannot programatically automatically send mail, and must use methods that require user intervention. As such, I should use the API directly. As I understand it, I need to open a URL to do this, and so I should use some form of NSURLSession, as per this SO answer
Code
MailGun provides documentation for Python, which is as follows:
def send_simple_message():
return requests.post(
"https://api.mailgun.net/v3/sandbox(Personal info).mailgun.org/messages",
auth=("api", "key-(Personal info)"),
data={"from": "Excited User <(Personal info)>",
"to": ["bar#example.com", "(Personal info)"],
"subject": "Hello",
"text": "Testing some Mailgun awesomness!"})
with (Personal info) being substituted for keys/information/emails.
Question
How do I do that in Swift?
Thanks!
In python, the auth is being passed in the header.
You have to do a http post request, passing both the header and the body.
This is a working code:
func test() {
let session = NSURLSession.sharedSession()
let request = NSMutableURLRequest(URL: NSURL(string: "https://api.mailgun.net/v3/sandbox(Personal info).mailgun.org/messages")!)
request.HTTPMethod = "POST"
let data = "from: Excited User <(Personal info)>&to: [bar#example.com,(Personal info)]&subject:Hello&text:Testinggsome Mailgun awesomness!"
request.HTTPBody = data.dataUsingEncoding(NSASCIIStringEncoding)
request.setValue("key-(Personal info)", 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()
}
people are getting 400 or 401 errors because none of the other answers construct the url correctly. here is some code that works in swift 5 and iOS15:
func sendEmail() {
// variablize our https path with API key, recipient and message text
let mailgunAPIPath = "https://api:YOUR_API_KEY#api.mailgun.net/v3/YOUR_DOMAIN/messages?"
let emailRecipient = "RECIPIENT#EMAIL.COM"
let emailMessage = "Testing%20email%20sender%20variables"
// Create a session and fill it with our request
let session = URLSession.shared
let request = NSMutableURLRequest(url: NSURL(string: mailgunAPIPath + "from=USER#YOUR_DOMAIN&to=\(emailRecipient)&subject=A%20New%20Test%21&text=\(emailMessage)")! as URL)
// POST and report back with any errors and response codes
request.httpMethod = "POST"
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()
}
requests.post sends an HTTP POST request, encoding key/value pairs as application/x-www-form-urlencoded. You need to do the same.
convert the set of key-value pairs into application/x-www-form-urlencoded as per How to escape the HTTP params in Swift
compose the request using the resulting string for data & send it as per iOS : http Post using swift
I spent hours trying to get the selected answer working, but to no avail.
Although I was finally 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!
Swift 3 answer:
func test() {
let session = URLSession.shared
var request = URLRequest(url: URL(string: "https://api.mailgun.net/v3/sandbox(Personal info).mailgun.org/messages")!)
request.httpMethod = "POST"
let data = "from: Excited User <(Personal info)>&to: [bar#example.com,(Personal info)]&subject:Hello&text:Testinggsome Mailgun awesomness!"
request.httpBody = data.data(using: .ascii)
request.setValue("key-(Personal info)", forHTTPHeaderField: "api")
let task = session.dataTask(with: 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! HTTPURLResponse
print("response code = \(httpResponse.statusCode)")
}
})
task.resume()
}