(UPDATE added at end)
I have a native iOS MobileFirst (7.0) client written in Swift. The MobileFirst Server is behind a firewall and accessed though a junction on IBM Security Access Manager for Web (ISAM). ISAM is being used for adapter authentication. I've already tested the app without ISAM in the middle (no authentication), and it works fine.
A custom challenge handler is registered:
let myCH = MyChallengeHandler(vc: self)
WLClient.sharedInstance().registerChallengeHandler(myCH)
MyChallengeHandler sets the realm in its init() function:
init(vc: LoginViewController){
self.vc = vc
super.init(realm: "HeaderAuthRealm")
}
The app first connects to the server using wlConnectWithDelegate:
WLClient.sharedInstance().wlConnectWithDelegate(ConnectListener(vc: self))
And once the connection is made, it should call a adapter method on the server to look up the user info (using invokeProcedure):
let invocationData = WLProcedureInvocationData(adapterName: "login", procedureName: "lookUpRole")
invocationData.parameters = [userid]
WLClient.sharedInstance().invokeProcedure(invocationData, withDelegate: LoginListener(vc: self))
However it's not getting that far.
When ISAM is involved, it is protecting everything, include the connect URL, so the challenge handler is first getting called when the wlConnectWithDelegate() is attempted because ISAM returns a login page.
The challenge handler is properly detecting the login page and the handleChallenge() function is being called. Userid/password is collected from the user if necessary, and then it calls a function that calls submitLoginForm(). The custom onConnect() and onFailure() functions are defined in the challenge handler as well:
override func handleChallenge(response: WLResponse!)
{
handleChallengeISAM(response)
}
func handleChallengeISAM(response: WLResponse!)
{
//HPDIA0200W Authentication failed. You have used an invalid user name, password or client certificate.
let failedLogin = response.responseText.rangeOfString("HPDIA0200W") != nil
if vc.security.authDataSet && !failedLogin
{
println("=========== Sending login data directly")
submitISAMAuthData()
}
else
{
println("=========== A login screen form should appear")
if failedLogin {
needCredentials("Please check your credentials.")
} else {
needCredentials(nil)
}
}
}
func submitISAMAuthData()
{
let loginPostUrl = "https://wstest.clearlake.ibm.com/pkmslogin.form"
let params = ["username" : vc.security.userid , "password" : vc.security.password, "login-form-type" : "pwd"]
println("=========== Sending submitLoginForm request")
self.submitLoginForm( loginPostUrl, requestParameters: params, requestHeaders: nil, requestTimeoutInMilliSeconds: -1, requestMethod: nil)
println("=========== submitLoginForm request Sent")
}
override func onSuccess(response: WLResponse!)
{
println("=========== onSuccess")
let isLoginResponse = isCustomResponse(response)
if isLoginResponse {
println("=========== RE-challenged")
handleChallenge(response)
} else {
println("=========== Challenge success")
submitSuccess(response)
}
}
override func onFailure(response: WLFailResponse!)
{
println("=========== Challenge failure")
println("\(response.errorMsg)")
submitFailure(response)
}
The problem is that is as far as it gets. The request never gets to the ISAM device, and the onSuccess() or onFailure() functions are never called. The iOS simulator log states that the request was made, but that's it. No indication that it actually did anything.
=========== Sending submitLoginForm request
2015-04-09 15:00:12.866 ThirdPartyCompliance[54200:2903010] [DEBUG] [WL_AFHTTPCLIENTWRAPPER_PACKAGE] +[WLAFHTTPClientWrapper requestWithURL:] in WLAFHTTPClientWrapper.m:46 :: Request url is https://wstest.clearlake.ibm.com/pkmslogin.form
2015-04-09 15:00:12.871 ThirdPartyCompliance[54200:2903010] [DEBUG] [WL_REQUEST] -[WLRequest sendRequest:path:withOptions:] in WLRequest.m:141 :: Request timeout is 10.000000
2015-04-09 15:00:12.876 ThirdPartyCompliance[54200:2903010] [DEBUG] [WL_REQUEST] -[WLRequest sendRequest:path:withOptions:] in WLRequest.m:220 :: Sending request (https://wstest.clearlake.ibm.com/pkmslogin.form) with headers:
{
"Accept-Language" = en;
"User-Agent" = "ThirdPartyCompliance/1 (iPad Simulator; iOS 8.2; Scale/2.00)/WLNativeAPI/7.0.0.0";
"X-Requested-With" = XMLHttpRequest;
"x-wl-app-version" = "1.0";
"x-wl-device-id" = "C1CFD648-C648-439C-AC9F-8292FDAC20E6";
"x-wl-platform-version" = "7.0.0.0";
}
You can see the request body in the Analytics platform logs.
=========== submitLoginForm request Sent
The ISAM logs don't show the submitLoginForm request ever being sent despite what the iOS MobileFirst API logs say. Is something wrong with submitLoginForm() in v7.0?
UPDATE:
It seems that WLClient.sharedInstance().wlConnectWithDelegate() has to succeed before ChallengeHandler.submitLoginForm() will work. I verified this by using a proxy server in front of the ISAM device to send adapter service requests through ISAM, and all other MobileFirst server connections bypass ISAM. In this architecture, the submitLoginForm() function works fine, since wlConnectWithDelegate() is succeeding without login.
This is a little confusing since the ChallengeHandler is called when using wlConnectWithDelegate(), but some of the ChallengeHandler's methods don't yet work until after wlConnectWithDelegate() finishes. Also I can't find it documented anywhere that it works this way.