Serverless authentication with Instagram

Instagram + Unity

In this post I will explain how to implement a Unity app for Android which allows the user to authenticate using an Instagram account – without a server (i.e., directly against Instagram’s server.) In the process we’ll also learn how to write an Android plugin.

Overview

Instagram supports client-side authentication (OAuth implicit flow, a.k.a. 2-legged OAuth). This flow enables an app to obtain a user’s OAuth credentials directly: the access token is delivered to the app via the response URI. However, the flow was designed with JavaScript applications in mind, as the app is expected to have access to the response URI (“Simply grab the access_token off the URL fragment and you’re good to go.”)  Alas, this is a challenge for a mobile app, as the app needs to relinquish control to the browser when directing the user to authenticate. How then can the app regain control and obtain the response URI with the access token?

The solution is to configure a redirect URI with a custom scheme (instead of https). An intent filter will be added to the app to handle this custom scheme. This will result in the browser directing the flow back to the app when the response URI arrives. Adding an intent filter to an Android Unity app is seemingly a simple task, but it has its share of pit falls and online examples are scarce. Full instructions ahead.

Flow

The chronological order of events in runtime (once everything is already set up and running):

  1. A user opens the app manually
  2. The user chooses to sign in. The app redirects the user to a browser to sign in on Instagram’s page
  3. Upon successful authentication Instagram redirects the browser to a URI with a custom scheme
  4. The Android OS opens the app automatically to handle this URI
  5. The plugin (Java) code is called, which parses the URI and then hands over the data to a script in the Unity project

Tools

I’m using Windows 7. The tools I used for the job:

  1. Unity Android Pro v4.1.3
  2. Android SDK v21
  3. Eclipse 4.2.1 + ADT v22

For help with installation see my previous post.

Note: The Unity scripts in this example are written in Boo. There’s about a 98% chance you’re writing in C# or UnityScript, but I’m sure you’ll find no problem understanding the scripts – they’re quite minimal.

Register Your Application with Instagram

First you will need an Instagram account. Then you should register your app.

Configure the Redirect URI using a custom scheme.
For example: mycustomscheme://oauth/callback/instagram/.
The important part is the scheme: mycustomscheme. Choose something unique.

The Website URL is not important. Choose whatever you want.

When the registration is complete you will get a Client ID. It looks something like this: c46692e6a1e57748a2b3f2b553f84c67. Keep it for later. You can access and edit this information any time.

Tip: You can manage your applications to revoke access. This might come in handy later, during debugging, in case you’ve already signed in and granted your application access but want to deny it. (Suppose you want to test the error case.)

Create an Android Plugin

The plugin will handle the response URI, extract the access token and send it to the Unity app. It will be written in Java.

Time to open Eclipse. Create a new Android Application Project. Most of the parameters are not important, such as application name, project name or Android SDK versions. They are controlled by Unity’s build settings. You should, however, pay attention to 3 things:

  1. Package name. For example: com.companyname.appname. Make sure this is the same as the Bundle Identifier in Unity’s Player Settings.
  2. Check the “Mark this project as a library” to produce a JAR file. (This can also be done later from the project properties under Android.)
  3. Activity Name. I’ll use MainActivity (the default) in this example. You will need to use this later when adding the plugin to your Unity project.

Add classes.jar to the Classpath

The location of classes.jar depends on your installation of Unity. The default location is: C:\Program Files (x86)\Unity\Editor\Data\PlaybackEngines\androidplayer\bin\classes.jar. More info here.

  1. Open: Project => Properties => Java Build Path => Add External JARs…
  2. Locate and select classes.jar

MainActivity.java

We will subclass UnityPlayerActivity and implement onNewIntent:

package com.companyname.appname;

import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;

import com.unity3d.player.UnityPlayer;
import com.unity3d.player.UnityPlayerActivity;

public class MainActivity extends UnityPlayerActivity {
    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        handleAccessToken(intent);
    }

    private void handleAccessToken(Intent intent) {
        Uri uri = intent.getData();
        if (uri != null && uri.toString().startsWith("mycustomscheme")) {
            String accessToken = "";
            if (uri.getFragment() != null) {
                accessToken = uri.getFragment().replace("access_token=", "");
                Log.d("Unity", "Found access token: " + accessToken);
            } else {
                Log.d("Unity", "Access token not found. URI: " + uri.toString());
            }
            UnityPlayer.UnitySendMessage("AccessManager", "OnAccessToken", accessToken);
        }
    }
}

Our new activity extends the standard UnityPlayerActivity class. The onNewIntent method will be called when the activity is re-launched. If a URI is found, it is parsed and the access token is extracted. UnityPlayer.UnitySendMessage sends the token to a script inside Unity, where AccessManager is the name of the game object and OnAccessToken is the name of the method. We’ll later create them in Unity.

That’s it. You should find an output JAR file in the project’s bin folder. We’ll copy it later to the Unity project.

Tip: When writing log output (Log.d) I used “Unity” for the tag. This is the same tag that Unity uses for Debug.Log. So when I use logcat to read the log output I can filter easily:

adb logcat Unity:V *:S

Error Handling

The response URI for a successful sign-in looks like this:

mycustomscheme://oauth/callback/instagram/#access_token=316140977.f598def.cf36764c5c6f4488fa95362592f14f91

For a failure it looks something like this:

mycustomscheme://oauth/callback/instagram/?error_reason=user_denied&error=access_denied&error_description=The+user+denied+your+request.

When the code above detects there is no fragment, it passes an empty string instead of an access token.

The Unity App

The Unity app needs to open the sign-in URL when the user chooses to sign in. And it needs to receive the access token from the plugin we wrote earlier.

Open Sign-In URL in Browser

All you need to do is open this link:

https://instagram.com/oauth/authorize/?client_id=c46692e6a1e57748a2b3f2b553f84c67&redirect_uri=mycustomscheme%3A%2F%2Foauth%2Fcallback%2Finstagram%2F&response_type=token
  • Fill in the Client ID you received from Instagram.
  • Use the Redirect URI you configured earlier. Replace the colon with “%3A” and all the slashes with “%2F” as above.

Notice the last parameter: response_type=token. This instructs Instagram to use the implicit flow and return the access token in the response URI.

The following Boo code simply formats the link string and calls Application.OpenURL:

SIGN_IN_URL_FORMAT = "https://instagram.com/oauth/authorize/?client_id={0}&redirect_uri={1}&response_type=token"
CLIENT_ID = "c46692e6a1e57748a2b3f2b553f84c67"
SIGN_IN_REDIRECT_URL = "mycustomscheme%3A%2F%2Foauth%2Fcallback%2Finstagram%2F"

signInUrl = SIGN_IN_URL % (CLIENT_ID, SIGN_IN_REDIRECT_URL)
Application.OpenURL(signInUrl)

Handling the Response

Create a game object named AccessManager and attach a script to it with a method named OnAccessToken. Make sure to use the same game object and method names as in the Java plugin code (UnityPlayer.UnitySendMessage). The method should have one parameter of type string, which will receive the access token. You can then use it to access Instagram API. (You may also want to store it using PlayerPrefs.)

Example code in Boo:

def OnAccessToken(accessToken as string):
    Debug.Log("Received access token: " + accessToken)
    PlayerPrefs.SetString("access_token", accessToken)
    // do something useful here

Adding the Plugin

The last part is to add the plugin we created earlier to the Unity project:

  1. Create a subdirectory Assets/Plugins/Android in your Unity project directory
  2. Copy the JAR file we created earlier to Assets/Plugins/Android.
  3. Create an AndroidManifest.xml file in Assets/Plugins/Android.
AndroidManifest.xml

Here we define our activity, MainActivity, as the main activity and also add an intent filter:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
  <application android:icon="@drawable/app_icon" android:label="@string/app_name">
    <activity android:name=".MainActivity" android:label="@string/app_name" android:launchMode="singleTask" android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen" android:screenOrientation="sensor">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
      <intent-filter>
        <data android:scheme="mycustomscheme" />
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
      </intent-filter>
    </activity>
  </application>
</manifest>

Use the same names you used in the Java plugin:

  • Activity (.MainActivity). Mind the prepending dot!
  • Custom scheme (mycustomscheme)

There is no need to specify the package name (com.companyname.appname) here. It is determined by Unity’s Player Settings (“Bundle Identifier”).

Notice the parameter android:launchMode=”singleTask”. This is the only mode that worked for me.

During a build Unity will extend this file and merge a few bits into it, such as the bundle identifier (package name), Android versions and permissions. The extended file, with the same name, can be found in the Temp/StagingArea subdirectory, which is deleted when Unity is closed.

That’s it! Should be working now.

Testing the Custom Scheme URI

If you open a browser on your mobile and try to open your URI (e.g. mycustomscheme://oauth/callback/instagram/) you will most likely find yourself in a Google search results page. That’s just how the browser works. To manually open your URI you’ll have to find a creative way to open a URI directly. For example:

  • Create a simple HTML that redirects to your URI, copy (email?) it to the mobile device and open it with some HTML viewer
  • Create a redirecting HTML and put it on your server, if you have one
  • Open the URI using your Unity app using Application.OpenURL (haven’t tried this one myself)

What’s Next?

There are two issue worth mentioning. I haven’t finished investigating them and tinkering with them yet, but I’ll say a few words.

UnityPlayerNativeActivity and UnityPlayerProxyActivity

In this solution we subclassed UnityPlayerActivity. We ignored two other (important?) classes, UnityPlayerNativeActivity and UnityPlayerProxyActivity, which were introduced in Gingerbread.

  • UnityPlayerNativeActivity – Parallel to UnityPlayerActivity. Can also be subclassed.
  • UnityPlayerProxyActivity – The top activity; it determines which of the other two activities (regular or native) should be started.

It is possible that ignoring the native player results in reduced performance in some cases.

WebView

I managed to open a WebView inside Unity using gree’s plugin. I will soon try to use a WebView for signing in instead of sending the user to the browser.

Edit: I’ve tried using WebView and it didn’t work. It would not delegate handling of a custom scheme. Instead it shows an error. I assume this behavior could be overridden if I had a way to set my own WebViewClient and override shouldOverrideUrlLoading.

iOS

See you then.

Enjoy!

Building Unity for Android with Eclipse

Eclipse + Unity

While working on a Unity project for Android I found the need to move the build process to Eclipse. This process used to be complicated and tricky, but thankfully it became rather simple with Unity 4.

Note: The instructions below relate to Windows 7. YMMV.

Before we begin, you’ll need to install two tools:

  1. Android SDK
  2. Eclipse + ADT Plugin

You can download them both here. They’re bundled together, named “ADT Bundle”. In the same page you can click “Use an existing IDE” to download only the SDK.

Android SDK

If you’re not familiar with the Android SDK, you should know that it consists of multiple components. After you install it you can manage which components are installed using the SDK Manager. The components that you must have are SDK Tools (installed by default) and at least one SDK Platform. I decided to go with Android 4.0.3 (API 15). Choose whatever suits you. Once you choose your platform, you only have to install “SDK Platform”. The rest is optional. (You may want “ARM EABI v7a System Image” to use the emulator.)

I also recommended installing whatever the SDK Manager suggest under Tools and Extras, namely: SDK Platform-tools, SDK Build-tools, Android Support Library and Google USB Driver.

Invokation Failed

I’ve encountered a bug with version 22 of the SDK. Unity could not build with it. I’d get the following error:

Edit: This bug seems to have been solved with version 22.0.1 of Android SDK.

At least this is the case with version 4.1.2f1 of Unity. If you have this problem, try downgrading your Android SDK version: Uninstall version 22 and then install version 21 (available here). Strangely enough, after you install version 21 you can let the SDK Manager upgrade “Android SDK Tools” (under “Tools”) to rev. 22 and Unity will still be able to work with it. Voodoo.

After you downgrade to rev. 21 and you’ll be seeing this error each time you open Eclipse:

android_sdk_tools_22_required

Don’t worry about it. Just hit Close.

Eclipse & ADT Plugin

If you’ve already built stuff for Android using Unity then you probably already have the SDK installed. You could install Eclipse for Mobile separately and then the ADT Plugin. But you can also download the ADT Bundle and take from it only the “eclipse” directory. It’s a download overkill (>400MB), but it’s easier to install.

Anyway, all Eclipse installations are simply ZIP files. There is no installer. Extract the ZIP file contents to wherever you want, e.g. “C:\eclipse”.

Export Eclipse Project from Unity

It’s time to open Unity. Open the build settings for Android and you’ll see a check box “Create Eclipse project”. Selecting it directs Unity to create an Eclipse project instead of building an APK file. When asked, choose a target directory for the Eclipse project.

Import Project into Eclipse

Open Eclipse. Choose “File => Import”. Under “Android” choose “Existing Android Code Into Workspace”. Fill the “Root Directory” field with the path of the project directory and hit Enter. You should see your project in the list. You might see more than one project if you’re using plugins. Deselect the plugins, you don’t need to import them – they’re included in the main project. Click “Finish”. That’s it. Your project is ready to be built.