Enterprise Java

TIP: Activate via URL and Send Arguments

The most secure password in the world is the one that doesn’t exist. You remove the user from the equation with a completely random key. To be fair this has some drawbacks and a password still exists somewhere (in your phone/email) but generally this works rather well…​

The trick is simple, if we want to authenticate a user we can email him a single use URL e.g. mycoolapp://act-32548b09-d328-4330-8243-d7d30c322e40. As you can see that’s pretty hard to guess or brute force. Once clicked the URL becomes invalid so even if it’s exposed somehow it would still be irrelevant. To do this we need two parts:

  • The server logic
  • Client URL handling

Both are pretty easy.

The Server

One caveat is that the mycoolapp will work on the device but you can’t click it in an email or in a browser. So we will need an https URL from your server.

The server would look something like this, notice that this is Spring Boot Controller code but you should be able to use any server out there:

public boolean sendSigninEmail(String e) {
    List<UserObj> ul = users.findByEmailIgnoreCase(e);
    if(ul.isEmpty()) {
        return false;
    }
    UserObj u = ul.get(0);
    u.setHashedActivationToken(UUID.randomUUID().toString()); (1)
    users.save(u); (2)
    email.sendEmail(e, "Signin to the Codename One App", "This is a one time link to activate the Codename One App. Click this link on your mobile device: \n\nhttps://ourserverurl.com/app/activateURL?token=act-" + u.getHashedActivationToken()); (3)
    return true;
}
public User activateViaToken(String t) throws ServerAppAPIException {
    List<UserObj> ul = users.findByHashedActivationToken(t); (4)
    if(ul.isEmpty()) {
        throw new ServerAppAPIException(ServerErrorCodes.NOT_FOUND);
    }
    UserObj u = ul.get(0);
    String val = u.getAppToken(); (5)
    u.setHashedActivationToken(null); (6)
    users.save(u);
    User r = u.getUser();
    r.setAppToken(u.getAppToken());
    return r;
}
1We use UUID to generate the long activation string
2We save it in the database overwriting an older URL if it exists
3We can send an email or SMS with the HTTPS URL to activate the app
4Next we activate the user account with the received token. We find the right account entry
5An access token is a secure password generated by the server that’s completely random and only visible to the app
6The activation token used in the URL is removed now making the URL a single use tool

All of that is mostly simple but there is still one missing piece. Our app will expect a mycoolapp URL and an HTTPS URL won’t launch it. The solution is a 302 redirect:

@RequestMapping(value="/activateURL", method=RequestMethod.GET)
public void activateURL(@RequestParam String token, HttpServletResponse httpServletResponse)  {
    httpServletResponse.setHeader("Location", "mycoolapp://" + token);
    httpServletResponse.setStatus(302);
}

This sends the device to the mycoolapp URL automatically and launches your app with the token!

Client Side

On the client we need to intercept the mycoolapp URL and parse it. First we need to add two new build hints:

android.xintent_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="mycoolapp" />  </intent-filter>
ios.plistInject=<key>CFBundleURLTypes</key>     <array>         <dict>             <key>CFBundleURLName</key>             <string>com.mycompany.myapp.package.name</string>         </dict>         <dict>             <key>CFBundleURLSchemes</key>             <array>                 <string>mycoolapp</string>             </array>         </dict>     </array>

Don’t forget to fix mycoolapp and com.mycompany.myapp.package.name to the appropriate values in your app

Next all we need to do is detect the URL in the start() method. This needs to reside before the code that checks the current Form:

String arg = getProperty("AppArg", null); (1)
if(arg != null) {
    if(arg.contains("//")) { (2)
        List<String> strs = StringUtil.tokenize(arg, "/");
        arg = strs.get(strs.size() - 1);
        while(arg.startsWith("/")) {
            arg = arg.substring(1);
        }
    }
    if(!arg.startsWith("act-")) { (3)
        showLoginForm();
        callSerially(() ->
            Dialog.show("Invalid Key", "The Activation URL is invalid", "OK", null));
        return;
    }
    arg = arg.substring(4);
    Form activating = new Form("Activating", new BorderLayout(BorderLayout.CENTER_BEHAVIOR_CENTER));
    activating.add(CENTER, new InfiniteProgress());
    activating.show();
    sendActivationTokenToServer(arg); (4)
    return;
}
1This is from the CN class globally imported. The app argument is the URL
2We remove the URL portion of the argument
3The act- prefix is there to validate the URL is correct
4This sends the activation key to the server logic we discussed above

Testing in The Simulator

This will work in iOS and Android. Starting next week you could also test this on the simulator using the new Send App Argument menu option in the simulator.

To integrate this properly into an app you would normally have a login menu that accepts only the email/phone. Or a system in your web based UI to send an invite link to the app.

Whatsapp uses an inverse of this trick to activate their desktop app. They show a QR code to your device and once you scan that QR code with your whatsapp phone install the desktop version is activated. That’s much better than passwords.

Published on Java Code Geeks with permission by Shai Almog, partner at our JCG program. See the original article here: TIP: Activate via URL and Send Arguments

Opinions expressed by Java Code Geeks contributors are their own.

Shai Almog

Shai is the co-founder of Codename One, he has been programming professionally for over 20 years and developing in Java since 96. Shai worked for countless industry leaders including Sun Microsystems where he was a part of the original WTK (Wireless Toolkit) team & the co-creator of LWUIT. He worked with most major device operators/manufactures including Nokia, Samsung, Sony Ericson, Sprint, Vodafone, Verizon, NTT DoCoMo etc. Shai is a blogger and writer who often speaks at conventions. He is a Java One rockstar and top rated speaker for JavaZone, corporate conventions from Oracle, IBM and many others.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button