Using Google Sign-In with your server

This is the third part in a blog series on using Google Sign-In on Android, and how you can take advantage of world-class security in your Android apps. In part 1, we spoke about the user experience improvements that are available to you. In part 2, we then took a deeper dive into the client-side changes to the Google Sign-In APIs that make coding a lot simpler.

In this post, we will demonstrate how you can use Google Sign-In with your backend. By doing so, users signing in on their device can be securely authenticated to access their data on your backend servers.

Using Credentials on your server


First, let’s take a look at what happens if a user signs in on your app, but they also need to authenticate for access to your back-end server. Consider this scenario: You’ve built an app that delivers food to users at their location. They sign into your app, and your app gets their identity. You store their address and order preferences in a database on your server.
Unless your server endpoints are protected with some authentication mechanism, attackers could read and write to your user database by simply guessing the email addresses of your users.



Figure 1. An attacker could submit a fake request to your server with an email address
This isn’t just a bad user experience, it’s a risk that customer data can be stolen and misused. You can prevent this by getting a token from Google when the user signs in to the app, and then passing this token to your server. Your server would then validate that this token really was issued by Google, to the desired user, and intended for your app (based on your audience setting, see below). At this point your server can know that it really is your user making the call, and not a nefarious attacker. It can then respond with the required details.


Figure 2. Attacker’s Forged Tokens will be rejected
Let’s take a look at the steps for doing this:
Step 1: Your Android app gets an ID token (*) after signing in with Google. There’s a great sample that demonstrates this here. To do this, the requestIdToken method is called when creating the GoogleSignInOptions object.

 GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)  
         .requestIdToken(getString(R.string.server_client_id))  
         .requestEmail()  
         .build();  
This requires you to get a client ID for your server. Details on how to obtain this are available here (see Step 4).
Once your Android app has the token, it can POST it over HTTPS to your server, which will then try to validate it.
(*) An ID token is represented using JSON Web Token, as defined by RFC7519 and the OpenID Connect spec. These are an open, industry standard method for representing claims securely between two parties.
Step 2: Your Server receives the token from your Android client. It should then validate the token with methods that are provided in the Google API Client libraries, in particular, verifying that it was issued by Google and that the intended audience is your server.
Your server can use the GoogleIdTokenVerifier class to verify the token and then extract the required identity data. The ‘sub’ field (available from the getSubject() method) provides a stable string identifier that should be used to identify your users even if their email address changes, and key them in your database. Other ID token fields are available, including the name, email address and photo URL. Here’s an example of a servlet that was tested on Google App Engine that can verify tokens using a provided library. These libraries allow you to verify the token locally without a network call for every verification.

 GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(transport, jsonFactory)  
       // Here is where the audience is set -- checking that it really is your server  
       // based on your Server’s Client ID  
       .setAudience(Arrays.asList(ENTER_YOUR_SERVER_CLIENT_ID_HERE))  
       // Here is where we verify that Google issued the token  
      .setIssuer("https://accounts.google.com").build();  
 GoogleIdToken idToken = verifier.verify(idTokenString);  
 if (idToken != null) {  
       Payload payload = idToken.getPayload();  
       String userId = payload.getSubject();   
       // You can also access the following properties of the payload in order  
       // for other attributes of the user. Note that these fields are only  
       // available if the user has granted the 'profile' and 'email' OAuth  
       // scopes when requested. (e.g. you configure GoogleSignInOptions like   
       // the sample code above and got a successful GoogleSignInResult)   
       // Note that some fields may still be null.  
       String email = payload.getEmail();  
       boolean emailVerified = Boolean.valueOf(payload.getEmailVerified());  
       String name = (String) payload.get("name");  
       String pictureUrl = (String) payload.get("picture");  
       String locale = (String) payload.get("locale");  
       String familyName = (String) payload.get("family_name");  
       String givenName = (String) payload.get("given_name");  

How Fabulous and Yummly grew with App Invites

Introduced in May 2015, App Invites is an out-of-the-box solution for conducting app referrals and encouraging sharing. So far, we’ve seen very positive results on how the feature improves app discovery. While 52 percent of users discover apps by word of mouth, we have seen 92 percent of users trust recommendations from family and friends with App Invites. In this post, we’ll share some success stories from companies that have already used App Invites to grow their user base.

Fabulous is a research-based app incubated in Duke University's Center for Advanced Hindsight. The app helps users to embark on a journey to resetting poor habits, replacing them with healthy rituals, with the ultimate goal of improving health and well-being.

Users started taking advantage of App Invites within the app to share their experience with their friends and family. App Invites installs now account for 60 percent of all Fabulous installs via referrals. Sharing clicks also increased by 10 percent once App Invites were used. Fabulous also noticed increased user retention, with 2x the Life Time Value of the app for users that came in to it via App Invites. Fabulous simplified their user experience, combining SMS and email into a single interface, allowing users to focus on sharing.

Additionally, users that were acquired via App Invites versus other channels were found to be twice as likely to stay with the app.
CTO of Fabulous, Amine Laddhari, commented, “It took me only a few hours to implement App Invites versus several days of work when we built our own solution. It was straightforward!”
You can view the full case study from Fabulous here.
Yummly, a food discovery platform that views cooking a meal as a personalized, shareable experience wanted to expand its user base and generate awareness on the Android platform. It added App Invites so that users could recommend the app to their family and friends, giving functionality to share specific recipes, dinner ideas or shipping lists.
With App invites, they found that installation rates were about 60 percent higher compared to other sharing channels. Additionally, Yummly was able to take advantage of the seamless integration of Google Analytics. It’s the only share channel that has this integration, allowing data such as the number of invites sent, accepted and resulting installs to be accurately tracked.

Melissa Guyre, Product Manager at Yummly, commented, “The App Invites Integration process was seamless. A bonus feature is the excellent tracking tie-in with Google Analytics.”
You can view the full case study from Yummly here.

App Invites is available for Android or iOS, and you can learn how you can build it into your own apps at g.co/appinvites.

New features to better understand player behavior with Player Analytics

Google Play games services includes Player Analytics, a free reporting tool available in the Google Play Developer Console, to help you understand how players are progressing, spending, and churning. Now, you can see what Player Analytics looks like with an exemplary implementation of Play games services: try out the new sample game in the Google Play Developer Console, which we produced with help from Auxbrain, developer of Zombie Highway 2. The sample game uses randomized and anonymized data from a real game and will also let you try the new features we’re announcing today. Note: You need a Google Play Developer account in order to access the sample game.

Use predictive analytics to engage players before they might churn


To help you better understand your players’ behavior, we’ve extended the Player Stats API in Player Analytics with predictive functionality. The churn prediction method will return data on the probability that the player will churn, i.e., stop playing the game, so you can create content in response to this to entice them to stay in your game. Additionally, the spend prediction method will return the probability that the player will spend, and you could, for example, provide discounted in-app purchases or show ads based on these insights.

Create charts in the new funnels report to quickly visualize sequences of events


The funnels report enables you to create a funnel chart from any sequence events, such as achievements, spend, and custom events. For example, you could log custom events for each step in a tutorial flow (e.g., tutorial step 1, step 2, step 3), and then use the funnel report to visualize the exit points in your tutorial.


Measure and compare the effect of changes and cumulative values by new users with cohort’s report


The cohorts report allows you to take any event such as sessions, cumulative spend, and custom events, and compare the cumulative event values by new user cohorts - providing valuable insight into the impact of your decisions on your gaming model. For example, you can view users that started the day before you made a change and the day after. This allows you to measure and compare the effect of changes made, so if you doubled the price of all your items in your in-game store, you can see if the cumulative sessions started after the change was lower or higher than the users that started before the change.

Play Games Permissions are changing in 2016

We’re taking steps to reduce sign-in friction and unnecessary permission requests for players by moving the Games APIs to a new model. The new interaction is:
  • Players are prompted to sign-in once per account, rather than once per game
  • Players no longer need their account upgraded to Google+ to use Play Games services
  • Once players have signed-in for the first time, they will no longer need to sign in to any future games; they will be automatically signed in
  • Note: Players can turn off auto-sign-in through the Play Games App’s settings
Advantages:
  • Once a user signs in for first time, new games will generally be able to sign in without any user interaction
  • There is no consent screen required for signing in on any particular game. Sign-in will be automatic to each new game.
In order to respect user’s privacy and avoid revealing their real name, we also have to change the way player IDs work.
  • For existing players: Games will continue to get their Google+ ID (also called “player ID” in previous documentation) when they sign in.
  • For new players: Games will get a new player ID which is not the same as the previous IDs we’ve used.

Potential issues


Most games should see no interruption or change in service. There are a handful of cases, however, where some change is required.
Below are some issues, along with potential solutions.
These are:
  1. Asking for the Google+ scope unnecessarily
    • Issue: Your users will get unnecessary, potentially disturbing pop-up consent windows
    • Solution: Don’t request any additional scopes unless you absolutely need them
  2. Using the Play Games player ID for other Google APIs that are not games
    • Issue: You will not get valid data back from these other endpoints.
    • Solution: Don’t use player ID for other Google APIs.
  3. Using mobile/client access tokens on the server
    • Issue: Your access token may not contain the information you’re looking for
      • ...and this is not recommended in the first place.
    • Solution: Use the new GetServerAuthCode API instead.
Let’s cover each of these issues in detail.

Issue: Asking for unnecessary scopes


Early versions of our samples and documentation created a GoogleApiClient as follows:
 // Don’t do it this way!  
 GoogleApiClient gac = new GoogleApiClient.Builder(this, this, this)  
           .addApi(Games.API)  
           .addScope(Plus.SCOPE_PLUS_LOGIN) // The bad part  
           .build();  
 // Don’t do it this way!  
In this case, the developer is specifically requesting the plus.login scope. If you ask for plus.login, your users will get a consent dialog.

Solution: Ask only for the scopes you need


Remove any unneeded scopes from your GoogleApiClient construction along with any APIs you no longer use.
 // This way you won’t get a consent screen  
 GoogleApiClient gac = new GoogleApiClient.Builder(this, this, this)  
           .addApi(Games.API)  
           .build();  
 // This way you won’t get a consent screen  

For Google+ users


If your app uses specific Google+ features, such as requiring access to the player’s real-world Google+ social graph, be aware that new users will still be required to have a G+ profile to use your game. (Existing users who have already signed in won’t be asked to re-consent).
To require Google+ accounts to use your game, change your Games.API declaration to the following:
 .addApi(Games.API, new GamesOptions.Builder()  
                       .setRequireGooglePlus(true).build())  
This will ensure that your game continues to ask for the necessary permissions/scopes to continue using the player’s real-world social graph and real name profile.

Issue: Using the Player ID as another ID


If you call the Games.getCurrentPlayerId() API, the value returned here is the identifier that Games uses for this player.
Traditionally, this value could be passed into other APIs such as Plus.PeopleApi.load. In the new model, this is no longer the case. Player IDs are ONLY valid for use with Games APIs.

Solution - Don’t mix IDs


The Games APIs (those accessed from com.google.android.gms.games) all use the Player ID, and as long as you use only those, they are guaranteed to work with the new IDs.

Issue: Using mobile/client access tokens on the server


A common pattern we’ve seen is:
  • Use GoogleAuthUtil to obtain an access token
  • Send this token to a server
  • On the server, call Google to verify the authenticity. This is most commonly done by calling https://www.googleapis.com/oauth2/v1/tokeninfo and looking at the response
This is not recommended in the first place, and is even more not-recommended after the shift in scopes.
Reasons not to do this:
  • It requires your app to know the current account the user is using, which requires holding the GET_ACCOUNTS permission. On Android M, this will result in the user being asked to share their contacts with your app at runtime, which can be intimidating.
  • The tokeninfo endpoint isn’t really designed for this use case - it’s primarily designed as a debugging tool, not as a production API. This means that you may be rate limited in the future if you call this API.
  • The user_id returned by token info may no longer be present with the new model. And even if it is present, the value won’t be the same as the new player ID. (See problem 2 above)
  • The token could expire at any time (access token expiration times are not a guarantee).
  • Using client tokens on the server require extra validation checks to make sure the token is not granted to a different application.

Solution: Use the new GetServerAuthCode flow


Fortunately, the solution is known, and is basically the same as our server-side auth recommendations for web.
  1. Upgrade to the latest version of Google Play Services SDK - at least 8.4.87.
  2. Create a server client ID if you don’t already have one
    1. Go to the Google Developer Console, and select your project
    2. From the left nav, select API Manager, then select Credentials
    3. Select “New Credentials” and choose “OAuth Client ID”
    4. Select “Web Application” and name it something useful for your application
    5. The client id for this web application is now your server client id.
  3. In your game, connect your GoogleApiClient as normal.
  4. Once connected, call the following API:
    1. Games.getGamesServerAuthCode(googleApiClient, “your_server_client_id”)
    2. If you were using GoogleAuthUtil before, you were probably calling this on a background thread - in which case the code looks like this:

 // Good way  
 {  
      GetServerAuthCodeResult result =   
           Games.getGamesServerAuthCode(gac, clientId).await();  
      if (result.isSuccess()) {  
           String authCode = result.getCode();  
            // Send code to server.  
   }  
 }  
 // Good way  


  1. Send the auth code to your server, exactly the same as before.
  2. On your server, make an RPC to https://www.googleapis.com/oauth2/v4/token to exchange the auth code for an access token, probably using a Google Apis Client Library.
    1. You’ll have to provide the server client ID, server client secret (listed in the Developer Console when you created the server client ID), and the auth code.
    2. See more details here: https://developers.google.com/identity/protocols/OAuth2WebServer?utm_campaign=play games_discussion_permissions_012316&utm_source=anddev&utm_medium=blog#handlingresponse
    3. No, really:  You should use a Google Apis Client Library to make this process easier.
  3. Once you have the access token, you can now call www.googleapis.com/games/v1/applications/<app_id>/verify/ using that access token.
    1. Pass the auth token in a header as follows:
      1. “Authorization: OAuth <access_token>”
    2. The response value will contain the player ID for the user. This is the correct player ID to use for this user.
    3. This access token can be used to make additional server-to-server calls as needed.