Posted by Michael Winser, Product Lead, Google Apps and Wesley Chun, Developer Advocate, Google Apps
Last week, we clarified the expectations and responsibilities when accessing Google user data via OAuth 2.0. Today, we’re announcing that in order to better protect users, we are increasing account security for enterprise Gmail users effective October 5, 2016. At this time, a new policy will take effect whereby users in a Google Apps domain, while changing their passwords on or after this date, will result in the revocation of the OAuth 2.0 tokens of apps that access their mailboxes using Gmail-based authorization scopes. Please note that users will not notice any specific changes on this date and their applications will continue to work. It is only when a user changes their password from that point moving forward that their Gmail-related tokens become invalid.
Developers should modify their applications to handle HTTP 400 or 401 error codes resulting from revoked tokens and prompt their users to go through the OAuth flow again to re-authorize those apps, such that they can access the user’s mailbox again (additional details below). Late last year, we announced a similar, planned change to our security policy that impacted a broader set of authorization scopes. We later decided not to move forward with that change for Apps customers and began working on a less impactful update as described above.
What is a revoked token?
A revoked OAuth 2.0 token no longer provides access to a user’s resources. Any attempt to use a revoked token in API calls will result in an error. Any existing token strings will no longer have any value and should be discarded. Applications accessing Google APIs should be modified to handle failed API calls.
Token revocation itself is not a new feature. Users have always been able to revoke access to applications in Security Checkup, and Google Apps admins have the ability to do the same in the Admin console. In addition, tokens that were not used for extended periods of time have always been subject to expiration or revocation. This change in our security policy will likely increase the rate of revoked tokens that applications see, since in some cases the process will now take place automatically.
What APIs and scopes are impacted?
To achieve the security benefits of this policy change with minimal admin confusion and end-user disruption, we’ve decided to limit its application to mail scopes only and to exclude Apps Script tokens. Apps installed via the Google Apps Marketplace are also not subject to the token revocation. Once this change is in effect, third-party mail apps like Apple Mail and Thunderbird―as well as other applications that use multiple scopes that include at least one mail scope―will stop accessing data upon password reset until a new OAuth 2.0 token has been granted. Your application will need to detect this scenario, notify the user that your application has lost access to their account data, and prompt them to go through the OAuth 2.0 flow again.
Mobile mail applications are also included in this policy change. For example, users who use the native mail application on iOS will have to re-authorize with their Google account credentials when their password has been changed. This new behavior for third-party mail apps on mobile aligns with the current behavior of the Gmail apps on iOS and Android, which also require re-authorization upon password reset.
How can I determine if my token was revoked?
Both short-lived access tokens and long-lived refresh tokens will be revoked when a user changes their password. Using a revoked access token to access an API or to generate a new access token will result in either HTTP 400 or 401 errors. If your application uses a library to access the API or handle the OAuth flow, then these errors will likely be thrown as exceptions. Consult the library’s documentation for information on how to catch these exceptions. NOTE: because HTTP 400 errors may be caused by a variety of reasons, expect the payload from a 400 due to a revoked token to be similar to the following:
{ "error_description": "Token has been revoked.", "error": "invalid_grant" }
How should my application handle revoked tokens?
This change emphasizes that token revocation should be considered a normal condition, not an error scenario. Your application should expect and detect the condition, and your UI should be optimized for restoring tokens.
To ensure that your application works correctly, we recommend doing the following:
If your application uses incremental authorization to accrue multiple scopes in the same token, you should track which features and scopes a given user has enabled. The end result is that if your app requested and obtained authorization for multiple scopes, and at least one of them is a mail scope, that token will be revoked, meaning you will need to prompt your user to re-authorize for all scopes originally granted.
Many applications use tokens to perform background or server-to-server API calls. Users expect this background activity to continue reliably. Since this policy change also affects those apps, this makes prompt notification requesting re-authorization even more important.
What is the timeline for this change?
To summarize, properly configured applications should be expected to handle invalid tokens in general, whether they be from expiration, non-existence, and revocation as normal conditions. We encourage developers to make any necessary changes to give their users the best experience possible. The policy change is planned to take effect on October 5, 2016.
Please see this Help Center article and FAQ for more details and the full list of mail scopes. Moving forward, any additional scopes to be added to the policy will be communicated in advance. We will provide those details as they become available.
Calling all developers: try our developer preview today!
As you can see from above, Android add-ons offer a great opportunity to build innovative integrations and reach Docs and Sheets users around the world. They’re basically Android apps that connect with Google Apps Script projects on the server-side, allowing them to access and manipulate data from Google Docs or Sheets using standard Apps Script techniques. Check out our documentation which includes UI guidelines as well as sample code to get you started. We’ve also made it easy for you to publish your apps with the Apps Script editor.
Android add-ons are available today as a developer preview. We look forward to seeing what you build!
Posted by Sumit Chatterjee, Product Manager, Google Mobile Management and Wesley Chun, Developer Advocate, Google Apps
As security on mobile devices becomes increasingly important, we want to give Google Apps administrators the visibility to monitor key activities occurring on managed devices in their domain. With that in mind, today we are launching Mobile Audit for Google Apps Unlimited users, which audits activities on iOS and Android devices (using the native iOS Sync and Android Sync protocols) managed by Google Mobile Management. Examples of activities that are recorded include: operating system updates, device settings changes, and mobile application installs, updates and removals.
Google Apps Administrators can also go to the Reports page in the Admin console to see all the Mobile Audit events. Below is an example of what this page looks like querying one user’s activity, including newly registered devices:
As part of this launch, we are excited to make all Mobile Audit events available programmatically through the Admin SDK Reports API via Mobile Audit Activity Events. The Admin SDK is accessible through its REST API or Google Apps Script. With this new feature, administrators will be able to query all of the Mobile Audit information by user, by device, or by event, as well as verify that all of your deployed devices are running the latest security updates. You can even send this information to an asset management system to track all of your mobile inventory.
For example, if we wanted to download all of the newly registered devices, you could make a call like this to the API (via Google Apps Script):
AdminReports.Activities.list('all', 'mobile', { eventName: "DEVICE_REGISTER_UNREGISTER_EVENT", filters: "ACCOUNT_STATE==REGISTERED", maxResults: 1000 });
Once you have the response from the API, you could easily parse through that data, including device models, serial numbers, user email addresses, and any other relevant information to help you track your inventory.
A more critical use case that mobile administrators have been asking for is a way to take proactive actions on devices without requiring manual intervention. For example, a mobile administrator may want to automatically block a device when something suspicious is detected. This new feature enables admins to write simple apps that do just that.
Using the Mobile Audit Activity Events in the Reports API and the Mobile Devices component of the Directory API, administrators can create scheduled scripts to check for devices that are reporting suspicious activity, and then take actions on those devices.
The Apps Script snippet below returns results if there are any mobile audit events for suspicious activity:
var response = AdminReports.Activities.list('all', 'mobile', { eventName: "SUSPICIOUS_ACTIVITY_EVENT", maxResults: 1000 });
If there are results, the code below shows you how to retrieve the unique Google identifier for those devices, via the “RESOURCE_ID” parameter. Note that this snippet only assumes a single device reporting suspicious activity—you’ll need to tweak the code to support more than one.
var parameters = response.items[0].events[0].parameters; var resourceId; for (i = 0; i < parameters.length; i++) { if (parameters[i].name == ‘RESOURCE_ID’) { resourceId = parameters[i].value; break; } }
Now the Directory API can be invoked to block the device that corresponds with the above unique identifier using this Apps Script snippet:
AdminDirectory.Mobiledevices.action('my_customer', resourceId, { action: "block" });
We are really excited to see what uses you have for Mobile Audit in the Reports API. For more information and to get started, please see the Developer Guide, reference documentation, and the Help Center. (NOTE: Again, this feature is available only to our customers with users on the Google Apps Unlimited license.)
// Launch user interface and allow user to select file IntentSender i = Drive.DriveApi .newOpenFileActivityBuilder() .setMimeType(new String[] { “text/plain” }) .build(mGoogleApiClient); startIntentSenderForResult(i, REQ_CODE_OPEN, null, 0, 0, 0);
What does the new Google+ Sign-In mean for your Drive app, and why should you use it?
All APIs can be authorized using the “Sign in with Google” button, including the Drive API. To authorize additional scopes, just pass them in the markup for the “Sign in with Google” button like we’ve done in this example.
<span class="g-signin" data-scope="https://www.googleapis.com/auth/drive.file">
The “Sign in with Google” button can cater to whatever kind of application you create: web, client, or mobile. Now you can choose the authorization flow you like and get a token using the OAuth 2.0 client-side flow or server flow. There are loads of features, and the button is highly customizable.
I’ve saved my favorite feature until the end: when the user authorizes an application on the web, the mobile version of the app can be installed over the air onto their mobile device. Just add your Android package name when you create the button like in this second example, and your app will be automagically installed.
<span class="g-signin" data-apppackagename="org.aliafshar.android.driveapp">
I know many of your Drive apps have mobile and web components, so this should be really useful for you. This helps you provide your users with a beautiful and seamless experience on all of their devices.
All-in-all, we think you’ll find these features useful and recommend that you use the Google+ Sign-In as the preferred way to authorize a user with the Google Drive API from inside a user interface. Check out how to get started with Google+ Sign-In in the language of your choice.
When we launched version 1.0 of Google Play services to all Android 2.2+ devices worldwide in September, one of our main goals was to provide developers with better tools for working with OAuth 2.0 tokens in their Android apps.
Thanks to the new components, Android apps can get access to Google APIs with an easy-to-use authentication flow and can provide a consistent experience to both their users and developers. We recently decided to test that statement by writing a small camera app that automatically uploads photos you take to your Google Drive account.
We documented all the steps required to go from zero to hero in a quickstart guide. By following the step-by-step instructions in the guide, you’ll have a working Android app that uses Google Play services to perform authorization and the Google Drive API to upload files to Drive.
Do you want to learn how to build this app but prefer to watch a video tutorial instead of reading the documentation? We’ve got you covered! Check out the recording of the Google Developers Live session that covers the setup and running of the quickstart app.
If you’re building an Android app that integrates with Drive and have questions for us, please don’t hesitate to let us know on Stack Overflow.
Though developers are quite comfortable thinking in abstractions, we still find a lot of value in code examples and fully developed sample applications. Judging by the volume of comments, tweets, and git checkouts of the Au-to-do sample code we released a few weeks ago, our readers agree.
For Google Apps API developers who want to get started writing mobile apps, here’s a great new resource: a sample application that integrates Google Calendar API v3 with Android and illustrates best practices for OAuth 2.0. This meeting scheduler app built by Alain Vongsouvanh gets the user’s contact list, lets users select attendees, and matches free/busy times to suggest available meeting times. It provides a simple Android UI for user actions such as attendee selection:
The sample code for Meeting Scheduler demonstrates many key aspects of developing with Google Apps APIs. After authorizing all required access using OAuth 2.0, the Meeting Scheduler makes requests to the Calendar API. For example, the class FreeBusyTimesRetriever queries the free/busy endpoint to find available meeting times:
FreeBusyTimesRetriever
FreeBusyRequest request = new FreeBusyRequest(); request.setTimeMin(getDateTime(startDate, 0)); request.setTimeMax(getDateTime(startDate, timeSpan)); for (String attendee : attendees) { requestItems.add(new FreeBusyRequestItem().setId(attendee)); } request.setItems(requestItems); FreeBusyResponse busyTimes; try { Freebusy.Query query = service.freebusy().query(request); // Use partial GET to retrieve only needed fields. query.setFields("calendars"); busyTimes = query.execute(); // ... } catch (IOException e) { // ... }
In this snippet above, note the use of a partial GET request. Calendar API v3, along with many other new Google APIs, provides partial response with GET and PATCH to retrieve or update only the data fields you need for a given request. Using these methods can help you streamline your code and conserve system resources.
For the full context of all calls that Meeting Scheduler makes, including OAuth 2.0 flow and the handling of expired access tokens, see Getting Started with the Calendar API v3 and OAuth 2.0 on Android. The project site provides all the source code and other resources, and there’s a related wiki page with important configuration details.
We hope you’ll have a look at the sample and let us know what you think in the Google Apps Calendar API forum. You’re welcome to create a clone of the source code and do some Meeting Scheduler development of your own. If you find a bug or have an idea for a feature, don’t hesitate to file it for evaluation.
AccountManager
GoogleAccountManager
GoogleAccountManager googleAccountManager = new GoogleAccountManager( activity); Account[] accounts = accountManager.getAccounts();
AccountManager.getAuthToken()
AccountManagerCallback
googleAccountManager.manager.getAuthToken(account, AUTH_TOKEN_TYPE, null, activity, new AccountManagerCallback<Bundle>() { public void run(AccountManagerFuture<Bundle> future) { try { // If the user has authorized your application to use the tasks API // a token is available. String token = future.getResult().getString( AccountManager.KEY_AUTHTOKEN); // Now you can use the Tasks API... useTasksAPI(token); } catch (OperationCanceledException e) { // TODO: The user has denied you access to the API, you // should handle that } catch (Exception e) { handleException(e); } } }, null);
AUTH_TOKEN_TYPE
String AUTH_TOKEN_TYPE = ”Manage your tasks”;
useTasksAPI(String accessToken) { // Setting up the Tasks API Service HttpTransport transport = AndroidHttp.newCompatibleTransport(); AccessProtectedResource accessProtectedResource = new GoogleAccessProtectedResource(accessToken); Tasks service = new Tasks(transport, accessProtectedResource, new JacksonFactory()); service.setKey(INSERT_YOUR_API_KEY); service.setApplicationName("Google-TasksSample/1.0"); // TODO: now use the service to query the Tasks API }
service