Wednesday, 18 December 2013

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!