Tuesday, 11 June 2013

Common Issues With Google+ Sign-In On iOS

With everyone's hearts all a-flutter over the prettiness of iOS 7 from WWDC, I thought it would be a nice moment to summarise some potholes I've seen people trip over while implementing Google+ Sign-In on iOS. While overall it's pretty straightforward, there are some things that can make life a little tricky. However, for reference I've also put up a simple gist of a sign-in implementation that includes an AppDelegate and a ViewController with the sign-in button on.

In this case though, we'll take a look at some problems that might bite you during development, and some that might hit later on.

Forgetting the resource bundle

When you include the Google+ iOS SDK, you need three files: GoogleOpenSource.framework, GooglePlus.framework, and GooglePlus.bundle. If you forget the frameworks you're likely to get a big obvious compile error about not being able to find the classes you want to include, but the bundle can be a bit more subtle. It contains the translations for the supplied GPPSignInButton, and it includes the images which that button uses. If you don't include it everything will still compile - you'll just get an invisible button (which looks like the button hasn't loaded at all), as it can't find the images.

Forgetting -ObjC linker flag

The GoogleOpenSource.framework contains a number of files from the Google Toolbox for Mac, a very helpful collection of open source utility classes and libraries that are used extensively in Google libraries and apps. Several of these are implemented as categories, adding functionality often to foundation classes. Because of the vagaries of Objective-C linking, these category references don't necessarily cause their defining classes to be pulled in from the static libs that come with the SDK. This means that you get odd "unrecognized selector" errors around methods like gtm_httpArgumentsString.

The solution to this is to add the -ObjC flag to the "Other Linker Flags" in the project's Build Settings. This instructs the compiler to pull in the code for these categories, and everything can proceed smoothly.

One important point here is that the flag is case-sensitive: missing the capital O or C is pretty easy to do, and will result in the same kind of error.

Not setting up a callback URL - or having it slightly wrong

A number of operations in the SDK involve the user coming in from another application. These include sign-in, where the user will be redirected out to the Google+ app or browser to sign in, then directed back to the application; sharing where the user is taken out to the browser and then back; and deeplinking, where the user is sent to the application from the Google+ app. To do these things, the app needs to have defined a custom URL scheme, and that custom URL scheme needs to be registered in the API console. That custom URL scheme is based on the bundle ID of the application.

While failing to do this is fairly obvious (e.g. the user is left in Safari, Chrome or the Google+ app after the operation rather than being redirected back to your app), one issue that often trips people up is getting the bundle ID slightly wrong! It's very easy to have setup a client ID with a small typo in the bundle ID, that then doesn't match what is generated from the application itself. Always check that field closely if you have any errors around redirecting. Of course, once you redirect the call needs to go somewhere, leading to...

Not registering a openURL handler

Even if the bundle ID is right, it's really important to make sure that the GPPUrlHandler is defined to handle the call. This is a new-ish helper that manages routing openURL calls to GPPSignIn, GPPShare and GPPDeeplink depending on the type of the call. It returns a BOOL so you can easily check it if you have other openURL handlers.

Not using trySilentAuthentication

If you've ever wondered how to avoid having the user sign-in each time the app opens, trySilentAuthentication is your friend. If the user has already signed-in, the SDK stores a keychain entry for that, and (most of the time) calling trySilentAuthentication will fire the finishedWithAuth:error: call on the sign-in delegate in short-order.

The trySilentAuthentication call will return a BOOL to indicate whether it has a stored credential. If it does, you'll probably sign-in succesfully, but there is always the chance that the user has disconnected your application from the Google+ side, leading on to...

Not handling errors based on disconnect

Offering disconnect in an app is really important for Google+ Sign In. It's also pretty easy from the app itself: [[GPPSignIn sharedInstance] disconnect]. The tricky thing tends to be managing disconnects that have occurred in other applications, or (even more challenging) from the Google+ apps management page directly.

As of version 1.3.0 of the SDK, if there is no access token a new one will be automatically fetched, and finishedWithAuth:error: will be called with an error if the refresh fails. However, there is an edge case where the app is disconnected, but is still open on the user's iOS device. In this case, the access token will appear valid, but calls will fail.

To guard against this problem, you can check the error.code coming back in the completion block from API calls - 401 indicates that the call was unauthorised and will usually mean a token has been revoked - it's generally a sensible move to reset the user to a signed out state by calling [[GPPSignIn sharedInstance] signOut].

Not adding files for other APIs

It's common to want to use multiple Google APIs in one project, but just because the Google+ SDK contains a GoogleOpenSource framework, that doesn't mean it includes every class used by every other API. It's important to make sure to add any dependencies those APIs may have - for example if using the Google Drive SDK, GTMHTTPUploadFetcher is needed in order to upload files to Drive.

Forgetting to add a consent screen icon

You may have noticed that in the latest Google+ iOS app there is a place to actually manage your connected apps, and that the apps have an icon displayed there:

Just like on the consent screen, this icon is taken from the Branding Settings under your project in the API console. If you haven't uploaded an image there, you'll get some rather dull grey squares.

Hopefully you can avoid these issues in your own apps! If anything isn't clear check out the gist and the official documentation, and don't hesitate to ask a question in the Google+ Developers Community, or on the Stack Overflow tag!

Monday, 3 June 2013

Deeplinking Into The Google+ Apps

While setting up your application to receive deep links from the Google+ apps on web, Android and iOS is pretty well documented, it's not necessarily obvious that you can deep link into the Google+ apps on Android and iOS.

On Android, the Google+ app registers intent filters for (most of)the regular http://plus.google.com/* URLs. From version 4.4 of the Google+ iOS app, it also registers (again, most of) the web URLs, but with the custom gplus:// scheme. If you're reading this on one of those devices right now, you should be able to try these out.

Actually using these from your app is straightforward:

On Android, the easiest way of starting out is to just fire an intent for the desktop URL, which the Google+ Android app registers a filter to handle:

However, because we're using a regular web URL, the default action will be to give us a chooser, which is probably not the ideal result.

We can fix this by adding the package name to the intent, which will open Google+ directly:

However, if we do that and the user doesn't have the Google+ app installed, they're going to have a bad time. We can check in two ways: either by using the PackageManager, or (if we're integrating the Google+ SDK) using the GooglePlusUtil that comes as part of the SDK:

For iOS, we can use the UIApplication class to test whether the Google+ app is available, using canOpenURL:, and then make a call to openURL: with the gplus:// based URL to start the app:

One small caveat is that only numeric profile IDs work on the gplus:// URLs, so make sure not to use a +VanityUrl if linking to a profile!

Tuesday, 28 May 2013

Google+ iOS SDK 1.3.0

Google I/O was pretty busy for Google+ all round, and that includes from the point of view of anyone developing on iOS: we had (at my count) at least 25 iOS apps appearing from various partners at Google+ sandbox, there were a bunch of great questions coming our way at the developers sandbox, and on top of that my friends +Silvano Luciani and +Xiangtian Dai presented on integrating Sign-In, which you can watch on YouTube (or here!):

One bit of news that might easily have been missed was that there was a new version of the iOS SDK released, version 1.3.0. This was a pretty small release for features, but incorporates a lot of feedback from developers, and addresses a couple of common issues.

First, and possibly most helpfully, the various components have been packaged up as frameworks. There are now three packages in the SDK:

  • GoogleOpenSource.framework - the open Google Toolbox libraries, and the Google+ services
  • GooglePlus.framework - the headers and library file for the Google+ iOS SDK
  • GooglePlus.bundle - the translation strings and image assets for the Google+ Sign-In button.

This means adding the files is just a case of dropping in those frameworks, but it does mean that when upgrading you have to change imports to refer to the classes inside the framework name, e.g. #import <GooglePlus/GooglePlus.h>. Helpfully, that GooglePlus.h header includes all of the files you need, so unless you're keen to minimise links, you can just drop that in and forget about it.

The second feature is something I had completely missed, and only realised from watching XT demo the functionality! Rather than having to construct a GTLServicePlus, and pass it an authenticator, you can now actually get that directly from the GPPSignIn singleton via the plusService property:

Note that you might get yourself into trouble if you try this with a GPPSignIn that is not signed in - the authenticator wont be set, and you'll make unauthenticated calls. In the future, the team may have the library return nil for the plusService if its not authenticated, which should hopefully make this more obvious!

The third thing that was made easier was a common question from developers - how can I make sure I have an access token, to send to a server for example? In the background, the SDK holds a (hour-long) access token and a (long-lived) refresh token for you, and after the user interactively signs-in both are available. However, because the access token is relatively short-lived, only the refresh token is written to the keychain. This could cause a bit of confusion: when the user starts an app again and is signed-in smoothly with the trySilentAuthentication method, the sign-in delegate would get a callback, but then when it tried to grab the access token, the response would be nil as the SDK has only checked for the presence of the refresh token.

This would be taken care of automatically when calling GTLService* functions, and it was always resolvable by calling the authorizeRequest: method on the GTMOAuth2Authentication object (for example, with 'nil'as the request) to force a token to be generated, but that was not entirely obvious! In version 1.3.0 and up that is handled for you, so you can just grab the token and carry on. This also has the nice property that if the user has disconnected your app, then on calling trySilentAuthentication you'll receive a callback to didFinishWithAuth:error: with the error argument set appropriately.

Monday, 8 April 2013

Batching calls to Google APIs (Javascript)

One of the benefits of having a standardised API layer across all the (recent) Google APIs is that a bunch of features come for free. One of these handy items is batching, which is generally pretty easy to do.

For example, an awful lot of Google+ Sign-In implementations retrieve both the signed-in user's profile information and the collection of friends the user shared with the app when the user connects. This generally necessitates two calls, two connections, and two lots of overhead, but can be easily combined into a single request.

If you take a look at the Google+ Javascript QuickStart you'll see there is a profile function and a people function, each making a call to gapi.client.plus.people.something, and then request.execute. We can replace that with a single function that combines both, and looks a little like this:

All we've done here is created each request, and a new RPC batch with gapi.client.newRpcBatch(). It's called RPC batch as we're actually using the JSON RPC endpoint here - every API exposes both RPC and REST-style URL structured endpoints automatically, but the RPC one is a little easier to work with for batching.

We then add each of our requests (even though they're to different parts of the API) to the RPCBatch, and associate the callback with them. The other parameter we could pass in there is 'id', which would allow us to write a single callback function and pull out the requests we wanted if that was easier. One thing worth noting is that the callbacks are slightly different - in the batch version, the response we receive is a object containing the request ID and a 'result' object, which is equivalent to what we would have received when calling directly. This means there's an extra line of code to unwrap that, but otherwise the functions are the same as in the quickstart.

Finally, we just call rpcBatch.execute() and our callbacks are fired. For an idea of the savings, in a completely unscientific test I tried the standard quickstart and found the average time to fetch profile was around 180ms, and friends 500ms. With the batching operation, I found that the total was around 500ms - the friends was completely covering the time of the profile retrieval.

Where this type of thing really helps is in situations like on mobile, where establishing a TCP connection can really be painful. You can read more about the RPCBatch options in the Google APIs Javascript Client Library documentation.

Thursday, 21 March 2013

Retrieving The Signing Key Fingerprint on Android

This post is a bit of an aide-mémoire for myself. If you ever need to see which key signed an APK (for example to compare to a client ID in the API console when implementing Google+ Sign-In) you can actually extract the cert from the APK, and test it.

First you need to unzip the APK:

    unzip ~/my-app.apk

You're going to see a bunch of files extracted, including a CERT.RSA, which is usually in META-INF. If you use an alias for your key, it'll be THAT-ALIAS.RSA.

    inflating: META-INF/MANIFEST.MF
    inflating: META-INF/CERT.SF
    inflating: META-INF/CERT.RSA

You can then output the signatures for the certificate with the keytool app:

    keytool -printcert -file META-INF/CERT.RSA

This will print out the various fingerprints, and let you know the details of the certificates owner - handy for checking whether it was accidentally signed with a debug key (which will look something like this):

    Owner: CN=Android Debug, O=Android, C=US
    Issuer: CN=Android Debug, O=Android, C=US
    Serial number: 4f963ac8
    Valid from: Wed Apr 27 12:43:33 BST 2012 until: Fri Apr 20 12:43:33 BST 2042
    Certificate fingerprints:
        MD5: 84:9E:5D:C5:2C:F5:1A:D5:29:B5:D1:28:DF:1A:6D:86
        SHA1: 12:65:36:81:D2:8C:B3:7D:9E:48:55:66:DF:DD:1B:3D:6B:EC:E8:E9
        Signature algorithm name: SHA1withRSA
        Version: 3

Friday, 15 March 2013

Common problems with Google+ Sign-In on Android

It has been fantastic to see so many people trying out Google+ Sign-In, and through the bootcamps and other events I've had a chance to talk to some people who are actually implementing it in their apps. The Android integration is pretty straightforward, thanks to Google Play Services, but there are still some issues I've seem come up a couple of times.

tl;dr: make sure your app is set up in the API console, and make sure you can handle multiple onStart events coming in during sign-in.

1. Consent screen appears more than once. 

The basic life cycle of the PlusClient looks a bit like this:


When we call the onStart, we immediately call mPlusClient.connect(). Google Play Services checks whether the user is already signed in to the application, and, if so the onConnected method is instantly called and the user seamlessly signed in.

If the connect() call fails, then onConnectionFailed() is called with a ConnectionResult object, which represents the error. The error is probably RESOLUTION_REQUIRED, which unsurprisingly means it also has a resolution. That is generally an intent that can be kicked off. The first one that is likely to be seen is that the account needs to be chosen.

Starting the resolution for that result will display the chooser activity, and when it completed our onActivityResult() method will be called.  The onActivityResult() can then call connect() on the PlusClient again. If all the errors have been resolved, the onConnected() method is called, but we'll likely get another error, requiring the consent dialogue to be displayed. Once this has been accepted, and a token retrieved, the activity result is called, we connect() again and the onConnected() call is reached.

Two things throw flies into this ointment:

a) We generally don't want to resolve on the onConnectionFailed() error until the user presses a button
b) While the resolution is happening onStart() may be called multiple times on our activity

The first means we need to care about a bit of state in onConnectionFailed(). If the user hasn't pressed the button, we need to not start the error resolution, and in fact kick that off in response to the onClick(). Once they have started though, we should resolve every error we reach.

The second complicates this, because if we just resolve whenever the result has a resolution, if onStart gets called again, we'll get another connect and start kicking off the resolution again, displaying two dialogs!

The solution is just to put a flag around the functionality based on whether we're in the middle of a resolution - you can see that as the mResolveOnFail in the code above. This defaults to off, so that when the activity starts and calls the mPlusClient.connect() it doesn't immediately display the account chooser or consent dialogue to the user. We turn it on in response to the sign-in button being pressed.

We also flip it off again when we get the onConnected() callback, so that if the user signs out we're in the right state.


2. Sign-in succeeds, but we can't retrieve any profile information.

One of the often overlooked steps when setting up Google+ Sign-In on Android is that the project must be associated with a client ID from the API console. On the web and in iOS the developer has to specify this client ID and the application will show an error if it is not given, but on Android it is inferred automatically from the combination of the Android package name, and the SHA-1 fingerprint of the signing key.

The API console is where the developer enables various services, and where they can manage the quota they have assigned. If an application runs without a matching API console project, it is effectively assigned 0 quota. This means that an un-linked application can go through the sign-in flow, retrieve an oAuth 2.0 access token for the user, but not actually be able to make any calls. So, for example, calling a lot of the PlusClient method will result in unexciting nulls instead of exciting profile data.

Of course, it doesn't have to be that someone hasn't made a client ID! It can equally happen with a typo of the packagename, or (as in one case I saw earlier this week), the system having more than one Android keystore on it - so the fingerprint was from one, but the APK was signed with another. Either way, setting up the correct details in the console will resolve it.


If you're not sure whether this is the problem that you're having, there is a relatively easy way to check. Just sign-in as normal, and in you onConnected() callback retrieve the oAuth 2.0 access token. You need to do this off the main thread, else it can cause deadlocks:
You can then enter the access token into tokeninfo endpoint:
https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=YOUR_TOKEN_HERE
This should display a client ID that matches your project. If it displays something else it's probably not set up right. If it's 608941808256-43vtfndets79kf5hac8ieujto8837660.apps.googleusercontent.com, and looks a bit like this:

{
 "issued_to": "608941808256-43vtfndets79kf5hac8ieujto8837660.apps.googleusercontent.com",
 "audience": "608941808256-43vtfndets79kf5hac8ieujto8837660.apps.googleusercontent.com",
 "user_id": "104824858261236811362",
 "scope": "https://www.googleapis.com/auth/plus.login",
 "expires_in": 1353,
 "access_type": "online" 
}

Then the app hasn't matched a project at all,  so you'll need to configure it in the API console. If you see the error INVALID_KEY in the logs, it may also indicate that the API console project is not properly configured - though I must admit I haven't yet quite grokked under what circumstances that one does occur.

There's a full example activity with a plethora of comments in this Android Google+ Sign-In gist, which hopefully will be useful as a reference to one way of implementing sign in.

UPDATE - one additional bug that surfaced quite a lot around the release of the latest version of Google Play Services was the onConnectionFailed method being called with a ConnectionResult which does not have a resolution, and has an error code of ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED.

As you might guess from the name, this indicates that the version of Google Play Services on the device is too low. Normally new versions will be updated automatically, but there is always a time delay in the roll out, so it is quite possible to get this error as updates are released.

You can handle this in the onConnectionFailed by calling getErrorDialog on GooglePlayServicesUtil, but the best way is to actually check whether Google Play Services is installed and up to date before even trying to connect. You can see a snippet of how to do this in the documentation.

Wednesday, 6 March 2013

Postmessage & OAuth 2.0


As part of the release of Google+ Sign-In, some people have noticed that signing in via the Sign In button doesn't redirect them to Google, then back to the site, as would have happened if they'd been using the basic OAuth 2.0 flows.

One of the backbones of Javascript security is the same-origin policy, which limits running code from being able to see things from sources other than its own. For example, HappyImageWebsite.com can't go and read what's happening on another window showing SecureBank.com. Sometimes though it is helpful to be able to communicate between windows, or between a window and an iframe, that are from different origins. This is tricky, and has been the source of many interesting workarounds over the years.

The HTML 5 web message specification standardised a solution to this problem, in the form of the window.postMessage() method. As you might guess, this lets you send a message from one window to another in a pretty straightforward way, even if they're from different origins. E.g.:

var cw = document.getElementById("myiframe").contentWindow;
cw.postMessage("Hello World, "http://examplea.com");
The second argument is the target origin - if the loaded window is actually from a different place, then the message will not be sent (so you don't accidentally send messages to a rogue window). Within the window, we can listen for a callback by adding an event listener.
window.addEventListener("message",
function(e) {
if(e.origin == "http://exampleb.com")
alert(e.data);
},
false);


Here we can check the source origin to make sure it came from where we expected. This handy little API is supported basically everywhere, including IE8+.

So what does this mean for sign-in? In the normal OAuth 2.0 (client side) flow

1. The application generated a URL, and redirects the user to the provider
2. The provider shows them a consent screen or similar to approve the application
3. Once the user has submitted the consent form, they are redirected back to the site with an access token that can be used to access resources

The server side flow is broadly similar, except a shorter-lived code is sent which the server can exchange for an access token. Using postMessage, we can make this experience a bit easier.

1. The sign in button can create a hidden iframe as a post message relay
2. This can pop up a window as needed for consent and sign in, with a redirect-uri of "postmessage" rather than another site
3. Once the user has submitted the consent form, the window sends back an access token via postMessage to the consuming code

This saves roundtrips for the user, and potentially makes the experience faster and smoother.  Both sides can check the origin is what they expect (so registering the origin in console of the provider is still required). As it never puts an access token in the URL, its likely more secure as well.

This technique also allows "immediate mode" checking. In this case, when the button is created (or the Javascript initialised), a hidden iframe can be created pointing to the authentication URL, and this can go away and check whether the app is authorised. This is exactly what the Google+ Sign-In button does, meaning the site just has to setup a callback, and previously authorised users smoothly sign in.

It also allows some (potentially) useful functionality such as checking the session state without necessarily having to round-trip to a server.

So, wins! What's the downside?

1. You need Javascript, and that means adding another file on your page (if you don't already have the plusone button or other Google+ widgets). There is an asynchronous loading snippet that is definitely worth using to make sure it doesn't block your page rendering, but this can still be a concern.
2. Using this flow you can still request offline access which will return an access token for use in the client, and a short-lived code for the server*. However, as you're not generating the auth URL,  you need to ensure you send a one-time code out to the client and back with the code, and check it matches. This is basically what you would have been using as the 'state' parameter in the old flow.

If you want to have a look at how this looks in practice, check out https://code.google.com/p/oauth2-postmessage-profile - this application actually uses the shindig project's gadgets.rpc for the inter-frame communication, but that uses postMessage underneath. This kind of functionality is also explicitly called out in the OpenID connect spec, specifically the bit on session management.

* The server can exchange this for both an access token and a longer lived refresh token, which it can exchange for access tokens as needed.