Skip to content

Commit

Permalink
Merge pull request #282 from ForgeRock/Bug/FRURLProtocol_ignores_SSL_…
Browse files Browse the repository at this point in the history
…Pinning

FRURLProtocol ignores ssl pinning
  • Loading branch information
spetrov authored Jun 4, 2024
2 parents a4baee6 + f8bf9a3 commit 3814770
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 21 deletions.
82 changes: 61 additions & 21 deletions FRAuth/FRAuth/Network/FRURLProtocol.swift
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
//
//
// FRURLProtocol2.swift
// FRAuth
//
// Copyright (c) 2020 ForgeRock. All rights reserved.
// Copyright (c) 2020-2024 ForgeRock. All rights reserved.
//
// This software may be modified and distributed under the terms
// of the MIT license. See the LICENSE file for details.
//


import Foundation
import FRCore

@objc public class FRURLProtocol: URLProtocol {
@objc open class FRURLProtocol: URLProtocol {

// MARK: - Public Property

/// TokenManagementPolicy for URLProtocol
@objc public static var tokenManagementPolicy: TokenManagementPolicy?
/// AuthorizationPolicy for URLProtocol
@objc public static var authorizationPolicy: AuthorizationPolicy?
/// FRSecurityConfiguration for URLProtocol - Used for SSL Pinning
@objc public static var frSecurityConfiguration: FRSecurityConfiguration?

// MARK: - Private Property

Expand Down Expand Up @@ -131,7 +134,7 @@ import Foundation
self.sessionTask?.resume()
return
}

FRURLProtocol.setProperty(true, forKey: Constants.FRURLProtocolHandled, in: mutableRequest)
session?.dataTask(with: mutableRequest as URLRequest, completionHandler: { (data, response, error) in
if let returnData = data {
Expand Down Expand Up @@ -198,7 +201,7 @@ import Foundation


extension FRURLProtocol: URLSessionDataDelegate {

/// URLSessionDataDelegate method for receiving data
///
/// - Parameters:
Expand All @@ -214,7 +217,7 @@ extension FRURLProtocol: URLSessionDataDelegate {
self.responseData = data
}
}


/// URLSessionDataDelegate method for receiving response
///
Expand All @@ -229,7 +232,7 @@ extension FRURLProtocol: URLSessionDataDelegate {
completionHandler(.allow)
}


/// URLSessionDataDelegate method to notify completion of the request
///
/// In this delegation method, FRURLProtocol will validate the result of the request with given ValidatedURLs, RefreshTokenPolicy, and maximum retry attempt, and perform token refresh if necessary
Expand Down Expand Up @@ -317,7 +320,7 @@ extension FRURLProtocol: URLSessionDataDelegate {
}
}


if shouldRetry {
self.responseData = nil
self.client?.urlProtocol(self, didLoad: Data())
Expand All @@ -334,7 +337,7 @@ extension FRURLProtocol: URLSessionDataDelegate {
self.completeRequest(error: error)
}
}


/// URLSessionDataDelegate method for HTTP Redirection
///
Expand Down Expand Up @@ -373,7 +376,7 @@ extension FRURLProtocol: URLSessionDataDelegate {
client?.urlProtocol(self, wasRedirectedTo: request, redirectResponse: response)
completionHandler(request)
}


/// URLSessionDataDelegate method for invalidating the current request with an error
///
Expand All @@ -384,28 +387,65 @@ extension FRURLProtocol: URLSessionDataDelegate {
guard let error = error else { return }
client?.urlProtocol(self, didFailWithError: error)
}


/// URLSessionDataDelegate method for Authentication Challenge

/// URLSessionDelegate method for Authentication Challenge
///
/// - Parameters:
/// - session: URLSession
/// - challenge: URLAuthenticationChallenge
/// - completionHandler: Completion callback
public func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
let protectionSpace = challenge.protectionSpace
let sender = challenge.sender

if protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
if let serverTrust = protectionSpace.serverTrust {
let credential = URLCredential(trust: serverTrust)
sender?.use(credential, for: challenge)
completionHandler(.useCredential, credential)
if let frSecurityConfiguration = FRURLProtocol.frSecurityConfiguration {
frSecurityConfiguration.validateSessionAuthChallenge(session: session, challenge: challenge, completionHandler: completionHandler)
} else {
let protectionSpace = challenge.protectionSpace
let sender = challenge.sender

if protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
if let serverTrust = protectionSpace.serverTrust {
let credential = URLCredential(trust: serverTrust)
sender?.use(credential, for: challenge)
completionHandler(.useCredential, credential)
return
}
} else {
completionHandler(.performDefaultHandling, nil)
return
}
}

}


/// URLSessionTaskDelegate method for Authentication Challenge
///
/// - Parameters:
/// - session: URLSession
/// - task: URLSessionTask
/// - challenge: URLAuthenticationChallenge
/// - completionHandler: Completion callback
public func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
if let frSecurityConfiguration = FRURLProtocol.frSecurityConfiguration {
frSecurityConfiguration.validateTaskAuthChallenge(session: session, task: task, challenge: challenge, completionHandler: completionHandler)
} else {
let protectionSpace = challenge.protectionSpace
let sender = challenge.sender

if protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
if let serverTrust = protectionSpace.serverTrust {
let credential = URLCredential(trust: serverTrust)
sender?.use(credential, for: challenge)
completionHandler(.useCredential, credential)
return
}
} else {
completionHandler(.performDefaultHandling, nil)
return
}
}

}


/// URLSessionDataDelegate method
///
Expand Down
14 changes: 14 additions & 0 deletions SampleApps/FRExample/FRExample/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,20 @@ class ViewController: UIViewController, ErrorAlertShowing {
config.protocolClasses = [FRURLProtocol.self]
self.urlSession = URLSession(configuration: config)

// Configure FRSecurityConfiguration to set SSL Pinning on the FRURLProtocol
// This needs to be set up if using Authroization or Token Policies with FRURLProtocol
// and want to force SSL Pinning on those endpoints.
// Make sure to add the Key Hashes of the certificates that correspont to the URLs
// set on the `FRURLProtocol.tokenManagementPolicy` & `FRURLProtocol.authorizationPolicy`

var sslPinningKeyHashes: [String] = []
//sslPinningKeyHashes = ["Key1", "Key2"] --> Add Key hashes and uncomment to enable

if(!sslPinningKeyHashes.isEmpty) {
let frSecurityConfiguration = FRSecurityConfiguration(hashes: sslPinningKeyHashes)
FRURLProtocol.frSecurityConfiguration = frSecurityConfiguration
}

// - MARK: FRUI Customize Cell example
// Comment out below code to demonstrate FRUI customization
// CallbackTableViewCellFactory.shared.registerCallbackTableViewCell(callbackType: "NameCallback", cellClass: CustomNameCallbackCell.self, nibName: "CustomNameCallbackCell")
Expand Down

0 comments on commit 3814770

Please sign in to comment.