Improving Sign-In Implementations

I've written before about common technical problems with Google+ Sign-In implementations, but there is whole other class of errors that I've seen in applications. These generally aren't technical mistakes per se, but more about best practice.

One the aims of the guidance on developers.google.com/+ is to promote a standard experience - users trust new applications because they behave in a way they expect. A good implementation does this, and makes all three parties involved happy: the app developer, the identity provider, and the user.

Bad consent screens

Common problems with consent are asking for more access or more scopes than are needed, and not adding a logo and clean name. For example, take this consent screen

Here the app name is generic, and not tied to where the user was signing in. The app is requesting full access to both my Drive account and my YouTube data, and there is duplication - it asks for basic information, and basic profile. Each of these can be resolved. We can add an icon and a better name via the developer console, so that the user can be confident they're granting access to the app they expect. The redundant profile information is due to requesting https://www.googleapis.com/auth/userinfo.profile as well as https://www.googleapis.com/auth/plus.login, so we can remove userinfo.profile without any negative - all of the profile data will be available from the plus.people.get API.

The excessive YouTube and Drive scopes can be scoped down to just what we need - perhaps requesting only "view" access with the https://www.googleapis.com/auth/youtube.readonly scope, or just access for appdata on Drive. It may also be that we don't really need that scope at all - there are at least a couple of high profile sites who request the ability to manage your contacts on sign-in simply because they cut and pasted the scopes from a demo, and don't actually use them!

Another common problem is requesting access to too many different kinds of app activity. Only request the ones your app is actually going to write, or likely to write in the near future.

Blocking seamless sign on

The Google+ Sign-In view of identity is that it is a service that an application requests access to, much like an app requests access to location in a browser or native app. The app shouldn’t have to badger the user to renew it when no further access is being asked for. Therefore if a user signs-in to an application using Google+ sign-in on one computer, then goes to use the app on another which has the same Google account, they should still be signed in.

This is accomplished in on the web through the fact that most users are signed in to Google somewhere, perhaps Gmail or YouTube for example. As long as they have an active cookie, we can use an iframe pointing to Google to check that same cookie, and then look up whether the user has approved a given site. This means I could go to a site for the very first time on a certain browser, and it could immediately know I previously granted access, and smoothly sign me in without having to click a button. On Android this same functionality is accomplished via the AccountManager, which will store the user's Google account and again make it available to be checked.

Not using this functionality means users have to choose to sign-in to your app again, even though they have previously consented. There are generally three reasons why people disable it:

1. Implementing using redirect based OAuth 2.0 rather than the Javascript and postmessage based version. Normally this is due to migrating from one of the older scopes, or using middleware. This is a very reasonable way to implement sign-in, but it is normally not massively difficult to migrate to the Javascript method: replace generation of the URL with outputting the sign-in Javascript, and instead of redirecting the user to a callback URL with a code in the query parameters, XHR the code to the server and redirect once a cookie has been established.

2. Intentionally disabling the sign-in callback on the web in order to allow users to simulate sign-out. If you want to let you users mark themselves as signed out, the best way now is to use gapi.auth.signOut.

3. Not checking for the sign-in state. This one happens particularly on Android, where app does not connect a PlusClient on startup. This means that while the user may have previously approved the app, they aren’t actually signed in until they visit a login activity. The same kind of problem does occur on the web.

On the web, try implementing the sign-in button as default hidden with CSS, and only make it visible if you get an immediate_failed callback (or wait till the user clicks on a login page). This will allow you to include it in each page, and check whether the user has previously consented without necessarily having to show them an UI.

On Android, implement the PlusClient in a base activity, or in a fragment that your activities include. Again, if you get the onConnected callback you know the user is signed in, but if you get onConnectionFailed you could just ignore it until the user visited a login activity.

In both cases, you don’t have to actually display a sign-in button everywhere, but you should test for signed-in state anywhere the user can enter your application.

Immediately asking for a password or other registration data

Passwords are kind of bad. People re-use them, they take work to store properly, and they need to be combined with other checks such as SMS for 2-factor authentication in order to be properly secure. However, it is a very reasonable desire not to have a third party be the only access method for a user's account. Instead of asking for a password right after a user signs in, why not generate a long, random password for them - then they can always use a Forgot Password flow if they want to access their account without using Google.

If you need other data, think about whether you absolutely need to prompt the user for it right after sign-in. Sometimes this is unavoidable, for example if you need to have an explicit age check, but in most cases the user can fill in the extra information as it becomes necessary. For example, if your app can filter results by location, and you want to know their home city but don't have the data via their profile, rather than asking up front for the information you could present an option letting the user know why: "Filter these results to your home town" or similar. Then when the user clicks, it would be natural and straightforward to ask for their home city.

Using the wrong branding

There are some branding guidelines that are worth taking a look at. In general, make sure that if your button doesn’t say “Sign in/Login/Connect With Google+” but “With Google”. It should still have the G+ icon though! The easiest thing to do is to use the buttons that are provided - this ensures that users who are familiar with sign-in will immediately recognise it, which hopefully will lead to a higher percentage of them signing in.

Not allowing users to disconnect

Disconnect is a distinct action from sign-out. Thinking back to that idea of identity as a service that the user grants access to, sign-out is temporarily not using the server, while disconnect is full revoking access. In fact, that is how it is implemented - calling the token revocation endpoint. What this does, from a users perspective, is signs them out everywhere, and marks the app as disconnected in the Google+ apps page, or the equivalent in the Google+ Android and iOS apps.

This is an important part of the trust in a system - the knowledge that the user can sever the link between their app account and Google+ profile, and not leave any access. Not that many people actually do disconnect, but the fact that it is there is important. It’s best to put a disconnect option in a settings page - somewhere discoverable, but not top-line visible (that should probably be for sign out, if implemented). Actually implementing requires two things: revoking access, and removing any data stored from the Google+ API. It doesn’t require deleting the account or anything similar.

If you had stored the placesLived data from a users’s Google+ profile, and they disconnected, it would be reasonable to pop up an editable form with that data. This could ask them to confirm they wanted you to have this data, and perhaps also allow them to set a password for continued access.

Actually disconnecting is generally very easy. Both Android and iOS have helper methods: revokeAccessAndDisconnect on Android and disconnect on iOS. For the web or server side its just a case of hitting the revocation endpoint with either a refresh or access token - both will revoke all access.

Adding disconnect, requesting extra data in context, fixing the branding or consent screen and enabling seamless sign-on are generally not large amounts of work, but doing the right thing can help users feel confident in the application, and more likely to use it. I would say that of the lot, allowing seamless sign-in will likely have the biggest visible impact on usage, but all of them should help the flow of registration.

Popular posts from this blog

Common problems with Google+ Sign-In on Android

Client-Server Authentication with ID tokens

Are you using approval_prompt=force?