Closed edimoldovan closed 2 years ago
I did implement the session delegate like this, which seems to work. Feedback welcome in case anyone notices something that is still not ok. Code strongly inspired by this post https://stackoverflow.com/questions/48536925/how-do-i-make-a-request-to-a-rest-api-with-a-certificate-in-swift#48537659
class SessionDelegate: NSObject, URLSessionDelegate {
public func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
if let localCertPath = Bundle.main.url(forResource: "cert", withExtension: "pfx"),
let localCertData = try? Data(contentsOf: localCertPath)
{
let identityAndTrust: IdentityAndTrust = extractIdentity(certData: localCertData as NSData, certPassword: "pwd")
if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodClientCertificate {
let urlCredential:URLCredential = URLCredential(
identity: identityAndTrust.identityRef,
certificates: identityAndTrust.certArray as [AnyObject],
persistence: URLCredential.Persistence.forSession);
completionHandler(URLSession.AuthChallengeDisposition.useCredential, urlCredential);
return
}
}
challenge.sender?.cancel(challenge)
completionHandler(URLSession.AuthChallengeDisposition.rejectProtectionSpace, nil)
}
public struct IdentityAndTrust {
public var identityRef: SecIdentity
public var trust: SecTrust
public var certArray: NSArray
}
public func extractIdentity(certData:NSData, certPassword:String) -> IdentityAndTrust {
var identityAndTrust:IdentityAndTrust!
var securityError:OSStatus = errSecSuccess
var items: CFArray?
let certOptions: Dictionary = [ kSecImportExportPassphrase as String : certPassword ];
// import certificate to read its entries
securityError = SecPKCS12Import(certData, certOptions as CFDictionary, &items);
if securityError == errSecSuccess {
let certItems:CFArray = items as! CFArray;
let certItemsArray:Array = certItems as Array
let dict:AnyObject? = certItemsArray.first;
if let certEntry:Dictionary = dict as? Dictionary<String, AnyObject> {
// grab the identity
let identityPointer:AnyObject? = certEntry["identity"];
let secIdentityRef:SecIdentity = identityPointer as! SecIdentity;
// grab the trust
let trustPointer:AnyObject? = certEntry["trust"];
let trustRef:SecTrust = trustPointer as! SecTrust;
// grab the certificate chain
var certRef: SecCertificate?
SecIdentityCopyCertificate(secIdentityRef, &certRef);
let certArray:NSMutableArray = NSMutableArray();
certArray.add(certRef as! SecCertificate);
identityAndTrust = IdentityAndTrust(identityRef: secIdentityRef, trust: trustRef, certArray: certArray);
}
}
return identityAndTrust;
}
}
Then, called the code like this:
oauth2.sessionDelegate = SessionDelegate()
var req = oauth2.request(forURL: URL(string: APISettings.URL)!)
req.setValue(APISettings.clientID, forHTTPHeaderField: "client_id")
req.setValue(APISettings.clientSecret, forHTTPHeaderField: "client_secret")
req.setValue("application/json", forHTTPHeaderField: "Accept")
let task = oauth2.session.dataTask(with: req) { data, response, error in
///
}
task.resume()
Hi,
Great work on the library, it got me really quickly through most of my implementation. I do have one question though: I need to send requests signed with a certificate to my api endpoint. What would be the recommended way of doing this?
So far what I have is this, no certificate use here yet.
I'll be trying soon to define a session delegate and perhaps I could reuse that with the above?