7

I have a Hybrid Remote app built with Mobile SDK 3.1. I've created both an iOS and Android version of the app, and both exhibit the same behavior. To simplify the discussion below, I'll refer to the iOS app.

Background

This app is intended to be used by Community users. The community is setup in the sandbox and is working correctly, with the site prefix /tenant. The community is configured to use SiteLogin as the login page. Upon login, the app should load /tenant/mobile_index.

Steps Taken

I built the iOS app with forceios. It is configured to use a connected app in my sandbox. The app is essentially exactly the code the was generated by forceios, with only these changes:

  • In bootconfig.xml, startPage is set to "/mobile_index". I tried "/tenant/mobile_index", which tried to load /tenant/tenant/mobile_index after successful login. I have also tried /apex/mobile_index which works fine; after login the app loads /tenant/mobile_index. Switching between /apex/mobile_index and /mobile_index has no bearing on the issue below.
  • In platform/iOS/MobileApp/MobileApp-Info.plist, I added the following to replace the standard login page with the community login page, per the Mobile SDK Docs:

    <key>SFDCOAuthLoginHost</key>
    <string>dev-orgname.cs8.force.com/tenant</string>
    

What Works

On first launch, user is correctly presented with the community login page. Upon logging in as a community user, user is correctly presented with mobile_index page, and the app just works. If you shutdown the app and relaunch, the user remains logged in. Update: debugging the emulator's embedded browser via Safari, I can see that the URL for the login page shown is dev-orgname.cs8.force.com/tenant/setup/secur/RemoteAccessAuthorizationPage.apexp?source=<LONG STRING>. More on this in next section.

The Problem

However, if you close the app, wait long for the user's session to timeout, and relaunch the app, you are presented with the community login screen. (Aside: I'd really like the app to cache the login and avoid this step. Is this possible?) At this point, if you log in as the community user, you get back an empty login page with no error message.

Looking at the emulator console log, I can see the following events:

Finished load of: https://dev-orgname.cs8.force.com/tenant/secur/frontdoor.jsp?sid=**SNIP**&retURL=https://dev-orgname.cs8.force.com/tenant/mobile_index&display=touch

Then a delay while I type my credentials and submit. Then:

Resetting plugins due to page load.
Failed to load webpage with error: The operation couldn’t be completed. (NSURLErrorDomain error -999.)
Resetting plugins due to page load.
Finished load of: https://dev-orgname.cs8.force.com/tenant/secur/frontdoor.jsp?apv=1&cshc=0000001jupj0000005t8vb&loginURL=https%3A%2F%2Fdev-orgname.cs8.force.com%2Ftenant%2FSiteLogin&refURL=https%3A%2F%2Fdev-orgname.cs8.force.com%2Ftenant%2Fsecur%2Ffrontdoor.jsp&retURL=%2Ftenant%2Fsecur%2Ffrontdoor.jsp%3Fsid%**SNIP**%26retURL%3Dhttps%253A%252F%252Fdev-orgname.cs8.force.com%252Ftenant%252Fmobile_index%26display%3Dtouch&sid=**SNIP**&untethered=
Resetting plugins due to page load.
Finished load of: https://dev-orgname.cs8.force.com/tenant/secur/frontdoor.jsp?sid=**SNIP**&retURL=https%3A%2F%2Fdev-orgname.cs8.force.com%2Ftenant%2Fmobile_index&display=touch

It certainly appears that the login is successful, but then the loading of mobile_index fails with (NSURLErrorDomain error -999.). I've found some references indicating that the iOS web view throws this error when a page load is interrupted by another page load, but I'm not sure how that's relevant; I'm using the OOB Hybrid app framework. Shouldn't that be able to log in to the org a second time? Once this occurs, the only solution is to uninstall and reinstall the app.

Update As mentioned in the update to the "What Works" section, log in during initial launch goes to /tenant/setup/secur/RemoteAccessAuthorizationPage.apexp, but as shown here, when re-launching after a timeout, the log in page is /tenant/secur/frontdoor.jsp. Why? should this endpoint be avoided for a community login? How do I fix in the mobile app?

Other possibly relevant items

  • As mentioned above, the same thing happens with the Android app.
  • So far, the apps are only being run in emulators. Awaiting my iOS dev account, and awaiting Android hardware. Note: happening on device also; see update 2 below.
  • Not sure I understand the oauthRedirectURI in the context of a hybrid app. In my connected app, I made up a URI as suggested, (sfdc://orgname_ios), and put the same in my bootconfig.json.
  • While logged in, if you log out, you can log back in. It only seems to be when the logged in session times out that this issue occurs. Trying to log in as a different community user also fails; it doesn't appear to be tied to the user whose session timed out. Update While logged in, if you log out, you get /tenant/setup/secur/RemoteAccessAuthorizationPage.apexp.
  • The mobile app content is HTML5/Angular/Ionic served via VF Page, and uses VF Remote Actions to communicate with the server. Based on the section "Managing Sessions in Hybrid Apps" on p61 of the Mobile SDK docs pdf, I'm expecting that I'll need to handle session refreshes during Javascript Remoting calls using code similar to that found in subsection "JavaScript Remoting in Visualforce" starting on p62, but I haven't added that yet. However, this is a separate issue that happens when the app is not running, the session times out, and the app is relaunched. Note I have added this code, no effect. See Update 3 below.

Update: As requested, my bootconfig.json. Lightly sanitized to remove client information.

{
  "remoteAccessConsumerKey": " **SNIP** ",
  "oauthRedirectURI": "sfdc://orgname_ios",
  "oauthScopes": [
    "web",
    "api"
  ],
  "isLocal": false,
  "startPage": "/mobile_index",
  "errorPage": "error.html",
  "shouldAuthenticate": true,
  "attemptOfflineLoad": false
}

Update 2: installed on Android device, same behavior as emulator. Initial launch & login works fine; coming back hours later (after session times out) and re-launching, I get login screen; logging in just takes me back to the login screen with no error message. Just installed on iOS device; will test in a few hours after session times out.

However, in testing this, I learned something new. The first time I tried to log in after the session had timed out, I fat-fingered the password. The login page correctly displayed the message that my username/password was not recognized; upon correcting the PW, I was taken back to the login page, this time with no message. I think this confirms my suspicion that the login page is successfully completing a login, but the app for some reason isn't navigating to the start page correctly.

Update 3: I've added code that wraps my VF remoting calls, and detects if the session has timed out. If I catch the error I use com.salesforce.plugin.oauth to re-authenticate and retry the VF remoting call. This is working just fine now (and console logging proves the timeout and the refresh), but the login-after-launch-after-timeout issue persists, and the only solution remains to re-install the app. See also the inline updates above about RemoteAccessAuthorizationPage vs frontdoor.jsp.

Update 4: I've updated Cordova to 4.3.0 and cordova-ios to 3.8.0 (both latest), it does not resolve the problem.

Using @xn.'s suggestions from the comments, I've been using mitmdump/mitmproxy to watch what happens on a fresh install launch vs. a relaunch after timeout. So far I've gleaned a few things:

  • On first launch (after install), the first http request is for .../tenant/services/oauth2/authorize?client_id=<ID>&redirect_uri=sfdc://orgname_ios&..., which redirects to RemoteAccessAuthorizationPage.
  • On a relaunch, no call is made to /tenant/services/oauth2/authorize; instead it just calls frontdoor.jsp directly as outlined above. This request includes a sid param in the url.
  • The login process redirects to frontdoor.jsp a few times; each time the same sid param is included.
  • using mitmproxy, I intercepted the initial call to frontdoor.jsp and removed the sid param. This fixed the problem.

So, something is going wrong on relaunch when the request includes an expired session id, but I don't know how/why, or how to prevent.

Update 5: Based on my earlier findings with mitmproxy and the sid param, I tried to address the issue in my login page by detecting and removing the sid param in the page's action method. This met with partial success, and some surprising results. The summary:

  • In SiteLogin.jsp, in the apex:page tag, I added action="{!clearOldSid}"

  • In the controller,

    global PageReference clearOldSid() {
        PageReference ref = ApexPages.currentPage();
        if (ref.getParameters().containsKey('sid')) {
            return Page.stnhg_index;            
        }
        else { 
            return null;
        }
    }
    

    Note that I tried modifing ApexPages.currentPage() and returning that, but the system kept putting the sid back into the url upon redirect.

  • This worked partially. It did solve the "cannot login after re-launch after session timeout" issue, but it introduced a new issue. Previously, quitting the app (via iOS multitasking menu) and relaunching would reopen the app without a login page. After the above sid-stripping fix, every relaunch resulted in a login page. Apparently during relaunch, SiteLogin is being loaded even if it isn't displayed. (aside: just realized, that's probably the Mobile SDK in action, trapping the login page and refreshing the session). In trying to figure out what was going on, I tried modifying the if() test in my action method to only clear the sid when the page runs as guest user:

    if (ref.getParameters().containsKey('sid') && UserInfo.getUserType().contains('Guest')) {
        ...
    

    Surprisingly, this worked a little better - Fresh install, login, quit, relaunch opens the app without showing a login page; quit, timeout (or kill) session, relaunch shows the login page, but it works. However, quit & relaunch at this point (don't kill the session) takes you to the login page again, unlike first launch. Is this good enough? Not really, but at least the app doesn't die forever after the first timeout. It's also certainly not the right way to handle this.

Investigating this last behavior led me to take a closer look at the sessions listed in Session Management, where I found something very interesting. Upon a first launch, these are the sessions that get created after logging in:

First Launch Sessions

After killing those sessions, relaunching, and logging in, these sessions exist:

Relaunch Sessions

Different number and type of sessions. I don't know if this is because of my 'clear the sid' hack, but I doubt it, since that still gives a different behavior on relaunch. I suspect it has something to do with the different launch behavior on any relaunch. At this point, it seems like the root cause is that calling frontdoor.jsp with an expired session isn't working like it should.

  • Just wanted to clarify one thing: error -999 in the NSURLErrorDomain specifically refers to the page load being cancelled. Hard to say why the SDK would be interrupting the page load, though. – Jonathan Hersh Feb 21 '15 at 1:23
  • Interesting, I've never seen this with my Ionic apps with SDK 3.1. Do you mind posting your bootconfig.json? Please also provide a snippet for where you're invoking the auth flow – Gaurav Kheterpal Feb 21 '15 at 3:10
  • @GauravKheterpal I've added my bootconfig.json. I am not directly invoking the auth flow - I'm just using the OOB app generated by forceios. On launch, the app navigates to the startPage, showing the login page if necessary. The only thing I see in Mobile SDK docs about Hybrid apps & session management is the section on p61 that I reference above. – Jason Clark Feb 23 '15 at 16:34
  • There was a bug in cordova-ios 3.6 which caused problems with the Salesforce authentication. frontdoor.jsp redirects to contentDoor. contentDoor loads SfdcSessionBase190.js, which loads sessionserver190.html in an iframe, and also redirects to your Visualforce page on your instance domain, which in turn redirects to the visualforce domain. This triggers the bug described in github.com/apache/cordova-ios/pull/110 – xn. Mar 3 '15 at 21:46
  • @xn. that sounds like exactly the issue. However, cordova platform list tells me I have ios 3.7.0, which includes the pull request for the bug you cited. I also opened CordovaLib/Classes/CDVWebViewDelegate.m in my xcode project and confirmed that I have the code shown in the commit. I do however see the cordova-ios 3.8 just shipped, so I may try updating my cordova and cordova-ios to see if it helps. – Jason Clark Mar 3 '15 at 22:09
0

note: This original issue was based on Mobile SDK 3.1 & 3.12. I believe it may be present in 3.2, but I don't have an active project with which to confirm. I have been testing 3.3.3 lately, and am no longer seeing this issue, although I am seeing other issues.

I did find a workaround for this, based on points 4 and 5 above. In my custom login page, I call an action method in the controller on page load:

<apex:page controller='SiteLoginController' action='clearSid' ... >

The clearSid method checks to see if the current request has a sid param but the user is not logged in (see update 5 above); if so, it redirects the user to the app's landing page without the sid param. At that point, the user is just trying to access a protected resource, so the system redirects back to the login page, only with a session id.

While this prevents the app from being locked for ever after a timeout, session refreshes are not working correctly. I believe that to be a separate Mobile SDK issue, which has been at least partially resolved in 3.3.3 (see link above for more).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.