How does one make ApolloClient accept self-signed certificates? We are using it for internal apps, and calls to the GraphQL endpoints are failing because of the certification issues. I found this thread in the repository issues, but it seems that the API has changed some since that discussion, and the correct current approach is eluding me.
Currently, my integration test drops to the .failure
branch reporting certificate validation issues:
let apollo = AppConstants.current.apollo
apollo.fetch( query: DriverStatementsIncompleteQuery( onepass: "test-user" ) ) { result in
switch result {
case .success( let results ):
print( results )
XCTAssertNil( results.errors )
self.completedExpectation.fulfill()
case .failure( let error ):
XCTFail( error.localizedDescription )
self.completedExpectation.fulfill()
}
}
wait( for: [self.completedExpectation], timeout: 5 )
Ellen Shapiro on the Apollo iOS team very kindly pointed me in the right direction. Here's what I ended up with:
public struct PromiscuousApolloClientFactory {
/// Creates an `ApolloClient` instance that is configured to work with certificates that the
/// OS would otherwise deem invalid, like those that are self-signed.
/// - Parameter endpointURL: The URL of the GraphQL endpoint.
/// - Returns: The configured `ApolloClient` instance.
public static func make( with endpointURL: URL ) -> ApolloClient {
let store = ApolloStore( cache: InMemoryNormalizedCache() )
let sessionConfig = URLSessionConfiguration.default
let client = PromiscuousURLSessionClient( sessionConfiguration: sessionConfig )
let provider = LegacyInterceptorProvider( client: client, store: store )
let transport = RequestChainNetworkTransport( interceptorProvider: provider, endpointURL: endpointURL )
return ApolloClient( networkTransport: transport, store: store )
}
}
private class PromiscuousURLSessionClient: URLSessionClient {
override func urlSession( _ session: URLSession,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping ( URLSession.AuthChallengeDisposition, URLCredential? ) -> Void ) {
let protectionSpace = challenge.protectionSpace
guard protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust,
let serverTrust = protectionSpace.serverTrust else {
completionHandler( .performDefaultHandling, nil )
return
}
let credential = URLCredential( trust: serverTrust )
completionHandler( .useCredential, credential )
}
}
NOTE BENE: This is generally a bad practice, as it short-circuits security protections that are there for your own good. If you have a path to use certificates that the OS can validate, do that instead. :)