Detect whether users have uploaded a profile picture to Google+

Very short post on a small feature that I know will be a popular one with some people! A regular feature request has been the ability to determine whether the profile picture with a Google+ profile is the default blue head or not. You can do that right now with the people.get API calls - and you can try it yourself from that page using the explorer.

The change is that under the "image" key you'll see an "isDefault" value. For my user you can see it's false:

"image": {
"url": "https://lh6.googleusercontent.com/.../photo.jpg?sz=50",
"isDefault": false
},

But for this blue head user, it's true:

"image": {
"url": "https://lh3.googleusercontent.com/.../photo.jpg?sz=50",
"isDefault": true
},

Hopefully this should make it easier to determine whether to use the profile picture from Google+ when people sign in to your apps.Client libraries might take some time to be regenerated, but its certainly available in the API right now.

Read More

Google Sign In with Server Side Auth on iOS

The release today of version 1.7 of the Google+ iOS SDK added the ability to authorise both a client and server for access to Google APIs. This has been a feature for Android and Web based sign-ins for a while, and now is available across all three platforms. This should simplify server side code for people who have been building cross platform apps - for example if you need to retrieve profile information on the client, but retrieve circles on a server for a friend finding feature.

The implementation for iOS is fairly straightforward. When setting up authentication, simply specify the client ID of your server on on the GPPSignIn object. This should be a client ID from the developer console which is used by your server-side code, and should be part of the same console project as your iOS client ID. For those that have done it, this is the same parameter used when retrieving an ID token:

You can see that the first part of the client ID (the project number) is the same, but the other part varies. If you try and exchange a code generated for one client ID using a different client ID you'll get an invalid_client error, even if both client IDs are part of the same project. When the user is signed in via [GPPSignIn authenticate] you can retrieve the code parameter from the GPPSignIn instance as part of the auth callback:

You can send this parameter to your server exactly as you would with one from Javascript or Android. When exchanging, be sure to not set a redirect uri in the server side auth configuration or you will get back a HTTP 400 error "redirect_uri_mismatch" from the token POST call. The exchange should always result in a refresh token (unlike with the web), so be sure to store that safely in a database or other persistent store if you're using it.

If the user is seamlessly signed in via a call to trySilentAuthentication, you won’t receive a code to use. Instead of forcing the user to sign in again with authenticate (and swap out to the browser or Google+ app to consent again) when you’re establishing a session with the server, try sending across an ID token and checking whether the server already has a refresh token for that user - this will mean a smoother experience for the user in most cases.

Read More

Migrating Away From Userinfo

As part of the move to full OpenID connect support recently, the "userinfo" scopes and endpoint were deprecated and scheduled for shutdown in September 2014. If you are using the userinfo API endpoint to retrieve email address or profile information for a Google user, now is the time to change! Luckily, it's just a few minutes of work to move from the userinfo API to the people.get API for most people, and wont affect users at all.

What should you do?

  1. Check whether you're using userinfo
  2. Update scopes
  3. Replace userinfo API call with a call to plus.people.get

Are you using the endpoint?

Look for the section in your code where you retrieve the user's profile or email address. If you make the API call directly, you may see a URL like "https://www.googleapis.com/oauth2/v1/userinfo". The "v1" might also be "v2" as there are a couple of different versions of the api, but if it has /oauth2 then you're using the userinfo endpoint. If you're using a client library, look for using the (somewhat confusingly named) OAuth2 service or API. For example:

PHP: new Google_Service_Oauth2($client);
Python: service = build('oauth2', 'v2', http=http)
Java: oauth2 = new Oauth2.Builder(httpTransport, JSON_FACTORY, credential).build();

All of these are indicative of retrieving the user data from the userinfo API, and will need to be changed before September 2014.

What scopes should you use?

https://www.googleapis.com/auth/userinfo.profile and https://www.googleapis.com/auth/userinfo.email can be seamlessly replaced with the shorter strings profile and email. These are aliases, so they wont require the user to reconsent if they've already give access. If you're thinking about a larger change, check out my earlier post about choosing a sign in scope.

What API should you call?

All profile information for Google users is now served from the people.get API. This works for users whether or not they have a Google+ profile. It returns data in a slightly different format to the userinfo API, so you may have to change your code a little, but all the same data is available. A call to retrieve the users name, picture and email address would look like this in PHP (and should be analogous with any of the client libraries).

You can actually try the call direct from the "Try It" section at the bottom of the API page to see what the returned data looks like.

What if you want to make the smallest code change possible?

If you aren't using a client library, and don't want to change the parsing you're using, you can call the special getOpenIdConnect method, which returns data in the same format as userinfo. The only difference needed then is looking for the "sub" field rather than "id" (this was a spec change made during the development of OpenID Connect). Take a look at the differences between the calls to the API below

Userinfo API response

https://www.googleapis.com/oauth2/v1/userinfo?access_token=ya29.1.AA...
{
"id": "104824858261236811362",
"email": "ian@example.com",
"verified_email": true,
"name": "Ian Barber",
"given_name": "Ian",
"family_name": "Barber",
"link": "https://plus.google.com/+IanBarber",
"picture": "https://lh6.googleusercontent.com/-DXmOngLN6Gc/AAAAAAAAAAI/AAAAAAAAGc4/Roeci0EovY8/photo.jpg",
"locale": "en-GB"
}
openIdConnect API response:
https://www.googleapis.com/plus/v1/people/me/openIdConnect?access_token=ya29.1.AA>...
{
"kind": "plus#personOpenIdConnect",
"sub": "104824858261236811362",
"name": "Ian Barber",
"given_name": "Ian",
"family_name": "Barber",
"profile": "https://plus.google.com/+IanBarber",
"picture": "https:https://lh6.googleusercontent.com/-DXmOngLN6Gc/AAAAAAAAAAI/AAAAAAAAGc4/Roeci0EovY8/photo.jpg?sz=50",
"email": "ian@example.com",
"email_verified": "true",
"locale": "en-GB"
}

In both cases, you do get a fair bit less than using the people.get API, so I would strongly recommend using people.get where possible!

There are a lot more tips on migrating to the newer sign-in options on the auth migration page, and of course if you have any questions drop into the StackOverflow tags for google-plus and google-oauth.

Read More

Migrating from PlusClient to GoogleApiClient

Thanks for reading the blog! This post is pretty old, and the APIs it references have been improved upon significantly. If you're interested in authentication check out the new Google Sign In, and if you're looking to access profile data see the People API.

Version 4.2 of Google Play Services introduced a new replacement for PlusClient, GoogleApiClient. While PlusClient is still present, it will be removed in a future version so its a good idea to upgrade when possible. The new class has been designed based on feedback from developers across various Google Play services APIs, and provides a much more consistent, powerful base.

PlusClient was reasonably straightforward for developers implementing just Google+ Sign-In. Unfortunately, anyone looking to combine multiple services together quickly realised that managing a series of callbacks for all the different connections was way too painful. GoogleApiClient solves that by providing one central client to connect or disconnect. It allows multiple APIs to be added to a connection for Google+, Drive, Games, and the other services available.

Replacing PlusClient with GoogleApiClient isn’t too hard, and if you are using multiple clients makes life gets a lot easier - you don’t have to worry about what order to start clients in, and a much more consistent and simple workflow for calls to different services.

Building the client

The GoogleApiClient should be constructed in precisely the same place as the old PlusClient was, using GoogleApiClient.Builder. Instead of the builder taking the listener objects for the ConnectionCallbacks and OnConnectionFailedListener as constructor arguments (which usually lead to this, this, this as arguments), these are now passed in separate methods. Only the context is still passed as a constructor argument, and the others can be omitted if they aren't needed for that particular connection.

A bigger change is that each individual service has to be requested by calling addApi on the builder. No scopes will be requested by default, so most services will need a call to addScope as well. Because more than one addScope can be chained, we can avoid worrying about space separating a big string of scopes. Our basic GoogleApiClient setup to replace a straightforward PlusClient integration would look like this:

As you might guess, we’ll need to update the types of the connection failed and callbacks interfaces: GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener. You may have noticed that the onDisconnected callback is now slightly more clearly named as onConnectionSuspended. Whatever the name, this still indicates the binding to Google Play services is no longer active.

Other than that we call connect and disconnect in exactly the same way as before, such as in our onStart/onStop methods:

With the onConnectionFailed we save and resolve a ConnectionResult exactly as we would have with PlusClient. The difference is that once resolved this will connect all client services at once.

Retrieving Friends

The way to work with result sets has changed, as we can see if we look at retrieving the visible collection of friends. The new interface is much more consistently applied across the different APIs, so once you get used to it here you'll find it a familiar pattern with whichever Google service you use next. Rather than all the methods sitting on the client class, they've been divided into logical APIs, such as Plus.PeopleApi. Each method takes a GoogleApiClient as the first argument, which is used to execute the call.

The interesting thing is that we have a couple of options on what to do with the PendingResult that comes back. The old PlusClient offered a listener interface which we could implement, and the same thing can be applied with the result here.

However we can also call await on the response to get the result back in a synchronous fashion. This can be much easier if we know we will hit a point where we cannot continue without the result, and are working off the UI thread. In both cases we can test whether the call was a success by calling the getStatus method on the result, and then retrieve a buffer for the collection to iterate over as normal. Its important we close the buffer at the end.

Note: you must have enabled the Google+ API in the developer console, or you’re likely to get back a status of Status{statusCode=NETWORK_ERROR, resolution=null}.

The really cool side effect of the pending result is that we can make the call before the client is connected! If we put the loadVisible call in the onCreate and set up a callback, we’ll be pinged back as soon as the user has signed in. Because this and the other feature are part of PendingResult rather than being dependent on the API, these methods can be applied to almost any call exposed through the new Play services model, whichever product they're for.

App Activities

Reading App Activities is pretty much the same as the People example, so should be an easy port. Writing has become a little bit simpler, but we need to make sure to request the proper types of App Activity when creating the connection to ensure the user grants us permission to write them. The old addActions method is no more, so the activity type should be passed to the addApi method as in a second parameter. This will be familiar to anyone that has used GoogleAuthUtil to request offline access, except with a custom object instead of a Bundle.

The App Activity types are now set as separate strings, rather than one space separated one. We pass the resulting options object as the (optional) second parameter to our addApi call to the GoogleApiClient.Builder.

Writing app activities is pretty straightforward, but because a PendingResult is returned you can get a notification when the write happens with the same kind of callback as before if desired.

Sign out and revoke

You may have noticed that GoogleApiClient doesn’t have a methods for clearDefaultAccount, getAccountName, or revokeAccessAndDisconnect. These options from PlusClient are part of the Plus.AccountApi. The value of the consistent approach to responses really shows with something like revoking access - instead of a custom callback type to get a signal once the user is disconnected, it will return a PendingResult which you can await or setResultCallback on just as with any other API.

Using the client with multiple APIs

Part of the original motivation for this change was to make it possible to add other APIs into a single GoogleApiClient connection. There was an example of connecting both Plus and Drive in one call by adding both APIs in the post on the Android developers blog. All that is required is multiple addApi and addScope calls for the various scopes and services required.

That’s about it! As more APIs find their way into Google Play services this type of client should make life a lot easier, and much simpler to extend as you want to add more functionality to your integration. If you have any questions on moving over try the Google+ developers community, or Stack Overflow tag.

Read More

Choosing a Google identity scope

With all the changes to Google+ Sign-In at the end of last year, it was easy to miss some of the extended options that have been added. In particular, this update added "profile" as a valid Google+ Sign-In scope, and its not immediately obvious what the implications of choosing between the different sign-in scopes are.

To help give a bit of context to the problem, there are really only three states of users that need to be considered when choosing a scope:

  1. Google+ user: This is a Google or Google Apps user who has a Google+ profile.
  2. Non-Google+ user: This is a Google or Google Apps user that has not upgraded to Google+.
  3. Google+ disabled Apps user: This is a user of a Google Apps account where the administrator has disabled Google+.

We can take a look at the two main sign-in scopes with that in mind.

profile

The most basic sign-in scope is profile. This can be used for all three classes of users. A token will be returned through the normal OAuth 2.0 process, and you will be able to retrieve the user's profile from the plus.people.get - even if the user has not upgraded to Google+. Using this scope will not prompt the user to upgrade. The data you get back will vary depending on which bucket they fall in to though. You can use this scope with Google+ Sign-In branding, and you will still get the benefits of cross-device sign on and over the air installs of Android apps.

Using the profile scope will add a line "View basic information about your account" or similar on the consent dialog when the user is granting access to your app.

Retrieving the profile information will show a different amount of data depending on whether the user has Google+ or not. The response for my demo account looks like this. Importantly, note that isPlusUser is set to true, and the user has display name and image.

{
"kind":"plus#person",
"etag":"\"RVZ_f1bhF-B19rh4H4M0uhzoFng/tjequdQwl0vd7Jl18-qniz0_KfU\"",
"gender":"male",
"objectType":"person",
"id":"113340090911954741696",
"displayName":"Ian Demobarber",
"name":{
"familyName":"Demobarber",
"givenName":"Ian"
},
"aboutMe":"This is just a demo account, please don't add me to circles!",
"url":"https://plus.google.com/113340090911954741696",
"image":{
"url":"https://..."
},
"organizations":[
{
"name":"Google",
"title":"Demo Accounter",
"type":"work",
"primary":true
}
],
"placesLived":[
{
"value":"Manchester",
"primary":true
}
],
"isPlusUser":true,
"language":"en",
"verified":false,
"cover":{
"layout":"banner",
"coverPhoto":{
"url":"https://...",
"height":624,
"width":940
},
"coverInfo":{
"topImageOffset":0,
"leftImageOffset":0
}
}
}

In either the case of a user without Google+, or one who cannot upgrade due to their Apps account settings, you will get a shorter response where isPlusUser is false.

{
"kind":"plus#person",
"etag":"\"RVZ_f1bhF-B19rh4H4M0uhzoFng/TmQIK9e5cog4EqQDPpSv_2wJ0e4\"",
"objectType":"person",
"id":"104089738742024349415",
"displayName":"",
"name":{
"familyName":"Barber",
"givenName":"Ian"
},
"image":{
"url":"https://..."
},
"isPlusUser":false,
"language":"en",
"verified":false
}

Pros: Lowest number of hoops to jump through, small consent line.

Cons: No access to Circles or App Activity writes, and less guaranteed profile data.

Its also worth noting that on Android (at the time of writing) if you use the profile scope with the PlusClient, the user will be taken through the Google+ upgrade flow if they haven't previously upgraded. You can use GoogleAuthUtil directly to avoid this if you need.

https://www.googleapis.com/auth/plus.login

This is the main Google+ Sign-In scope. It enables access to full the full range of Google+ features, but requires users to have a Google+ profile. The practical implication of this is that users may get an extra step in their consent flow asking them to upgrade the first time they sign in to an application that requests plus.login. There's no value in using both together, so if you ask for plus.login, you can remove profile.

This will ask for consent to "Know your basic profile info and list of people in your circles” and "Allow Google to let the people in these circles know that you have signed in to this app with Google” (or some variation), in each case with the ability to select different circles.

Each of the classes of uses will get slightly different behaviour with plus.login:

  1. Google+ users: See consent dialog, then sign in.
  2. Non-Google+ users: See upgrade page, then consent dialog, then sign in.
  3. Google+ disabled Apps user: See consent dialog, then sign in, but resulting token is automatically down-scoped.

The last one there is the most interesting case. Because users on an apps domain which has Google+ disabled cannot upgrade, they aren’t shown an upgrade screen. Instead of blocking them from signing in at all, the resulting token is effectively limited to a profile style scope. This means that you should allow for the possibility of not being able to write App Activities or retrieve friends of a user. In these cases you will receive a 403 error back, which you could use to mark that you should not attempt further calls of that type for that user (see the note in the auth migration doc for more). You can also check the isPlusUser field in the plus.people.get response (as above) in order to see whether you should be able to make such calls.

The profile response for a fully upgraded user looks similar to above, but also contains the an age range field. Using the scope will also mean that the vast majority of users will have Google+ profiles, with the associated richer profile.

{
"kind":"plus#person",
"etag":"\"RVZ_f1bhF-B19rh4H4M0uhzoFng/1azU0ZyG8HpqnhDWPNjBuLksUJ0\"",
"gender":"male",
"objectType":"person",
"id":"113340090911954741696",
"displayName":"Ian Demobarber",
"name":{
"familyName":"Demobarber",
"givenName":"Ian"
},
"aboutMe":"This is just a demo account, please don't add me to circles!",
"url":"https://plus.google.com/113340090911954741696",
"image":{
"url":"https://..."
},
"organizations":[
{
"name":"Google",
"title":"Demo Accounter",
"type":"work",
"primary":true
}
],
"placesLived":[
{
"value":"Manchester",
"primary":true
}
],
"isPlusUser":true,
"language":"en",
"ageRange":{
"min":21
},
"verified":false,
"cover":{
"layout":"banner",
"coverPhoto":{
"url":"https://...",
"height":624,
"width":940
},
"coverInfo":{
"topImageOffset":0,
"leftImageOffset":0
}
}
}

What about some of the others?

email (or https://www.googleapis.com/auth/plus.profile.emails.read) - This scope requests access to the users email address, which is included in the plus.people.get response as above, under an emails key.

"emails": [
{
"value": "me@example.com",
"type": "account"
}
]

The primary verified email will always have the type "account". The emails value is always a JSON array, and there may be more than one address returned in some cases.

https://www.googleapis.com/auth/plus.me - This scope allows replacing the Google+ user ID with "me". As that makes it trivial to call plus.people.get for the current user, it will cause a line on the consent dialog that refers to profile information. In pretty much every case this should be replaced with the profile scope - at the moment the only place you should use plus.me is if there is a legacy requirement on in it in the specific API (such as, at time of writing, with the Google+ Domains API).

https://www.googleapis.com/auth/userinfo.email and userinfo.profile - Both of these are superseded by the profile and email scopes, so you should be able to drop them anywhere they are still used. Note that the old userinfo API endpoint is now actually be handled by the Google+ services, so there is no need to continue hitting the user info API at all - plus.people.get is the best choice in all cases.

Incremental

Finally, its worth pointing out that you don't always have to choose. You could start with profile, and when users take an action that requires the Google+ data ("find my friends" or similar), prompt for an incremental upgrade to https://www.googleapis.com/auth/plus.login. This is more work, but may be a solution for those that want the simplest consent screen without giving up access to other features.

Read More

Incremental Auth and YouTube scopes

In my previous post I mentioned that there are two issues which have been made more visible by incremental auth. The first of these is fairly straightforward, but the second is a little more subtle. Incremental auth is a great feature for simplifying the consent screen that users see when they first sign in to an app, but it can also introduce a bit more complexity in some cases. An example of this is when requesting access to YouTube.

Because YouTube profiles support delegated access to Google+ pages, their data can be associated with these pages as well as general Google accounts. Whenever you request access to a YouTube scope (even in combination with other scopes), the user will have the opportunity to choose one of their pages if they have any. Currently this only occurs on the web, and will result in the user seeing a screen like this:

So far, so good - everything works as expected. Where it can get tricky is that if you ask for a YouTube scope incrementally, you need to account for the possibility of the user choosing a Google+ page when previously they had selected their Google+ profile. This is particularly common for people that merged their YouTube channel to their Google+ profile, but decided to keep the old username.

In this situation, its important to check the user ID associated with the returned token, and make sure that you can distinguish between the two. If the user loads another page that only requests the base scopes again (e.g. without YouTube), any immediate auth check (such as triggered by page level config) will return the user originally selected.

What do I need to do?

If dealing with YouTube, the best thing to do is allow for the case where the user ID changes after a YouTube incremental auth request. This means keeping the user ID around. In a client server situation this is relatively easy to do by keeping the user ID in a session or similar, but in the sample code here we'll store it in a local Javascript value.

When offering the incremental option, it may be easiest to always treat the token as separated. You could even set include_granted_scopes/includegrantedscopes to false in order to request only the minimal scopes required, if scanning the user's watch history for example. In the sign-in callback you can check whether the user ID of the new user is different than the existing one. If so, you can send a fresh code to your backend server, or immediately use the token to make the YouTube API calls.

Note: The ID token unpacker here is used for convenience, and shouldn't be used for any security functions - if you need to trust the user ID value then send the entire ID token to the server side, where the certificate should be validated!

If sent to the server, the resulting token should be stored this along side the existing one as an additional YouTube-specific token. Any YouTube API calls should be made using this token, falling back to the main token if no YouTube token is present. In a disconnect call, both tokens should be revoked. This is a bit more complex, but means you can support authentication being delegated without getting into a confusing situation with two users for the same account!

Read More

Are you using approval_prompt=force?

The recent launch of incremental auth has highlighted a couple of problems in the way some sites have implemented Google+ Sign-In or Google OAuth 2.0. The most obvious of these is that there are a fair number of places that use approval_prompt=force much more often than they should, which leads to a much worse user experience than there needs to be.

What’s the problem

Several sites set approval_prompt to force either in the Javascript Google+ Sign-In button (where it is generally approvalprompt), or in a parameter in the auth URL as part of a redirect based flow. This parameter means that users have to see the consent screen even if they had previously granted access to the application for the requested scopes.

While this was never a great user experience, the recent release of incremental auth has made it even more visible. Because the user has granted access before, they are not shown most scopes. However, because force is specified, they have to be shown a consent dialog. The only reason to show a consent dialog when the user has consented previously is to get a code that can be exchanged for a new refresh token - used primarily for offline access. Therefore the consent dialog displays that the app is requesting to "Have offline access" only.

This looks pretty weird! If you want to try it yourself, there is a small demo below.

There are two main reasons (that I'm aware of) why people do this:

  1. Its a copy and paste from an example
  2. To stop immediate mode auth from firing and signing the user in when the button displays

The second is a concern for people implementing the Javascript sign in flows, and usually is seen with a site sign-out feature. For example, you might try to log people out by deleting the application cookie, but then see them immediately signed-in again due to the immediate mode check when rendering the sign-in button. The best way to resolve this issue is to use gapi.auth.signOut when you log the user out. This sets local state that prevents the immediate mode check from signing the user in, and allows you to display the button appropriately.

In the first case where it is just came that way, simply remove the parameter and users will be able to sign in without seeing another consent dialog.

When should I use force

Forcing the approval prompt should only be used when you need to acquire a new refresh token. For example, if a user signed-in but their refresh token stored server side is no longer valid (perhaps it was accidentally deleted), you may want to pop up a dialog to say “we lost access to your [Google Service Here], please sign-in to re-enable”. At that point you can pass force in order to force a consent dialog to appear, and to get a code that returns a refresh token when exchanged.

Any questions about this, feel free to leave in the comments!

Read More

Launch All The Things!

Today was a busy day of launching stuff in Google+ land, and it is pretty easy to miss out on one or two features - particularly as the rest of Google wasn’t exactly keeping quiet on that front either. I wanted to briefly review what has happened, and why its interesting.

Incremental Auth

One problem if you’re integrating multiple Google services is that you have to ask for a laundry list of scopes. This new release means that if a user has already granted you some scopes, when you ask for more they will only see the consent screen for the additional one(s). You can check this out on 8tracks right now on the web when you connect your YouTube profile after sign-in, but it’ll work cross-platform.

To use it on Android or iOS just reauthorise with the additional scope. On Android you’ll need to add the scope and reconnect the PlusClient:

On iOS, add the scope and call authenticate again.

In both cases, its best to track which scopes you have, so you know whether you’re going to prompt the user. The web, however, makes this a bit easier! There is a new parameter includegrantedscopes which will automatically add previously granted scopes to a token, so you just have to request the ones you want. This is definitely best used with the page level config. You can have the basic scopes defined there, and the additional requested via a gapi.auth.signIn call:

Ability to migrate from OpenID2 to Google+ Sign-In/OAuth 2.0

Up until now, developers that were using OpenID2 had no easy way to move to OAuth 2.0/OpenID Connect. This has been resolved, thanks to the addition of an open_id parameter in the ID token. This allows you to look up an existing OpenID user and associate them with the Google+ user ID.

This requires specifying the openidrealm parameter on the sign-in button, with the realm you are currently using for OpenID. Then, grab the ID token from the sign-in callback and send it to a server that can check the mapping by looking at the open_id parameter and the sub parameter for the regular Google+ ID.

Simplified sign-in scopes

Similarly, things were a bit complicated if you wanted to use Google+ Sign-In, but had users that didn’t have Google+ profiles. Two things have improved that:

  1. You can now use the scope profile (that’s just profile, no https://www.googleapis…) with the sign-in button, and request only basic profile information. This will work for users who have not yet upgraded to Google+, and won’t push them through the upgrade flow.
  2. If you have enterprise users who have Google+ disabled, they will additionally be able to log in with the https://www.googleapis.com/auth/plus.login scope. Of course, API calls for things like friends will fail, so you should watch out for 403 errors.

There’s more about this and the OpenID updates in the auth migration documentation.

Upgraded plus/people/me

Another pain point was having to call a different API to get the user’s email address, or for a non-Google+ users’ profile. No longer! The plus.people.get endpoint will now work for users who have not upgraded to Google+ (with the profile scope), and it will also include email addresses. You still have to request permission to read the email address, but you can use the shorter email scope (again, no https://www.goo.. prefix), or the new https://www.googleapis.com/auth/plus.profile.emails.read scope.

Both of these will return the account email address in the emails field of the response. However, this field is an array, in the plus.profile.emails.read case any other verified emails associated with the account will be returned, which can be handy for matching users against existing app accounts. The first email returned (of the account type) will always be the primary email.


{
"kind": "plus#person",
"etag": "\"z42GAODeSfK9shwebqCuJQuYEu0/ZPDmtuV2cBdoJq7ydemTXzboBfk\"",
"emails": [
{
"value": "email@address.com",
"type": "account"
}
]
...

people/me/people/connected

Another long-standing API request is the ability to not just get back a list of who the user has in circles, but also which of those users have used your app. This is accomplished with the new connected collection on people.list.

GET https://www.googleapis.com/plus/v1/people/me/people/connected


{
"kind": "plus#peopleFeed",
"etag": "\"z42GAODeSfK9shwebqCuJQuYEu0/X-wx7Kx1_yYxA7zSOPxnVdHh_qA\"",
"title": "Google+ List of Connected People",
"totalItems": 4,
"items": [
{
"kind": "plus#person",
"etag": "\"z42GAODeSfK9shwebqCuJQuYEu0/HLHQbLg-DN9PK6BJ5oFljAa71Uw\"",
"objectType": "person",
"id": "118051310819094153327",
"displayName": "Chirag Shah",
"url": "https://plus.google.com/+chirags",
"image": {
"url": "https://lh5.googleusercontent.com/-XnZDEo..."
}
},
...

While you could have tracked this sort of thing by hand before, this makes it a lot easier and less resource intensive.

Resource timing in the Google+ widgets

The first step to improve performance is generally to get good measurements. The W3C Resource Timing spec defines a standard interface to expose timing measurements, so that its easier to track the different stages of execution of chunks of Javascript. As of today, the Google+ plugins (and others!) support this interface, so you can see how much time they take to load.

That’s the lot for today! I’ve probably missed something as well, so feel free to comment if you can think of anything else, and watch out for more releases before the end of the year.

Read More

Seamless Sign-In Across Devices

At an event last week Ade, Lee and I hosted a discussion about cross-device sign-on, where we covered not only what it was, but why its interesting and how it works. One conclusion we came to was that minimising the number of times your users have to see the sign-in button and make an active choice to sign-in can really help smooth the experience of using an application. However, there are a couple of pitfalls that can mean you don’t feel the benefits from the feature.

What do we mean by cross device sign-in?

Once a third party is involved, authentication is no longer just about exchanging some credentials for a cookie or identifier of some kind. Unfortunately, a lot of the time we do exactly that: treat the sign-in as simply being a replacement for sending a username and password across to the server which can return a cookie. Once the user has a cookie, they are app-identified, and hence can use the site as normal.

The problem is that this approach basically treats auth as a one time deal - once its done, the apps interaction with Google is ended (bar any API calls). What this ignores is that the user consented to grant access to the app, and that consent persists and extends beyond the scope of the app's session cookie.

In the 'exchange' model, if the user signs in on their web browser at work they execute the flow and get a cookie. If they go home and sign in on their personal laptop then the flow starts again: credentials, lookup, cookie. However, in the Google+ Sign-In case, this flow can be short circuited as the user consent is still valid. This means the user has effectively already signed-in on their home machine, and should just be able to go the URL and carry on with what they were doing!

How does this work?

We can’t share cookies between the two machines in the above scenario, but they may well have access to a common third party - the user’s Google account. If the user is in an environment where they have an active session with Google, we should be able to shortcut the sign-in process (if they have previously given consent). Right now, that shortcut works on Android and on the the web. On Android the active session with Google is stored in the AccountManager of the device, and on the web it requires that they are signed in to their Google account in the browser session - which could be for Gmail, YouTube, or any other Google service they are using.

The flow in these environments therefore looks a bit different (in the no-app-cookie state):

  1. Test for existing consent with Google
  2. If connected, establish an app sessions
  3. If not connected, present sign-in options as normal

This works even in a multi-sign-in environment where other sign-in options are available. If you want to encourage users to connect other providers, it is easy to give them the option to associate extra profiles with their application account.

How do you implement this

  1. Use the Google+ SDKs: PlusClient in Google Play services for Android, the Google+ Javascript SDK for web.
  2. Make app settings consistent across platforms
  3. Test for consent anywhere the user can enter the site or app
  4. Handle the callback

Each of these points can be the source of a problem.

Using the Google+ SDKs.

On both platforms, there are other options for sign-in. These include redirect based OAuth 2.0, using gapi.auth.authorize in Javascript and using GoogleAuthUtil or a web-based sign-in on Android. In general, the best approach is to use the Google+ SDK functionality directly.

On Android, the PlusClient should be connected in the activity's onStart, which triggers the sign-in test as soon as the activity is started. If the user is not signed in, the ConnectionResult is just stored for later error resolution.

On the web, defining page level configuration in meta tags means the sign-in test will be triggered as soon as plusone.js/platform.js is loaded. The callback will be triggered whether or not the user is signed in, where you can easily test for the current state.

Consistent App Settings

Primarily this is ensuring that you are asking for the same OAuth 2.0 scopes everywhere. This should include the plus.login scope used for Google+ Sign-In. Its important to make sure requested scopes match in all environments. If they don’t, the user will have to consent again for the variations. This can be quite subtle - in at least one case I know of an app was asking for the ability to manage contacts on the web, but on Android just the ability to view contacts. Its also possible to miss scopes between different buttons in a web environment if not using page level config - particularly in easily overlooked places, such as interactive posts.

The best way to do this is to centralise on both platforms into standard includes. Because the scopes are just a string, you could even potentially have a build system source the config from the same place.

Testing for consent

Another common problem is only putting the sign-in button or connecting the client on a login page or activity. This means that the user will be treated as signed out until they go to the login page, at which point they’re suddenly signed in. This is not a good experience, and means they have been using a system anonymously until then. If using a roadblock type sign-in pattern this is less of an issue, but its definitely better to be flexible about where you present the sign-in button and test for consent anywhere that a user can enter the app.

This is why on Android we recommend that you setup your PlusClient somewhere available throughout your application - such as in a base activity, a fragment, or a service. You don’t necessarily have to present UI everywhere, but it is important to do the PlusClient.connect.

On the web, including the page level config and the platform.js file will perform an immediate check for sign-in when the page loads. Its important to make sure the page level config is defined before platform.js is loaded, so it is picked up properly. Alternatively, the sign-in button can be rendered in a hidden element in order to force a check.

Handling the callback

There are a few apps and sites out there that trigger the consent test, but for various reasons don’t actually take any action on the result. Since this may cause the users to see the “welcome back” toast, it can be an unusual user experience.

Make sure that anywhere you test for consent you also provide the callback code that properly establishes an app session. Again, centralising this in a standard Javascript include or a Android base activity can make life much easier.

What about sign-out

One legitimate concern with this seamless sign-in is around sign-out. If you want to offer your users the ability to sign out, but seamlessly sign people in, then you could end up with a situation where you clear their cookie only to have them seamlessly signed in and right back into the site.

Because of the ongoing relationship, there are two levels of connection that can be modified separately. The first one is the device specific user state. That is what we think of as sign-out. For example, I might sign out of a web site, but still be signed in to the matching Android app, or the site on another machine. For this purpose, there are device specific methods: gapi.auth.signOut on the web and PlusClient.clearDefaultAccount on Android.

However, there is also the consent between the user and the application. This is the “disconnect” case that apps should provide, to allow users to sever the connection between the app and their Google account (though the relationship between the user and the app could of course continue independently). Disconnecting will revoke access, which will stop further sign-in across all platforms. On Android, there is a helper method: PlusClient.revokeAccessAndDisconnect. On the web, it’s a little more involved as it requires making a call to the token revocation endpoint with an access token, which could be executed from client or server.

What about user switching

One question that came up during the session was how to deal with user switching? Some applications are more likely to see multiple users on the same computer. The best way of dealing with this, particularly if the pool of users is stable (such as with a family computer) is to encourage users to use multiple browser profiles (for example, with Chrome), or device profiles on Android.

If the use case is likely to be a more temporarily shared environment (such as an internet cafe or similar), it's worth reminding the user about what system they signed in with, to prompt them to properly sign out when they have finished their session. This is a good practice in any case, and can be done elegantly, as seen here with musiXmatch.

What about iOS

One final question is, what about iOS? Well, at the moment there is no way for an app to access a user's Google session without switching to another application. Usually this is the Google+ app, or Chrome/Safari. However, once a user has done this (by pressing the sign-in button), a long-lived token will be stored in the keychain of the device for that app, so the user shouldn't have to sign-in again. While this is very easy to implement it does mean that the user has to actively sign-in, so can't be seamless signed in on first use.

Read More

Google+ Sign-In on the Android Chromium WebView

During the development of the new Chromium powered Android WebView, Internet celebrity Matt Gaunt showed me some rather interesting demonstrations. This included integrating with Google Play services, in the form of letting the user natively sign-in from the WebView. Now all the changes are publicly available I had to try such a thing myself, and found it surprisingly straightforward!

The first step is to actually create the HTML that we’ll be using. While it would be possible to use the Google+ Javascript SDK to make API calls and so on, I couldn’t actually think of a good reason to do so. What feels like a more general use case is presenting data retrieved through the API calls in the native SDK, which is how we’ll structure it. This is what my test page looks like, which is saved as part of my Android project in assets/index.html.

Two things are of note here. First, we define a callback to be triggered after a sign-in. Second, we make two calls to MainActivity" one to indicate the document is ready to be interacted with, and one to trigger a sign-in process. This is what we’ll need to support in our Android activity.

The first step to do that is to initialise our WebView. Its included in the activity by adding it to the layout file:

Next we need to configure it. We do this by finding the view, and setting some key parameters.

Firstly, we enable Javascript. By default this is disabled, which makes sense if you’re just using the view to display some HTML formatted contents, but for our case we need to turn it on. Next we add a Javascript interface pointing to a class which we’re about to define. The second argument to addJavascriptInterface tells the WebView how to to expose it within the page, which is why our calls in the HTML snippet start with MainActivity. Finally, we load the HTML file.

Half the magic then happens within this SignInInterface, which we define as a private class:

We define the two calls which we are exposing to our page, documentReady and signIn. Both are annotated with @JavascriptInterface, which makes them eligible for inclusion in the WebView. The documentReady method just kicks off a connect, or if the PlusClient is already connected triggers the callback to the WebView. The sign-in method resolved the ConnectionResult retrieved from a onConnectionFailed callback, as with a regular sign-in flow on Android. When the Javascript sign-in method is called, the call is forwarded to our class here, and we prompt the user for consent.

When that happens, we’ll get the normal onConnected or onConnectionFailed callbacks. The onConnected is the one we really care about, and as in the documentReady case, we’ll just call the signalWebview method we're about to define on our activity.

The signalWebview method is where we make the callback to the WebView itself. This works by evaluating a Javascript string that we construct within the context of the view's Javascript thread. This uses evaluateJavscript, one of the new methods in KitKat that really make it easy to work with the WebView.

In this case we evaluate a call to the handleSignIn method, which passes the display name of the connected user. Its worth noting there's absolute no escaping on this, so if the user had a ' in their name, things would go wrong. Since a real app is likely to want multiple pieces of data, it would probably make sense to JSON encode the lot before passing it to the WebView.

The documentReady method in our Javascript lets us know that this function has been defined - otherwise we might get an onConnected callback before the Javascript function has been interpreted, so our evaluateJavscript call would fail to pass the information correctly. As it is, it works either way, and we can easily sign-in and retrieve data from our WebView.

You can read more about using the new features in the WebView in the Chrome documentation and the WebView class documentation.

Read More