Auth Challenge Alamofire 2.0
Making two requests is how the underlying URL Loading System was designed by Apple. The Alamofire authenticate
methods simply allow you to provide the credentials to supply to the challenge if it occurs.
If you want to provide the header directly, the use the headers
parameter on the request
method. Additionally, you could insert the user:password
credentials directly into the URL.
Alamofire 4.9.1 sessionManager.delegate.sessionDidReceiveChallenge is not getting assigned in iOS 15
I managed to solve the issue by modifying the SessionDelegate.swift file of the Alamofire pod itself. Namely, I initialized the certificate in the SessionDelegate.swift file and passed its urlCredential property to the task delegate's credential property in
open func urlSession(
_ session: URLSession,
task: URLSessionTask,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
and voilà, it worked.
So I re-defined the class (PKCS12) that lets me initialize the certificate inside the SessionDelegate.swift file and the body of the above function now looks like this:
if let taskDidReceiveChallenge = taskDidReceiveChallenge {
let result = taskDidReceiveChallenge(session, task, challenge)
completionHandler(result.0, result.1)
} else if let delegate = self[task]?.delegate {
// this gets executed in iOS 15 and we need to get a credential in order for the delegate to successfully set the disposition required for getting data from the server
let cert = PKCS12.init(mainBundleResource: "\(certNameHere)",
resourceType: "pfx",
password: "%^&^%*&")
delegate.credential = cert.urlCredential()
delegate.urlSession(
session,
task: task,
didReceive: challenge,
completionHandler: completionHandler
)
} else {
urlSession(session, didReceive: challenge, completionHandler: completionHandler)
}
This has solved the issue for us and I hope it is of help to others who may experience similar issues with iOS 15 and Alamofire 4.9.1. Thank you for reading.
Issue with Alamofire challenge delegate and escaping closure
It seems to me the missing piece of your question is whether the completion handler in Authhandler.handleChallenge
is escaping. It is, right?
But the taskDidReceiveChallengeWithCompletion
completionHandler is non-escaping. So you're trying to figure out how to let it escape when it's not allowed to escape.
Looking at the Alamofire source code, about 3 months ago, they changed that completionHandler to be @escaping! See here: https://github.com/Alamofire/Alamofire/commit/b03b43cc381ec02eb9855085427186ef89055eef
You need to update to a version of Alamofire after that PR got merged or you need to figure out how to handle the completionHandler in a fully non-escaping way. Meaning, your Authhandler.handleChallenge
can't have an escaped completionHandler.
Alamofire 5 alternative to sessionDidReceiveChallenge
First you need a ServerTrustEvaluating
that handle the certificate pinning a simple implement would be something similar to
public final class CertificatePinnerTrustEvaluator: ServerTrustEvaluating {
public init() {}
func setupCertificatePinner(host: String) -> CertificatePinner {
//get the CertificatePinner
}
public func evaluate(_ trust: SecTrust, forHost host: String) throws {
let pinner = setupCertificatePinner(host: host)
if (!pinner.validateCertificateTrustChain(trust)) {
print("failed: invalid certificate chain!")
throw AFError.serverTrustEvaluationFailed(reason: .noCertificatesFound)
}
if (!pinner.validateTrustPublicKeys(trust)) {
print ("couldn't validate trust for \(host)")
throw AFError.serverTrustEvaluationFailed(reason: .noCertificatesFound)
}
}
}
To be able to use the same evaluator I would suggest to subclass ServerTrustManager
to return the same evaluator I did it like this:
class CertificatePinnerServerTrustManager: ServerTrustManager {
let evaluator = CertificatePinnerTrustEvaluator()
init() {
super.init(allHostsMustBeEvaluated: true, evaluators: [:])
}
open override func serverTrustEvaluator(forHost host: String) throws -> ServerTrustEvaluating? {
return evaluator
}
}
after that you should be ready to go by creating the session and passing the manager to it
private let session: Session = {
let trustManager = CertificatePinnerServerTrustManager()
return Session(serverTrustManager: trustManager)
}()
My reference was the method urlSession(_:task:didReceive:completionHandler:)
in Alamofire source in SessionDelegate.swift at line 86 (Alamofire V5.2.1)
Authenticated http request swift Alamofire
So the majority of your code looks solid.
The error leads me to believe that CFNetwork is having difficulty figuring out how to compute the protection space for the challenge. I would also assume you are getting a basic auth challenge since you are attaching an Authorization
header.
Digging through your logic a bit more with this in mind led me to see that your not attaching your token
to the string properly inside the Authorization
header. You need to do the following instead.
defaultHeaders["Authorization"] = "bearer \(token!)"
Otherwise your Authorization
header value is going to include Optional(value)
instead of just value
.
That's the only issue I can see at the moment. If you could give that a try and comment back that would be great. I'll update my answer accordingly if that doesn't actually solve your problem.
Best of luck!
Related Topics
Array Extension Called from Other Module
Swift 4.1 Deinitialize and Deallocate(Capacity:) Deprecated
How to Print Escape Sequence Characters in Swift
Checking Conforms Protocol with Associatedtype in Swift
Add a Xib File to a Swift Package
Cannot Load Module Coredata as Coredata
Difference Between Optional Values in Swift
How to Read File Data Applications Document Directory in Swift
How to Customize the Title/Subtitle Font in Callout from Mkannotationview or Just Hide Them
Swift: Generics and Type Constraints, Strange Behavior
Dictionary of String:Any Does Not Conform to Protocol 'Decodable'
Swift Compilation Time with Nil Coalescing Operator
Iwatch: Wkinterfacelabel How to Stop Text from Being Cut Off with "..." at The End of a Label
Change Notification from Observable Object as a Nested Object in Swiftui