Failed to apply certificate pinning with Alamofire 5.0.2 - ssl-certificate

I am migrating my app to use Alamofire 5.0.2, in the past version it uses Alamofire 4.x and the certificate pinning is working fine.
Then I migrated Alamofire and its certificate pinning configuration with these changes:
//Usage example of the function `defaultSessionManager`
class ViewController: UIViewController {
let sessionManager = defaultSessionManager(defaultRequestInterceptor())
//...
}
private func defaultSessionManager(_ requestInterceptor: RequestInterceptor?) -> Alamofire.Session {
let evaluators: [String: ServerTrustEvaluating] = [
"https://myapp.com": PinnedCertificatesTrustEvaluator(certificates: pinnedCertificates()),
]
let configuration: URLSessionConfiguration = URLSessionConfiguration.af.default
configuration.timeoutIntervalForRequest = 10 // seconds
configuration.timeoutIntervalForResource = 10 // seconds
return Alamofire.Session(
configuration: configuration,
interceptor: requestInterceptor,
serverTrustManager: ServerTrustManager(evaluators: evaluators))
}
func pinnedCertificates() -> [SecCertificate] {
var certificates: [SecCertificate] = []
let directoryContents: [URL] = //...
let certificateName: String = "app.cer" // Replaced for the demo
let pinnedCertificateURL: URL? = directoryContents.first { (url: URL) in url.lastPathComponent == certificateName }
if let pinnedCertificateURL: URL = pinnedCertificateURL {
do {
let pinnedCertificateData: CFData = try Data(contentsOf: pinnedCertificateURL) as CFData
if let pinnedCertificate: SecCertificate = SecCertificateCreateWithData(nil, pinnedCertificateData) {
certificates.append(pinnedCertificate)
}
} catch {
//...
}
}
return certificates
}
With the solution above, I am getting the error:
MyApp[374:21470] Task <DDC8F9FD-81A3-EBA4-8AA2-D7C99DD3E63B>.<1> HTTP load failed, 0/0 bytes (error code: -999 [1:89])
If I remove the line serverTrustManager: ServerTrustManager(evaluators: evaluators)), Alamofire works but without certificate pinning.
Any idea how to solve this and what I am doing wrong?
Thank you.

The String in your evaluator mapping should only be the host, not a full url:
let evaluators: [String: ServerTrustEvaluating] = [
"myapp.com": PinnedCertificatesTrustEvaluator(certificates: pinnedCertificates()),
]
Also, you need to make sure the host exactly matches the domains you're making requests against.
Additionally, Alamofire will automatically find certificates in your bundle so you may not need to find it yourself.

Related

Example of URL builder in Ktor

I'm using Ktor client to make calls to an API and I didn't find any examples of how to construct a URL with query parameters.
I wanted something like this:
protocol = HTTPS,
host = api.server.com,
path = get/items,
queryParams = List(
Pair("since", "2020-07-17"),
)
I can't find any examples of how to use URL builder for this.
If you want to specify each of this element (protocol, host, path and params) separately you can use a HttpClient.request method to construct your url. Inside this method you have access to HttpRequestBuilder and then you can configure url with usage of UrlBuilder
client.request<Response> {
url {
protocol = URLProtocol.HTTPS
host = "api.server.com"
path("get", "items")
parameters.append("since", "2020-07-17")
}
}
Response type is your response, you can specify there whatever you need
It would also be helpful if someone wants to add a base URL to all their requests :
HttpClient(Android) {
expectSuccess = false
//config Client Serialization
install(JsonFeature) {
serializer = KotlinxSerializer(json)
}
//config client logging
install(Logging) {
level = LogLevel.BODY
}
//Config timeout
install(HttpTimeout) {
requestTimeoutMillis = 30 * 1000L
connectTimeoutMillis = 10 * 1000L
}
//Config Base Url
defaultRequest {
url {
protocol =URLProtocol.HTTPS
host = baseUrl
}
}
}
val json = kotlinx.serialization.json.Json {
ignoreUnknownKeys = true
isLenient = true
encodeDefaults = false
}

Alamofire.download by post with parameters not working

Hello I am doing download file by post with parameters. But server can't receive post parameters.
But if i do same thing with get with url parameters. Everything works fine.
Almofire.request also works fine by post with parameters. But only Almofire.download by post with parameter does not work.
Why Alamofire.download does not send paramters by post method ??
var sourceStringURL : String = "\(tmp_url)download"
let destination: DownloadRequest.DownloadFileDestination =
{
_, _ in
let fileURL = URL(fileURLWithPath: destPath)
return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
}
Alamofire.download(sourceStringURL, method: .post, parameters: ["id": idStr, "var": varStr], encoding: JSONEncoding.default, headers: nil, to: destination)
.downloadProgress
{
progress in
var tmpPercent : Int = Int(progress.fractionCompleted*100 / 1.0)
}
.response
{
response in
if let error = response.error
{
print(error)
}
else
{
//success
}
}
Server receives post request correctly with Retrofit library in Android.
I just found that if i change JSONEncoding.default to URLEncoding.default.
It works fine.

Setting Up Permissions with Realm Object Server for shared Realms

I am trying to set up a shared realm, which all users have access to. I also intend for users to create realms as required, which represent projects. I would like to give the user permissions to read and write any project realms that they have created, however have read access to all of the other users' realms. I would also like the ability to assign write permissions as required, to other users, without giving them admin status in the Realm Object Server.
I am thinking that my application will allow a user to login with minimal permissions, and having a second admin user working in the background, for managing permissions. The admin user would not be exposed to the user.
I have been following an example provided at https://github.com/realm-demos/realm-teamwork-MR, however haven't had any success with setting up permissions. My test case is as follows:
import UIKit
import RealmSwift
import Realm
let ApplicationName = "SyncTest"
let syncHost = "127.0.0.1" // The realm-oject-server is hosted on AWS, however changed for this example to keep user data private. HTTPS has also been implemented.
let syncAuthURL = URL(string: "https://\(syncHost):9443")!
let commonRealmURL:URL = URL(string: "realms://\(syncHost):9443/\(ApplicationName)-CommonRealm")!
class Dog: Object {
dynamic var name = ""
dynamic var age = 0
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
updateUserPermissions()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
private func updateUserPermissions() {
// Create the callback block that will perform the request
let logInBlock: ((SyncCredentials) -> Void) = { credentials in
SyncUser.logIn(with: credentials, server: syncAuthURL, timeout: 30, onCompletion: { (user, error) in
DispatchQueue.main.async {
// Display an error message if the login failed
if let error = error {
self.showError(title: "Unable to Sign In", message: error.localizedDescription)
return
}
guard let user = user else { return }
print("ID: \(String(describing: user.identity)), Total Users Logged In: \(SyncUser.all.count)")
let config = Realm.Configuration(syncConfiguration: SyncConfiguration(user: user, realmURL: commonRealmURL), objectTypes: [Dog.self])
let adminRealm:Realm = try! Realm(configuration: config)
let permission = SyncPermissionValue(realmPath: adminRealm.configuration.syncConfiguration!.realmURL.path,
username: "user#host.com",
accessLevel: .write)
user.applyPermission(permission) { error in
if let error = error {
self.showError(title: "Unable to Apply Permissions", message: error.localizedDescription)
return
}
}
let myDog = Dog()
myDog.name = "admin" + Date().description
myDog.age = 1
try! adminRealm.write {
adminRealm.add(myDog)
}
let results = adminRealm.objects(Dog.self)
print("Number of results after admin login: \(results.count)")
self.logInUser()
}
})
}
let credentials = SyncCredentials.usernamePassword(username: "admin#host.com", password: "admin", register: false)
logInBlock(credentials)
}
private func showError(title: String, message: String) {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self.present(alertController, animated: true, completion: nil)
}
private func logInUser() {
// Create the callback block that will perform the request
let logInBlock: ((SyncCredentials) -> Void) = { credentials in
SyncUser.logIn(with: credentials, server: syncAuthURL, timeout: 30, onCompletion: { (user, error) in
DispatchQueue.main.async {
// Display an error message if the login failed
if let error = error {
self.showError(title: "Unable to Sign In", message: error.localizedDescription)
return
}
guard let user = user else { return }
let config = Realm.Configuration(syncConfiguration: SyncConfiguration(user: user, realmURL: commonRealmURL), objectTypes: [Dog.self])
let userRealm = try! Realm(configuration: config)
let myDog = Dog()
myDog.name = "user" + Date().description
myDog.age = 2
try! userRealm.write {
userRealm.add(myDog)
}
let results = userRealm.objects(Dog.self)
print("Number of results after user login: \(results.count)")
}
})
}
let credentials = SyncCredentials.usernamePassword(username: "user#host.com", password: "user", register: false)
logInBlock(credentials)
}
}
Any ideas how I can successfully assign permissions with a background admin user? Or would I be better off using a different structure for my databases? Thanks!

AlamoFire with Swift 1.2: Ambiguous use of 'responseJSON'

I'm attempting to use AlamoFire with Swift 1.2 in XCode 6.3. I've fixed most of the problems (i.e. changing as to as!) but I have one that I can't figure out.
The following code - and snippets like it - generates a compile time error with the message "Ambiguous use of 'responseJSON'" at the line 5 ("req.responseJSON(){"). What do I need to change in the AlamoFire library or my code to fix it? Note: I imported the project as described in the documentation and it worked fantastic in Swift 1.1 and XCode 6.1.1
func theaters(delegate:GlobalNetworkingDelegate){
if let url = self.mainNetworkingUrl{
var urlToUse = url + "theaters"
var req:Request = Alamofire.request(.GET, urlToUse, parameters: [:], encoding: .URL)
req.responseJSON(){
(req, response, jsonOut, error) in
if(response.statusCode == 200 && error == nil){
var ajson = JSON(jsonOut!)
delegate.globalTheatersOutomce!(true, json: jsonOut, error: error)
}
}
}
}
I have also gotten the following to work:
Alamofire.manager.request(.PUT, pathWithId(user.key), parameters: user.toDict(), encoding: .JSON)
.responseString( completionHandler: {
(request: NSURLRequest, response: NSHTTPURLResponse?, responseBody: String?, error: NSError?) -> Void in
if responseBody == "OK" {
completion(user, nil)
} else {
completion(nil, error)
}
})
i.e. by explicitly stating the parameter name of the closure instead of letting it trail after the method paranthesis. It seems that the new compiler has a problem identifying the method otherwise.
Separating the trailing closure into its own variable and then calling resonseJSON(closure) fixes the problem, but I'm not sure why. Anyone have a reason? Here is the working code:
func theaters(delegate:GlobalNetworkingDelegate){
if let url = self.mainNetworkingUrl{
var urlToUse = url + "theaters"
var req:Request = Alamofire.request(.GET, urlToUse, parameters: [:], encoding: .URL)
var aClosure = {(req:NSURLRequest, response:NSHTTPURLResponse?, jsonOut:AnyObject?, error:NSError?) -> Void in
if(response!.statusCode == 200 && error == nil){
var ajson = JSON(jsonOut!)
delegate.globalTheatersOutomce!(true, json: jsonOut, error: error)
}
}
req.responseJSON(aClosure)
}
}
If you wrap the closure in () instead of leaving it trailing it works also. It works for the same reason as the other answers here, just another way to write it.
func theaters(delegate:GlobalNetworkingDelegate){
if let url = self.mainNetworkingUrl {
var urlToUse = url + "theaters"
var req:Request = Alamofire.request(.GET, urlToUse, parameters: [:], encoding: .URL)
req.responseJSON({
(req, response, jsonOut, error) in
if(response.statusCode == 200 && error == nil){
var ajson = JSON(jsonOut!)
delegate.globalTheatersOutomce!(true, json: jsonOut, error: error)
}
})
}
}
I ran into the same issue. Updating your Alamofire to the latest version (1.2.2 as the time I wrote the answer) solved the problem for me.

Missing argument for parameter #2 in call to Alamofire.request(URLRequestConvertible)

Trying to pass an Authentication header in, using the recommended approach of a custom URLRequestConvertible.
So here is my URLRequestConvertible object that conforms to the protocol:
class SecureJSONRouter : URLRequestConvertible {
var type: String
var token: String
var parameters: [String: AnyObject]
init(typevar: String, tokenvar: String, parametersvar: [String: AnyObject]) {
type = typevar
token = tokenvar
parameters = parametersvar
}
var URLRequest: NSURLRequest {
let URL = NSURL(string: da_url)!
let URLRequesting = NSMutableURLRequest(URL: URL.URLByAppendingPathComponent(type))
let encoding = Alamofire.ParameterEncoding.JSON
URLRequesting.setValue(token, forHTTPHeaderField: "Authorization")
return encoding.encode(URLRequesting, parameters: parameters).0
}
}
It's basically a place to store a few things, like a bit of URL, and a token, and a way to create a NSURLRequest with a full URL, the JSON parameters, and the header field for authorization.
When I try to call it like this:
let myUrlRequest: URLRequestConvertible = SecureJSONRouter(typevar: "locations", tokenvar: token!, parametersvar: parameters)
Alamofire.request(myUrlRequest).response{ (req, resp, data, error) in
if (error != nil) {
println(req)
println(resp)
} else {
println("saved \(data)")
}
}
it won't compile, gives me a "Missing argument for parameter #2 in call" error, at the request line.
Any ideas?
PS: I did have my SecureJSONRouter thingy as the recommended Enum instead of a Class, but I got the same error. I was looking at the protocol definition, and figured there's no reason it can't be a simpler (class) in my case, so I changed it. Still the same error.
I think there's a few possible culprits here. First off, are you sure you're calling Alamofire and not AlamoFire? I've seen people make that mistake before.
As for the sample code you posted, it didn't compile for a few different reasons. I couldn't reproduce exactly the same compiler errors you were seeing, but here's a slightly modified version of you original post that does compile.
class SecureJSONRouter : URLRequestConvertible {
var type: String
var token: String
var parameters: [String: AnyObject]
init(typevar: String, tokenvar: String, parametersvar: [String: AnyObject]) {
type = typevar
token = tokenvar
parameters = parametersvar
}
var URLRequest: NSURLRequest {
let URL = NSURL(string: "http://httpbin.org")!
let URLRequesting = NSMutableURLRequest(URL: URL.URLByAppendingPathComponent(type))
let encoding = Alamofire.ParameterEncoding.JSON
URLRequesting.setValue(token, forHTTPHeaderField: "Authorization")
return encoding.encode(URLRequesting, parameters: parameters).0
}
}
Then here's an example of calling your SecureJSONRouter.
let token: String? = "my_fancy_token"
let parameters: [String: AnyObject] = ["sample_parameter": "sample_parameter_value"]
let myUrlRequest: URLRequestConvertible = SecureJSONRouter(typevar: "locations", tokenvar: token!, parametersvar: parameters)
Alamofire.request(myUrlRequest).response{ (req, resp, data, error) in
if (error != nil) {
println(req)
println(resp)
} else {
println("saved \(data)")
}
}
That is compiling with Xcode 6.1.1 against the Alamofire 1.1.3 release. Hope that helps!