I have a static function which I use to retrieve a configuration file through AFNetworking library, as follows:
static func getConfiguration(success: NetworkServiceSuccessBlock, failure: NetworkServiceFailureBlock) -> AFHTTPSessionManager? {
let sessionManager = AFHTTPSessionManager(sessionConfiguration: NSURLSessionConfiguration.defaultSessionConfiguration())
sessionManager.requestSerializer.cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringLocalCacheData
sessionManager.GET(getConfigurationUrl()!, parameters: nil, progress: nil, success: success, failure: failure)
return sessionManager
}
I need to check the server SSL certificate domain to be the proper one, something like challenge.protectionSpace.host for NSURLSession, and I need the check to determine whether the GET request will fail or not.
EDIT: I don't want to perform SSL Pinning with certificates stored in the app bundle, it is enough for me to verify the server certificate domain is correct.
Can someone point me in the right direction to perform this?
Related
I'm trying to implement Google Vision API for the my app via REST.
https://cloud.google.com/vision/docs/pdf
Is there any examples or any suggestions how to do this?
Documentation says that they require service account token but can't find any examples how to get service account token from iOS app side.
I've tried via GTMAppAuth but getting 403 error
I was able to generate this token from my mac machine and all worked, but token has limited life time and after 3-4 hours it expiries
I figured out and did this by my own.
Documentation:
https://developers.google.com/identity/protocols/OAuth2ServiceAccount#jwt-auth
1)You need to create service account, download p12 or JSON file. JSON file contains private key and public certificate url. You need to create text file, where private key on the top, certificate on the bottom and run this command:
openssl pkcs12 -export -in file_name.txt -out file_name.p12
2)Create jwt token with parameters which described in the documentation (for jwt I used this library: https://github.com/yourkarma/JWT)
3) Make POST request to https://www.googleapis.com/oauth2/v4/token with parameters which described in the documentation
Hope it will help somebody in the future
First you need to get a Bearer Token... You can get it by following the instructions on this page...
https://cloud.google.com/vision/docs/auth#using_an_api_key
The Bearer Token will not expire, you can implemented it to your code...
A Basic Version on how to add a Bearer Token to your Request in Swift is Shown below... All other Stuff goes as a JSON in Data Format to the "body" parameter
This Link will show you how to build up the JSON for your request to Vision... https://cloud.google.com/vision/docs/using-curl
class APIHandler {
private let API_TOKEN = "Your Token"
func requestVisionFromREST(body: Data, completion: #escaping (_ response: Data?)-> Void) {
let config = URLSessionConfiguration.default
var headers = [String:String]()
headers["Authorization"] = "Bearer \(API_TOKEN)"
config.httpAdditionalHeaders = headers
var urlRequest = URLRequest(url: URL(string: "YOUR URL")!)
urlRequest.httpMethod = "POST"
urlRequest.httpBody = body
URLSession(configuration: config).dataTask(with: urlRequest) { (data, response, error) in
completion(data)
}.resume()
}
}
EDIT
If you want to use the Google Sign In instead of the OAuth Method, to reauth your apps and get a fresh token, you can follow the instructions on the googles instructions page below:
https://developers.google.com/identity/sign-in/ios/offline-access
I have a HTTP REST server with a self-signed certificate. I want to talk to this server from an iOS Swift app, using alamofire. The current code that I have is:
```
let Almgr : Alamofire.SessionManager = {
// Create the server trust policies
let serverTrustPolicies: [String: ServerTrustPolicy] = [
"localhost": .disableEvaluation
]
// Create custom manager
let configuration = URLSessionConfiguration.default
configuration.httpAdditionalHeaders = Alamofire.SessionManager.defaultHTTPHeaders
let man = Alamofire.SessionManager(
configuration: URLSessionConfiguration.default,
serverTrustPolicyManager: ServerTrustPolicyManager(policies: serverTrustPolicies)
)
return man
}()
Almgr.request(url, method: .post, parameters: params).responseJSON {
response in
if response.result.isSuccess {
print("Success")
} else {
print("Failure")
}
}
With the above code snippet, I am always getting an error when I try to make the http call Almgr.request. The error message is:
2017-12-30 18:24:20.114486+0530 myApp[58036:2721102] ATS failed system trust
2017-12-30 18:24:20.114625+0530 myApp[58036:2721102] System Trust failed for [1:0x600000178a80]
2017-12-30 18:24:20.114814+0530 myApp[58036:2721102] TIC SSL Trust Error [1:0x600000178a80]: 3:0
2017-12-30 18:24:20.115142+0530 myApp[58036:2721102] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802)
2017-12-30 18:24:20.115274+0530 myApp[58036:2721102] Task <4E3D9E88-B9CE-48C4-850C-5A3E7C9A6A72>.<1> HTTP load failed (error code: -1200 [3:-9802])
2017-12-30 18:24:20.115469+0530 myApp[58036:2721231] Task <4E3D9E88-B9CE-48C4-850C-5A3E7C9A6A72>.<1> finished with error - code: -1200
Any idea how to get this fixed ? I do not want any checks to be done if the url is localhost on port 8000. I have even tried with adding port to the serverTrustPolicies definition but that does not make any difference and I still get error. Any help ?
Update: My problem I believe is related to https://developer.apple.com/library/content/qa/qa1948/_index.html but not found the way to fix yet.
1.
Your approach of modifying server trust policies should work when providing the port. Also see this post. Maybe you are testing your app with the simulator and trying to connect to a web server on the same machine? This can cause all kinds of connection problems (or why are you trying to connect to localhost anyway?).
2.
You should never set NSAllowsLocalNetworking or similar parameters. It breaks SSL and you never know what may happen, even in the local network. If absolutely necessary, you should just make exceptions for single hosts and ports as stated above.
3.
You should never use self signed certificates because this also breaks SSL. It is very easy to obtain a valid certificate using Let's Encrypt. Though, in some cases it is just not possible to obtain a valid certificate. You should then create your own certificate authority and export the CA root certificate to your device. This way, you also make an exception for only one specific host.
Please note that security is crucial in all applications. Please only make exceptions if you exactly know what you are doing.
I have figured out the solution. We need to edit the Info.plist file and add the following section:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsLocalNetworking</key>
<true/>
</dict>
to let iOS allow local networking without https errors.
I was facing same problems. What I figured out and what works:
let almgr:Alamofire.SessionManager = {
//getcertificates is my own method where i create certificate with data from my .cer file
let certificates = getCertificates()
let trustPolicy = ServerTrustPolicy.pinCertificates(certificates: certificates, validateCertificateChain: true, validateHost: true)
// Here was the problem. I had to modify that dict: (localhost with port and with .disableEvaluation)
let serverTrustPolicies = ["liper:8000":trustPolicy, "liper":.disableEvaluation]
let serverTrustPolicyManager = ServerTrustPolicyManager(policies: serverTrustPolicies)
let configuration = URLSessionConfiguration.default
configuration.httpAdditionalHeaders = Alamofire.SessionManager.defaultHTTPHeaders
let man = Alamofire.SessionManager(configuration: URLSessionConfiguration.default, serverTrustPolicyManager: serverTrustPolicyManager)
return man
}()
I apologize in advance if i made a duplicate of this question. I'm working on a mobile app that communicates with an API on a .local domain. I'm stuck at trying to establish a secure connection (using Alamofire). I Tried to write a custom ServerTrustPolicy just as Alamofire wiki suggests, and include my certificate chain, but it always fails. The only way to get the app to connect is by configuring App Transport Security in .plist file and adding the domain as exception. Does anyone have experiance with using certificates trough alamofire with .local domains? Would really appreciate any advice. Thank you in advance.
This is my Alamofire.Manger:
static let sharedInstance: Manager = {
var apiObject = ApiBaseService()
//let domainName = apiObject.getDomain()
let pathToCert = NSBundle.mainBundle().pathForResource("certca", ofType: "der")
let localCertificate:NSData = NSData(contentsOfFile: pathToCert!)!
let serverTrustPolicy = ServerTrustPolicy.PinCertificates(
certificates: ServerTrustPolicy.certificatesInBundle(),
validateCertificateChain: true,
validateHost: true
)
let value = GlobalConstants.APICallErrorLocations.actionsGetActions
let serverTrustPolicies: [String: ServerTrustPolicy] = [
"test.ex01.local": serverTrustPolicy
]
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
configuration.HTTPAdditionalHeaders = Alamofire.Manager.defaultHTTPHeaders
configuration.timeoutIntervalForRequest = NSTimeInterval(12.0)
return Manager(configuration: configuration,
serverTrustPolicyManager: ServerTrustPolicyManager(policies: serverTrustPolicies))
}()
Am I doing something wrong?
Kristian
EDIT:
The thing was TLS version was v1.0 which i discovered with this command:
openssl s_client -connect test.ex01.local:5000
After updating TLS version and including certificate chain in ServerTrustPolicy object, everything works.
Hope this will help someone in the future.
If your certificate chain is not valid and is self-signed (assuming this is the case), then you MUST disable ATS for that host, or you'll never be able to use the Alamofire cert pinning logic. ATS will never even give Alamofire a chance to evaluate the cert chain.
The reason for this behavior is that ATS first evaluates the connection challenge before giving the NSURLSessionDelegate a chance to evaluate the challenge. If ATS evaluates the properties of the challenge and determines it should not trust the connection, it stops there and the request will not succeed. It doesn't call the NSURLSessionDelegate to give you a second chance to override it.
However, when you disable ATS, the first ATS checks no longer happen, and the challenge is sent to the NSURLSessionDelegate for evaluation which is where Alamofire kicks in.
AppTransportSecurity is used to list connections that you want that are not HTTPS. iOS blocks non HTTPS connections that are not in this list!
I'm new to Alamofire so I'm sorry if this it's a noob question: this framework stores the cookies automatically?
This is because I have a simple request like this:
Alamofire.request(.POST, loginURL, parameters: ["fb_id": fbId, "fb_access_token": fbToken])
.responseJSON { response in
//print(response.request) // original URL request
//print(response.response) // URL response
//print(response.data) // server data
//print(response.result) // result of response serialization
if let JSON = response.result.value {
print("loginURL - JSON: \(JSON)")
}
}
this request response with a cookie session that I need to do other requests for security reason; the strange thing is that like magic I already can do the other requests after this first POST without read manually the cookie and store it. I'm sure the other requests need the cookie session because they fail on postman for example but not here.
It's just a feature? Because I can't find anything on that also on the official GitHub page.
Yes! Alamofire is basically a wrapper around NSURLSession. Its manager uses a default NSURLSessionConfiguration by calling defaultSessionConfiguration().
As its github page says under Advanced Usage section:
Alamofire is built on NSURLSession and the Foundation URL Loading System. To make the most of this framework, it is recommended that you be familiar with the concepts and capabilities of the underlying networking stack.
And under Manager section:
Top-level convenience methods like Alamofire.request use a shared instance of Alamofire.Manager, which is configured with the default NSURLSessionConfiguration.
And the NSURLSessionConfiguration reference for defaultSessionConfiguration() says:
The default session configuration uses a persistent disk-based cache (except when the result is downloaded to a file) and stores credentials in the user’s keychain. It also stores cookies (by default) in the same shared cookie store as the NSURLConnection and NSURLDownload classes.
For those who use Moya and want to disable stored cookies
(fixing the X-CSRF-Token request header is missing)
Very basic example:
public final class DisableCookiePlugin: PluginType {
public init() {
}
public func prepare(_ request: URLRequest, target: TargetType) -> URLRequest {
var mutableRequest = request
mutableRequest.httpShouldHandleCookies = false
return mutableRequest
}
}
And then use it
MoyaProvider<Api>(
plugins: [
//NetworkLoggerPlugin(configuration: .init(logOptions: .verbose)),
DisableCookiePlugin()
]
I'm trying to download an image (jpeg) from a given URL using this code:
let manager = AFHTTPRequestOperationManager()
manager.responseSerializer = AFImageResponseSerializer()
manager.responseSerializer.acceptableContentTypes = NSSet(array: ["application/octet-stream"]) as Set<NSObject>
manager.GET(imageURL, parameters: nil,
success: { (operation: AFHTTPRequestOperation!, responseObject: AnyObject!) -> Void in
println("IMAGE SUCCESS")
}, failure: { (operation: AFHTTPRequestOperation!, error: NSError!) in
println("IMAGE FAIL")
})
With my internet at the moment, the success block is being called when testing on the simulator. However, the failure block is being called when testing on an iPhone. On another network, the success block is always called on both iPhone and simulator.
I can't figure out what's causing the issue. I have tried setting the Content-Type to "image/jpeg" but still got the same results. Did I miss something?
The error is:
Error Domain=NSURLErrorDomain Code=-1001 "The request timed out." UserInfo=0x1675f381 {NSErrorFailingURLStringKey=<...>, NSErrorFailingURLKey=<...>, NSLocalizedDescription=The request timed out., NSUnderlyingError=0x166c7891 "The request timed out."}
Are you requesting many images (or doing many network requests)? If so, you might want to constrain the AFHTTPRequestOperationManager:
manager.operationQueue.maxConcurrentOperationCount = 5
If you don't do this and then proceed to initiate many requests, the "timeout" starts when the network requests are enqueued even though NSURLConnection (which AFHTTPRequestOperationManager uses behind the scenes) can only run 4 or 5 at a time and the latter requests may not even start until earlier ones finish. You want to start the timeout logic for network requests on the basis of when the request starts, not when the request was enqueued. By constraining the maxConcurrentOperationCount, you're ensuring that network requests aren't initiated before they can reasonably be performed.
Note, that presumes that you use the same AFHTTPRequestOperationManager for all of your requests, not instantiating new operation managers for new requests. But this will ensure that requests aren't started until the backlog of other requests has been reduced to an acceptable level.
--
As an aside, rather than replacing acceptableContentTypes, I'd merely suggest augmenting it:
var acceptableContentTypes = manager.responseSerializer.acceptableContentTypes ?? Set<NSObject>()
acceptableContentTypes.insert("application/octet-stream")
manager.responseSerializer.acceptableContentTypes = acceptableContentTypes
Frankly, I'd rather see you fix the Content-Type of the images you're retrieving from the server so that it didn't send application/octet-stream responses, but if you can't do that, the above is probably a more prudent way to adjust the content types. This ensures that you accept both your custom application/octet-stream type, as well as the standard image types.