Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mobilefirst server returns error 403 after session timeout handling in a native ios application

I'm using Adapter-based authentication in native iOS applications to connect my native ios application (swift) to the Mobilefirst server (7.0).

The mechanism of authentication is working fine but the problem comes when the session expires after 10 minutes.

Here you can see the part of the code where I handle the authentication and the session timeout:

override func isCustomResponse(response: WLResponse!) -> Bool {
    if response != nil && response.responseJSON != nil {
        let responseJson: NSDictionary = response.responseJSON as NSDictionary
        if responseJson.objectForKey("authRequired") != nil{
            return responseJson.objectForKey("authRequired") as! Bool
        }
    }
    return false
}

override func handleChallenge(response: WLResponse!) {

    NSLog("A login form should appear")

    if self.vc.navigationController?.visibleViewController!.isKindOfClass(LoginViewController) == true {
        NSLog("Already the login form")
        dispatch_async(dispatch_get_main_queue()) {
            let loginController : LoginViewController! = self.vc.navigationController?.visibleViewController as? LoginViewController

            let myInvocationData = WLProcedureInvocationData(adapterName: "AuthenticationJavaAdapter", procedureName: "authenticate")
            myInvocationData.parameters = [loginController.userID, loginController.userPass]
            self.submitAdapterAuthentication(myInvocationData, options: nil)
        }
    } else if (self.vc.navigationController?.visibleViewController!.isKindOfClass(SignUpViewController) == true) {
        NSLog("Already the signup form")
        dispatch_async(dispatch_get_main_queue()) {
            NSLog("AuthenticationJavaAdapter")
            let sigupController : SignUpViewController! = self.vc.navigationController?.visibleViewController as? SignUpViewController

            let myInvocationData = WLProcedureInvocationData(adapterName: "AuthenticationJavaAdapter", procedureName: "authenticate")
            myInvocationData.parameters = [sigupController.userID, sigupController.userPass]
            self.submitAdapterAuthentication(myInvocationData, options: nil)
        }
    }else { //TEST
        NSLog("A login form is not there yet")
        //After 10 minutes this will execute, it will perform a unwind segue to the login
        //timeOutController is a global var declared in LoginViewController
        timeOutController.performSegueWithIdentifier("logOutDueToTimeOut", sender: nil)
    }
}

When the session expires being the application in background and then comes back to foreground and calls a protected adapter, this part of code is executed:

timeOutController.performSegueWithIdentifier("logOutDueToTimeOut", sender: nil)

The login view loads with success and I can submit again the credentials to login in. The problem is that my application is not longer able to authenticate to the Mobilefirst server, getting this error:

[DEBUG] [WL_REQUEST] -[WLRequest requestFinished:] in WLRequest.m:385 :: no token present
2016-05-13 12:58:29.241 BNNDesignCollection[46327:318014] [DEBUG] [WL_PUSH] -[WLPush updateToken:] in WLPush.m:410 :: Server token is (null)
....
....
....
2016-05-13 12:58:29.352 BNNDesignCollection[46327:318014] [DEBUG] [WL_AFHTTPCLIENTWRAPPER_PACKAGE] -[WLAFHTTPClientWrapper requestFailed:error:] in WLAFHTTPClientWrapper.m:335 :: Response Status Code : 403
2016-05-13 12:58:29.352 BNNDesignCollection[46327:318014] [DEBUG] [WL_AFHTTPCLIENTWRAPPER_PACKAGE] -[WLAFHTTPClientWrapper requestFailed:error:] in WLAFHTTPClientWrapper.m:336 :: Response Error : Expected status code in (200-299), got 403

It seems that the request does not have a token or is invalid, but I don't get the "authrequired" field in the JSON response so that i can't authenticate again as I did the first time the application authenticates before any Mobilefirst session timeout.

The detailed step by step execution is this one:

  1. The application shows the login screen to the user
  2. The user types the credentials
  3. As a protected adapter is called during this process the mobilefirst server returns a response with "authrequired = true". The isCustomResponse method is automatically called, and returns true.
  4. As isCustomResponse returns true, The handleChallenge method is called and as the visible viewController is the loginViewController, the first "If" statement is executed, launching the authentication.
  5. The authentication succeeds and now the user can navigate throughout the application accessing all their protected resources.
  6. I put the application in background for 10 minutes (Established MobileFirst session timeout in the server).
  7. I put the application in foreground and start navigating again.
  8. As the MobileFirst session has expired, once i try to call a protected adapter again the mobilefirst server returns a response with "authrequired = true". The isCustomResponse method is automatically called again, and returns true.
  9. As isCustomResponse returns true, The handleChallenge method is called and as the visible viewController is NOT the loginViewController, the third "If" statement is executed, showing up the login screen again.
  10. The user types the credentials.
  11. The server returns a 403 response. The isCustomResponse method is automatically called but returns false as the response does not contain the "authrequired" field.

Any ideas on how to handle this?

like image 469
Irene Marquet Avatar asked Oct 30 '22 03:10

Irene Marquet


1 Answers

There are several problems that I can see with your authentication flow.

  • Your steps starts with "The application shows the login screen to the user". Are you hardcoding the application to start with a login screen? Adapter-based authentication does not support "preemptive" authentication. Meaning only a challenge should show the login screen. The usual way to do this is to call a protected resource during the app startup, or use the login API to trigger the challenge. Your handleChallenge should then display the login screen.
  • "As a protected adapter is called during this process". You are calling a protected resource in the middle of the authentication flow. No good. Don't do that.
  • Your handleChallenge automatically submits the credentials if the LoginViewController view controller is already on screen. What if the user enters incorrect credentials? Wouldn't it send the bad credentials in a loop? Usually, if LoginViewController is already on-screen, you just want to update the UI, for example "wrong credentials, try again".
  • Clicking on the "login" button should be what triggers submitAdapterAuthentication. A challenge should not trigger submitAdapterAuthentication.
  • Every single incoming challenge needs to be answered before you do anything else. Meaning, as soon as handleChallenge is called, you cannot attempt to make any other protected requests or trigger some other challenge.

Regarding your specific issue, this is what I think happens after the timeout:

  • handleChallenge is called, which opens the login screen.
  • You wrote that "a protected adapter is called during this process", which you cannot do because you are in the middle of a challenge.
  • The application enters an unstable state.

If you follow the comments I wrote above, you should be OK. But it will require some architectural changes to your application. The order in which events happen is important:

  1. Make a protected request.
  2. handleChallenge shows the login form.
  3. User clicks on submit.
  4. submitAdapterAuthentication

It does not matter if it's the first run, pre-emptive or after time out, make sure you follow the order of operations.

like image 200
Nathan H Avatar answered Nov 08 '22 15:11

Nathan H