Swift 4 URLsession with Authentication API Key - swift

I am trying to make a POST Request for the Shippo REST API in Swift, but since it needs an API Authentication key I cannot figure out how to implement the key in the URLSession in Swift 4. As the Apple documentation puts it I cannot use urlsession.shared method.
The cURL command is:
curl https://api.goshippo.com/addresses/ \
-H "Authorization: ShippoToken shippo_test_Token_Here" \
-d name="Shawn Ippotle" \ -d company="Shippo" \
-d street1="215 Clayton St." \
-d street2="" \
-d city="San Francisco" \
-d state="CA" \
-d zip=94117 \
-d country="US" \
-d phone="+1 555 341 9393" \
-d email="shippotle#goshippo.com"\
-d is_residential=True\
-d metadata="Customer ID 123456"

what about something like:
// preparing http request
let yourUrl = URL(string: "https://api.goshippo.com/addresses/")! // whatever is your url
let yourAuthorizationToken = "ShippoToken shippo_test_Token_Here" // whatever is your token
let yourPayload = Data() // whatever is your payload
var request = URLRequest(url:yourUrl)
request.httpMethod = "POST"
request.setValue(yourAuthorizationToken, forHTTPHeaderField: "Authorization")
request.httpBody = yourPayload
// executing the call
let session = URLSession(configuration: URLSessionConfiguration.default)
let task = session.dataTask(with: request, completionHandler: {data, response, error -> Void in
// your stuff here
})
task.resume()

Here it is using Alamofire. I hope this helps. I have tested it and it works.
func mainRequest(){
var headers: HTTPHeaders = [
"content-type": "application/json"
]
let credentials = "shippo_test_8dc780c66d5dae1c42868596c0b359ba89108df9"
headers["Authorization"] = "ShippoToken \(credentials)"
var parameters:Parameters = [String : Any]()
parameters["name"] = "Shawn Ippotle"
parameters["company"] = "Shippo"
parameters["street1"] = "215 Clayton St."
parameters["street2"] = ""
parameters["city"] = "San Francisco"
parameters["state"] = "CA"
parameters["zip"] = "94117"
parameters["phone"] = "+1 555 341 9393"
parameters["country"] = "US"
parameters["email"] = "shippotle#goshippo.com"
parameters["is_residential"] = "True"
parameters["metadata"] = "Customer ID 123456"
let url = "https://api.goshippo.com/addresses"
Alamofire.request(url, method: .post, parameters: parameters , encoding: JSONEncoding.default, headers: headers)
.responseJSON { (response) in
switch response.result {
case .success(let value):
let swiftyJson = JSON(value)
print ("return as JSON using swiftyJson is: \(swiftyJson)")
case .failure(let error):
print ("error: \(error)")
}
}
}

Related

Curl command works on CLI but Alamofire fails to authenticate

Here is the curl command that works:
curl -d {"Key1":"value1"} -k -vvvv --request POST --header "Content-Type: application/json" --key KEY.pem --cacert CRT.pem --cert KEY.pem "URL"
How do I translate this to Alamofire request command? I get the authentication failure errors:
Connection 1: default TLS Trust evaluation failed(-9807)
2020-04-09 01:51:46.604692-0600 CertificatePinningExample[7192:1891639] Connection 1: TLS Trust encountered error 3:-9807
2020-04-09 01:51:46.604879-0600 CertificatePinningExample[7192:1891639] Connection 1: encountered error(3:-9807)
2020-04-09 01:51:46.606672-0600 CertificatePinningExample[7192:1891639] Connection 1: unable to determine interface type without an established connection
2020-04-09 01:51:46.650936-0600 CertificatePinningExample[7192:1891639] Task <9E539D4B-9694-426E-B382-6350044743B0>.<1> HTTP load failed, 0/0 bytes (error code: -1202 [3:-9807])
2020-04-09 01:51:46.662507-0600 CertificatePinningExample[7192:1891652] Task <9E539D4B-9694-426E-B382-6350044743B0>.<1> finished with error [-1202] Error Domain=NSURLErrorDomain Code=-1202 "The certificate for this server is invalid. You might be connecting to a server that is pretending to be “BLAH” which could put your confidential information at risk." UserInfo={NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, NSErrorPeerCertificateChainKey=(
// Alamofire code to fire up the request
override func viewDidLoad() {
super.viewDidLoad()
let evaluators = [
"SERVER_IP":
PinnedCertificatesTrustEvaluator(certificates:
getCertificates()
)
]
let session = Session(
serverTrustManager: ServerTrustManager(evaluators: evaluators)
)
let url = URL(string: "URL_TO_HIT")!
//enableCertificatePinning()
var dict: NSDictionary = ["SessionId":""]
var data: Data = Data.init()
do {
data = try JSONSerialization.data(withJSONObject: dict, options: [])
} catch{
data = "".data(using: .utf8)!
}
//let data = try JSONSerialization.data(withJSONObject: dict, options: [])
let request = AF.request(url, method: .post)
// 2
request.responseJSON { (data) in
print(data)
}
}
private func getCertificates() -> [SecCertificate] {
let url = Bundle.main.url(forResource: "ExampleCert", withExtension: "der")!
let localCertificate = try! Data(contentsOf: url) as CFData
guard let certificate = SecCertificateCreateWithData(nil, localCertificate)
else { return [] }
return [certificate]
}
My concern is: Alamofire is asking for only one cert vs I am using three cert flags in curl. How do I translate Alamofire request to the same as curl?

Use groovy-wslite make a post request from CURL request

Hi i have the following CURL request that i'd like to include in a groovy script using the groovy-wslite library put struggling to get the request working.
curl -s -X POST -k -u user:password https://artifactory_url/api/search/aql -H 'Content-Type: text/plain' -d 'items.find({"type":"file","repo":{"$eq": "my-repo-name"},"path":{"$match":"com/mycompany/product1/subcat/mob/*"},"name":{"$match":"*apk"}}).sort({"$desc":["path"]}).limit(1)'
You can use http-builder-ng and your code could look like
compile 'io.github.http-builder-ng:http-builder-ng-CLIENT:1.0.4'
HttpBuilder.configure {
request.uri = 'https://artifactory_url/api/search/aql'
request.auth.basic 'un', 'pw'
request.contentType = 'text/plain'
}.post {
request.body = 'items.find({"type":"file","repo":{"$eq": "my-repo-name"},"path":{"$match":"com/mycompany/product1/subcat/mob/*"},"name":{"$match":"*apk"}}).sort({"$desc":["path"]}).limit(1)'
response.success { FromServer fs, Object body ->
println body
}
}

How do I upload an image to Parse Server using Kotlin/Jvm via Rest Service?

I'm creating a Kotlin/Jvm (without Android Sdk) application that interacts with a instance of a Parse Server (Back4App). Unfortunately, parse doesn't provide a Sdk implementation to use with Java/Kotlin without Android.
So I'm using the rest Api. Now I trying to upload a image from my disk into Back4App file server. In the doc there is snippet using curl. But I wasn't able to translate into a Retrofit service:
curl -X POST \
-H "X-Parse-Application-Id: 4MGgDJ0ZiQloXoSTE2I9VM6YUYIz8EwCKF4pK7zr" \
-H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
-H "Content-Type: image/jpeg" \
--data-binary '#myPicture.jpg' \
https://YOUR.PARSE-SERVER.HERE/parse/files/pic.jpg
So I based my implementation in this article and other snippets from GitHub and created a retrofit service for it:
#Multipart
#POST("/parse/files")
fun upload(
#Part file: MultipartBody.Part
): Call<ResponseBody>
And call:
var file = File("assets/escudo.png")
var requestFile = RequestBody.create(MediaType.parse("**/image"), file)
var body = MultipartBody.Part.createFormData("picture", file.name, requestFile)
var r = getService().upload(body).execute()
I created the retrofit instance as below:
fun getService(): ParserService {
val retrofit = Retrofit
.Builder()
.baseUrl("https://parseapi.back4app.com")
.addConverterFactory(GsonConverterFactory.create())
.client(createClient()).build()
return retrofit.create(ParserService::class.java)
}
fun createClient(): OkHttpClient {
return OkHttpClient.Builder().addInterceptor(createHeadInterceptor()).build()
}
fun createHeadInterceptor(): Interceptor {
return HeaderInterceptor()
}
class HeaderInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response =
chain.run {
val credentials = CredentialsUtils.readCredentials()
log.info { credentials }
proceed(
request().newBuilder()
// .addHeader("Content-Type", "application/json")
.addHeader("Content-Type", "image/png")
.addHeader("X-Parse-Application-Id", credentials.back4appAppId)
.addHeader("X-Parse-REST-API-Key", credentials.back4appRestApiKey)
.build()
)
}
}
I was able to use it to posting Json data (by uncommenting the content/type header). But when I tried to upload an image I receive this response:
Response{protocol=h2, code=400, message=, url=https://parseapi.back4app.com/parse/files}
More info:
-- EDIT
I tried a different approuch without Retrofit, it gives a 201 response code and gives me an objectId, but it doesn't upload the file:
val file2 = File("assets/escudo.png")
val serverUrl = "https://parseapi.back4app.com/classes/myfiles"
val url = URL(serverUrl)
val conn = url.openConnection() as HttpURLConnection
conn.requestMethod = "POST"
conn.doOutput = true
val postData = file2.readBytes()
conn.addRequestProperty("Content-length", postData.size.toString())
conn.setRequestProperty("Content-Type", "image/*")
conn.setRequestProperty("X-Parse-Application-Id", credentials.back4appAppId)
conn.setRequestProperty("X-Parse-REST-API-Key", credentials.back4appRestApiKey)
val outputStream = DataOutputStream(conn.outputStream)
outputStream.write(postData)
outputStream.flush()
println(conn.responseCode)
-- EDIT
Trying now using Khttp:
val file = File("assets/foto.jpg")
val file2 = File("assets/escudo.png")
val serverUrl = "https://parseapi.back4app.com/classes/myfiles"
val files = listOf(FileLike("foto.jpg", file), FileLike("escudo.png", file2))
val response = post(serverUrl, headers = getHeaders(), files = files)
println(response)
println(response.text)
}
fun getHeaders(): Map<String, String> {
return mapOf(
"Content-Type" to "image/*",
"X-Parse-Application-Id" to credentials.back4appAppId,
"X-Parse-REST-API-Key" to credentials.back4appRestApiKey
)
}
Getting this error:
<Response [400]>
{"error":"Unexpected token - in JSON at position 0"}
If you're using Back4App, the correct Server URL is:
https://parseapi.back4app.com/files/pic.jpg

send msg to Azure service bus que via REST

The Azure Queues are exposed to REST API.To make the REST call works. I ran a sample test on POSTMAN. The POST call
https://yournamespace.servicebus.windows.net/yourentity/messages
Also, Passing below 2 headers and values.
Header 1:
Authorization: SharedAccessSignature sr=https%3A%2F%2F.servicebus.windows.net%2Fyourentity&sig=yoursignature from code above&se=1529928563&skn=KeyName
Example:
SharedAccessSignature sr=https%3A%2F%2Fservicebussoatest1.servicebus.windows.net%2Fpublishque&sig=a0wmRklbCGFCYoSCViij9gagtZV9Bg+vU=&se=1529928563&skn=testpolicy
Header 2:
Content-Type: application/json
But even though I have passed the correct Authorization value, I am getting the error below:
401:Invalid Authorization Token signature
401:Invalid Authorization Token signature
According to the 401 error meanings that the token is not vaild.
Firstly please make sure that your policy has access to send the message.
Secondly, if you want to use the azure service bus Send Message Rest APi. The format should be following.
POST https://<yournamespace>.servicebus.windows.net/<yourentity>/messages
Authorization: SharedAccessSignature sr=https%3A%2F%2F<yournamespace>.servicebus.windows.net%2F<yourentity>&sig=<yoursignature from code above>&se=1438205742&skn=KeyName
ContentType: application/atom+xml;type=entry;charset=utf-8
We also could get more information about Service Bus access control with Shared Access
Signatures from this article.
I also do a demo with postman. It works correctly on my side.
I use the following code to get the SAS token.
public static string GetSasToken(string resourceUri, string keyName, string key, TimeSpan ttl)
{
var expiry = GetExpiry(ttl);
string stringToSign = HttpUtility.UrlEncode(resourceUri) + "\n" + expiry;
HMACSHA256 hmac = new HMACSHA256(Encoding.UTF8.GetBytes(key));
var signature = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign)));
var sasToken = String.Format(CultureInfo.InvariantCulture, "SharedAccessSignature sr={0}&sig={1}&se={2}&skn={3}",
HttpUtility.UrlEncode(resourceUri), HttpUtility.UrlEncode(signature), expiry, keyName);
return sasToken;
}
private static string GetExpiry(TimeSpan ttl)
{
TimeSpan expirySinceEpoch = DateTime.UtcNow - new DateTime(1970, 1, 1) + ttl;
return Convert.ToString((int)expirySinceEpoch.TotalSeconds);
}
string queueUrl = "https://tomtestsb.servicebus.windows.net/" + "queue" + "/messages";
string token = GetSasToken(queueUrl,"Key", "value", TimeSpan.FromDays(1));
We could get the key and value with Azure portal
Test it with Postman.
Headers:
Authorization:SharedAccessSignature sr=https%3a%2f%2fyournamespace.servicebus.windows.net%2fqueuename%2fmessages&sig=SyumAUNnqWFjW2MqjwlomU%2fbblqZljq6LPJp3jpfU%2b4%3d&se=1529478623&skn=KeyName
Content-Type:application/xml
Body
<string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">This is a message.</string>
Test Result:
Simple 2 step process:
First you can import the below Curl in postman:
curl --location --request POST '{{topicQueueForAzureServiceBusUri}}' \
--header 'ContentType: application/atom+xml;type=entry;charset=utf-8' \
--header 'Authorization: {{SasTokenForAzureServiceBus}}' \
--header 'Content-Type: application/json' \
--data-raw '{"YOUR JSON"}'
Copy All the key generation script to the prerequisite step of Postman Request and replace first four variable values from your topic/queue config:
var namespace = "YOUR Namespace";
var topicQueueName = "YOUR TOPIC/QUEUE Name";
var sharedAccessKeyName = "YOUR sharedAccessKeyName";
var sharedAccessKey = "YOUR sharedAccessKey";
var topicQueueForAzureServiceBusUri = "https://" + namespace + ".servicebus.windows.net/" + topicQueueName + "/messages";
pm.collectionVariables.set("topicQueueForAzureServiceBusUri", topicQueueForAzureServiceBusUri);
var sasToken = createSharedAccessToken(topicQueueForAzureServiceBusUri, sharedAccessKeyName, sharedAccessKey);
pm.collectionVariables.set("SasTokenForAzureServiceBus", sasToken);
function createSharedAccessToken(uri, saName, saKey) {
if (!uri || !saName || !saKey) {
throw "Missing required parameter";
}
var encoded = encodeURIComponent(uri).toLowerCase();
var now = new Date();
var week = 60*60*24*7;
var ttl = Math.round(now.getTime() / 1000) + week;
var signature = encoded + '\n' + ttl;
var hash = CryptoJS.HmacSHA256(signature, saKey);
var hashInBase64 = CryptoJS.enc.Base64.stringify(hash);
return 'SharedAccessSignature sr=' + encoded + '&sig=' +
encodeURIComponent(hashInBase64) + '&se=' + ttl + '&skn=' + saName;
}
This worked for me:
The url to POST to: https://[ServiceBusNamespace].servicebus.windows.net/[QueueName]/messages
Authorization: use code provided by Tom Sun - MSFT
Content-Type: application/json

Convert REST curl command to Apex Request

I have a curl example of a REST API call as follows
curl https://api.endpoint.com/api_action.json \
-u key:secret \
-d 'message=Hello World' \
-d id=12345
Now I'm trying to replicate this using the Apex HttpRequest class but I'm unsure as to how I should be passing the options into the call.
What I have so far is as follows
HttpRequest req = new HttpRequest();
req.setEndpoint(https://api.endpoint.com/api_action.json);
req.setMethod('POST');
// what goes in these
req.setHeader(stuff);
req.setBody(stuff);
Http http = new Http();
HttpResponse res = http.send(req);
From #sfdcfox answer
Given this comment:
curl -i -X POST --data "first_name=test&last_name=testlast&email=test#test.com&phone=1234567&company=Ac‌​meInc&years_in_business=3&amount=4000&dealer_id=524b2833f7494317db000001" https://example.com/webform/start/
The code you need would be approximately:
String payload='first_name=test&last_name=testlast&email=test#test.com&phone=1234567&company=Ac‌​meInc&years_in_business=3&amount=4000&dealer_id=524b2833f7494317db000001';
HttpRequest req = new HttpRequest();
req.setMethod('POST');
req.setEndpoint('https://example.com/webform/start/');
req.setHeader('Content-Type','application/x-www-form-urlencoded');
req.setHeader('Content-Length',String.valueOf(payload.length()));
req.setBody(payload);
Http binding = new Http();
HttpResponse res = binding.send(req);
When you are using APEX, you can use the build-in apex_web_service.make_rest_request to do this:
declare
v_clob clob;
begin
v_clob := apex_web_service.make_rest_request(
p_url => <your URL>,
p_http_method => 'POST',
p_body => <whatever body>,
p_username => 'username',
p_password => 'password' );
htp.p(v_clob);
end;