Wednesday, February 15, 2017

Password Reset Flow With Native Android

The Problem Scenario

I want a native Android application that will pull the user back after they have used their mail/SMS client to continue the password reset flow:

  1. using the native app, our user requests password reset via email or SMS
  2. the user presses link in their email or SMS client
  3. the user opens the native app to complete password reset
We write native applications for a richer user experience. For all of the valid security reasons the password reset must be out of phase. By pulling the user back into the native app to complete the password reset flow we can guide them to the native experience that we want to provide. (that we have spent money and time creating)


The scenario is defined by the following two gherkin test cases.
Given the user has submitted a password reset
And the system has emailed the long url with the website's address
When the user presses on the link in their phone's mail client
Then the native android app is offered to handle the URL
And when the user chooses the native android app they are taken directly to the screen to save a new password
Given the user has submitted a password reset
And the system has sent an SMS with a bit.ly shortened URL
When the user presses on the link in their phone's SMS client
Then the native android app should be offered to handle the URL
And when the user chooses the native android app they are taken directly to the screen to save a new password

The Android Activity Registration

In Android we can provide one Activity that handles completing the password reset flow. That Activity needs the appropriate intent filters so the operating system knows it can handle the long and the short URLs:

<activity
    android:name=".authentication.CompleteResetPasswordActivity">
    <intent-filter>
        <action android:name="android.intent.action.VIEW"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <category android:name="android.intent.category.BROWSABLE"/>
        <data android:scheme="https" android:host="${host}" android:pathPrefix="/passwordreset"/>
    </intent-filter>
    <intent-filter>
        <action android:name="android.intent.action.VIEW"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <category android:name="android.intent.category.BROWSABLE"/>
        <data android:scheme="https" android:host="m.my.bitly.domain"/>
    </intent-filter>
</activity>

Did you see that "${host}". That is resolved in build.gradle via:
....
buildTypes {
   debut {
      ...
      manifestPlacehodlers = [host:"qa.mydomain"]
   }
   release {
      ...
      manifestPlacehodlers = [host:"www.mydomain"]
   }
...

The Code

Android delivers the URL pressed by the user via the getIntent().getData() method. Which is an android.net.Uri instance. Play with it. Massage it. Turn it into whatever you want. For the url shortened Uri you will of course have to resolve that thing into the full URI. Perhaps you will be using either the REST or Android bi.ly API -- https://dev.bitly.com/.

You will notice that your CompletePasswordResetActivity is launched as the root of it's Task that has affinity to the email or messaging app. Tasks are tricky in Android. These aren't things I've had to deal with much in the past. But tasks and task affinity are things you will need to understand if you want to provide this type of user experience. But that's all I'm going to say about that here. Dealing with the task and getting the user back into the "main task" is worthy of it's own post.

Quality Assurance

There are a plethora of scenarios to test. For the email flow, does the user use gmail, outlook-web, some other web client, some other native mail client? For SMS you have each carrier's custom messaging client as well as Hangouts and other options from Google. I have so far tested with:

  • my Project Fi Nexus 6. I am stuck with Hangouts.  It does NOT work. Hangouts launches the link into it's own internal browser.
  • a Verizon Motorolla Droid.  That phone has Messaging, Messaging+ (Verizon's offering), and Hangouts. The above solution works as expected in all three SMS clients. We can open Gmail and press the full URL link as well.

So.... More testing is needed. Samsung has a wide following for sure. So I'll test on a few flavors of those.