Tag Archives: signing

Games authentication adopting Google Sign-In API

Posted by Clayton Wilkinson, Developer Platform Engineer

Some changes are coming to Play Game Services in early 2017:

Changes to Google API Client building

In November, we announced an update to Google Sign-In API. Play Game Services is being updated to use Google Sign-In API for authentication. The advantages are:

  • Games and Sign-In in same client connection.
  • Single API for getting Auth code to send to backend servers.

This change unifies the Google Sign-in and the Games API Sign-in, so there are updates to how to build the Google API Client:

// Defaults to Games Lite scope, no server component
  GoogleSignInOptions gso = new
     GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN).build();

// OR for apps with a server component
   GoogleSignInOptions gso = new
     GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN)
         .requestServerAuthCode(SERVER_CLIENT_ID)
         .build();

// OR for developers who need real user Identity
  GoogleSignInOptions gso = new
     GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN)
         .requestEmail()
         .build();

// Build the api client.
     mApiClient = new GoogleApiClient.Builder(this)
                .addApi(Games.API)
                .addApi(Auth.GOOGLE_SIGN_IN_API, gso)
                .addConnectionCallbacks(this)
                .build();
    }

 @Override
    public void onConnected(Bundle connectionHint) {
        if (mApiClient.hasConnectedApi(Games.API)) {
            Auth.GoogleSignInApi.silentSignIn(mApiClient).setResultCallback(
                   new ResultCallback() {
                       @Override
                       public void onResult(GoogleSignInResult googleSignInResult) {
                           // In this case, we are sure the result is a success.
                           GoogleSignInAccount acct = 
                              googleSignInResult.getGoogleSignInAccount());
 
                          // For Games with a server, send the auth code to your server.
                          String serverAuthCode = signInAccount.getServerAuthCode();
 
                         // Use the API client as normal.
                        Player player = Games.API.getCurrentPlayer(mApiClient);
                       }
                   }
            );
        } else {
            onSignedOut();
        }
    }

Account creation within iOS is no longer supported

  • Currently, there is no support for new players to create a Play Games account on iOS. Additionally, the Google+ integration has been removed from iOS. As a result "social" APIs will return result codes indicating success, but return empty lists. This includes the "standard" UIs for leaderboards and multiplayer invitations.

Google+ is no longer integrated

  • Announced last year, Games is decoupled from Google+ during this transition. As a result the public APIs for getting connected players via circles stopped working, but the standard UIs for multiplayer invitations and social leaderboards continued to work. Starting from February 2017, the standard UIs will also not display the Social graph results as Google+ data becomes inaccessible. This will affect multiplayer games, social leaderboards, and gifts API on Android. The effect will be that these APIs will return successfully, but with an empty list of players.

List of APIs that are deprecated by removing Google+ integration (and their C++ equivalents):

  1. Games.Players.getPlayerSearchIntent()
  2. Games.Players.loadConnectedPlayers()
  3. Games.Players.loadInvitablePlayers()
  4. The value LeaderboardVariant.COLLECTION_SOCIAL
  5. Invitations.loadInvitations()
  6. RealtimeMultiplayer.getSelectOpponentsIntent()
  7. TurnBasedMultiplayer.getSelectOpponentsIntent()
  8. All methods in the Requests package.

We realize this is a large change, but moving forward Play Game Services are much better aligned with the rest of the Mobile platform from Google and will lead to better developer experience for Android game developers.

Understanding APK packaging in Android Studio 2.2

Posted by Wojtek Kaliciński, Android Developer Advocate

Android Studio 2.2 launched recently with many new and improved features. Some of the changes are easy to miss because they happened under the hood in the Android Gradle plugin, such as the newly rewritten integrated APK packaging and signing step.

APK Signature Scheme v2

With the introduction of the new APK Signature Scheme v2 in Android 7.0 Nougat, we decided to rewrite how assembling APKs works in the Android Gradle plugin. You can read all about the low-level technical details of v2 signatures in the documentation, but here's a quick tl;dr summary of the info you need as an Android app developer:

  • The cryptographic signature of the APK that is used to verify its integrity is now located immediately before the ZIP Central Directory.
  • The signature is computed and verified over the binary contents of the whole APK file, as opposed to decompressed file contents of each file in the archive in v1.
  • An APK can be signed by both v1 and v2 signatures at the same time, so it remains backwards compatible with previous Android releases.

Why introduce this change to how Android verifies APKs? Firstly, for enhanced security and extensibility of this new signing format, and secondly for performance - the new signatures take significantly less time to verify on the device (no need for costly decompression), resulting in faster app installation times.

The consequence of this new signing scheme, however, is that there are new constraints on the APK creation process. Since only uncompressed file contents were verified in v1, that allowed for quite a lot of modifications to be made after APK signing - files could be moved around or even recompressed. In fact, the zipalign tool which was part of the build process did exactly that - it was used to align ZIP entries on correct byte boundaries for improved runtime performance.

Because v2 signatures verify all bytes in the archive and not individual ZIP entries, running zipalign is no longer possible after signing. That's why compression, aligning and signing now happens in a single, integrated step of the build process.

If you have any custom tasks in your build process that involve tampering with or post-processing the APK file in any way, please make sure you disable them or you risk invalidating the v2 signature and thus making your APKs incompatible with Android 7.0 and above.

Should you choose to do signing and aligning manually (such as from the command line), we offer a new tool in the Android SDK, called apksigner, that provides both v1 and v2 APK signing and verification. Note that you need to run zipalign before running apksigner if you are using v2 signatures. Also remember the jarsigner tool from the JDK is not compatible with Android v2 signatures, so you can't use it to re-sign your APKs if you want to retain the v2 signature.

In case you want to disable adding v1 or v2 signatures when building with the Android Gradle plugin, you can add these lines to your signingConfig section in build.gradle:

v1SigningEnabled false
v2SigningEnabled false

Note: both signing schemes are enabled by default in Android Gradle plugin 2.2.

Release builds for smaller APKs

We took this opportunity when rewriting the packager to make some optimizations to the size of release APKs, resulting in faster downloads, smaller delta updates on the Play Store, and less wasted space on the device. Here are some of the changes we made:

  • Files in the archive are now sorted to minimize differences between APK builds.
  • All file timestamps and metadata are zeroed out.
  • Level 6 and level 9 compression is checked for all files in parallel and the optimal one is used, i.e. if L9 provides little benefit in terms of size, then L6 may be chosen for better performance
  • Native libraries are stored uncompressed and page aligned in the APK. This brings support for the android:extractNativeLibs="false" option from Android 6.0 Marshmallow and lets applications use less space on the device as well as generate smaller updates on the Play Store
  • Zopfli compression is not used to better support Play Store update algorithms. It is not recommended to recompress your APKs with Zopfli. Pre-optimizing individual resources such as PNG files in your projects is still fine and recommended.

These changes help make your releases as small as possible so that users can download and update your app even on a slower connection or on less capable devices. But what about debug builds?

Debug builds for installation speed

When developing apps you want to keep the iteration cycle fast - change code, build, and deploy on a connected device or emulator. Since Android Studio 2.0 we've been working to make all the steps as fast as possible. With Instant Run we're now able to update only the changed code and resources during runtime, while the new Emulator brings multi-processor support and faster ADB speeds for quicker APK transfer and installation. Build improvements can cut that time even further and in Android Studio 2.2 we're introducing incremental packaging and parallel compression for debug builds. Together with other features like selectively packaging resources for the target device density and ABI this will make your development even faster.

A word of caution: the APK files created for Instant Run or by invoking a debug build are not meant for distribution on the Play Store! They contain additional instrumentation code for Instant Run and are missing resources for device configurations other than the one that was connected when you started the build. Make sure you only distribute release versions of the APK which you can create using the Android Studio Generate Signed APK command or the assembleRelease Gradle task.

Using Google Sign-In with your server

Posted by Laurence Moroney, Developer Advocate

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. A third party could spoof your server with a fake email

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. Third Party Fake 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 JSON Web Token, as defined by RFC7519. 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.


 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. Even when requested, some fields may 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");  

Note that if you have an existing app using GoogleAuthUtil to get a token to pass to your backend, you should switch to the latest ID token validation libraries and mechanisms described above. We’ll describe recommendations for server-side best practices in a future post.

This post demonstrates how to use authentication technologies to ensure your user is who they claim they are. In the next post, we’ll cover using the Google Sign-In API for authorization, so that users can, for example, access Google services such as Google Drive from within your app and backend service.

You can learn more about authentication technologies from Google at the Google Identity Platform developers site.